4 #===============================================================================================================================
5 # Name : Résolution VF du système Euler 2D barotrope sans terme source
6 # \partial_t rho + \div q = 0
7 # \partial_t q + \div q\otimes q/rho + \grad p = 0
8 # Author : Michaël Ndjinga
9 # Copyright : CEA Saclay 2019
10 # Description : Propagation d'une onde de choc sphérique
11 # Utilisation du schéma de Roe upwind explicite ou implicite sur un maillage général
12 # Initialisation par une surpression sphérique
13 # Conditions aux limites parois
14 # Création et sauvegarde du champ résultant et des figures
15 #================================================================================================================================
24 p0=155e5#reference pressure in a pressurised nuclear vessel
25 c0=700.#reference sound speed for water at 155 bars, 600K
26 rho0=p0/c0*c0#reference density
29 def initial_conditions_shock(my_mesh, isCircle):
30 print( "Initial data : Spherical wave" )
31 dim = my_mesh.getMeshDimension()
32 nbCells = my_mesh.getNumberOfCells()
44 pressure_field = cdmath.Field("Pressure", cdmath.CELLS, my_mesh, 1)
45 velocity_field = cdmath.Field("Velocity", cdmath.CELLS, my_mesh, 3)
47 for i in range(nbCells):
48 velocity_field[i,0] = 0
49 velocity_field[i,1] = 0
50 velocity_field[i,2] = 0
52 x = my_mesh.getCell(i).x()
53 valX = (x - xcentre) * (x - xcentre)
58 y = my_mesh.getCell(i).y()
59 valY = (y - ycentre) * (y - ycentre)
60 val = sqrt(valX + valY)
62 y = my_mesh.getCell(i).y()
63 z = my_mesh.getCell(i).z()
64 valY = (y - ycentre) * (y - ycentre)
65 valZ = (z - zcentre) * (z - zcentre)
66 val = sqrt(valX + valY + valZ)
69 pressure_field[i] = p0
72 pressure_field[i] = p0/2
76 return pressure_field, velocity_field
79 def jacobianMatrices(normale,coeff,rho_l,q_l,rho_r,q_r):
80 RoeMat = cdmath.Matrix(3,3);
81 AbsRoeMa = cdmath.Matrix(3,3);
83 tangent=cdmath.Vector(2);
84 tangent[0]= normale[1];
85 tangent[1]=-normale[0];
89 if rho_l<0 or rho_r<0 :
90 print( "rho_l=",rho_l, " rho_r= ",rho_r )
91 raise ValueError("Negative density")
92 u = (u_l*sqrt(rho_l)+u_r*sqrt(rho_r))/(sqrt(rho_l)+sqrt(rho_r));
96 RoeMat[0,1] = normale[0]
97 RoeMat[0,2] = normale[1]
98 RoeMat[1,0] = c0*c0*normale[0] - un*u[0]
99 RoeMat[2,0] = c0*c0*normale[1] - un*u[1]
100 RoeMat[1,1] = un + normale[0]*u[0]
101 RoeMat[1,2] = normale[1]*u[0]
102 RoeMat[2,2] = un + normale[1]*u[1]
103 RoeMat[2,1] = normale[0]*u[1]
105 AbsRoeMa[0,0]=(abs(un-c0)*(un+c0)+abs(un+c0)*(c0-un))/(2*c0);
106 AbsRoeMa[0,1]= (abs(un+c0)-abs(un-c0))/(2*c0)*normale[0];
107 AbsRoeMa[0,2]= (abs(un+c0)-abs(un-c0))/(2*c0)*normale[1];
108 AbsRoeMa[1,0]=(abs(un-c0)*(un+c0)*(u[0]-c0*normale[0])-abs(un+c0)*(un-c0)*(u[0]+c0*normale[0]))/(2*c0)-abs(un)*(u*tangent)*tangent[0];
109 AbsRoeMa[2,0]=(abs(un-c0)*(un+c0)*(u[1]-c0*normale[1])-abs(un+c0)*(un-c0)*(u[1]+c0*normale[1]))/(2*c0)-abs(un)*(u*tangent)*tangent[1];
110 #subMatrix=(abs(un+c0)*((u-c0*normale)^normale)-abs(un-c0)*((u-c0*normale)^normale))/(2*c0)+abs(un)*(tangent^tangent);
111 AbsRoeMa[1,1]=(abs(un+c0)*((u[0]-c0*normale[0])*normale[0])-abs(un-c0)*((u[0]-c0*normale[0])*normale[0]))/(2*c0)+abs(un)*(tangent[0]*tangent[0]);#subMatrix[0,0];
112 AbsRoeMa[1,2]=(abs(un+c0)*((u[0]-c0*normale[0])*normale[1])-abs(un-c0)*((u[0]-c0*normale[0])*normale[1]))/(2*c0)+abs(un)*(tangent[0]*tangent[1]);#subMatrix[0,1];
113 AbsRoeMa[2,1]=(abs(un+c0)*((u[1]-c0*normale[1])*normale[0])-abs(un-c0)*((u[1]-c0*normale[1])*normale[0]))/(2*c0)+abs(un)*(tangent[1]*tangent[0]);
114 AbsRoeMa[2,2]=(abs(un+c0)*((u[1]-c0*normale[1])*normale[1])-abs(un-c0)*((u[1]-c0*normale[1])*normale[1]))/(2*c0)+abs(un)*(tangent[1]*tangent[1]);
116 return (RoeMat-AbsRoeMa)*coeff/2,un;
118 def computeDivergenceMatrix(my_mesh,implMat,Un):
119 nbCells = my_mesh.getNumberOfCells()
120 dim=my_mesh.getMeshDimension()
122 normal=cdmath.Vector(dim)
124 q_l=cdmath.Vector(2);
125 q_r=cdmath.Vector(2);
127 idMoinsJacCL=cdmath.Matrix(nbComp)
129 for j in range(nbCells):#On parcourt les cellules
130 Cj = my_mesh.getCell(j)
131 nbFaces = Cj.getNumberOfFaces();
133 for k in range(nbFaces) :
134 indexFace = Cj.getFacesId()[k];
135 Fk = my_mesh.getFace(indexFace);
136 for i in range(dim) :
137 normal[i] = Cj.getNormalVector(k, i);#normale sortante
140 if ( not Fk.isBorder()) :
141 # hypothese: La cellule d'index indexC1 est la cellule courante index j */
142 if (Fk.getCellsId()[0] == j) :
144 cellAutre = Fk.getCellsId()[1];
145 elif(Fk.getCellsId()[1] == j) :
146 # hypothese non verifiée
147 cellAutre = Fk.getCellsId()[0];
149 raise ValueError("computeFluxes: problem with mesh, unknown cell number")
151 q_l[0]=Un[j*nbComp+1]
152 q_l[1]=Un[j*nbComp+2]
153 q_r[0]=Un[cellAutre*nbComp+1]
154 q_r[1]=Un[cellAutre*nbComp+2]
155 Am, un=jacobianMatrices( normal,Fk.getMeasure()/Cj.getMeasure(),Un[j*nbComp],q_l,Un[cellAutre*nbComp],q_r);
157 implMat.addValue(j*nbComp,cellAutre*nbComp,Am)
158 implMat.addValue(j*nbComp, j*nbComp,Am*(-1.))
160 if( Fk.getGroupName() != "Periodic" and Fk.getGroupName() != "Neumann"):#Wall boundary condition unless Periodic/Neumann specified explicitly
161 q_l[0]=Un[j*nbComp+1]
162 q_l[1]=Un[j*nbComp+2]
163 q_r=q_l-normal*2*(q_l*normal)
164 Am, un=jacobianMatrices( normal,Fk.getMeasure()/Cj.getMeasure(),Un[j*nbComp],q_l,Un[j*nbComp],q_r);
166 v=cdmath.Vector(dim+1)
167 for i in range(dim) :
169 idMoinsJacCL=v.tensProduct(v)*2
171 implMat.addValue(j*nbComp,j*nbComp,Am*(-1.)*idMoinsJacCL)
173 elif( Fk.getGroupName() == "Periodic"):#Periodic boundary condition
174 q_l[0]=Un[j*nbComp+1]
175 q_l[1]=Un[j*nbComp+2]
176 q_r[0]=Un[cellAutre*nbComp+1]
177 q_r[1]=Un[cellAutre*nbComp+2]
178 Am, un=jacobianMatrices( normal,Fk.getMeasure()/Cj.getMeasure(),Un[j*nbComp],Un[j*nbComp+1],Un[j*nbComp+2],Un[cellAutre*nbComp],Un[cellAutre*nbComp+1],Un[cellAutre*nbComp+2]);
180 indexFP=my_mesh.getIndexFacePeriodic(indexFace)
181 Fp = my_mesh.getFace(indexFP)
182 cellAutre = Fp.getCellsId()[0]
184 implMat.addValue(j*nbComp,cellAutre*nbComp,Am)
185 implMat.addValue(j*nbComp, j*nbComp,Am*(-1.))
186 elif(Fk.getGroupName() != "Neumann"):#Nothing to do for Neumann boundary condition
187 print( Fk.getGroupName() )
188 raise ValueError("computeFluxes: Unknown boundary condition name");
190 maxAbsEigVa = max(maxAbsEigVa,abs(un+c0),abs(un-c0));
194 def EulerSystemVF(ntmax, tmax, cfl, my_mesh, output_freq, filename,resolution, isImplicit):
195 dim=my_mesh.getMeshDimension()
196 nbCells = my_mesh.getNumberOfCells()
197 meshName=my_mesh.getName()
203 nbVoisinsMax=my_mesh.getMaxNbNeighbours(cdmath.CELLS)
205 # Initial conditions #
206 print("Construction of the initial condition …")
207 if(dim==1 or filename.find("square")>-1 or filename.find("Square")>-1 or filename.find("cube")>-1 or filename.find("Cube")>-1):
208 pressure_field, velocity_field = initial_conditions_shock(my_mesh,False)
209 elif(filename.find("disk")>-1 or filename.find("Disk")>-1):
210 pressure_field, velocity_field = initial_conditions_shock(my_mesh,True)
212 print( "Mesh name : ", filename )
213 raise ValueError("Mesh name should contain substring square, cube or disk")
216 Un =cdmath.Vector(nbCells*(dim+1))
217 dUn=cdmath.Vector(nbCells*(dim+1))
219 for k in range(nbCells):
220 Un[k*(dim+1)+0] = pressure_field[k]
221 Un[k*(dim+1)+1] =rho0*velocity_field[k,0]
223 Un[k + 2*nbCells] = rho0*velocity_field[k,1] # value on the bottom face
225 Un[k + 3*nbCells] = rho0*initial_velocity[k,2]
227 #sauvegarde de la donnée initiale
228 pressure_field.setTime(time,it);
229 pressure_field.writeVTK("EulerSystem"+str(dim)+"DUpwind"+"_isImplicit"+str(isImplicit)+meshName+"_pressure");
230 velocity_field.setTime(time,it);
231 velocity_field.writeVTK("EulerSystem"+str(dim)+"DUpwind"+"_isImplicit"+str(isImplicit)+meshName+"_velocity");
233 dx_min=my_mesh.minRatioVolSurf()
235 divMat=cdmath.SparseMatrixPetsc(nbCells*(1+dim),nbCells*(1+dim),(nbVoisinsMax+1)*(1+dim))
238 LS=cdmath.LinearSolver(divMat,Un,iterGMRESMax, precision, "GMRES","ILU")
240 print("Starting computation of the linear wave system with an explicit UPWIND scheme …")
243 while (it<ntmax and time <= tmax and not isStationary):
244 divMat.zeroEntries()#sets the matrix coefficients to zero
245 vp_max=computeDivergenceMatrix(my_mesh,divMat,Un)#To update at each time step
246 dt = cfl * dx_min / vp_max#To update at each time step
249 #Adding the identity matrix on the diagonal
250 divMat.diagonalShift(1)#only after filling all coefficients
254 if(not LS.getStatus()):
255 print( "Linear system did not converge ", LS.getNumberOfIter(), " GMRES iterations")
256 raise ValueError("Pas de convergence du système linéaire");
267 if(it%output_freq==0 or it>=ntmax or isStationary or time >=tmax):
268 print("-- Iter: " + str(it) + ", Time: " + str(time) + ", dt: " + str(dt))
270 for k in range(nbCells):
271 pressure_field[k] =Un[k*(dim+1)+0]
272 velocity_field[k,0]=Un[k*(dim+1)+1]/rho0
274 velocity_field[k,1]=Un[k*(dim+1)+2]/rho0
276 velocity_field[k,2]=Un[k*(dim+1)+3]/rho0
278 pressure_field.setTime(time,it);
279 pressure_field.writeVTK("EulerSystem"+str(dim)+"DUpwind"+"_isImplicit"+str(isImplicit)+meshName+"_pressure",False);
280 velocity_field.setTime(time,it);
281 velocity_field.writeVTK("EulerSystem"+str(dim)+"DUpwind"+"_isImplicit"+str(isImplicit)+meshName+"_velocity",False);
283 print("-- Iter: " + str(it) + ", Time: " + str(time) + ", dt: " + str(dt))
287 print( "Nombre de pas de temps maximum ntmax= ", ntmax, " atteint")
289 print( "Régime stationnaire atteint au pas de temps ", it, ", t= ", time)
291 pressure_field.setTime(time,0);
292 pressure_field.writeVTK("EulerSystem"+str(dim)+"DUpwind"+"_isImplicit"+str(isImplicit)+meshName+"_pressure_Stat");
293 velocity_field.setTime(time,0);
294 velocity_field.writeVTK("EulerSystem"+str(dim)+"DUpwind"+"_isImplicit"+str(isImplicit)+meshName+"_velocity_Stat");
296 #Postprocessing : Extraction of the diagonal data
297 diag_data_press=VTK_routines.Extract_field_data_over_line_to_numpyArray(pressure_field,[0,1,0],[1,0,0], resolution)
298 diag_data_vel =VTK_routines.Extract_field_data_over_line_to_numpyArray(velocity_field,[0,1,0],[1,0,0], resolution)
299 #Postprocessing : save 2D picture
300 PV_routines.Save_PV_data_to_picture_file("EulerSystem"+str(dim)+"DUpwind"+"_isImplicit"+str(isImplicit)+meshName+"_pressure_Stat"+'_0.vtu',"Pressure",'CELLS',"EulerSystem"+str(dim)+"DUpwind"+meshName+"_pressure_Stat")
301 PV_routines.Save_PV_data_to_picture_file("EulerSystem"+str(dim)+"DUpwind"+"_isImplicit"+str(isImplicit)+meshName+"_velocity_Stat"+'_0.vtu',"Velocity",'CELLS',"EulerSystem"+str(dim)+"DUpwind"+meshName+"_velocity_Stat")
304 print( "Temps maximum Tmax= ", tmax, " atteint")
307 def solve(my_mesh,filename,resolution, isImplicit):
308 print( "Resolution of the Euler system in dimension ", my_mesh.getSpaceDimension())
309 if( my_mesh.getSpaceDimension()!=2):
310 raise ValueError("Only dimension 2 simulations allowed")
311 print( "Numerical method : upwind")
312 print( "Initial data : spherical wave")
313 print( "Wall boundary conditions")
314 print( "Mesh name : ",filename , my_mesh.getNumberOfCells(), " cells")
319 cfl = 1./my_mesh.getSpaceDimension()
322 EulerSystemVF(ntmax, tmax, cfl, my_mesh, output_freq, filename,resolution, isImplicit)
324 def solve_file( filename,resolution, isImplicit):
325 my_mesh = cdmath.Mesh(filename+".med")
326 solve(my_mesh, filename,resolution, isImplicit)
328 if __name__ == """__main__""":
329 if len(sys.argv) >2 :
331 isImplicit=bool(int(sys.argv[2]))
332 my_mesh = cdmath.Mesh(filename)
333 solve(my_mesh,filename,100, isImplicit)
335 raise ValueError("EulerSystemUpwind.py expects a mesh file name and a boolean (isImplicit)")