Example #1
0
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))
Example #2
0
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(args.output_folder + '/' + args.job_name + '_pressure.txt',
                 'w')
    csv_u = open(args.output_folder + '/' + args.job_name + '_velocity.txt',
                 'w')
    csv_c = open(
        args.output_folder + '/' + args.job_name + '_concentration.txt', 'w')
    csv_rv = open(args.output_folder + '/' + args.job_name + '_radius.txt',
                  'w')

    csv_mass = open(args.output_folder + '/' + args.job_name + '_mass.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 = args.output_folder + '/' + args.job_name + '_PVSinfo.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

    # initialise logging

    logging.info(
        ' ______     ______        _                 _       _   _             '
    )
    logging.info(
        '|  _ \ \   / / ___|   ___(_)_ __ ___  _   _| | __ _| |_(_) ___  _ __  '
    )
    logging.info(
        "| |_) \ \ / /\___ \  / __| | '_ ` _ \| | | | |/ _` | __| |/ _ \| '_ \ "
    )
    logging.info(
        '|  __/ \ V /  ___) | \__ \ | | | | | | |_| | | (_| | |_| | (_) | | | |'
    )
    logging.info(
        '|_|     \_/  |____/  |___/_|_| |_| |_|\__,_|_|\__,_|\__|_|\___/|_| |_|\n'
    )

    logging.info(
        title1(
            "Simulation of the PVS flow and tracer transport using stoke solver and 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
    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)

    # test presence of the SAS compartment on the mesh
    isSAS = args.SAS

    if isSAS:
        logging.info('Add a SAS compartment on the left')
        Rsas = args.radius_sas + Rv
        Lsas = args.length_sas
        logging.info('SAS length : %e cm' % Lsas)
        logging.info('SAS radius : %e cm' % Rsas)

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

    # Setup of boundary conditions
    logging.info(title1("Boundary conditions"))

    logging.info('\n * Cross section area parameters')

    if args.cycle:
        logging.info('frequency and amplitude data from cycle ' + args.cycle)
        timedependentfa = True
    else:
        timedependentfa = False
        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))

    if timedependentfa:

        ##
        logging.info('creation of cycle')

        cycleObj = ReadCycle('../stages/cycles.yml', args.cycle)
        totalcycletime = np.sum(cycleObj.durations)
        spantime, listspana, listspanf, spanRv, spanh0, spanRpvs = cycleObj.generatedata(
            int(tfinal / totalcycletime) + 1)

        logging.info('*** Simulation of %s cycle ' % cycleObj.name)

        # adjust last time in order to be able to interpolate
        #spantime[-1]=max(tfinal+2*dt,spantime[-1])

        from scipy.interpolate import interp1d
        varflist = {}
        varalist = {}

        for freq in listspanf:
            varflist[freq] = interp1d(spantime, listspanf[freq])
            varalist[freq] = interp1d(spantime, listspana[freq])

        varh0 = interp1d(spantime, spanh0)
        varRpvs = interp1d(spantime, spanRpvs)

        #varda=interp1d(spantime,dadt,kind="previous")

        fs = 1 / dt  # test if same result if multiplying by 10 ?

        time = 0 + np.arange(
            int((tfinal + 2 * dt) * fs)
        ) / fs  # longer than the simulation time because we need tn+1 for the U ALE computation

        modulation = {}
        for freq in listspanf:
            modulation[freq] = varalist[freq](time) * np.sin(
                2 * np.pi * np.cumsum(varflist[freq](time)) / fs)

        OuterRadius = varRpvs(time)
        InnerRadius = varRpvs(time) - varh0(time) * (
            1 + modulation['cardiac'] + modulation['resp'] + modulation['LF'] +
            modulation['VLF'])

        # define the thickness interpolation function
        interph0 = interp1d(
            time,
            varh0(time) * (1 + modulation['cardiac'] + modulation['resp'] +
                           modulation['LF'] + modulation['VLF']))

        ## More easy here to take the numerical derivative of the radius
        ##dadt= np.array(list(np.diff(vara(time))/np.diff(time))+[0.0])
        ##dRadiusdt= -(Rpvs-Rv)*(dadt*np.sin(2*np.pi*np.cumsum(varf(time))/fs)+vara(time)*np.cos((2*np.pi*np.cumsum(varf(time))/fs))*(2*np.pi*varf(time)))
        douterRadiusdt = np.array(
            list(np.diff(OuterRadius) / np.diff(time)) + [0.0])
        dinnerRadiusdt = np.array(
            list(np.diff(InnerRadius) / np.diff(time)) + [0.0])

        # define an expression for the radius and the derivative

        class Interp(UserExpression):
            def __init__(self, x, y, **kwargs):
                super().__init__(self, **kwargs)
                self.interp = interp1d(x, y)
                self.tn = 0

            def eval(self, values, x):
                values[0] = 0
                values[1] = self.interp(self.tn)

            def value_shape(self):
                return (2, )

        class Interpdiff(UserExpression):
            def __init__(self, x, y, **kwargs):
                super().__init__(self, **kwargs)
                self.interp = interp1d(x, y)
                self.tn = 0
                self.tnp1 = dt

            def eval(self, values, x):
                values[0] = 0
                values[1] = self.interp(self.tnp1) - self.interp(self.tn)

            def value_shape(self):
                return (2, )

        interpRpvs = Interp(time, OuterRadius, degree=1)
        interpRv = Interp(time, InnerRadius, degree=1)

        #initial values for Rv and Rpvs
        uale_top = Interpdiff(time, OuterRadius, degree=1)
        uale_bottom = Interpdiff(time, InnerRadius, degree=1)
        vf_top = Interp(time, douterRadiusdt, degree=1)
        vf_bottom = Interp(time, dinnerRadiusdt, degree=1)

        Rvfunction = interp1d(time, InnerRadius)
        Rpvsfunction = interp1d(time, OuterRadius)
        dRvdtfunction = interp1d(time, dinnerRadiusdt)

    else:

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

        vf_top = Constant((0, 0))
        uale_top = Constant((0, 0))

        ## Is there a better way to do that ?
        Rvfunction = lambda t: functionR.subs(tn, t).evalf()
        Rpvsfunction = lambda t: Rpvs
        dRvdtfunction = lambda t: functionV.subs(tn, t).evalf()

    if isSAS:
        import sympy
        tn = sympy.symbols("tn")
        sin = sympy.sin
        # We add the possibility for rigid moion of the brain
        a_rigid = args.arigid  #cm
        f_rigid = args.frigid  #Hz
        logging.info('ridig motion of the brain, amplitude : %.2e um' %
                     a_rigid)
        logging.info('ridig motion of the brain, frequency : %.2e Hz' %
                     f_rigid)

        functionY = a_rigid * sin(2 * pi * f_rigid * tn)
        functionVbone = sympy.diff(functionY, tn)  # velocity
        V_bone = sympy.printing.ccode(functionVbone)

        vf_bone = Expression(('0', V_bone), tn=0, degree=2)

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

    sas_bc = args.sasbc
    init_concentration_type = args.c0init
    init_concentration_value = args.c0valuePVS

    logging.info('Left BC scenario :', sas_bc)

    if lateral_bc == 'free':
        logging.info('Right : zero concentration')
    else:
        productionrate = args.productionrate
        if productionrate:
            logging.info(
                'Right : imposed solute production rate : %e ([c]/s)' %
                args.productionrate)
        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')

    # Mesh
    logging.info(title1('Meshing'))

    if isSAS:
        Rv = Rvfunction(0)
        Rpvs = Rpvsfunction(0)
        DR = (Rpvs - Rv) / Nr

        logging.info('cell size : %e cm' % (DR))

        from sleep.mesh import mesh_model2d, load_mesh2d, set_mesh_size
        import gmsh

        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

        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', Rvfunction(0))
        field.setNumber(fid, 'YMax', Rpvsfunction(0))
        field.setNumber(fid, 'VIn', DR)
        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()

    else:
        # simple PVS mesh
        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, Rvfunction(0)),
                               Point(L, Rpvsfunction(0)), Nl, Nr)

        ## Refinement at the SAS boundary
        if args.refineleft:
            x = mesh_f.coordinates()[:, 0]
            y = mesh_f.coordinates()[:, 1]

            #Deformation of the mesh

            def deform_mesh(x, y):
                x = L * (x / L)**2.5
                return [x, y]

            x_bar, y_bar = deform_mesh(x, y)
            xy_bar_coor = np.array([x_bar, y_bar]).transpose()
            mesh_f.coordinates()[:] = xy_bar_coor
            mesh_f.bounding_box_tree().build(mesh_f)

    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

    if isSAS:

        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_out': 1,
            'pvs_tissue': 2,
            'vessel': 3,
            'sas_bone': 4,
            'sas_tissue': 5,
            'pvs_end': 6
        }

    else:
        # simple PVS mesh

        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 = {
            'sas_out': 1,
            'vessel': 2,
            'pvs_end': 3,
            'pvs_tissue': 4
        }

    facets_out << fluid_bdries

    # Now we wire up

    rate_prod = Expression('rate/surface',
                           rate=productionrate,
                           surface=1,
                           degree=1)

    if lateral_bc == 'free':
        bcs_fluid = {
            'velocity': [(facet_lookup['vessel'], vf_bottom),
                         (facet_lookup['pvs_tissue'], vf_top)],
            'traction': [],
            'pressure': [(facet_lookup['sas_out'], Constant(0)),
                         (facet_lookup['pvs_end'], 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['pvs_tissue'], vf_top)],
            'traction': [],
            'pressure': [(facet_lookup['sas_out'], Constant(0)),
                         (facet_lookup['pvs_end'], Rpressure)]
        }
    else:
        bcs_fluid = {
            'velocity':
            [(facet_lookup['vessel'], vf_bottom),
             (facet_lookup['pvs_tissue'], vf_top),
             (facet_lookup['pvs_end'], Constant(
                 (0, 0)))],  # I would like only normal flow to be zero 
            'traction': [],
            'pressure': [(facet_lookup['sas_out'], Constant(0))]
        }

    #### This is overwritten later depending on the scenario

    bcs_tracer_in = {
        'concentration': [(facet_lookup['sas_out'], 0)],
        'flux': [(facet_lookup['pvs_end'], rate_prod),
                 (facet_lookup['pvs_tissue'], Constant(0)),
                 (facet_lookup['vessel'], Constant(0))]
    }

    bcs_tracer_out = {
        'concentration': [],
        'flux': [(facet_lookup['sas_out'], Constant(0)),
                 (facet_lookup['pvs_end'], rate_prod),
                 (facet_lookup['pvs_tissue'], Constant(0)),
                 (facet_lookup['vessel'], Constant(0))]
    }

    # todo : add possibility to have other BC at pvs_end
    #if lateral_bc=='free' :
    #    bcs_tracer = {'concentration': [(facet_lookup['pvs_end'], Constant(0)),
    #                                    (facet_lookup['sas_out'], c_SAS)],
    #                'flux': [(facet_lookup['pvs_tissue'], Constant(0)),
    #                        (facet_lookup['vessel'], Constant(0))]}

    # add BC on the extra boundary in the mesh of the SAS
    if isSAS:
        bcs_fluid['velocity'].append((facet_lookup['sas_bone'], vf_bone))
        bcs_fluid['velocity'].append(
            (facet_lookup['sas_tissue'], Constant((0, 0))))

        bcs_tracer_in['flux'].append((facet_lookup['sas_bone'], Constant(0)))
        bcs_tracer_in['flux'].append((facet_lookup['sas_tissue'], Constant(0)))
        bcs_tracer_out['flux'].append((facet_lookup['sas_bone'], Constant(0)))
        bcs_tracer_out['flux'].append(
            (facet_lookup['sas_tissue'], Constant(0)))

    # BC for ALE (not used anymore)
    bcs_ale = {
        'dirichlet': [(facet_lookup['vessel'], uale_bottom),
                      (facet_lookup['pvs_tissue'], uale_top)],
        'neumann': [(facet_lookup['sas_out'], Constant((0, 0))),
                    (facet_lookup['pvs_end'], Constant((0, 0)))]
    }

    # We collect the time dependent BC for update
    driving_expressions = [uale_bottom, vf_bottom, uale_top, vf_top]

    if isSAS:
        driving_expressions.append(vf_bone)

    #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')

    # Initialisation :
    logging.info(title1("Initialisation"))

    c_SAS = Expression('m/VCSF', m=0, VCSF=40e-3, degree=2)

    #initial concentration in SAS
    if sas_bc == 'scenarioA':
        cSAS = 0
    else:
        cSAS = args.c0valueSAS

    # number of vessels used for mass balance
    Nvessels = 6090
    # initial volume of CSF in PVS
    z, r = SpatialCoordinate(mesh_f)
    ds = Measure('ds', domain=mesh_f, subdomain_data=fluid_bdries)
    n = FacetNormal(mesh_f)

    # volume of pvs
    VPVS = 2 * np.pi * assemble(Constant(1.0) * r * dx(mesh_f))

    # initial volume of CSF in SAS : assumed to be 10 times larger than volume in PVS
    VCSF = 10 * VPVS  #40e-3

    # initial pressure of the CSF
    PCSF = 4  # mmHg
    # initial volume of arterial blood
    Vblood = 4e-3  # ml
    # equivalent vessel length used for the compliance function and assessement of ICP
    leq = Vblood / (np.pi * Rvfunction(0)**2)

    # initial tracer mass in the CSF
    m = cSAS * VCSF

    # constant production of CSF
    Qprod = 6e-6  # ml/s

    # Outflow resistance
    Rcsf = 5 / 1.7e-5  # mmHg/(ml/s)
    # CSF compliance
    Ccsf = 1e-3  #ml/mmHg

    if sas_bc == 'scenarioA':
        logging.info('Left : zero concentration')
        # initial outflow of CSF (not used, just for output file)
        Qout = 0
    elif sas_bc == 'scenarioB':
        logging.info('Left : mass conservation, no CSF outflow')
        # initial outflow of CSF
        Qout = 0
    elif sas_bc == 'scenarioC':
        logging.info('Left : mass conservation, constant CSF outflow')
        # initial outflow of CSF
        Qout = Qprod
    elif sas_bc == 'scenarioD':
        logging.info(
            'Left : mass conservation, pressure dependent CSF outflow')
        # initial outflow of CSF
        Qout = Qprod
        # venous pressure
        Pss = PCSF - Qout * Rcsf

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

    if init_concentration_type == 'gaussian':
        logging.info("Concentration : Gaussian profile")
        logging.info("                Centered at xi = %e" % xi_gauss)
        logging.info("                STD parameter = %e" % sigma_gauss)
        logging.info("                Max value=%e" % init_concentration_value)

        c_0 = Expression('c0*exp(-a*pow(x[0]-b, 2)) ',
                         degree=1,
                         a=1 / 2 / sigma_gauss**2,
                         b=xi_gauss,
                         c0=init_concentration_value)
        c_n = project(c_0, Ct)
    elif init_concentration_type == 'constant':
        logging.info("Concentration : Uniform profile")

        if isSAS:
            logging.info("Value in PVS=%e" % init_concentration_value)
            logging.info("Value in SAS=%e" % cSAS)
            c_0 = Expression('x[0]>0 ? cPVS : cSAS ',
                             degree=2,
                             cPVS=init_concentration_value,
                             cSAS=cSAS)
            c_n = project(c_0, Ct)
        else:
            logging.info("Value=%e" % init_concentration_value)
            c_n = project(Constant(init_concentration_value), Ct)
    elif init_concentration_type == 'null':
        logging.info("Concentration : zero in the vessel")
        c_n = project(Constant(0), Ct)
    else:
        logging.info("Concentration : Uniform profile (default)")
        logging.info("Value=%e" % 0)
        ## Initialisation
        c_n = project(Constant(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 ############

    logging.info(title1("Run"))

    # Time loop
    time = 0.
    timestep = 0

    z, r = SpatialCoordinate(mesh_f)
    ds = Measure('ds', domain=mesh_f, subdomain_data=fluid_bdries)
    n = FacetNormal(mesh_f)

    # volume of pvs
    volume = 2 * np.pi * assemble(Constant(1.0) * r * dx(mesh_f))
    # integral of concentration
    intc = 2 * np.pi * assemble(r * c_n * dx(mesh_f))

    # tracer mass out of the system
    mout = 0

    csv_mass.write('%s, %s, %s, %s, %s, %s, %s, %s, %s\n' %
                   ('time', 'mass PVS', 'mass CSF', 'mass out', 'Total mass',
                    'PVS volume', 'CSF volume', 'P csf', 'Q out'))
    csv_mass.write('%e, %e, %e, %e, %e, %e, %e, %e, %e\n' %
                   (time, Nvessels * intc, m, mout, Nvessels * intc + m + mout,
                    Nvessels * volume, VCSF, PCSF, Qout))

    # ALE deformation function
    expressionDeformation = Expression((
        "0",
        "x[1]<=rpvs ? (x[1]-rpvs)/(rpvs-rvessel)*htarget+rpvstarget-x[1]:rpvstarget-rpvs"
    ),
                                       rvessel=0,
                                       rpvs=1,
                                       rpvstarget=1,
                                       htarget=1,
                                       degree=1)

    # Extend normal to 3d as GradAxisym(scalar) is 3-vector
    normal = as_vector((Constant(-1), Constant(0), Constant(0)))

    # Here I dont know if there will be several dt for advdiff and fluid solver
    while time < tfinal:

        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':
            Flow = assemble(2 * pi * r * dot(uf_n, n) *
                            ds(facet_lookup['pvs_end']))

            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)

        # Or just compute the deformation
        xy = mesh_f.coordinates()
        x, y = xy.T

        expressionDeformation.rpvstarget = Rpvsfunction(time)
        expressionDeformation.htarget = Rpvsfunction(time) - Rvfunction(time)
        expressionDeformation.rvessel = min(
            y[x > 0])  # We look only in the PVS (x>0) not the SAS
        expressionDeformation.rpvs = max(y[x > 0])  #

        #eta_f = interpolate(expressionDeformation,VectorFunctionSpace(mesh_f,"CG",1))
        eta_f = project(expressionDeformation, Va)

        ALE.move(mesh_f, eta_f)
        mesh_f.bounding_box_tree().build(mesh_f)

        # update the coordinates
        z, r = SpatialCoordinate(mesh_f)
        ds = Measure('ds', domain=mesh_f, subdomain_data=fluid_bdries)
        n = FacetNormal(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)

        # Solve tracer problem
        tracer_parameters["T0"] = time
        tracer_parameters["nsteps"] = 1
        tracer_parameters["dt"] = dt

        # If the fluid is exiting the PVS we compute the amount of mass entering the SAS. The tracer left BC is free.
        # If the fluid is entering the PVS then we impose the concentration in the SAS at the left BC.

        #Fluid flow at the BC
        FluidFlow = assemble(2 * pi * r * dot(uf_, n) *
                             ds(facet_lookup['sas_out']))

        #  n is directed in the outward direction : is it ?

        print('fluid flow : ', FluidFlow)

        if FluidFlow > 0:
            # then the fluid is going out and we impose natural BC for concentration
            bcs_tracer = bcs_tracer_out
        else:
            # then the fluid is going in and we impose the SAS concentration
            cmean = assemble(
                2 * pi * r * c_n * ds(facet_lookup['sas_out'])) / assemble(
                    2 * pi * r * Constant(1) * ds(facet_lookup['sas_out']))
            # we allow the possibility to use a relaxation here
            alpha = 0.  # 0 means no relaxation
            c_imposed = (1 - alpha) * cSAS + alpha * cmean
            c_imposed = max(c_imposed, 0)

            bcs_tracer = bcs_tracer_in
            bcs_tracer['concentration'] = [(facet_lookup['sas_out'],
                                            Constant(c_imposed))]

        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)

        Massflow = assemble(2 * pi * r * dot(uf_ - eta_f / Constant(dt), n) *
                            c_ * ds(facet_lookup['sas_out']))
        Massdiffusion = tracer_parameters["kappa"] * assemble(2 * pi * r * dot(
            cyl.GradAxisym(c_), normal) * ds(facet_lookup['sas_out']))

        if sas_bc == 'scenarioD':

            # update CSF outflow
            Qout = max((PCSF - Pss) / Rcsf, 0)  # valve
            # update CSF pressure
            PCSF += dt / Ccsf * (Qprod - Qout) + np.pi * leq * (
                Rvfunction(time + dt)**2 - Rvfunction(time)**2) / Ccsf

            # link between leq and Nvessels ?

        if sas_bc == 'scenarioA':
            if FluidFlow >= 0:
                # mainly advection
                mout += dt * Nvessels * Massflow
                # lost mass in the PVS due to diffusion
                mout += -dt * Nvessels * Massdiffusion

        else:
            # Advected mass
            m += dt * Nvessels * Massflow - dt * Qout * cSAS
            if FluidFlow >= 0:  # when in-flow we impose c sas at the boundary so no c gradient
                # Adding diffusion
                m += -dt * Nvessels * Massdiffusion

            mout += Qout * cSAS * dt

        # update the volume of CSF
        #VCSF+=dt*Nvessels*FluidFlow
        # should correspond to the volume change due to vessel dilation
        #VCSF+=np.pi*leq*(Rvfunction(time+dt)**2-Rvfunction(time)**2)

        # update tracer concentration in SAS
        cSAS = m / VCSF

        rate_prod.surface = assemble(2 * pi * r * ds(facet_lookup['pvs_end']))

        # 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[x > 0])
            ymax = max(y[x > 0])

            # update the coordinates
            z, r = SpatialCoordinate(mesh_f)
            ds = Measure('ds', domain=mesh_f, subdomain_data=fluid_bdries)
            n = FacetNormal(mesh_f)

            #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_file.flush()

            csv_rv.write(('%e, %e\n') % (time, ymin))
            csv_rv.flush()

            # volume of pvs
            volume = 2 * np.pi * assemble(Constant(1.0) * r * dx(mesh_f))
            # integral of concentration
            intc = 2 * np.pi * assemble(r * c_ * dx(mesh_f))

            csv_mass.write('%e, %e, %e, %e, %e, %e, %e, %e, %e\n' %
                           (time, Nvessels * intc, m, mout, Nvessels * intc +
                            m + mout, Nvessels * volume, VCSF, PCSF, Qout))
            csv_mass.flush()
Example #3
0
        field.setNumber(fid, 'YMin', ymin)
        field.setNumber(fid, 'YMax', ymax)
        field.setNumber(fid, 'VIn', Vin)
        field.setNumber(fid, 'VOut', Vout)
        field.setNumber(fid, 'Thickness', t)

        boxes.append(fid)
        fid += 1
    # Combine
    field.add('Min', fid)
    field.setNumbers(fid, 'FieldsList', boxes)
    field.setAsBackgroundMesh(fid)

    model.occ.synchronize()

    # gmsh.fltk.initialize()
    # gmsh.fltk.run()

    h5_filename = './test/fbb_domain.h5'
    mesh_model2d(model, tags, h5_filename)

    mesh, markers, lookup = load_mesh2d(h5_filename)
    cell_f, facet_f = markers

    check_markers(cell_f, facet_f, lookup, geometry_parameters)

    gmsh.finalize()

    df.File('./test/fbb_cells.pvd') << cell_f
    df.File('./test/fbb_facets.pvd') << facet_f
Example #4
0
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)