|
| 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