1+ import numpy as np
2+ from dolfin import *
3+ from mshr import *
4+ from baseflow import navier_stokes , make_pipe_mesh , get_marker_ids
5+ from os import path
6+
7+
8+ def main (case , delta_p ):
9+ print ('Running case: ' + case )
10+
11+ if case .find ('poise' )> - 1 :
12+ print ('Running poise case' )
13+ # Define parameters
14+ p_in = 2.0
15+ p_out = 1.0
16+ nu = 1.0
17+ rho = 1.0
18+ radius = 1
19+ length = 5
20+ N = 10 # Resolution for mesh generation
21+
22+
23+ # Make pipe mesh so we can solve for Poiseuille flow
24+ mesh , boundaries = make_pipe_mesh (radius , N )
25+ inflow_marker = [2 ]
26+ outflow_marker = [3 ]
27+ no_slip_marker = [1 ]
28+
29+ # Compute max velocity, Reynolds number and check ratio between length and radius of pipe
30+ U_max = (p_in - p_out ) * radius ** 2 / (length * nu * rho * 4 )
31+ Re = U_max * radius / nu
32+ ratio = length / radius
33+ if ratio <= 1 / 48 * Re :
34+ print (ratio , 1 / 48 * Re , Re )
35+ print ("Ratio = length / radius must be larger than 1/48*Re for the Hagen–Poiseuille law to be valid." )
36+ exit ()
37+
38+ results_folder = 'aneurysm/Eigenmodes/poiseuille/'
39+
40+ print ("Reynolds number: {:.3f}" .format (Re ))
41+
42+ else :
43+
44+ # Get artery mesh
45+ case_names = ["C0015_healthy" , "C0015_terminal" , "C0019" , "C0065_healthy" , "C0065_saccular" ]
46+ case = int (case )
47+
48+ print ('Running artery case ' + case_names [case ])
49+ mesh_name = path .join ("models" , case_names [case ] + ".xml.gz" )
50+ mesh = Mesh ('aneurysm/' + mesh_name )
51+ boundaries = MeshFunction ("size_t" , mesh , mesh .geometry ().dim () - 1 , mesh .domains ())
52+ inflow_marker , outflow_marker , no_slip_marker = get_marker_ids (case )
53+
54+ # Rescale mesh from mm to m
55+ coords = mesh .coordinates ()
56+ coords *= 1.0e-3
57+
58+ nu = 3.0e-6 # kinematic visc of blood is 3 cP
59+ mmHg = 133.0
60+ p_in = delta_p * mmHg
61+ p_out = 0.0
62+
63+ results_folder = 'aneurysm/Eigenmodes/' + case_names [case ] + '/' + str (int (delta_p )) + 'mmHg/'
64+
65+ print ('Results will be stored in ' + results_folder )
66+
67+
68+ # Check if baseflow is already computed
69+ import os as os
70+ str_match = max ([ffile .find ('u0' ) for ffile in os .listdir (results_folder )])
71+ baseflow_file_exists = (str_match > - 1 )
72+
73+ if baseflow_file_exists :
74+ print ('Using previously computed baseflow' )
75+ ## Set up mesh
76+ mesh = Mesh ()
77+ h5 = HDF5File (mesh .mpi_comm (), results_folder + 'mesh.h5' , 'r' )
78+ h5 .read (mesh , '/mesh' , False )
79+
80+ P2 = VectorFunctionSpace (mesh , 'CG' , 2 , 3 )
81+ P1 = FunctionSpace (mesh , 'CG' , 1 )
82+
83+ uf = HDF5File (mesh .mpi_comm (), results_folder + 'u0.h5' , "r" )
84+ u0 = Function (P2 )
85+ uf .read (u0 , "/u" )
86+ uf .close ()
87+
88+ else :
89+ print ('Computing baseflow' )
90+ u0 , p = navier_stokes (mesh , boundaries , nu , p_in , p_out , inflow_marker , outflow_marker , no_slip_marker )
91+
92+ file = HDF5File (mesh .mpi_comm (), results_folder + 'mesh.h5' , "w" )
93+ file .write (p .function_space ().mesh (), "/mesh" )
94+ file .close ()
95+
96+ file = HDF5File (mesh .mpi_comm (), results_folder + 'u0.h5' , "w" )
97+ file .write (u0 , "/u" )
98+ file .close ()
99+
100+ file = HDF5File (mesh .mpi_comm (), results_folder + 'p0.h5' , "w" )
101+ file .write (p , "/p" )
102+ file .close ()
103+
104+
105+
106+ File (results_folder + 'baseflow.pvd' ) << interpolate (u0 , VectorFunctionSpace (mesh , 'CG' , 1 , 3 ))
107+
108+ ## Setup eigenvalue matrices and solver
109+ print ('Setting up eigenvalue problem, storing results in ' + results_folder )
110+ A , B , W = get_eigenvalue_matrices (mesh , nu , u_init = u0 )
111+
112+ E = setup_slepc_solver (A , B , target_eigvalue = - 1.0e-5 , max_it = 5 , n_eigvals = 6 )
113+
114+ # Solve for eigenvalues
115+ print ('Solving eigenvalue problem' )
116+ import time
117+ tic = time .perf_counter ()
118+ E .solve ()
119+ toc = time .perf_counter ()
120+ print ('Done solving, process took %4.0f s' % float (toc - tic ))
121+
122+ # Write the results to terminal and a .text file
123+ eigvalue_results = results_folder + 'Eigenmodes'
124+
125+ nev , ncv , mpd = E .getDimensions () # number of requested eigenvalues and Krylov vectors
126+ nconv = E .getConverged ()
127+
128+ print ('****** nu:' , nu , '******' )
129+
130+ lines = []
131+ lines .append ('Parameters: nu %1.4f' % (nu ))
132+ lines .append ("Number of iterations of the method: %d" % E .getIterationNumber ())
133+ lines .append ("Number of requested eigenvalues: %d" % nev )
134+ lines .append ("Stopping condition: tol=%.4g, maxit=%d" % E .getTolerances ())
135+ lines .append ("Number of converged eigenpairs: %d" % nconv )
136+ lines .append ("Solution method: %s" % E .getType ())
137+ lines .append ('Solver time: %0.1fs' % float (toc - tic ))
138+
139+ lines .append (' ' )
140+
141+ lines .append (" lambda (residual) lambda_num lamda_den" )
142+ lines .append ("---------------------------------------------------\n " )
143+
144+ with open (eigvalue_results , "w" ) as ffile :
145+ ffile .write ('\n ' .join (lines ))
146+ print ('\n ' .join (lines ))
147+
148+ file_u , file_p , file_e = (File (results_folder + name + '.pvd' )
149+ for name in ('eigvecs' , 'eigpressures' , 'eigvals' ))
150+
151+ if nconv == 0 :
152+ with open (eigvalue_results , "w+" ) as ffile :
153+ ffile .write ('No converged values :( ' )
154+ if nconv > 0 :
155+ # Create the results vectors
156+ vr , wr = A .getVecs ()
157+ vi , wi = A .getVecs ()
158+
159+ for i in range (nconv ):
160+ k = E .getEigenpair (i , vr , vi )
161+ lamda = 1.0 / k .real
162+
163+ u_r , u_im = Function (W ), Function (W )
164+ E .getEigenpair (i , u_r .vector ().vec (), u_im .vector ().vec ())
165+ u , p , c = u_r .split ()
166+
167+ # Store eigenmode
168+ fFile = HDF5File (MPI .comm_world , results_folder + 'eigvecs' + '_n' + str (i ) + ".h5" , "w" )
169+ fFile .write (u , "/f" )
170+ fFile .close ()
171+
172+ fFile = HDF5File (MPI .comm_world , results_folder + 'eigenmode' + '_n' + str (i ) + ".h5" , "w" )
173+ fFile .write (u_r , "/f" )
174+ fFile .close ()
175+
176+ # Plot eigenvector and corresponding eigenvalue
177+ u .rename ('eigvec' , '0.0' )
178+ file_u << (u , float (i ))
179+
180+ p .rename ('eigpressure' , '0.0' )
181+ file_p << (p , float (i ))
182+
183+ lamda_i = interpolate (Constant (lamda ), W .sub (2 ).collapse ())
184+ lamda_i .rename ('eigval' , '0.0' )
185+ file_e << (lamda_i , float (i ))
186+
187+ if k .imag != 0.0 :
188+ line = " %9f%+9f j" % (1.0 / k .real , 1.0 / k .imag )
189+ else :
190+ line = " %12f" % (1.0 / k .real )
191+
192+ print (line )
193+ with open (eigvalue_results , "a" ) as ffile :
194+ ffile .write (line + '\n ' )
195+
196+
197+ def create_mesh (radius , length , N ):
198+ cylinder = Cylinder (Point (0 , 0 , 0 ), Point (length , 0 , 0 ), radius , radius )
199+ mesh = generate_mesh (cylinder , N )
200+ return mesh
201+
202+
203+ def setup_slepc_solver (A , B , target_eigvalue , max_it , n_eigvals ):
204+ ## Solve eigenvalue problem using slepc
205+ from slepc4py import SLEPc
206+ E = SLEPc .EPS ()
207+ E .create ()
208+
209+ # A and B are both symmetric, but B is singular
210+ # For this reason, the problem is a Generalized Indefinite Eigenvalue Problem (GIEP)
211+ E .setOperators (A , B )
212+ E .setProblemType (SLEPc .EPS .ProblemType .GNHEP )
213+
214+ # Specify solvers and target values
215+ E .setType (SLEPc .EPS .Type .KRYLOVSCHUR )
216+ E .setWhichEigenpairs (E .Which .TARGET_MAGNITUDE )
217+ E .setTarget (target_eigvalue )
218+
219+ E .setFromOptions ()
220+
221+ st = E .getST ()
222+ st .setType ('sinvert' )
223+
224+ # Since B is singular, we need to change the linear solver to avoid zero pivots
225+ ksp = st .getKSP ()
226+ ksp .setType ('gmres' )
227+ pc = ksp .getPC ()
228+ pc .setType ('hypre' )
229+ pc .setFactorSolverType ('mumps' )
230+
231+ E .setTolerances (tol = 1.0e-8 , max_it = max_it )
232+
233+ n_krvecs = int (3.0 * n_eigvals )
234+
235+ E .setDimensions (n_eigvals , n_krvecs )
236+ return E
237+
238+
239+ def D (u ):
240+ gradu = grad (u )
241+ return gradu + gradu .T
242+
243+
244+ def get_eigenvalue_matrices (mesh , nu , u_init ):
245+ dim = np .shape (mesh .coordinates ())[1 ]
246+ if dim == 2 : zero = Constant ((0.0 , 0.0 ))
247+ if dim == 3 : zero = Constant ((0.0 , 0.0 , 0.0 ))
248+
249+ # Make a mixed space
250+ P2 = VectorElement ("CG" , mesh .ufl_cell (), 2 )
251+ P1 = FiniteElement ("CG" , mesh .ufl_cell (), 1 )
252+ LM = FiniteElement ('R' , mesh .ufl_cell (), 0 )
253+
254+ TH = MixedElement ([P2 , P1 , LM ])
255+ W = FunctionSpace (mesh , TH )
256+
257+ # Set up boundary conditions for space
258+ def all_boundaries (x , on_boundary ):
259+ return on_boundary
260+
261+ bc = DirichletBC (W .sub (0 ), zero , all_boundaries )
262+
263+ # Define variational problem
264+ (u , p , c ) = TrialFunctions (W )
265+ (v , q , d ) = TestFunctions (W )
266+
267+ a = EIG_A_cyl (u , p , c , v , q , d , nu )
268+
269+ V = W .sub (0 ).collapse ()
270+ u_zero_i = interpolate (u_init , V )
271+
272+ b = EIG_B_cyl (u , p , c , v , q , d , u_zero_i )
273+
274+ dummy = v [0 ] * dx
275+ A = PETScMatrix ()
276+ assemble_system (a , dummy , bc , A_tensor = A )
277+ B = PETScMatrix ()
278+ assemble_system (b , dummy , [], A_tensor = B )
279+
280+ A , B = A .mat (), B .mat ()
281+
282+ return A , B , W
283+
284+
285+ ## Set up operators for eigenvalue problem ##
286+
287+
288+ def EIG_A_cyl (u , p , c , v , q , d , mu ):
289+ eiga = (0.5 * mu * inner (D (u ), D (v )) * dx - div (v ) * p * dx - q * div (u ) * dx
290+ + c * q * dx + d * p * dx
291+ )
292+ return eiga
293+
294+
295+ def EIG_B_cyl (u , p , c , v , q , d , u0 ):
296+ return 0.5 * inner (dot (D (u0 ), v ), u ) * dx
297+
298+
299+
300+ import argparse
301+
302+ if __name__ == "__main__" :
303+ parser = argparse .ArgumentParser ()
304+
305+ # Problem parameters, nu is allowed to be a list
306+ parser .add_argument ('--case' , help = 'which case to run. \n Options: poise, 0 (C0015_healthy), 1 (C0015_terminal), 2 (C0019), 3 (C0065_healthy), 4 (C0065_healthy)' , default = 'poise' , type = str )
307+ parser .add_argument ('--delta_p' , help = 'pressure drop in mmHg' , default = 5 , type = float )
308+
309+ args = parser .parse_args ()
310+
311+ main (args .case , args .delta_p )
0 commit comments