from sleep.fbb_DD.solid import solve_solid from sleep.fbb_DD.fluid import solve_fluid from sleep.fbb_DD.ale import solve_ale from sleep.utils import EmbeddedMesh from sleep.mesh import load_mesh2d from dolfin import * # We get the fluid domain mesh from the full PVS mesh h5_filename = '../mesh/test/fbb_domain.h5' # The mesh is typically generated by sleep/mesh/fbb_mesh.py mesh, markers, lookup = load_mesh2d(h5_filename) cell_f, facet_f = markers cell_lookup, facet_lookup = lookup['cell'], lookup['facet'] # The mesh has 3 subdomains: corresponding to fluid and two Biot domain. # We now split into fluid and solid meshes in order to keep only the fluid mesh mesh_f = EmbeddedMesh(cell_f, cell_lookup['F']) mesh_s = EmbeddedMesh(cell_f, (cell_lookup['S1'], cell_lookup['S2'])) # We get the boundary markers fluid_markers = ('F_left', 'F_bottom', 'F_right', 'I_bottom') fluid_markers = tuple(facet_lookup[k] for k in fluid_markers) fluid_bdries = mesh_f.translate_markers(facet_f, fluid_markers) # Parameters setup ------------------------------------------------ FIXME mu_F = Constant(7e-3) #----------------------------------- fluid_parameters = {'mu': mu_F} # Setup fem spaces --------------------------------------------------- Vf_elm = VectorElement('Lagrange', triangle, 2)
def transfer_DD(expr, V, interface, tol=1E-10): ''' Let interface = (bdries-domain-1, bdries-domain-2, shared-bdry-index). Using space V defined on bdries-domain-1.mesh() we build a good enough representation of it in V(bdiers-domain-2.mesh()). ''' if len(interface) == 2: # Same domain interface = (interface[0], interface[0], interface[1]) return transfer_DD(expr, V, interface, tol) bdries1, bdries2, tag = interface # Share? assert tag in set(bdries1.array()) and tag in set(bdries2.array()) # On first mesh1 = bdries1.mesh() assert V.mesh().id() == mesh1.id() same_mesh = mesh1.id() == bdries2.mesh().id() # We assume that the shared surface is on the boundary, duh :). So # the idea is to basically do an L^2 projection of expr onto the trace # space of V on the interface ds_ = Measure('ds', domain=mesh1, subdomain_data=bdries1) # Rhs of the projection problem that we will need to restrict v = TestFunction(V) b = assemble(inner(v, expr) * ds_(tag)) b_norm = b.norm('l2') # The trace space is interface = EmbeddedMesh(bdries1, tag) not same_mesh and interface.compute_embedding( bdries2, tag, tol) # Fails if the meshes are far apart # The trace space TV = trace_space(V, interface) Tb = Function(TV).vector() # Restrict Rmat = trace_matrix(V, TV, interface) Rmat.mult(b, Tb) Tb_norm = Tb.norm('l2') # Lhs of the projection problem u, v = TrialFunction(TV), TestFunction(TV) M = assemble(inner(u, v) * dx) # Project Texpr = Function(TV) # Trace of the expression (sort of) solve(M, Texpr.vector(), Tb) # Finally we extend this to V(mesh2). NOTE: it is pretty much only # on the interface where the result makes some sense mesh2 = bdries2.mesh() if not same_mesh: V2 = FunctionSpace(mesh2, V.ufl_element().reconstruct(cell=mesh2.ufl_cell())) Rmat = trace_matrix(V2, TV, interface) else: # Already have Rmat V2 = V u2 = Function(V2) # Extend Rmat.transpmult(Texpr.vector(), u2.vector()) ds_ = Measure('ds', domain=mesh2, subdomain_data=bdries2) c = assemble(inner(TestFunction(V2), u2) * ds_(tag)) c_norm = c.norm('l2') info('Transfer |b|->|Tb|->|ETb| = %g %g %g' % (b_norm, Tb_norm, c_norm)) return u2
# work is # # us -> take is trace Tus -> extend to fluid -> compute the stress there # # You'll that this does not work and the work around is to take trace of # stresses and extend those from sleep.utils import EmbeddedMesh, trace_matrix from dolfin import * fs_domain = UnitSquareMesh(128, 128) cell_f = MeshFunction('size_t', fs_domain, 2, 1) CompiledSubDomain('x[0] > 0.5 - DOLFIN_EPS').mark(cell_f, 2) dx_fs = Measure('dx', domain=fs_domain, subdomain_data=cell_f) fluid = EmbeddedMesh(cell_f, 1) solid = EmbeddedMesh(cell_f, 2) # We take fluid domain and master and define interaface wrt to it fluid_facets = MeshFunction('size_t', fluid, 1, 0) CompiledSubDomain('near(x[0], 0.5)').mark(fluid_facets, 1) # Corresponding suraface integral dI_f = Measure('ds', domain=fluid, subdomain_data=fluid_facets, subdomain_id=1) # Fluid is master interface = EmbeddedMesh(fluid_facets, 1) solid_facets = MeshFunction('size_t', solid, 1, 0) CompiledSubDomain('near(x[0], 0.5)').mark(solid_facets, 2) # Corresponding suraface integral dI_s = Measure('ds', domain=solid, subdomain_data=solid_facets, subdomain_id=2)
u = Function(V) n = Constant((1, 0)) subs = {u: sp.Matrix([sp.sin(x**2 + y**2), sp.cos(x**2 - 3 * y**2)])} displacement = ulfy.Expression(u, subs=subs, degree=2) stress = ulfy.Expression(sym(grad(u)), subs=subs, degree=2) traction = ulfy.Expression(dot(n, sym(grad(u))), subs=subs, degree=2) traction_n = ulfy.Expression(dot(n, dot(n, sym(grad(u)))), subs=subs, degree=2) displacement_n = ulfy.Expression(dot(u, n) * n, subs=subs, degree=2) cell_f = MeshFunction('size_t', mesh, 2, 2) CompiledSubDomain('x[0] < 0.5+DOLFIN_EPS').mark(cell_f, 1) left = EmbeddedMesh(cell_f, 1) left_bdries = MeshFunction('size_t', left, 1, 0) CompiledSubDomain('near(x[0], 0.5)').mark(left_bdries, 1) right = EmbeddedMesh(cell_f, 2) right_bdries = MeshFunction('size_t', right, 1, 0) CompiledSubDomain('near(x[0], 0.5)').mark(right_bdries, 1) # Visual inspection - collect interface midpoints _, f2v = (left.init(1, 0), left.topology()(1, 0)) idx, = np.where(left_bdries.array() == 1) midpoints = np.mean(left.coordinates()[np.row_stack([f2v(i) for i in idx])], axis=1) midpoints = midpoints[np.argsort(midpoints[:, 1])]
def foo(n): mesh = UnitSquareMesh(n, n) V = VectorFunctionSpace(mesh, 'CG', 2) x, y = sp.symbols('x y') u = Function(V) n = Constant((1, 0)) subs = {u: sp.Matrix([sp.sin(x**2 + y**2), sp.cos(x**2 - 3 * y**2)])} displacement = ulfy.Expression(u, subs=subs, degree=2) stress = ulfy.Expression(sym(grad(u)), subs=subs, degree=2) traction = ulfy.Expression(dot(n, sym(grad(u))), subs=subs, degree=2) cell_f = MeshFunction('size_t', mesh, 2, 2) CompiledSubDomain('x[0] < 0.5+DOLFIN_EPS').mark(cell_f, 1) left = EmbeddedMesh(cell_f, 1) left_bdries = MeshFunction('size_t', left, 1, 0) CompiledSubDomain('near(x[0], 0.5)').mark(left_bdries, 1) interface = EmbeddedMesh(left_bdries, 1) right = EmbeddedMesh(cell_f, 2) right_bdries = MeshFunction('size_t', right, 1, 0) CompiledSubDomain('near(x[0], 0.5)').mark(right_bdries, 2) # Don't redefine! interface.compute_embedding(right_bdries, 2) # Okay so let's be on left with displacement V = VectorFunctionSpace(left, 'CG', 2) u = interpolate(displacement, V) # Is this a way to compute the stress? Q = VectorFunctionSpace(left, 'DG', 1) q = TestFunction(Q) # Use DG1? n = FacetNormal(left) expr = dot(sym(grad(u)), n) ds_ = Measure('ds', domain=left, subdomain_data=left_bdries) b = assemble(inner(expr, q) * ds_(1)) # This B = VectorFunctionSpace(interface, 'DG', 1) # Match the test function of form MTb = Function(B) trace_matrix(Q, B, interface).mult(b, MTb.vector()) # Now y is in the dual. we need it back x = Function(B) M = assemble(inner(TrialFunction(B), TestFunction(B)) * dx) solve(M, x.vector(), MTb.vector()) # x is now expr|_interface print assemble(inner(x - traction, x - traction) * dx) # What I want to do next is to extend it to right domain so that # if I there perform surface integral over the interface I get close # to what the left boundary said R = VectorFunctionSpace(right, 'DG', 1) extend = Function(R) trace_matrix(R, B, interface).transpmult(x.vector(), extend.vector()) import matplotlib.pyplot as plt xx = np.sort([cell.midpoint().array()[1] for cell in cells(interface)]) yy = np.array([x(0.5, xxi)[0] for xxi in xx]) zz = np.array([traction(0.5, xxi)[0] for xxi in xx]) rr = np.array([extend(0.5, xxi)[0] for xxi in xx]) plt.figure() plt.plot(xx, yy, label='num') plt.plot(xx, zz, label='true') plt.plot(xx, rr, label='rec') plt.legend() plt.show()
return df.PETScMatrix(mat) # -------------------------------------------------------------------- if __name__ == '__main__': from sleep.utils import EmbeddedMesh from sleep.utils import transpose_matrix from dolfin import * fs_domain = UnitSquareMesh(32, 32) cell_f = MeshFunction('size_t', fs_domain, 2, 1) CompiledSubDomain('x[0] > 0.5 - DOLFIN_EPS').mark(cell_f, 2) dx_fs = Measure('dx', domain=fs_domain, subdomain_data=cell_f) fluid = EmbeddedMesh(cell_f, 1) solid = EmbeddedMesh(cell_f, 2) u = Expression('x[0]+2*x[1]', degree=1) # Test putting scalar to subdomain ------------------------------- FS = FunctionSpace(fs_domain, 'CG', 1) u_fs = interpolate(u, FS) # The subdomain space F = FunctionSpace(fluid, 'CG', 1) R = restriction_matrix(FS, F, fluid) # Now we can tranport u_f = Function(F) R.mult(u_fs.vector(), u_f.vector()) e = inner(u_f - u, u_f - u)*dx # Implied, fluid
from dolfin import * import numpy as np class Foo(Expression): def eval_cell(self, values, x, ufc_cell): values[0] = ufc_cell.index f = Foo(degree=0) mesh = UnitSquareMesh(32, 32) facet_f = MeshFunction('size_t', mesh, 1, 0) DomainBoundary().mark(facet_f, 1) Tmesh = EmbeddedMesh(facet_f, 1) V = FunctionSpace(mesh, 'DG', 1) u = interpolate(f, V) TV = FunctionSpace(Tmesh, 'DG', 1) Tu = Function(TV) trace_matrix(V, TV, Tmesh).mult(u.vector(), Tu.vector()) assert assemble(inner(f - Tu, f - Tu) * ds) < 1E-13 # What if it is discontinuous in vertices class Foo(Expression): def eval_cell(self, values, x, ufc_cell): values[0] = ufc_cell.index + np.linalg.norm(x)
def PVSbrain_simulation(args): """ Test case for simple diffusion from PVS to the brain. Outputs : - a logfile with information about the simulation - .pvd files at specified args.toutput time period with the u, p and c fields in stokes domain and u, p , q, phi and c fields in Biot domain - .csv files of u, p, c 1D array of the u, p, c fields on the middle line of the PVS - .csv files of the total mass in the domain, mass flux from the brain to sas, brain to PVS, PVS to SAS """ # 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 + '/fields'): os.makedirs(outputfolder + '/fields') # Create output files #pvd files c_out = File(outputfolder + 'fields' + '/c.pvd') facets_out_fluid = File(outputfolder + 'fields' + '/facets_fluid.pvd') facets_out_solid = File(outputfolder + 'fields' + '/facets_solid.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 + '/', 'PVSBrain_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( "Test case of simple diffusion from PVS to the brain using the diffusion-advection solver" )) 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 Rbrain = args.radius_brain # centimeters L = args.length # centimeters logging.info('Vessel radius : %e cm' % Rv) logging.info('PVS radius : %e cm' % Rpvs) logging.info('Brain radius : %e cm' % Rbrain) logging.info('length : %e cm' % L) #Mesh logging.info('\n * Mesh') #number of cells in the radial direction fluid domain Nr = args.N_radial_fluid #number of cells in the radial direction biot domain Nr_biot = args.N_radial_biot s_biot = args.biot_progression 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 PVS: %e' % Nr) logging.info('N radial Biot: %e' % Nr_biot) logging.info('progression parameter in biot: %e' % s_biot) #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) dt_advdiff = dt 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 * Porous medium properties') porosity_0 = args.biot_porosity tortuosity = args.biot_tortuosity dt_solid = dt logging.info('initial porosity: %e ' % porosity_0) logging.info('tortuosity: %e ' % tortuosity) ## The tracer is solver in the full domain, so it has two subdomains # 1 for solid # 0 for fluid tracer_parameters = { 'kappa_0': D, 'kappa_1': D * tortuosity, 'dt': dt_advdiff, 'nsteps': 1 } # Mesh logging.info(title1('Meshing')) meshing = args.mesh_method ##### Meshing method : regular if meshing == 'regular': # Create a mesh using Rectangle mesh : all the cells are regulars but this means a lot of cells logging.info('cell size : %e cm' % (np.sqrt(DR**2 + DY**2))) # Create a rectangle mesh with Nr + Nsolid cells in the radias direction and Nl cells in the axial direction # the geometrical progression for the solid mesh is s #Creation of the uniform mesh Rext = 1 + Nr_biot / Nr mesh = RectangleMesh(Point(0, 0), Point(L, Rext), Nl, Nr + Nr_biot) x = mesh.coordinates()[:, 0] y = mesh.coordinates()[:, 1] #Deformation of the mesh def deform_mesh(x, y): transform_fluid = Rv + (Rpvs - Rv) * y transform_solid = Rpvs + (Rbrain - Rpvs) * ((y - 1) / (Rext - 1))**s_biot yp = np.where(y <= 1, transform_fluid, transform_solid) return [x, yp] x_bar, y_bar = deform_mesh(x, y) xy_bar_coor = np.array([x_bar, y_bar]).transpose() mesh.coordinates()[:] = xy_bar_coor mesh.bounding_box_tree().build(mesh) else: ##### Meshing method : gmsh with box for refinement 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(0, Rv, 0) b = factory.addPoint(L, Rv, 0) c = factory.addPoint(L, Rpvs, 0) d = factory.addPoint(0, Rpvs, 0) e = factory.addPoint(L, Rbrain, 0) f = factory.addPoint(0, Rbrain, 0) fluid_lines = [ factory.addLine(*p) for p in ((a, b), (b, c), (c, d), (d, a)) ] named_lines = dict( zip(('bottom', 'pvs_right', 'interface', 'pvs_left'), fluid_lines)) fluid_loop = factory.addCurveLoop(fluid_lines) fluid = factory.addPlaneSurface([fluid_loop]) solid_lines = [ factory.addLine(*p) for p in ((d, c), (c, e), (e, f), (f, d)) ] named_lines.update( dict( zip(('interface', 'brain_right', 'brain_top', 'brain_left'), solid_lines))) solid_loop = factory.addCurveLoop(solid_lines) solid = factory.addPlaneSurface([solid_loop]) factory.synchronize() tags = {'cell': {'F': 1, 'S': 2}, 'facet': {}} model.addPhysicalGroup(2, [fluid], 1) model.addPhysicalGroup(2, [solid], 2) 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 * 2) field.setNumber(fid, 'VOut', DR * 50) field.setNumber(fid, 'Thickness', (Rpvs - Rv) / 4) boxes.append(fid) # Combine field.add('Min', fid + 1) field.setNumbers(fid + 1, 'FieldsList', boxes) field.setAsBackgroundMesh(fid + 1) model.occ.synchronize() h5_filename = outputfolder + '/mesh.h5' mesh_model2d(model, tags, h5_filename) mesh, markers, lookup = load_mesh2d(h5_filename) from IPython import embed embed() gmsh.finalize() ## Define subdomains x = mesh.coordinates()[:, 0] y = mesh.coordinates()[:, 1] tol = 1e-7 class Omega_0(SubDomain): def inside(self, x, on_boundary): return x[1] < Rpvs + tol class Omega_1(SubDomain): def inside(self, x, on_boundary): return x[1] > Rpvs - tol subdomains = MeshFunction("size_t", mesh, mesh.topology().dim(), 0) subdomain_0 = Omega_0() subdomain_1 = Omega_1() subdomain_0.mark(subdomains, 0) subdomain_1.mark(subdomains, 1) mesh_f = EmbeddedMesh(subdomains, 0) mesh_s = EmbeddedMesh(subdomains, 1) ## Define boundaries solid_bdries = MeshFunction("size_t", mesh_s, mesh_s.topology().dim() - 1, 0) fluid_bdries = MeshFunction("size_t", mesh_f, mesh_f.topology().dim() - 1, 0) full_bdries = MeshFunction("size_t", mesh, mesh.topology().dim() - 1, 0) # Label facets class Boundary_left_fluid(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[0], 0, tol) and (x[1] <= Rpvs + tol ) #left fluid class Boundary_right_fluid(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[0], L, tol) and (x[1] <= Rpvs + tol ) # right fluid class Boundary_left_solid(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[0], 0, tol) and (x[1] >= Rpvs - tol ) #left solid class Boundary_right_solid(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[0], L, tol) and (x[1] >= Rpvs - tol ) # right solid class Boundary_bottom(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[1], Rv, tol) #bottom class Boundary_top(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[1], Rbrain, tol) #top class Boundary_interface(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[1], Rpvs, tol) #interface #todo : keep separate F and S for right and left in the full bdries btop = Boundary_top() bbottom = Boundary_bottom() bleft_fluid = Boundary_left_fluid() bright_fluid = Boundary_right_fluid() bleft_solid = Boundary_left_solid() bright_solid = Boundary_right_solid() binterface = Boundary_interface() bbottom.mark(fluid_bdries, 2) binterface.mark(fluid_bdries, 4) bleft_fluid.mark(fluid_bdries, 1) bright_fluid.mark(fluid_bdries, 3) binterface.mark(solid_bdries, 4) bright_solid.mark(solid_bdries, 5) btop.mark(solid_bdries, 6) bleft_solid.mark(solid_bdries, 7) bleft_fluid.mark(full_bdries, 1) bleft_solid.mark(full_bdries, 7) bbottom.mark(full_bdries, 2) bright_fluid.mark(full_bdries, 3) bright_solid.mark(full_bdries, 5) btop.mark(full_bdries, 6) facet_lookup = { 'F_left': 1, 'F_bottom': 2, 'F_right': 3, 'Interface': 4, 'S_left': 7, 'S_top': 6, 'S_right': 5 } facets_out_fluid << fluid_bdries facets_out_solid << solid_bdries # define the domain specific parameters for the tracer # NOTE: Here we do P0 projection dx = Measure('dx', domain=mesh, subdomain_data=subdomains) CoefSpace = FunctionSpace(mesh, 'DG', 0) q = TestFunction(CoefSpace) # Remove coef = 'kappa' fluid_coef = tracer_parameters.pop('%s_0' % coef) solid_coef = tracer_parameters.pop('%s_1' % coef) form = ((1 / CellVolume(mesh)) * fluid_coef * q * dx(0) + (1 / CellVolume(mesh)) * solid_coef * q * dx(1)) tracer_parameters[coef] = Function(CoefSpace, assemble(form)) #FEM space logging.info(title1("Set FEM spaces")) logging.info('\n * Tracer') #### Todo : I would like to be able to have discontinuous concentration when we will have the membrane #### Beter to solve in two domains or one domain with discontinuous lagrange element ? Ct_elm = FiniteElement('Lagrange', triangle, 1) Ct = FunctionSpace(mesh, Ct_elm) logging.info('Concentration : "Lagrange", triangle, 1') #Advection velocity FS_advvel = VectorFunctionSpace(mesh, 'CG', 2) # Setup of boundary conditions logging.info(title1("Boundary conditions")) ## to do : allow zero concentration if fluid BC is free on the right logging.info('\n * Tracer concentration') bcs_tracer = { 'concentration': [(facet_lookup['S_top'], Constant(0))], 'flux': [ (facet_lookup['S_left'], Constant(0)), (facet_lookup['S_right'], Constant(0)), (facet_lookup['F_right'], Constant(0)), (facet_lookup['F_left'], Constant(0)), (facet_lookup['F_bottom'], Constant(0)), ] } # Initialisation : # 1 in the PVS cf_0 = Expression('x[1]<= Rpvs ? 1 : 0 ', degree=2, a=1 / 2 / sigma_gauss**2, b=xi_gauss, Rv=Rv, Rpvs=Rpvs) c_n = project(cf_0, Ct) # File('initial_reg.pvd') << c_n exit() #Initial deformation of the fluid domain # We start at a time shift tshift = 0 # c_n.rename("c", "tmp") c_out << (c_n, 0) ############# RUN ############3 logging.info(title1("Run")) # Time loop time = tshift timestep = 0 # Here I dont know if there will be several dt for advdiff and fluid solver while time < tfinal + tshift: time += dt timestep += 1 print('time', time - tshift) # Solve tracer problem tracer_parameters["T0"] = time advection_velocity = project(Constant((0, 0)), FS_advvel) c_, T0 = solve_adv_diff(Ct, velocity=advection_velocity, phi=Constant(0.2), f=Constant(0), c_0=c_n, phi_0=Constant(0.2), bdries=full_bdries, bcs=bcs_tracer, parameters=tracer_parameters) # Update current solution c_n.assign(c_) # 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 c_n.rename("c", "tmp") c_out << (c_n, time - tshift) advection_velocity.rename("adv_vel", "tmp") File(outputfolder + 'fields' + '/adv_vel.pvd') << (advection_velocity, time - tshift)