Skip to content

Commit c52ebc2

Browse files
authored
Merge pull request #21 from keiyamamo/add_robinbc
add robin boundary condition
2 parents deb43f7 + b7a2eff commit c52ebc2

File tree

6 files changed

+407
-9
lines changed

6 files changed

+407
-9
lines changed

turtleFSI/modules/domain.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@
44
# PURPOSE.
55

66
from turtleFSI.modules import *
7-
from dolfin import Constant, inner, inv, grad, div
7+
from dolfin import ds, MPI
88

9+
"""
10+
Date: 2022-11-07
11+
Comment from Kei: Naming here is a bit confusing. For example, dx_f, ds_s are hard to understand.
12+
dx refers to the area of the domain while ds refers to the area of the boundary.
13+
f refere to the fluid while s refers to the solid.
14+
"""
915

10-
def assign_domain_properties(dx, dx_f_id, rho_f, mu_f, fluid_properties, dx_s_id, material_model, rho_s, mu_s, lambda_s, solid_properties, domains, **namespace):
16+
def assign_domain_properties(dx, dx_f_id, rho_f, mu_f, fluid_properties, dx_s_id, material_model, rho_s, mu_s, lambda_s, solid_properties, domains, ds_s_id, boundaries, robin_bc, **namespace):
1117
"""
1218
Assigns solid and fluid properties to each region.
1319
"""
@@ -48,6 +54,19 @@ def assign_domain_properties(dx, dx_f_id, rho_f, mu_f, fluid_properties, dx_s_id
4854
solid_properties.append({"dx_s_id":dx_s_id,"material_model":material_model,"rho_s":rho_s,"mu_s":mu_s,"lambda_s":lambda_s})
4955
elif isinstance(solid_properties, dict):
5056
solid_properties = [solid_properties]
57+
58+
# RobinBC
59+
if robin_bc:
60+
ds_s = {}
61+
if isinstance(ds_s_id, list): # If ds_s_id is a list (i.e, if there are multiple boundary regions):
62+
for i, solid_boundaries in enumerate(ds_s_id):
63+
ds_s[i] = ds(solid_boundaries, subdomain_data=boundaries) # Create ds_s for each boundary
64+
ds_s_ext_id_list=ds_s_id
65+
else:
66+
ds_s[0] = ds(ds_s_id, subdomain_data=boundaries)
67+
ds_s_ext_id_list=[ds_s_id] # If there aren't multpile boundary regions, and the boundary parameters are given as floats, convert solid parameters to lists.
68+
else:
69+
ds_s = None
70+
ds_s_ext_id_list = None
5171

52-
53-
return dict(dx_f=dx_f, dx_f_id_list=dx_f_id_list, fluid_properties=fluid_properties, dx_s=dx_s, dx_s_id_list=dx_s_id_list, solid_properties=solid_properties)
72+
return dict(dx_f=dx_f, dx_f_id_list=dx_f_id_list, ds_s_ext_id_list=ds_s_ext_id_list, ds_s=ds_s, fluid_properties=fluid_properties, dx_s=dx_s, dx_s_id_list=dx_s_id_list, solid_properties=solid_properties)

turtleFSI/modules/solid.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,11 @@
44
# PURPOSE.
55

66
from turtleFSI.modules import *
7-
7+
from turtleFSI.problems import info_blue
88
from dolfin import Constant, inner, grad, MPI
99

10-
11-
def solid_setup(d_, v_, phi, psi, dx_s, dx_s_id_list, solid_properties, k, theta,
12-
gravity, mesh, **namespace):
10+
def solid_setup(d_, v_, phi, psi, dx_s, ds_s, dx_s_id_list, ds_s_ext_id_list, solid_properties, k, theta,
11+
gravity, mesh, robin_bc, k_s, c_s, **namespace):
1312

1413
# DB added gravity in 3d functionality and multi material capability 16/3/21
1514
#
@@ -60,10 +59,23 @@ def solid_setup(d_, v_, phi, psi, dx_s, dx_s_id_list, solid_properties, k, theta
6059
# Stress (Note that if viscoelasticity is used, Piola1() is no longer the total stress, it is the non-rate dependant (elastic) component of the stress)
6160
F_solid_nonlinear += theta0 * inner(Piola1(d_["n"], solid_properties[solid_region]), grad(psi)) * dx_s[solid_region]
6261
F_solid_linear += theta1 * inner(Piola1(d_["n-1"], solid_properties[solid_region]), grad(psi)) * dx_s[solid_region]
63-
# Gravity
62+
# Gravity - y direction only
6463
if gravity is not None and mesh.geometry().dim() == 2:
6564
F_solid_linear -= inner(Constant((0, -gravity * rho_s)), psi)*dx_s[solid_region]
6665
elif gravity is not None and mesh.geometry().dim() == 3:
6766
F_solid_linear -= inner(Constant((0, -gravity * rho_s,0)), psi)*dx_s[solid_region]
67+
68+
# Robin BC
69+
"""
70+
The derivation comes from the eq.(9) in the followling paper:
71+
Moireau, P., Xiao, N., Astorino, M. et al. External tissue support and fluid–structure simulation in blood flows.
72+
Biomech Model Mechanobiol 11, 1–18 (2012). https://doi.org/10.1007/s10237-011-0289-z
73+
"""
74+
if robin_bc:
75+
info_blue("Robin BC is used for the solid domain.")
76+
for solid_boundaries in range(len(ds_s_ext_id_list)):
77+
F_solid_linear += theta0 * inner((k_s * d_["n"] + c_s * v_["n"]), psi)*ds_s[solid_boundaries]
78+
F_solid_linear += theta1 * inner((k_s * d_["n-1"] + c_s * v_["n-1"]), psi)*ds_s[solid_boundaries]
79+
6880

6981
return dict(F_solid_linear=F_solid_linear, F_solid_nonlinear=F_solid_nonlinear)
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
from dolfin import *
2+
from turtleFSI.problems import *
3+
import numpy as np
4+
from scipy.integrate import odeint
5+
from turtleFSI.utils.Probe import Probe
6+
import matplotlib.pyplot as plt
7+
8+
"""
9+
This problem is a validation of the Robin BC implementation in the solid solver.
10+
The validation is done by using a mass-spring-damper system and comparing the results.
11+
We use cylinder mesh which is subjected to a constant gravity force in y-direction.
12+
This sciprt is meant to run with a single core, but can be easily parallelized.
13+
14+
Mesh can be found in the following link:
15+
https://drive.google.com/drive/folders/1roV_iE_16Q847AQ_0tEsznIT-6EICX4o?usp=sharing
16+
"""
17+
18+
# Set compiler arguments
19+
parameters["form_compiler"]["quadrature_degree"] = 6
20+
parameters["form_compiler"]["optimize"] = True
21+
# The "ghost_mode" has to do with the assembly of form containing the facet normals n('+') within interior boundaries (dS). For 3D mesh the value should be "shared_vertex", for 2D mesh "shared_facet", the default value is "none".
22+
parameters["ghost_mode"] = "shared_vertex" #3D case
23+
_compiler_parameters = dict(parameters["form_compiler"])
24+
25+
26+
def set_problem_parameters(default_variables, **namespace):
27+
# Overwrite default values
28+
E_s_val = 1E6 # Young modulus (elasticity) [Pa]
29+
nu_s_val = 0.45 # Poisson ratio (compressibility)
30+
mu_s_val = E_s_val / (2 * (1 + nu_s_val)) # Shear modulus
31+
lambda_s_val = nu_s_val * 2. * mu_s_val / (1. - 2. * nu_s_val)
32+
33+
# define and set problem variables values
34+
default_variables.update(dict(
35+
T=0.1, # Simulation end time
36+
dt=0.0005, # Time step size
37+
theta=0.501, # Theta scheme (implicit/explicit time stepping): 0.5 + dt
38+
atol=1e-7, # Absolute tolerance in the Newton solver
39+
rtol=1e-7, # Relative tolerance in the Newton solver
40+
mesh_file="cylinder", # Mesh file name
41+
inlet_id=2, # inlet id
42+
outlet_id1=3, # outlet id
43+
inlet_outlet_s_id=1011, # solid inlet and outlet id
44+
fsi_id=1022, # fsi Interface
45+
rigid_id=1011, # "rigid wall" id for the fluid and mesh problem
46+
outer_wall_id=1033, # outer surface / external id
47+
ds_s_id=[1033], # ID of solid external wall (where we want to test Robin BC)
48+
rho_f=1.025E3, # Fluid density [kg/m3]
49+
mu_f=3.5E-3, # Fluid dynamic viscosity [Pa.s]
50+
rho_s=1.0E3, # Solid density [kg/m3]
51+
mu_s=mu_s_val, # Solid shear modulus or 2nd Lame Coef. [Pa]
52+
nu_s=nu_s_val, # Solid Poisson ratio [-]
53+
lambda_s=lambda_s_val, # Solid 1rst Lamé coef. [Pa]
54+
robin_bc = True, # Robin BC
55+
k_s = 1.0E5, # elastic response necesary for RobinBC
56+
c_s = 1.0E2, # viscoelastic response necesary for RobinBC
57+
dx_f_id=1, # ID of marker in the fluid domain
58+
dx_s_id=1002, # ID of marker in the solid domain
59+
extrapolation="laplace", # laplace, elastic, biharmonic, no-extrapolation
60+
extrapolation_sub_type="constant", # constant, small_constant, volume, volume_change, bc1, bc2
61+
recompute=30, # Number of iterations before recompute Jacobian.
62+
recompute_tstep=10, # Number of time steps before recompute Jacobian.
63+
save_step=1, # Save frequency of files for visualisation
64+
folder="robinbc_validation", # Folder where the results will be stored
65+
checkpoint_step=50, # checkpoint frequency
66+
kill_time=100000, # in seconds, after this time start dumping checkpoints every timestep
67+
save_deg=1, # Default could be 1. 1 saves the nodal values only while 2 takes full advantage of the mide side nodes available in the P2 solution. P2 for nice visualisations
68+
gravity=2.0, # Gravitational force [m/s^2]
69+
fluid="no_fluid" # Do not solve for the fluid
70+
))
71+
72+
return default_variables
73+
74+
75+
def get_mesh_domain_and_boundaries(mesh_file, **namespace):
76+
#Import mesh file
77+
mesh = Mesh()
78+
hdf = HDF5File(mesh.mpi_comm(), "mesh/" + mesh_file + ".h5", "r")
79+
hdf.read(mesh, "/mesh", False)
80+
boundaries = MeshFunction("size_t", mesh, 2)
81+
hdf.read(boundaries, "/boundaries")
82+
domains = MeshFunction("size_t", mesh, 3)
83+
hdf.read(domains, "/domains")
84+
85+
#Set all solid
86+
domains.set_all(1002)
87+
88+
return mesh, domains, boundaries
89+
90+
def create_bcs(**namespace):
91+
"""
92+
In this problem we use Robin boundary condition which is implemented in the solid.py file.
93+
Thus, we do not need specify any boundary conditions in this function.
94+
"""
95+
return dict(bcs=[])
96+
97+
def _mass_spring_damper_system_ode(x, t, params_dict):
98+
# Umpack parameters
99+
F = params_dict['F'] # Volume of the domain
100+
A = params_dict['A'] # Area of the external surface (where Robin BC is applied)
101+
c = params_dict['c'] # Damping constant
102+
k = params_dict['k'] # Stiffness of the spring
103+
m = params_dict['m'] # Mass of the domain
104+
# Solve the system of ODEs
105+
dx1dt = x[1]
106+
dx2dt = (F - c*x[1]*A - k*x[0]*A)/m
107+
108+
dxdt = [dx1dt, dx2dt]
109+
return dxdt
110+
111+
def initiate(dvp_, mesh, DVP, **namespace):
112+
# Position to probe
113+
x_coordinate = mesh.coordinates()[:, 0]
114+
y_coordinate = mesh.coordinates()[:, 1]
115+
z_coordinate = mesh.coordinates()[:, 2]
116+
117+
x_middle = (x_coordinate.max() + x_coordinate.min())/2
118+
y_middle = (y_coordinate.max() + y_coordinate.min())/2
119+
z_middle = (z_coordinate.max() + z_coordinate.min())/2
120+
121+
middle_point = np.array([x_middle, y_middle, z_middle])
122+
d_probe = Probe(middle_point, DVP.sub(0))
123+
d_probe(dvp_["n"].sub(0, deepcopy=True))
124+
return dict(d_probe=d_probe)
125+
126+
def post_solve(d_probe, dvp_, **namespace):
127+
d_probe(dvp_["n"].sub(0, deepcopy=True))
128+
129+
def finished(T, dt, mesh, rho_s, k_s, c_s, boundaries, gravity, d_probe, **namespace):
130+
# Define time step and initial conditions
131+
t_analytical = np.linspace(0, T, int(T/dt) + 1)
132+
analytical_solution_init = [0,0]
133+
# Define parameters for the analytical solution
134+
volume = assemble(1*dx(mesh))
135+
ds_robin = Measure("ds", domain=mesh, subdomain_data=boundaries, subdomain_id=1033)
136+
params_dict = dict()
137+
params_dict["m"] = volume*rho_s
138+
params_dict["k"] = k_s
139+
params_dict["c"] = c_s
140+
params_dict["A"] = assemble(1*ds_robin)
141+
params_dict["F"] = -gravity*volume*rho_s
142+
# Solve the ode to compute the analytical solution
143+
analytical_solution = odeint(_mass_spring_damper_system_ode, analytical_solution_init, t_analytical, args=(params_dict,))
144+
analytical_displacement = analytical_solution[:,0]
145+
# Plot both numerical and analytical solutions for a comparison
146+
plt.plot(d_probe.get_probe_sub(1), label="turtleFSI")
147+
plt.plot(analytical_displacement, label="analytical")
148+
plt.legend()
149+
plt.show()
150+

0 commit comments

Comments
 (0)