hasattr(expr, 't1') and setattr(expr, 't1', time) hasattr(expr, 't2') and setattr(expr, 't2', time+fluid_parameters['dt']) # Solve fluid problem uf_, pf_ = solve_fluid(Wf, f=Constant((0, 0)), u_n=uf_n, p_n=pf_n, bdries=fluid_bdries, bcs=bcs_fluid, parameters=fluid_parameters) # Update current solution uf_n.assign(uf_) pf_n.assign(pf_) # Solve ALE # compute the displacement #ale_u_bottom=Expression((UALE_vessel,'0'), t1 = time, t2=time+dt, degree=2) eta_f = solve_ale(Va, f=Constant((0, 0)), bdries=fluid_bdries, bcs=bcs_ale, parameters=ale_parameters) # Move domains ALE.move(mesh_f, eta_f) # Save output if(timestep % int(Toutput/fluid_parameters['dt']) == 0): eta_f.rename("eta_f", "tmp") etaf_out << (eta_f, time) uf_.rename("uf", "tmp") pf_.rename("pf", "tmp") uf_out << (uf_, time)
def PVS_simulation(args): """ Solve the flow and tracer transport in the PVS : Outputs : - a logfile with information about the simulation - .pvd files with the u, p and c field at specified args.toutput time period Return : u, p, c 1D array of the u, p, c fields on the middle line """ # output folder name outputfolder=args.output_folder+'/'+args.job_name+'/' if not os.path.exists(outputfolder): os.makedirs(outputfolder) if not os.path.exists(outputfolder+'/profiles'): os.makedirs(outputfolder+'/profiles') if not os.path.exists(outputfolder+'/fields'): os.makedirs(outputfolder+'/fields') # Create output files #txt files csv_p=open(outputfolder+'profiles'+'/pressure.txt', 'w') csv_u=open(outputfolder+'profiles'+'/velocity.txt', 'w') csv_c=open(outputfolder+'profiles'+'/concentration.txt', 'w') csv_rv=open(outputfolder+'profiles'+'/radius.txt', 'w') #pvd files uf_out, pf_out= File(outputfolder+'fields'+'/uf.pvd'), File(outputfolder+'fields'+'/pf.pvd') c_out= File(outputfolder+'fields'+'/c.pvd') facets_out=File(outputfolder+'fields'+'/facets.pvd') # Create logger logger = logging.getLogger() logger.setLevel(logging.INFO) # log to a file now = datetime.now().strftime("%Y%m%d_%H%M%S") filename = os.path.join(outputfolder+'/', 'PVS_info.log') file_handler = logging.FileHandler(filename,mode='w') file_handler.setLevel(logging.INFO) #formatter = logging.Formatter("%(asctime)s %(filename)s, %(lineno)d, %(funcName)s: %(message)s") #file_handler.setFormatter(formatter) logger.addHandler(file_handler) # log to the console console_handler = logging.StreamHandler() level = logging.INFO console_handler.setLevel(level) logger.addHandler(console_handler) # initialise logging logging.info(title1("Simulation of the PVS flow and tracer transport using non steady solver and diffusion-advection solvers")) logging.info("Date and time:"+datetime.now().strftime("%m/%d/%Y, %H:%M:%S")) logging.info('Job name : '+args.job_name) logging.debug('logging initialized') # Set parameters logging.info(title1("Parameters")) # Geometry params logging.info('\n * Geometry') Rv = args.radius_vessel # centimeters Rpvs =args.radius_pvs # centimeters L = args.length # centimeters logging.info('Vessel radius : %e cm'%Rv) logging.info('PVS radius : %e cm'%Rpvs) logging.info('PVS length : %e cm'%L) #Mesh logging.info('\n * Mesh') #number of cells in the radial direction Nr=args.N_radial DR=(Rpvs-Rv)/Nr #number of cells in the axial direction if args.N_axial : Nl=args.N_axial else : Nl=round(L/DR) DY=L/Nl logging.info('N axial : %i'%Nl) logging.info('N radial : %e'%Nr) #time parameters logging.info('\n * Time') toutput=args.toutput tfinal=args.tend dt=args.time_step logging.info('final time: %e s'%tfinal) logging.info('output period : %e s'%toutput) logging.info('time step : %e s'%dt) # approximate CFL for fluid solver : need to compute max velocity depending on the wall displacement... # maybe just add a warning in computation with actual velocity #Uapprox=500e-4 #upper limit for extected max velocity #CFL_dt=0.25*DY/Uapprox #if CFL_dt < dt : # logging.warning('The specified time step of %.2e s does not fullfil the fluid CFL condition. New fluid time step : %.2e s'%(dt, CFL_dt)) #dt_fluid=min(dt,CFL_dt) dt_fluid=dt # approximate CFL for tracer solver dt_advdiff=dt # material parameters logging.info('\n * Fluid properties') mu=args.viscosity rho=args.density logging.info('density: %e g/cm3'%rho) logging.info('dynamic viscosity : %e dyn s/cm2'%mu) logging.info('\n* Tracer properties') D=args.diffusion_coef sigma_gauss=args.sigma logging.info('Free diffusion coef: %e cm2/s'%D) logging.info('STD of initial gaussian profile: %e '%sigma_gauss) xi_gauss=args.initial_pos logging.info('Initial position: %e cm2'%xi_gauss) logging.info('\n * ALE') kappa=args.ale_parameter logging.info('ALE parameter: %e '%kappa) logging.info('\n * Lateral BC') resistance=args.resistance logging.info('inner resistance: %e '%resistance) if resistance == 0 : lateral_bc='free' logging.info('right BC will be set to the free assumption') elif resistance < 0 : lateral_bc='noflow' logging.info('right BC will be set to the no flow assumption') else : lateral_bc='resistance' logging.info('right BC will be set to the resistance assumption') fluid_parameters = {'mu': mu, 'rho': rho, 'dt':dt_fluid} tracer_parameters={'kappa': D, 'dt':dt_advdiff} ale_parameters = {'kappa': kappa} # Mesh logging.info(title1('Meshing')) logging.info('cell size : %e cm'%(np.sqrt(DR**2+DY**2))) logging.info('nb cells: %i'%(Nl*Nr*2)) mesh_f= RectangleMesh(Point(0, Rv), Point(L, Rpvs), Nl, Nr) fluid_bdries = MeshFunction("size_t", mesh_f, mesh_f.topology().dim()-1,0) # Label facets xy = mesh_f.coordinates().copy() x, y = xy.T xmin=x.min() xmax=x.max() ymin=y.min() ymax=y.max() tol=min(DR,DY)/2 #cm class Boundary_left(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[0],xmin,tol) #left class Boundary_right(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[0],xmax,tol)# right class Boundary_bottom(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[1], ymin,tol) #bottom class Boundary_top(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[1], ymax,tol) #top btop= Boundary_top() bbottom = Boundary_bottom() bleft = Boundary_left() bright = Boundary_right() bbottom.mark(fluid_bdries, 2) btop.mark(fluid_bdries, 4) bleft.mark(fluid_bdries, 1) bright.mark(fluid_bdries, 3) facet_lookup = {'x_min': 1 ,'y_min': 2, 'x_max': 3, 'y_max': 4} facets_out << fluid_bdries #FEM space logging.info(title1("Set FEM spaces")) logging.info('\n * Fluid') Vf_elm = VectorElement('Lagrange', triangle, 2) Qf_elm = FiniteElement('Lagrange', triangle, 1) Wf_elm = MixedElement([Vf_elm, Qf_elm]) Wf = FunctionSpace(mesh_f, Wf_elm) logging.info('Velocity : "Lagrange", triangle, 2') logging.info('Pressure : "Lagrange", triangle, 1') logging.info('\n * Tracer') Ct_elm = FiniteElement('Lagrange', triangle, 1) Ct = FunctionSpace(mesh_f, Ct_elm) logging.info('Concentration : "Lagrange", triangle, 1') logging.info('\n * ALE') Va_elm = VectorElement('Lagrange', triangle, 1) Va = FunctionSpace(mesh_f, Va_elm) logging.info('ALE displacement: "Lagrange", triangle, 1') # Setup of boundary conditions logging.info(title1("Boundary conditions")) import sympy tn = sympy.symbols("tn") tnp1 = sympy.symbols("tnp1") sin = sympy.sin sqrt = sympy.sqrt logging.info('\n * Cross section area parameters') ai=args.ai fi=args.fi phii=args.phii logging.info('ai (dimensionless): '+'%e '*len(ai)%tuple(ai)) logging.info('fi (Hz) : '+'%e '*len(fi)%tuple(fi)) logging.info('phii (rad) : '+'%e '*len(phii)%tuple(phii)) functionR = Rpvs-(Rpvs-Rv)*(1+sum([a*sin(2*pi*f*tn+phi) for a,f,phi in zip(ai,fi,phii)])) # displacement R_vessel = sympy.printing.ccode(functionR) functionV = sympy.diff(functionR,tn) # velocity V_vessel = sympy.printing.ccode(functionV) #Delta U for ALE. I dont really like this functionUALE=-(Rpvs-Rv)*(1+sum([a*sin(2*pi*f*tnp1+phi) for a,f,phi in zip(ai,fi,phii)]))+(Rpvs-Rv)*(1+sum([a*sin(2*pi*f*tn+phi) for a,f,phi in zip(ai,fi,phii)])) UALE_vessel = sympy.printing.ccode(functionUALE) vf_bottom = Expression(('0',V_vessel ), tn = 0, degree=2) # no slip no gap condition at vessel wall uale_bottom = Expression(('0',UALE_vessel ), tn = 0, tnp1=1, degree=2) # displacement for ALE at vessel wall logging.info('\n * Lateral assumption') logging.info(lateral_bc) logging.info('\n * Fluid') logging.info('Left : zero pressure') if lateral_bc=='free' : logging.info('Right : zero pressure') elif lateral_bc=='resistance' : logging.info('Right : resistance') else : logging.info('Right : no flow') logging.info('Top : no slip no gap fixed wall') logging.info('Bottom : no slip no gap moving wall') logging.info('\n * Tracer concentration') logging.info('Left : zero concentration') if lateral_bc=='free' : logging.info('Right : zero concentration') else : logging.info('Right : no flux') logging.info('Top : no flux') logging.info('Bottom : no flux') logging.info('\n * ALE') logging.info('Left : no flux') logging.info('Right : no flux') logging.info('Top : no displacement') logging.info('Bottom : vessel displacement') # Now we wire up if lateral_bc=='free' : bcs_fluid = {'velocity': [(facet_lookup['y_min'],vf_bottom), (facet_lookup['y_max'], Constant((0,0)))], 'traction': [], 'pressure': [(facet_lookup['x_min'], Constant(0)), (facet_lookup['x_max'], Constant(0))]} elif lateral_bc=='resistance' : Rpressure=Expression('R*Q+p0', R = resistance, Q=0, p0=0, degree=1) # # Compute pressure to impose according to the flow at previous time step and resistance. bcs_fluid = {'velocity': [(facet_lookup['y_min'],vf_bottom), (facet_lookup['y_max'], Constant((0,0)))], 'traction': [], 'pressure': [(facet_lookup['x_min'], Constant(0)), (facet_lookup['x_max'], Rpressure)]} else : bcs_fluid = {'velocity': [(facet_lookup['y_min'],vf_bottom), (facet_lookup['y_max'], Constant((0,0))), (facet_lookup['x_max'], Constant((0,0)))], # I would like only normal flow to be zero 'traction': [], 'pressure': [(facet_lookup['x_min'], Constant(0))]} if lateral_bc=='free' : bcs_tracer = {'concentration': [(facet_lookup['x_max'], Constant(0)), (facet_lookup['x_min'], Constant(0))], 'flux': [(facet_lookup['y_max'], Constant(0)), (facet_lookup['y_min'], Constant(0))]} else : bcs_tracer = {'concentration': [(facet_lookup['x_min'], Constant(0))], 'flux': [(facet_lookup['x_max'], Constant(0)), (facet_lookup['y_max'], Constant(0)), (facet_lookup['y_min'], Constant(0))]} bcs_ale = {'dirichlet': [(facet_lookup['y_min'], uale_bottom), (facet_lookup['y_max'], Constant((0, 0)))], 'neumann': [(facet_lookup['x_min'], Constant((0, 0))), (facet_lookup['x_max'], Constant((0, 0)))]} # We collect the time dependent BC for update driving_expressions = (uale_bottom,vf_bottom) # Initialisation : logging.info(title1("Initialisation")) logging.info("\n * Fluid") logging.info("Velocity : zero field") logging.info("Pressure : zero field") uf_n = project(Constant((0, 0)), Wf.sub(0).collapse()) pf_n = project(Constant(0), Wf.sub(1).collapse()) logging.info("\n * Tracer") logging.info("Concentration : Gaussian profile") logging.info(" Centered at mid length") logging.info(" STD parameter = %e"%sigma_gauss) c_0 = Expression('exp(-a*pow(x[0]-b, 2)) ', degree=1, a=1/2/sigma_gauss**2, b=xi_gauss) c_n = project(c_0,Ct) # Save initial state uf_n.rename("uf", "tmp") pf_n.rename("pf", "tmp") c_n.rename("c", "tmp") uf_out << (uf_n, 0) pf_out << (pf_n, 0) c_out << (c_n, 0) files=[csv_p,csv_u,csv_c] fields=[pf_n,uf_n.sub(0),c_n] slice_line = line([0,(Rpvs+Rv)/2],[L,(Rpvs+Rv)/2], 100) for csv_file,field in zip(files,fields) : #print the x scale values=np.linspace(0,L,100) row=[0]+list(values) csv_file.write(('%e'+', %e'*len(values)+'\n')%tuple(row)) #print the initial 1D slice values = line_sample(slice_line, field) row=[0]+list(values) csv_file.write(('%e'+', %e'*len(values)+'\n')%tuple(row)) ############# RUN ############3 logging.info(title1("Run")) # Time loop time = 0. timestep=0 # Here I dont know if there will be several dt for advdiff and fluid solver while time < tfinal: # Update boundary conditions for expr in driving_expressions: hasattr(expr, 'tn') and setattr(expr, 'tn', time) hasattr(expr, 'tnp1') and setattr(expr, 'tnp1', time+dt) if lateral_bc=='resistance' : z, r = SpatialCoordinate(mesh_f) ds = Measure('ds', domain=mesh_f, subdomain_data=fluid_bdries) n = FacetNormal(mesh_f) Flow=assemble(2*pi*r*dot(uf_n, n)*ds(facet_lookup['x_max'])) print('Right outflow : %e \n'%Flow) setattr(Rpressure, 'Q', Flow) #Solve ALE and move mesh eta_f = solve_ale(Va, f=Constant((0, 0)), bdries=fluid_bdries, bcs=bcs_ale, parameters=ale_parameters) ALE.move(mesh_f, eta_f) mesh_f.bounding_box_tree().build(mesh_f) # Solve fluid problem uf_, pf_ = solve_fluid(Wf, u_0=uf_n, f=Constant((0, 0)), bdries=fluid_bdries, bcs=bcs_fluid, parameters=fluid_parameters) tracer_parameters["T0"]=time tracer_parameters["nsteps"]=1 tracer_parameters["dt"]=dt # Solve tracer problem c_, T0= solve_adv_diff(Ct, velocity=uf_-eta_f/Constant(dt), phi=Constant(1), f=Constant(0), c_0=c_n, phi_0=Constant(1), bdries=fluid_bdries, bcs=bcs_tracer, parameters=tracer_parameters) # Update current solution uf_n.assign(uf_) pf_n.assign(pf_) c_n.assign(c_) #Update time time += dt timestep+=1 # Save output if(timestep % int(toutput/dt) == 0): logging.info("\n*** save output time %e s"%time) logging.info("number of time steps %i"%timestep) # may report Courant number or other important values that indicate how is doing the run uf_.rename("uf", "tmp") pf_.rename("pf", "tmp") c_.rename("c", "tmp") uf_out << (uf_, time) pf_out << (pf_, time) c_out << (c_, time) # Get the 1 D profiles at umax (to be changed in cyl coordinate) mesh_points=mesh_f.coordinates() x=mesh_points[:,0] y=mesh_points[:,1] xmin=min(x) xmax=max(x) ymin=min(y) ymax=max(y) #slice_line = line([xmin,(ymin+ymax)/2],[xmax,(ymin+ymax)/2], 100) logging.info('Rpvs : %e'%ymax) logging.info('Rvn : %e'%ymin) files=[csv_p,csv_u,csv_c] fields=[pf_n,uf_n.sub(0),c_n] field_names=['pressure (dyn/cm2)','axial velocity (cm/s)','concentration'] for csv_file,field,name in zip(files,fields,field_names) : #values = line_sample(slice_line, field) values =profile(field,xmin,xmax,ymin,ymax) logging.info('Max '+name+' : %.2e'%max(abs(values))) #logging.info('Norm '+name+' : %.2e'%field.vector().norm('linf')) row=[time]+list(values) csv_file.write(('%e'+', %e'*len(values)+'\n')%tuple(row)) csv_rv.write('%e, %e\n'%(time,ymin))
def PVS_simulation(args): """ Solve the flow and tracer transport in the PVS, assumption flat plates : Outputs : - a logfile with information about the simulation - .pvd files with the u, p and c field at specified args.toutput time period Return : u, p, c 1D array of the u, p, c fields on the middle line """ # output folder name outputfolder = args.output_folder + '/' + args.job_name + '/' if not os.path.exists(outputfolder): os.makedirs(outputfolder) if not os.path.exists(outputfolder + '/profiles'): os.makedirs(outputfolder + '/profiles') if not os.path.exists(outputfolder + '/fields'): os.makedirs(outputfolder + '/fields') # Create output files #txt files csv_p = open(outputfolder + 'profiles' + '/pressure.txt', 'w') csv_u = open(outputfolder + 'profiles' + '/velocity.txt', 'w') csv_c = open(outputfolder + 'profiles' + '/concentration.txt', 'w') csv_rv = open(outputfolder + 'profiles' + '/radius.txt', 'w') #pvd files uf_out, pf_out = File(outputfolder + 'fields' + '/uf.pvd'), File(outputfolder + 'fields' + '/pf.pvd') c_out = File(outputfolder + 'fields' + '/c.pvd') facets_out = File(outputfolder + 'fields' + '/facets.pvd') # Create logger logger = logging.getLogger() logger.setLevel(logging.INFO) # log to a file now = datetime.now().strftime("%Y%m%d_%H%M%S") filename = os.path.join(outputfolder + '/', 'PVS_info.log') file_handler = logging.FileHandler(filename, mode='w') file_handler.setLevel(logging.INFO) #formatter = logging.Formatter("%(asctime)s %(filename)s, %(lineno)d, %(funcName)s: %(message)s") #file_handler.setFormatter(formatter) logger.addHandler(file_handler) # log to the console console_handler = logging.StreamHandler() level = logging.INFO console_handler.setLevel(level) logger.addHandler(console_handler) # initialise logging logging.info( ' ______ ______ _ _ _ _ \n' ) logging.info( '| _ \ \ / / ___| ___(_)_ __ ___ _ _| | __ _| |_(_) ___ _ __ \n' ) logging.info( "| |_) \ \ / /\___ \ / __| | '_ ` _ \| | | | |/ _` | __| |/ _ \| '_ \ \n" ) logging.info( '| __/ \ V / ___) | \__ \ | | | | | | |_| | | (_| | |_| | (_) | | | |\n' ) logging.info( '|_| \_/ |____/ |___/_|_| |_| |_|\__,_|_|\__,_|\__|_|\___/|_| |_|\n\n' ) logging.info( title1( "Simulation of the PVS flow and tracer transport using steady stokes solver and diffusion-advection solver. Flat plates geometry" )) logging.info("Date and time:" + datetime.now().strftime("%m/%d/%Y, %H:%M:%S")) logging.info('Job name : ' + args.job_name) logging.debug('logging initialized') # Set parameters logging.info(title1("Parameters")) # Geometry params logging.info('\n * Geometry') Rv = args.radius_vessel # centimeters Rpvs = args.radius_pvs # centimeters L = args.length # centimeters Rsas = args.radius_sas + Rv Lsas = args.length_sas logging.info('Vessel radius : %e cm' % Rv) logging.info('PVS radius : %e cm' % Rpvs) logging.info('PVS length : %e cm' % L) logging.info('SAS length : %e cm' % Lsas) logging.info('SAS radius : %e cm' % Rsas) logging.info('\n * Cross section area deformation parameters') ai = args.ai fi = args.fi phii = args.phii logging.info('ai (dimensionless): ' + '%e ' * len(ai) % tuple(ai)) logging.info('fi (Hz) : ' + '%e ' * len(fi) % tuple(fi)) logging.info('phii (rad) : ' + '%e ' * len(phii) % tuple(phii)) #Mesh logging.info('\n * Mesh') #number of cells in the radial direction Nr = args.N_radial DR = (Rpvs - Rv) / Nr logging.info('N radial : %e' % Nr) #time parameters logging.info('\n * Time') toutput = args.toutput tfinal = args.tend dt = args.time_step logging.info('final time: %e s' % tfinal) logging.info('output period : %e s' % toutput) logging.info('time step : %e s' % dt) # approximate CFL for fluid solver : need to compute max velocity depending on the wall displacement... # maybe just add a warning in computation with actual velocity #Uapprox=500e-4 #upper limit for extected max velocity #CFL_dt=0.25*DY/Uapprox #if CFL_dt < dt : # logging.warning('The specified time step of %.2e s does not fullfil the fluid CFL condition. New fluid time step : %.2e s'%(dt, CFL_dt)) #dt_fluid=min(dt,CFL_dt) dt_fluid = dt # approximate CFL for tracer solver dt_advdiff = dt # material parameters logging.info('\n * Fluid properties') mu = args.viscosity rho = args.density logging.info('density: %e g/cm3' % rho) logging.info('dynamic viscosity : %e dyn s/cm2' % mu) logging.info('\n* Tracer properties') D = args.diffusion_coef sigma_gauss = args.sigma logging.info('Free diffusion coef: %e cm2/s' % D) logging.info('STD of initial gaussian profile: %e ' % sigma_gauss) xi_gauss = args.initial_pos logging.info('Initial position: %e cm2' % xi_gauss) logging.info('\n * ALE') kappa = args.ale_parameter logging.info('ALE parameter: %e ' % kappa) logging.info('\n * Lateral BC') resistance = args.resistance logging.info('inner resistance: %e ' % resistance) if resistance == 0: lateral_bc = 'free' logging.info('right BC will be set to the free assumption') elif resistance < 0: lateral_bc = 'noflow' logging.info('right BC will be set to the no flow assumption') else: lateral_bc = 'resistance' logging.info('right BC will be set to the resistance assumption') fluid_parameters = {'mu': mu, 'rho': rho, 'dt': dt_fluid} tracer_parameters = {'kappa': D, 'dt': dt_advdiff} ale_parameters = {'kappa': kappa} # Mesh logging.info(title1('Meshing')) logging.info('cell size : %e cm' % (DR)) from sleep.mesh import mesh_model2d, load_mesh2d, set_mesh_size import sys gmsh.initialize(['', '-format', 'msh2']) model = gmsh.model import math Apvs0 = math.pi * Rpvs**2 Av0 = math.pi * Rv**2 A0 = Apvs0 - Av0 # progressive mesh factory = model.occ a = factory.addPoint(-Lsas, Rv, 0) b = factory.addPoint(L, Rv, 0) c = factory.addPoint(L, Rpvs, 0) d = factory.addPoint(0, Rpvs, 0) e = factory.addPoint(0, Rsas, 0) f = factory.addPoint(-Lsas, Rsas, 0) fluid_lines = [ factory.addLine(*p) for p in ((a, b), (b, c), (c, d), (d, e), (e, f), (f, a)) ] named_lines = dict( zip(('bottom', 'pvs_right', 'pvs_top', 'brain_surf', 'sas_top', 'sas_left'), fluid_lines)) fluid_loop = factory.addCurveLoop(fluid_lines) fluid = factory.addPlaneSurface([fluid_loop]) factory.synchronize() model.addPhysicalGroup(2, [fluid], 1) for name in named_lines: tag = named_lines[name] model.addPhysicalGroup(1, [tag], tag) # boxes for mesh refinement cell_size = DR * (Rpvs - Rv) / (Rpvs - Rv) boxes = [] # add box on the PVS for mesh field = model.mesh.field fid = 1 field.add('Box', fid) field.setNumber(fid, 'XMin', 0) field.setNumber(fid, 'XMax', L) field.setNumber(fid, 'YMin', Rv) field.setNumber(fid, 'YMax', Rpvs) field.setNumber(fid, 'VIn', cell_size) field.setNumber(fid, 'VOut', DR * 50) field.setNumber(fid, 'Thickness', Rsas) boxes.append(fid) # Combine field.add('Min', 2) field.setNumbers(2, 'FieldsList', boxes) field.setAsBackgroundMesh(2) model.occ.synchronize() h5_filename = outputfolder + '/mesh.h5' tags = {'cell': {'F': 1}, 'facet': {}} mesh_model2d(model, tags, h5_filename) mesh_f, markers, lookup = load_mesh2d(h5_filename) gmsh.finalize() # todo : how to know nb of cells ? #logging.info('nb cells: %i'%(Nl*Nr*2)) fluid_bdries = MeshFunction("size_t", mesh_f, mesh_f.topology().dim() - 1, 0) # Label facets xy = mesh_f.coordinates().copy() x, y = xy.T xmin = x.min() xmax = x.max() ymin = y.min() ymax = y.max() tol = cell_size / 2 #cm class Boundary_sas_left(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[0], -Lsas, tol) # downstream class Boundary_pvs_right(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[0], L, tol) class Boundary_sas_top(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[1], Rsas, tol) and (x[0] < tol) class Boundary_pvs_top(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[1], Rpvs, tol) and (x[0] > -tol) # brain class Boundary_brainsurf(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[0], 0, tol) and (x[1] > Rpvs - tol) class Boundary_bottom(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[1], ymin, tol) btop_sas = Boundary_sas_top() btop_pvs = Boundary_pvs_top() bbottom = Boundary_bottom() bvert_left = Boundary_sas_left() bvert_brain = Boundary_brainsurf() bvert_right = Boundary_pvs_right() btop_sas.mark(fluid_bdries, 1) btop_pvs.mark(fluid_bdries, 2) bbottom.mark(fluid_bdries, 3) bvert_left.mark(fluid_bdries, 4) bvert_brain.mark(fluid_bdries, 5) bvert_right.mark(fluid_bdries, 6) facet_lookup = { 'sas_top': 1, 'pvs_top': 2, 'vessel': 3, 'sas_left': 4, 'brain_surf': 5, 'pvs_right': 6 } facets_out << fluid_bdries #FEM space logging.info(title1("Set FEM spaces")) logging.info('\n * Fluid') Vf_elm = VectorElement('Lagrange', triangle, 2) Qf_elm = FiniteElement('Lagrange', triangle, 1) Wf_elm = MixedElement([Vf_elm, Qf_elm]) Wf = FunctionSpace(mesh_f, Wf_elm) logging.info('Velocity : "Lagrange", triangle, 2') logging.info('Pressure : "Lagrange", triangle, 1') logging.info('\n * Tracer') Ct_elm = FiniteElement('Lagrange', triangle, 1) Ct = FunctionSpace(mesh_f, Ct_elm) logging.info('Concentration : "Lagrange", triangle, 1') logging.info('\n * ALE') Va_elm = VectorElement('Lagrange', triangle, 1) Va = FunctionSpace(mesh_f, Va_elm) logging.info('ALE displacement: "Lagrange", triangle, 1') # Setup of boundary conditions logging.info(title1("Boundary conditions")) import sympy tn = sympy.symbols("tn") tnp1 = sympy.symbols("tnp1") sin = sympy.sin sqrt = sympy.sqrt functionR = Rpvs - (Rpvs - Rv) * (1 + sum( [a * sin(2 * pi * f * tn + phi) for a, f, phi in zip(ai, fi, phii)])) # displacement bottom plate R_vessel = sympy.printing.ccode(functionR) functionV = sympy.diff(functionR, tn) # velocity V_vessel = sympy.printing.ccode(functionV) #Delta U for ALE. I dont really like this functionUALE = -(Rpvs - Rv) * (1 + sum([ a * sin(2 * pi * f * tnp1 + phi) for a, f, phi in zip(ai, fi, phii) ])) + (Rpvs - Rv) * (1 + sum( [a * sin(2 * pi * f * tn + phi) for a, f, phi in zip(ai, fi, phii)])) UALE_vessel = sympy.printing.ccode(functionUALE) vf_bottom = Expression(('0', V_vessel), tn=0, degree=2) # no slip no gap condition at vessel wall uale_bottom = Expression(('0', UALE_vessel), tn=0, tnp1=1, degree=2) # displacement for ALE at vessel wall logging.info('\n * Lateral assumption') logging.info(lateral_bc) logging.info('\n * Fluid') logging.info('Left : zero pressure') if lateral_bc == 'free': logging.info('Right : zero pressure') elif lateral_bc == 'resistance': logging.info('Right : resistance') else: logging.info('Right : no flow') logging.info('Top : no slip no gap fixed wall') logging.info('Bottom : no slip no gap moving wall') logging.info('\n * Tracer concentration') logging.info('Left : zero concentration') if lateral_bc == 'free': logging.info('Right : zero concentration') else: logging.info('Right : no flux') logging.info('Top : no flux') logging.info('Bottom : no flux') logging.info('\n * ALE') logging.info('Left : no flux') logging.info('Right : no flux') logging.info('Top : no displacement') logging.info('Bottom : vessel displacement') # Now we wire up if lateral_bc == 'free': bcs_fluid = { 'velocity': [(facet_lookup['vessel'], vf_bottom), (facet_lookup['sas_left'], Constant((0, 0))), (facet_lookup['brain_surf'], Constant((0, 0))), (facet_lookup['pvs_top'], Constant((0, 0)))], 'traction': [], 'pressure': [(facet_lookup['sas_top'], Constant(0)), (facet_lookup['pvs_right'], Constant(0))] } elif lateral_bc == 'resistance': Rpressure = Expression('R*Q+p0', R=resistance, Q=0, p0=0, degree=1) # # Compute pressure to impose according to the flow at previous time step and resistance. bcs_fluid = { 'velocity': [(facet_lookup['vessel'], vf_bottom), (facet_lookup['sas_left'], Constant((0, 0))), (facet_lookup['brain_surf'], Constant((0, 0))), (facet_lookup['pvs_top'], Constant((0, 0)))], 'traction': [], 'pressure': [(facet_lookup['sas_top'], Constant(0)), (facet_lookup['pvs_right'], Rpressure)] } else: bcs_fluid = { 'velocity': [(facet_lookup['vessel'], vf_bottom), (facet_lookup['sas_left'], Constant((0, 0))), (facet_lookup['brain_surf'], Constant((0, 0))), (facet_lookup['pvs_top'], Constant((0, 0))), (facet_lookup['pvs_right'], Constant( (0, 0)))], # would be more correct to impose only on u x 'traction': [], 'pressure': [(facet_lookup['sas_top'], Constant(0))] } if lateral_bc == 'free': bcs_tracer = { 'concentration': [(facet_lookup['sas_top'], Constant(0)), (facet_lookup['pvs_right'], Constant(0))], 'flux': [(facet_lookup['vessel'], Constant(0)), (facet_lookup['sas_left'], Constant(0)), (facet_lookup['brain_surf'], Constant(0)), (facet_lookup['pvs_top'], Constant(0))] } else: bcs_tracer = { 'concentration': [(facet_lookup['sas_top'], Constant(0))], 'flux': [(facet_lookup['vessel'], Constant(0)), (facet_lookup['sas_left'], Constant(0)), (facet_lookup['brain_surf'], Constant(0)), (facet_lookup['pvs_top'], Constant(0)), (facet_lookup['pvs_right'], Constant(0))] } bcs_ale = { 'dirichlet': [(facet_lookup['vessel'], uale_bottom), (facet_lookup['sas_top'], Constant((0, 0))), (facet_lookup['pvs_top'], Constant((0, 0))), (facet_lookup['brain_surf'], Constant((0, 0)))], 'neumann': [(facet_lookup['sas_left'], Constant((0, 0))), (facet_lookup['pvs_right'], Constant((0, 0)))] } # We collect the time dependent BC for update driving_expressions = (uale_bottom, vf_bottom) # Initialisation : logging.info(title1("Initialisation")) # We start at a time shift tshift = -1 / 4 / fi[0] ##todo : modify to be compatible with phii # Update boundary conditions for expr in driving_expressions: hasattr(expr, 'tn') and setattr(expr, 'tn', 0) hasattr(expr, 'tnp1') and setattr(expr, 'tnp1', tshift) #Solve ALE and move mesh eta_f = solve_ale(Va, f=Constant((0, 0)), bdries=fluid_bdries, bcs=bcs_ale, parameters=ale_parameters) ALE.move(mesh_f, eta_f) mesh_f.bounding_box_tree().build(mesh_f) logging.info("\n * Fluid") logging.info("Velocity : zero field") logging.info("Pressure : zero field") uf_n = project(Constant((0, 0)), Wf.sub(0).collapse()) pf_n = project(Constant(0), Wf.sub(1).collapse()) logging.info("\n * Tracer") logging.info("Concentration : Gaussian profile") logging.info(" Centered at mid length") logging.info(" STD parameter = %e" % sigma_gauss) c_0 = Expression('exp(-a*pow(x[0]-b, 2)) ', degree=1, a=1 / 2 / sigma_gauss**2, b=xi_gauss) c_n = project(c_0, Ct) # Save initial state uf_n.rename("uf", "tmp") pf_n.rename("pf", "tmp") c_n.rename("c", "tmp") uf_out << (uf_n, 0) pf_out << (pf_n, 0) c_out << (c_n, 0) # Get the 1 D profiles at umax (to be changed in cyl coordinate) mesh_points = mesh_f.coordinates() x = mesh_points[:, 0] y = mesh_points[:, 1] xmin = 0 xmax = L ymin = min(y) ymax = Rpvs files = [csv_p, csv_u, csv_c] fields = [pf_n, uf_n.sub(0), c_n] field_names = [ 'pressure (dyn/cm2)', 'axial velocity (cm/s)', 'concentration' ] for csv_file, field, name in zip(files, fields, field_names): #values = line_sample(slice_line, field) values = profile(field, xmin, xmax, ymin, ymax) logging.info('Max ' + name + ' : %.2e' % max(abs(values))) #logging.info('Norm '+name+' : %.2e'%field.vector().norm('linf')) row = [0] + list(values) csv_file.write(('%e' + ', %e' * len(values) + '\n') % tuple(row)) csv_rv.write('%e, %e' % (0, ymin)) ############# RUN ############3 logging.info(title1("Run")) # Time loop ### We start at the quarter of the period so that velocity=0 print('tini = ', tshift) time = tshift timestep = 0 # Here I dont know if there will be several dt for advdiff and fluid solver while time < tfinal + tshift: # Update boundary conditions for expr in driving_expressions: hasattr(expr, 'tn') and setattr(expr, 'tn', time) hasattr(expr, 'tnp1') and setattr(expr, 'tnp1', time + dt) if lateral_bc == 'resistance': z, r = SpatialCoordinate(mesh_f) ds = Measure('ds', domain=mesh_f, subdomain_data=fluid_bdries) n = FacetNormal(mesh_f) Flow = assemble(2 * pi * r * dot(uf_n, n) * ds(facet_lookup['x_max'])) print('Right outflow : %e \n' % Flow) setattr(Rpressure, 'Q', Flow) #Solve ALE and move mesh eta_f = solve_ale(Va, f=Constant((0, 0)), bdries=fluid_bdries, bcs=bcs_ale, parameters=ale_parameters) ALE.move(mesh_f, eta_f) mesh_f.bounding_box_tree().build(mesh_f) # Solve fluid problem uf_, pf_ = solve_fluid(Wf, u_0=uf_n, f=Constant((0, 0)), bdries=fluid_bdries, bcs=bcs_fluid, parameters=fluid_parameters) tracer_parameters["T0"] = time tracer_parameters["nsteps"] = 1 tracer_parameters["dt"] = dt # Solve tracer problem c_, T0 = solve_adv_diff(Ct, velocity=uf_ - eta_f / Constant(dt), phi=Constant(1), f=Constant(0), c_0=c_n, phi_0=Constant(1), bdries=fluid_bdries, bcs=bcs_tracer, parameters=tracer_parameters) # Update current solution uf_n.assign(uf_) pf_n.assign(pf_) c_n.assign(c_) #Update time time += dt timestep += 1 # Save output if (timestep % int(toutput / dt) == 0): logging.info("\n*** save output time %e s" % (time - tshift)) logging.info("number of time steps %i" % timestep) # may report Courant number or other important values that indicate how is doing the run uf_.rename("uf", "tmp") pf_.rename("pf", "tmp") c_.rename("c", "tmp") uf_out << (uf_, time - tshift) pf_out << (pf_, time - tshift) c_out << (c_, time - tshift) # Get the 1 D profiles at umax (to be changed in cyl coordinate) mesh_points = mesh_f.coordinates() x = mesh_points[:, 0] y = mesh_points[:, 1] xmin = 0 xmax = L ymin = min(y) ymax = Rpvs #slice_line = line([xmin,(ymin+ymax)/2],[xmax,(ymin+ymax)/2], 100) logging.info('Rpvs : %e' % ymax) logging.info('Rvn : %e' % ymin) files = [csv_p, csv_u, csv_c] fields = [pf_n, uf_n.sub(0), c_n] field_names = [ 'pressure (dyn/cm2)', 'axial velocity (cm/s)', 'concentration' ] for csv_file, field, name in zip(files, fields, field_names): #values = line_sample(slice_line, field) values = profile(field, xmin, xmax, ymin, ymax) logging.info('Max ' + name + ' : %.2e' % max(abs(values))) #logging.info('Norm '+name+' : %.2e'%field.vector().norm('linf')) row = [time - tshift] + list(values) csv_file.write( ('%e' + ', %e' * len(values) + '\n') % tuple(row)) csv_rv.write('%e, %e' % (time - tshift, ymin))