Skip to content

Commit 46f10f4

Browse files
Update
1 parent deb0d28 commit 46f10f4

File tree

1 file changed

+311
-0
lines changed

1 file changed

+311
-0
lines changed

find_perturbation.py

Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
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

Comments
 (0)