2 #===============================================================================================================================
3 # Name : Résolution EF de l'équation de Poisson 2D -\triangle u = 0 sur un carré avec conditions aux limites de Dirichlet discontinues
4 # Author : Michaël Ndjinga
5 # Copyright : CEA Saclay 2019
6 # Description : Utilisation de la méthode des éléménts finis P1 avec champs u et f discrétisés aux noeuds d'un maillage triangulaire
7 # Création et sauvegarde du champ résultant ainsi que du champ second membre en utilisant la librairie CDMATH
8 # Comparaison de la solution numérique avec la solution exacte u=atan((x*x-y*y)/(2*x*y))
9 #================================================================================================================================
12 from math import atan, pi
18 test_desc["Initial_data"]="None"
19 test_desc["Boundary_conditions"]="Dirichlet"
20 test_desc["Global_name"]="FE simulation of the 2D Poisson equation"
21 test_desc["Global_comment"]="Condition limite discontinue, Maillage triangulaire"
22 test_desc["PDE_model"]="Poisson"
23 test_desc["PDE_is_stationary"]=True
24 test_desc["PDE_search_for_stationary_solution"]=False
25 test_desc["Numerical_method_name"]="P1 FE"
26 test_desc["Numerical_method_space_discretization"]="Finite elements"
27 test_desc["Numerical_method_time_discretization"]="None"
28 test_desc["Mesh_is_unstructured"]=True
29 test_desc["Geometry"]="Square"
30 test_desc["Part_of_mesh_convergence_analysis"]=True
32 # Fonction qui remplace successivement les colonnes d'une matrices par un vecteur donné et retourne la liste des déterminants
33 def gradientNodal(M, values):
34 matrices=[0]*(len(values)-1)
35 for i in range(len(values)-1):
36 matrices[i] = M.deepCopy()
37 for j in range(len(values)):
38 matrices[i][j,i] = values[j]
40 result = cdmath.Vector(len(values)-1)
41 for i in range(len(values)-1):
42 result[i] = matrices[i].determinant()
46 def solve(filename,resolution,meshName, testColor):
48 test_desc["Mesh_name"]=meshName
49 test_desc["Test_color"]=testColor
51 #Chargement du maillage triangulaire du carré unité [0,1]x[0,1]
52 #================================================================
53 my_mesh = cdmath.Mesh(filename+".med")
54 if( my_mesh.getSpaceDimension()!=2 or my_mesh.getMeshDimension()!=2) :
55 raise ValueError("Wrong space or mesh dimension : space and mesh dimensions should be 2")
56 if(not my_mesh.isTriangular()) :
57 raise ValueError("Wrong cell types : mesh is not made of triangles")
59 nbNodes = my_mesh.getNumberOfNodes()
60 nbCells = my_mesh.getNumberOfCells()
62 test_desc["Space_dimension"]=my_mesh.getSpaceDimension()
63 test_desc["Mesh_dimension"]=my_mesh.getMeshDimension()
64 test_desc["Mesh_number_of_elements"]=my_mesh.getNumberOfNodes()
65 test_desc["Mesh_cell_type"]=my_mesh.getElementTypesNames()
67 print("Mesh loading done")
68 print("Number of nodes=", nbNodes)
69 print("Number of cells=", nbCells)
71 #Détermination des noeuds intérieurs
72 #===================================
73 my_ExactSol = cdmath.Field("Exact_field", cdmath.NODES, my_mesh, 1)
76 maxNbNeighbours = 0#This is to determine the number of non zero coefficients in the sparse finite element rigidity matrix
81 #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
82 for i in range(nbNodes):
87 #Robust calculation of atan((x*x-y*y)/(2*x*y))
88 if x<-eps or x>1+eps or y<-eps or y>1+eps :
89 print("!!! Warning Mesh ", meshName," !!! Node is not in the unit square.",", eps=",eps, ", x= ",x, ", y= ",y)
90 #raise ValueError("!!! Domain should be the unit square.")
92 my_ExactSol[i] = atan((x*x-y*y)/(2*x*y))
96 my_ExactSol[i] = -pi/2
100 if my_mesh.isBorderNode(i): # Détection des noeuds frontière
101 boundaryNodes.append(i)
102 nbBoundaryNodes=nbBoundaryNodes+1
103 else: # Détection des noeuds intérieurs
104 interiorNodes.append(i)
105 nbInteriorNodes=nbInteriorNodes+1
106 maxNbNeighbours= max(1+Ni.getNumberOfCells(),maxNbNeighbours) #true only in 2D, need a function Ni.getNumberOfNeighbourNodes()
108 test_desc["Mesh_max_number_of_neighbours"]=maxNbNeighbours
110 print("Right hand side discretisation done")
111 print("Number of interior nodes=", nbInteriorNodes)
112 print("Number of boundary nodes=", nbBoundaryNodes)
113 print("Max number of neighbours=", maxNbNeighbours)
115 # Construction de la matrice de rigidité et du vecteur second membre du système linéaire
116 #=======================================================================================
117 Rigidite=cdmath.SparseMatrixPetsc(nbInteriorNodes,nbInteriorNodes,maxNbNeighbours) # warning : third argument is maximum number of non zero coefficients per line of the matrix
118 RHS=cdmath.Vector(nbInteriorNodes)
121 # Vecteurs gradient de la fonction de forme associée à chaque noeud d'un triangle (hypothèse 2D)
122 GradShapeFunc0=cdmath.Vector(2)
123 GradShapeFunc1=cdmath.Vector(2)
124 GradShapeFunc2=cdmath.Vector(2)
126 #On parcourt les triangles du domaine
127 for i in range(nbCells):
129 Ci=my_mesh.getCell(i)
131 #Contribution à la matrice de rigidité
132 nodeId0=Ci.getNodeId(0)
133 nodeId1=Ci.getNodeId(1)
134 nodeId2=Ci.getNodeId(2)
136 N0=my_mesh.getNode(nodeId0)
137 N1=my_mesh.getNode(nodeId1)
138 N2=my_mesh.getNode(nodeId2)
150 #Values of each shape function at each node
155 GradShapeFunc0 = gradientNodal(M,values0)/2
156 GradShapeFunc1 = gradientNodal(M,values1)/2
157 GradShapeFunc2 = gradientNodal(M,values2)/2
159 #Création d'un tableau (numéro du noeud, gradient de la fonction de forme)
160 GradShapeFuncs={nodeId0 : GradShapeFunc0}
161 GradShapeFuncs[nodeId1]=GradShapeFunc1
162 GradShapeFuncs[nodeId2]=GradShapeFunc2
165 # Remplissage de la matrice de rigidité et du second membre
166 for j in [nodeId0,nodeId1,nodeId2] :
167 if boundaryNodes.count(j)==0 : #seuls les noeuds intérieurs contribuent au système linéaire (matrice de rigidité et second membre)
168 j_int=interiorNodes.index(j)#indice du noeud j en tant que noeud intérieur
169 #Pas de contribution au second membre car pas de terme source
170 boundaryContributionAdded=False#Needed in case j is a border cell
171 #Contribution de la cellule triangulaire i à la ligne j_int du système linéaire
172 for k in [nodeId0,nodeId1,nodeId2] :
173 if boundaryNodes.count(k)==0 : #seuls les noeuds intérieurs contribuent à la matrice du système linéaire
174 k_int=interiorNodes.index(k)#indice du noeud k en tant que noeud intérieur
175 Rigidite.addValue(j_int,k_int,GradShapeFuncs[j]*GradShapeFuncs[k]/Ci.getMeasure())
176 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
177 if boundaryNodes.count(nodeId0)!=0 :
178 u0=my_ExactSol[nodeId0]
181 if boundaryNodes.count(nodeId1)!=0 :
182 u1=my_ExactSol[nodeId1]
185 if boundaryNodes.count(nodeId2)!=0 :
186 u2=my_ExactSol[nodeId2]
189 boundaryContributionAdded=True#Contribution from the boundary to matrix line j is done in one step
190 GradGh = gradientNodal(M,[u0,u1,u2])/2
191 RHS[j_int] += -(GradGh*GradShapeFuncs[j])/Ci.getMeasure()
193 print("Linear system matrix building done")
195 # Résolution du système linéaire
196 #=================================
197 LS=cdmath.LinearSolver(Rigidite,RHS,100,1.E-6,"CG","LU")#Remplacer CG par CHOLESKY pour solveur direct
198 LS.setComputeConditionNumber()
201 print( "Preconditioner used : ", LS.getNameOfPc() )
202 print( "Number of iterations used : ", LS.getNumberOfIter() )
203 print("Linear system solved")
205 test_desc["Linear_solver_algorithm"]=LS.getNameOfMethod()
206 test_desc["Linear_solver_preconditioner"]=LS.getNameOfPc()
207 test_desc["Linear_solver_precision"]=LS.getTolerance()
208 test_desc["Linear_solver_maximum_iterations"]=LS.getNumberMaxOfIter()
209 test_desc["Linear_system_max_actual_iterations_number"]=LS.getNumberOfIter()
210 test_desc["Linear_system_max_actual_error"]=LS.getResidu()
211 test_desc["Linear_system_max_actual_condition number"]=LS.getConditionNumber()
214 # Création du champ résultat
215 #===========================
216 my_ResultField = cdmath.Field("ResultField", cdmath.NODES, my_mesh, 1)
217 for j in range(nbInteriorNodes):
218 my_ResultField[interiorNodes[j]]=SolSyst[j];#remplissage des valeurs pour les noeuds intérieurs
219 for j in range(nbBoundaryNodes):
220 my_ResultField[boundaryNodes[j]]=my_ExactSol[boundaryNodes[j]];#remplissage des valeurs pour les noeuds frontière (condition limite)
221 #sauvegarde sur le disque dur du résultat dans un fichier paraview
222 my_ResultField.writeVTK("FiniteElements2DPoissonStiffBC_SQUARE_"+meshName+str(nbNodes))
224 print("Numerical solution of 2D Poisson equation on a square using finite elements done")
228 #Calcul de l'erreur commise par rapport à la solution exacte
229 #===========================================================
230 l2_norm_sol_exacte=my_ExactSol.normL2()[0]
231 l2_error = (my_ExactSol - my_ResultField).normL2()[0]
233 print("L2 relative error = norm( exact solution - numerical solution )/norm( exact solution ) = ", l2_error/l2_norm_sol_exacte)
234 print("Maximum numerical solution = ", my_ResultField.max(), " Minimum numerical solution = ", my_ResultField.min())
235 print("Maximum exact solution = ", my_ExactSol.max(), " Minimum exact solution = ", my_ExactSol.min())
239 # Extraction of the diagonal data
240 diag_data=VTK_routines.Extract_field_data_over_line_to_numpyArray(my_ResultField,[0,0,0],[1,1,0], resolution)
242 PV_routines.Save_PV_data_to_picture_file("FiniteElements2DPoissonStiffBC_SQUARE_"+meshName+str(nbNodes)+'_0.vtu',"ResultField",'NODES',"FiniteElements2DPoissonStiffBC_SQUARE_"+meshName+str(nbNodes))
244 test_desc["Computational_time_taken_by_run"]=end-start
245 test_desc["Absolute_error"]=l2_error
246 test_desc["Relative_error"]=l2_error/l2_norm_sol_exacte
248 with open('test_PoissonStiffBC'+str(my_mesh.getMeshDimension())+'D_EF_'+"SQUARE_"+meshName+str(nbCells)+ "Cells.json", 'w') as outfile:
249 json.dump(test_desc, outfile)
251 return l2_error/l2_norm_sol_exacte, nbNodes, diag_data, my_ResultField.min(), my_ResultField.max(), end - start
253 if __name__ == """__main__""":
254 solve("squareWithTriangles",100,"Unstructured_triangles","Green")