Example #1
0
    def set_target_velocity(self, u=None, v=None, U=None):
        """ 
    Set target velocity.

    Accepts a list of surface velocity data, and generates a dolfin
    expression from these.  Then projects this onto the velocity 
    function space.  The sum square error between this velocity 
    and modelled surface velocity is the objective function.
    
    :param u : Surface velocity
    :param v : Surface velocity perpendicular to :attr:`u`
    :param U : 2-D surface velocity data
    
    """
        model = self.model
        S = model.S
        Q = model.Q

        if u != None and v != None:
            model.u_o = project(u, Q)
            model.v_o = project(v, Q)

        elif U != None:
            Smag = project(sqrt(S.dx(0)**2 + S.dx(1)**2 + 1e-10), Q)
            model.U_o.interpolate(U)
            model.u_o = project(-model.U_o * S.dx(0) / Smag, Q)
            model.v_o = project(-model.U_o * S.dx(1) / Smag, Q)
Example #2
0
def project_gradient_neumann(
        f0,
        degree=None,
        mesh=None,
        solver_type='gmres',
        preconditioner_type='default'
    ):
    """Find an approximation to f0 that has the same gradient

    The resulting function also satisfies homogeneous Neumann boundary
    conditions.

    Parameters:    
    f0: the function to approximate
    mesh=None: the mesh on which to approximate it If not provided, the
        mesh is extracted from f0.
    degree=None: degree of the polynomial approximation. extracted
        from f0 if not provided. 
    solver_type='gmres': The linear solver type to use.
    preconditioner_type='default': Preconditioner type to use
    """
    if not mesh: mesh = f0.function_space().mesh()
    element = f0.ufl_element()
    if not degree:
        degree = element.degree()
    CE = FiniteElement('CG', mesh.ufl_cell(), degree)
    CS = FunctionSpace(mesh, CE)
    DE = FiniteElement('DG', mesh.ufl_cell(), degree)
    DS = FunctionSpace(mesh, DE)
    CVE = VectorElement('CG', mesh.ufl_cell(), degree - 1)
    CV = FunctionSpace(mesh, CVE)
    RE = FiniteElement('R', mesh.ufl_cell(), 0)
    R = FunctionSpace(mesh, RE)
    CRE = MixedElement([CE, RE])
    CR = FunctionSpace(mesh, CRE)
    f = fe.project(f0, CS,
                   solver_type=solver_type,
                   preconditioner_type=preconditioner_type)
    g = fe.project(fe.grad(f), CV,
                   solver_type=solver_type,
                   preconditioner_type=preconditioner_type)
    lf = fe.project(fe.nabla_div(g), CS,
                    solver_type=solver_type,
                    preconditioner_type=preconditioner_type)
    tf, tc = TrialFunction(CR)
    wf, wc = TestFunctions(CR)
    dx = Measure('dx', domain=mesh,
                 metadata={'quadrature_degree': min(degree, 10)})
    a = (fe.dot(fe.grad(tf), fe.grad(wf)) + tc * wf + tf * wc) * dx
    L = (f * wc - lf * wf) * dx
    igc = Function(CR)
    fe.solve(a == L, igc,
             solver_parameters={'linear_solver': solver_type,
                                 'preconditioner': preconditioner_type}
    )
    ig, c = igc.sub(0), igc.sub(1)
    igd = fe.project(ig, DS,
                     solver_type=solver_type,
                     preconditioner_type=preconditioner_type)
    return igd
Example #3
0
def output_fn(domain: UDomain, t):
    time.append(t)
    lams.append(
        fe.project(domain.F[0, 0], domain.S).vector().get_local().mean())
    stress.append(
        fe.project(domain.constitutive_model.stress(domain.u)[0, 0],
                   domain.S).vector().get_local().mean())
Example #4
0
    def _residual(u_n, u_nm1, u_nm2, f_n):
        """
        Compute the residual of the PDE at time step n.
        """
        # BUG: project into higher order functional space?
        V = u_n.function_space()
        mesh = V.mesh()
        basis_degree = V.ufl_element().degree()
        U = F.FunctionSpace(mesh, "P", basis_degree + 3)

        f_n_v = F.project(f_n, U).vector()[:]
        u_nm1_v = F.project(u_nm1, U).vector()[:]
        u_nm2_v = F.project(u_nm2, U).vector()[:]

        u_n_ = F.project(u_n, U)
        u_n_v = u_n_.vector()[:]
        ddu_n_v = F.project(u_n_.dx(0).dx(0), U).vector()[:]

        # or use the same order?
        # f_n_ = F.project(f_n, V).vector()[:]
        # u_n_ = F.project(u_n, V).vector()[:]
        # u_nm1_ = F.project(u_nm1, V).vector()[:]
        # u_nm2_ = F.project(u_nm2, V).vector()[:]
        # ddu_n = F.project(u_n.dx(0).dx(0), V).vector()[:]

        R = np.sum(u_n_v - (dt**2) * (c**2) * ddu_n_v - (dt**2) * f_n_v -
                   2 * u_nm1_v + u_nm2_v)
        return R
Example #5
0
def residual(u_n, u_nm1, u_nm2, f_n, dt, c):
    '''
    Compute the residual of the PDE at time step n.
    '''
    # Project into higher-order functional space
    V = u_n.function_space()
    mesh = V.mesh()
    basis_degree = V.ufl_element().degree()
    U = F.FunctionSpace(mesh, 'P', basis_degree + 3)

    f_n_v = F.project(f_n, U).vector()[:]
    u_nm1_v = F.project(u_nm1, U).vector()[:]
    u_nm2_v = F.project(u_nm2, U).vector()[:]

    u_n_ = F.project(u_n, U)
    u_n_v = u_n_.vector()[:]
    ddu_n_v = F.project(u_n_.dx(0).dx(0), U).vector()[:]

    # Or use the space of same degree
    # f_n_ = F.project(f_n, V).vector()[:]
    # u_n_ = F.project(u_n, V).vector()[:]
    # u_nm1_ = F.project(u_nm1, V).vector()[:]
    # u_nm2_ = F.project(u_nm2, V).vector()[:]
    # ddu_n = F.project(u_n.dx(0).dx(0), V).vector()[:]

    R = np.sum(u_n_v - (dt ** 2) * (c ** 2) * ddu_n_v - (dt ** 2) * f_n_v -
               2 * u_nm1_v + u_nm2_v)
    return R
Example #6
0
    def compute_operators(self):
        v = fa.Function(self.V)
        w = fa.Function(self.W)
        F00 = []
        F01 = []
        F10 = []
        F11 = []
        for i in range(self.num_dofs):
            v.vector()[:] = 0
            v.vector()[i] = 1
            F = self.DeformationGradient(v)
            f00 = fa.project(F[0, 0], self.W)
            f01 = fa.project(F[0, 1], self.W)
            f10 = fa.project(F[1, 0], self.W)
            f11 = fa.project(F[1, 1], self.W)
            F00.append(np.array(f00.vector()))
            F01.append(np.array(f01.vector()))
            F10.append(np.array(f10.vector()))
            F11.append(np.array(f11.vector()))

        # Do not forget to add 1 later
        F00 = np.transpose(np.array(F00)) - 1
        F01 = np.transpose(np.array(F01))
        F10 = np.transpose(np.array(F10))
        F11 = np.transpose(np.array(F11)) - 1

        F = [F00, F01, F10, F11]
        np.save(self.args.root_path + '/' +
                self.args.numpy_path + '/robot/' + 'F' + '.npy', F)
        return F
Example #7
0
  def get_projection(self, fn, dg=False, near=False, 
                     bool_data=False, kx=3, ky=3):
    """
    Return a projection of data with filname <fn> on the functionspace.
    If multiple instances of the DataInput class are present, both initialized 
    with identical meshes, the projections returned by this function may be
    used by the same mathematical problem.

    If <dg> is True, use a discontinuous space, otherwise, continuous.

    If <bool_data> is True, convert all values > 0 to 1.
    """
    print "::: getting %s projection :::" % fn

    if dg:
      interp = self.get_nearest_expression(fn, bool_data=bool_data)
      proj   = project(interp, self.func_space_dg)
    
    else:
      if near:
        interp = self.get_nearest_expression(fn, bool_data=bool_data)
        proj   = project(interp, self.func_space)
      else:
        interp = self.get_spline_expression(fn,kx=kx,ky=ky,bool_data=bool_data)
        proj   = project(interp, self.func_space)
        
    return proj
Example #8
0
        def J(c_array, *args):
            """
      Solve adjoint model, calculate gradient
      """
            # dolfin.adjoint method:
            n = len(c_array) / len(config['adjoint']['control_variable'])
            for ii, c in enumerate(config['adjoint']['control_variable']):
                set_local_from_global(c, c_array[ii * n:(ii + 1) * n])
            self.adjoint_instance.solve()

            # This is not the best place for this, but we leave it here for now
            # so that we can see the impact of every line search update on the
            # variables of interest.
            Js = []
            for JJ in self.adjoint_instance.J:
                Js.extend(get_global(assemble(JJ)))
            Js = array(Js)
            # FIXME: project and extrude ruin the output for paraview, we just
            #        save when finished for now.
            U = project(as_vector([model.u, model.v, model.w]))
            dSdt = project(- (model.u*model.S.dx(0) + model.v*model.S.dx(1)) \
                           + model.w + model.adot)
            file_b_pvd << model.extrude(model.beta2, 3, 2)
            file_u_pvd << U
            file_dSdt_pvd << dSdt
            return Js
Example #9
0
    def __init__(self, model, config):
        """
    Calculate each of the component stresses which define the full stress
    of the ice-sheet.
    
    RETURNS:
      tau_lon - longitudinal stress field
      tau_lat - lateral stress field
      tau_bas - frictional sliding stress at the bed
      tau_drv - driving stress of the system 
    
    Note: tau_drv = tau_lon + tau_lat + tau_bas
    
    """
        print "::: initializing 'stokes-balance' solver :::"
        self.model = model
        self.config = config

        Q = model.Q
        u = model.u
        v = model.v
        w = model.w
        S = model.S
        B = model.B
        H = S - B
        eta = model.eta
        beta2 = model.beta2

        # get the values at the bed :
        beta2_e = model.extrude(beta2, 3, 2, Q)
        u_b_e = model.extrude(u, 3, 2, Q)
        v_b_e = model.extrude(v, 3, 2, Q)

        # vertically average :
        etabar = model.vert_integrate(eta, Q)
        etabar = project(model.extrude(etabar, 2, 2, Q) / H)
        ubar = model.vert_integrate(u, Q)
        ubar = project(model.extrude(ubar, 2, 2, Q) / H)
        vbar = model.vert_integrate(v, Q)
        vbar = project(model.extrude(vbar, 2, 2, Q) / H)

        # set the model variables so the physics object can solve it :
        model.beta2_e = beta2_e
        model.u_b_e = u_b_e
        model.v_b_e = v_b_e
        model.etabar = etabar
        model.ubar = ubar
        model.vbar = vbar

        # calculate the driving stress and basal drag once :
        model.tau_d = model.calc_tau_drv(Q)
        model.tau_b = model.calc_tau_bas(Q)

        self.Q = Q

        self.stress_balance_instance = StokesBalance(model, config)
Example #10
0
def evenodd_functions_old(
    omesh,
    degree,
    func,
    width=None,
    evenodd=None
):
    """Break a function into even and odd components

    Required parameters:
    omesh: the mesh on which the function is defined
    degree: the degree of the FunctionSpace 
    func: the Function. This has to be something that fe.interpolate
        can interpolate onto a FunctionSpace or that fe.project can
        project onto a FunctionSpace.
    width: the width of the domain on which func is defined. (If not
        provided, this will be determined from omesh.
    evenodd: the symmetries of the functions to be constructed
        evenodd_symmetries(dim) is used if this is not provided
    """
    SS = FunctionSpace(omesh, 'CG', degree)
    dim = omesh.geometry().dim()
    if width is None:
        stats = mesh_stats(omesh)
        width = stats['xmax']
    if evenodd is None:
        evenodd = evenodd_symmetries(dim)
    try:
        f0 = fe.interpolate(func, SS)
    except TypeError:
        f0 = fe.project(func, SS)
    ffuncs = []
    flips = evenodd_symmetries(dim)
    for flip in (flips):
        fmesh = Mesh(omesh)
        SSf = FunctionSpace(fmesh, 'CG', degree)
        ffunc = fe.interpolate(f0, SSf)
        fmesh.coordinates()[:, :] = (width*flip
                                     + (1 - 2*flip)*fmesh.coordinates())
        fmesh.bounding_box_tree().build(fmesh)
        ffuncs.append(ffunc)
    E = evenodd_matrix(evenodd)
    components = matmul(2**(-dim)*E, ffuncs)
    cs = []
    for c in components:
        try:
            cs.append(fe.interpolate(c, SS))
        except TypeError:
            cs.append(fe.project(c, SS, solver_type='lu'))
    return(cs)
    def write_results_table_row(self, table_filepath):

        with open(table_filepath, "a") as table_file:

            table_file.write(
                str(self.pressure_penalty_factor.__float__()) + "," \
                + str(self.solid_viscosity.__float__()) + "," \
                + str(self.temperature_rayleigh_number.__float__()) + "," \
                + str(self.concentration_rayleigh_number.__float__()) + ", " \
                + str(self.prandtl_number.__float__()) + ", " \
                + str(self.stefan_number.__float__()) + ", " \
                + str(self.schmidt_number.__float__()) + ", " \
                + str(self.liquidus_slope.__float__()) + ", " \
                + str(self.pure_liquidus_temperature.__float__()) + ", " \
                + str(self.regularization_central_temperature_offset.__float__()) + ", " \
                + str(self.regularization_smoothing_parameter.__float__()) + ", " \
                + str(1./float(self.uniform_gridsize)) + ", " \
                + str(self.timestep_size.__float__()) + ", " \
                + str(self.time_order) + ", " \
                + str(self.time) + ", ")

            solid_area = fenics.assemble(self.solid_area_integrand())

            area_above_critical_phi = fenics.assemble(
                self.area_above_critical_phi_integrand())

            solute_mass = fenics.assemble(self.solute_mass_integrand())

            p, u, T, C = self.solution.leaf_node().split(deepcopy=True)

            phi = fenics.project(self.semi_phasefield(T=T, C=C),
                                 mesh=self.mesh.leaf_node())

            Cbar = fenics.project(C * (1. - phi), mesh=self.mesh.leaf_node())

            table_file.write(
                str(solid_area) + ", " \
                + str(area_above_critical_phi) + ", " \
                + str(solute_mass) + ", " \
                + str(p.vector().min()) + ", " \
                + str(p.vector().max()) + ", " \
                + str(fenics.norm(u.vector(), "linf"))  + ", " \
                + str(T.vector().min()) + ", " \
                + str(T.vector().max()) + ", " \
                + str(Cbar.vector().min()) + ", " \
                + str(Cbar.vector().max()) + ", " \
                + str(phi.vector().min()) + ", " \
                + str(phi.vector().max()))

            table_file.write("\n")
Example #12
0
 def solve_pde(self):
     u = fe.Function(self.function_space)
     v = fe.TestFunction(self.function_space)
     u_old = fe.project(self.u_0, self.function_space)
     # Todo: Average right hand side if we choose it non-constant
     flux = self.dt * fe.dot(
         self.composed_diff_coef *
         (fe.grad(u) + self.drift_function(u)), fe.grad(v)) * fe.dx
     bilin_part = u * v * fe.dx + flux
     funtional_part = self.rhs * v * self.dt * fe.dx + u_old * v * fe.dx
     full_form = bilin_part - funtional_part
     num_steps = int(self.T / self.dt) + 1
     bc = fe.DirichletBC(self.function_space, self.u_boundary,
                         MembraneSimulator.full_boundary)
     for n in range(num_steps):
         print("Step %d" % n)
         self.time += self.dt
         fe.solve(full_form == 0, u, bc)
         # fe.plot(u)
         # plt.show()
         # print(fe.errornorm(u_old, u))
         u_old.assign(u)
         self.file << (u, self.time)
     f = fe.plot(u)
     plt.rc('text', usetex=True)
     plt.colorbar(f, format='%.0e')
     plt.title(r'Macroscopic density profile of $u(x,t)$ at $t=1$')
     plt.xlabel(r'$x_1$')
     plt.ylabel(r'$x_2$')
     plt.show()
Example #13
0
    def set_initial_conditions(self):

        self.current_time = self.initial_time

        #Initial condition
        #self.u_n = fe.project(self.boundary_fun, self.function_space)

        if self.mode == 'test':
            print('Setting initial conditions')
            self.boundary_fun.t = self.current_time
            self.u_n = fe.interpolate(self.boundary_fun, self.function_space)

        else:

            if self.dimension == 2:
                self.u_n = fe.project(self.ic_fun, self.function_space)

            if self.dimension == 1:
                #self.u_n = fe.interpolate(fe.Constant(0), self.function_space)
                #self.u_n.vector()[self.dof_map[0]] = 1
                self.boundary_fun.t = self.current_time
                self.u_n = fe.interpolate(self.boundary_fun, self.function_space)

        self.u = fe.Function(self.function_space)

        self.compute_error()
        self.save_snapshot()
Example #14
0
def problem(f, nx=8, ny=8, degrees=[1, 2]):
    """
    Plot u along x=const or y=const for Lagrange elements,
    of given degrees, on a nx times ny mesh. f is a SymPy expression.
    """
    f = sym.printing.ccode(f)
    f = fe.Expression(f, degree=2)
    mesh = fe.RectangleMesh(fe.Point(-1, 0), fe.Point(1, 2), 2, 2)
    for degree in degrees:
        if degree == 0:
            # The P0 element is specified like this in FEniCS
            V = fe.FunctionSpace(mesh, 'DG', 0)
        else:
            # The Lagrange Pd family of elements, d=1,2,3,...
            V = fe.FunctionSpace(mesh, 'P', degree)
        u = fe.project(f, V)
        u_error = fe.errornorm(f, u, 'L2')
        print('||u-f||=%g' % u_error, degree)
        comparison_plot2D(u,
                          f,
                          n=50,
                          value=0.4,
                          variation='x',
                          plottitle='Approximation by P%d elements' % degree,
                          filename='approx_fenics_by_P%d' % degree,
                          tol=1E-3)
Example #15
0
def compute_gradient(u, mesh):
    """Computes the gradient of the value field at the mesh points"""

    W = fen.VectorFunctionSpace(mesh, 'P', 1)
    gradient = fen.project(fen.grad(u), W)

    return gradient
Example #16
0
def problem(f, nx=8, ny=8, degrees=[1,2]):
    """
    Plot u along x=const or y=const for Lagrange elements,
    of given degrees, on a nx times ny mesh. f is a SymPy expression.
    """
    f = sym.printing.ccode(f)
    f = fe.Expression(f)
    mesh = fe.RectangleMesh(
        fe.Point(-1, 0), fe.Point(1, 2), 2, 2)
    for degree in degrees:
        if degree == 0:
            # The P0 element is specified like this in FEniCS
            V = fe.FunctionSpace(mesh, 'DG', 0)
        else:
            # The Lagrange Pd family of elements, d=1,2,3,...
            V = fe.FunctionSpace(mesh, 'P', degree)
        u = fe.project(f, V)
        u_error = fe.errornorm(f, u, 'L2')
        print '||u-f||=%g' % u_error, degree
        comparison_plot2D(
            u, f,
            n=50,
            value=0.4, variation='x',
            plottitle='Approximation by P%d elements' % degree,
            filename='approx_fenics_by_P%d' % degree,
            tol=1E-3)
Example #17
0
    def project(self, t=None):
        """Project solution onto CG subspace.

        soln.project(t)

        project projects the solution at time t onto a CG
        FunctionSpace. The projected function is left in
        self.CGfunction. If argument t is not supplied, the currently
        loaded solution will be used. (In this case, you must have
        called load(t) before calling project().

        project returns self.CGfunction as its value.
        """
        solver_type = 'petsc'
        if t is not None:
            self.load(t)
        if not hasattr(self, 'CS'):
            #
            # create CG FunctionSpace
            #
            self.CE = CGelement(self.fs)
            self.CS = FunctionSpace(self.fs.mesh(), self.CE)
        self.CGfunction = fe.project(self.function,
                                     self.CS,
                                     solver_type=solver_type)
        return self.CGfunction
Example #18
0
    def plot_result(self):
        # Calculate preliminary relationships
        dofmap = self.DV.dofmap()
        cell_dofs = N.array([
            dofmap.cell_dofs(c)[0] for c in N.arange(self.mesh.num_cells())
            if (self.subdomains[c] in v.tissues["tumour"]["indices"])
        ])
        volumes = N.array([
            d.Cell(self.mesh, c).volume()
            for c in N.arange(self.mesh.num_cells())
            if (self.subdomains[c] in v.tissues["tumour"]["indices"])
        ])

        # Create a horizontal axis
        cc_haxis = N.linspace(5000, 1e5, 200)

        # Calculate the tumour volume; this is what we will compare against
        tumour_volume = self.get_tumour_volume()

        # Initialize the output_arrays vector a rescale the x to V/cm
        output_arrays = [cc_haxis / 100]

        # Loop through the electrode pairs
        for i, triple in enumerate(v.electrode_triples):
            # Project the max e values for this triple to DG0 - this forces an evaluation of the function at the mid-point of each tet, DG0's only DOF
            e_dg = d.project(self.max_es[i], self.DV)

            # Calculate the "max e" contribution for each cell
            contributor = N.vectorize(lambda c: e_dg.vector()[c])
            contributions = contributor(cell_dofs)

            # Sum the tet volumes for tets with a midpoint value greater than x, looping over x as e-norm thresholds (also scale to tumour volume)
            elim = N.vectorize(
                lambda x: volumes[contributions > x].sum() / tumour_volume)
            output_arrays.append(elim(cc_haxis))

        # Compile into a convenient array
        output = N.array(zip(*output_arrays))

        # Output cumulative coverage curves as CSV
        N.savetxt('../%s-coverage_curves.csv' % input_mesh, output)

        # Plot the coverage curves
        for (anode, cathode, voltage), a in zip(v.electrode_triples,
                                                output_arrays[1:]):
            P.plot(output_arrays[0], a, label="%d - %d" % (anode, cathode))

        # Draw the plot
        P.draw()
        P.xlabel(r"Threshold level of $|E|$ ($\mathrm{J}$)")
        P.ylabel(r"Fraction of tumour beneath level")

        # Show a legend for the plot
        P.legend(loc=3)

        # Display the plot
        P.savefig('%s-coverage_curves' % input_mesh)
Example #19
0
    def increase_conductivity(self, cond, e):
        # Set up the three way choice function
        intermediate = e * self.k + self.h
        not_less_than = ufl.conditional(ufl.gt(e, self.threshold_irreversible), self.sigma_end, intermediate)
        cond_expression = ufl.conditional(ufl.lt(e, self.threshold_reversible), self.sigma_start, not_less_than)

        # Project this onto the function space
        cond_function = d.project(ufl.Max(cond_expression, cond), cond.function_space())
        cond.assign(cond_function)
Example #20
0
def adapt_coarse_solution_to_fine_solution(scalar,
                                           coarse_solution,
                                           fine_solution,
                                           element,
                                           absolute_tolerance=1.e-2,
                                           maximum_refinement_cycles=6,
                                           circumradius_threshold=0.01):
    """ Refine the mesh of the coarse solution until the interpolation error tolerance is met. """
    adapted_coarse_mesh = fenics.Mesh(coarse_solution.function_space().mesh())

    adapted_coarse_function_space = fenics.FunctionSpace(
        adapted_coarse_mesh, element)

    adapted_coarse_solution = fenics.Function(adapted_coarse_function_space)

    adapted_coarse_solution.leaf_node().vector(
    )[:] = coarse_solution.leaf_node().vector()

    for refinement_cycle in range(maximum_refinement_cycles):

        cell_markers = fenics.MeshFunction(
            "bool", adapted_coarse_mesh.leaf_node(),
            adapted_coarse_mesh.topology().dim(), False)

        for coarse_cell in fenics.cells(adapted_coarse_mesh.leaf_node()):

            coarse_value = scalar(adapted_coarse_solution.leaf_node(),
                                  coarse_cell.midpoint())

            fine_value = scalar(fine_solution.leaf_node(),
                                coarse_cell.midpoint())

            if (abs(coarse_value - fine_value) > absolute_tolerance):

                cell_markers[coarse_cell] = True

        cell_markers = unmark_cells_below_circumradius(
            adapted_coarse_mesh.leaf_node(), cell_markers,
            circumradius_threshold)

        if any(cell_markers):

            adapted_coarse_mesh = fenics.refine(adapted_coarse_mesh,
                                                cell_markers)

            adapted_coarse_function_space = fenics.FunctionSpace(
                adapted_coarse_mesh, element)

            adapted_coarse_solution = fenics.project(
                fine_solution.leaf_node(),
                adapted_coarse_function_space.leaf_node())

        else:

            break

    return adapted_coarse_solution, adapted_coarse_function_space, adapted_coarse_mesh
Example #21
0
def evenodd_functions(
    omesh,
    degree,
    func,
    width=None,
    evenodd=None
):
    """Break a function into even and odd components

    Required parameters:
    omesh: the mesh on which the function is defined
    degree: the degree of the FunctionSpace 
    func: the Function. This has to be something that fe.interpolate
        can interpolate onto a FunctionSpace or that fe.project can
        project onto a FunctionSpace.
    width: the width of the domain on which func is defined. (If not
        provided, this will be determined from omesh.
    evenodd: the symmetries of the functions to be constructed
        evenodd_symmetries(dim) is used if this is not provided
    """
    SS = FunctionSpace(omesh, 'CG', degree)
    dim = omesh.geometry().dim()
    comm = omesh.mpi_comm()
    rank = comm.rank
    if width is None:
        stats = mesh_stats(omesh)
        width = stats['xmax']
    if evenodd is None:
        evenodd = evenodd_symmetries(dim)
    try:
        f0 = fe.interpolate(func, SS)
    except TypeError:
        f0 = fe.project(func, SS)
    vec0 = f0.vector().gather_on_zero()
    dofcoords = gather_dof_coords(SS)
    ndofs = len(dofcoords)
    fvecs = np.empty((2**dim, len(vec0)), float)
    flips = evenodd_symmetries(dim)
    for row,flip in enumerate(flips):
        newcoords = (width*flip
                     + (1 - 2*flip)*dofcoords)
        remap = coord_remap(SS, newcoords)
        E = evenodd_matrix(evenodd)
        if rank == 0:
            fvecs[row, :] = vec0[remap]
            components = np.matmul(2**(-dim)*E, fvecs)
        else:
            components = np.zeros((2**dim, ndofs), float)
    logPERIODIC('components.shape', components.shape)
    fs = []
    logPERIODIC('broadcasting function DOFs')
    for c in components:
        f = Function(SS)
        f = bcast_function(SS, c, f)
        # f.set_allow_extrapolation(True)
        fs.append(f)
    return(fs)
Example #22
0
    def fenics_p_electric(self, my_d):
        """
        @description:
            Solve poisson equation to get potential and electric field
        @Modify:
            2021/08/31
        """
        if "plugin3D" in self.det_model:
            bc_l = []
            bc_l = self.boundary_definition_3D(my_d, "Possion")
        elif "planar3D" in self.det_model:
            bc_l = self.boundary_definition_2D(my_d, "Possion")

        u = fenics.TrialFunction(self.V)
        v = fenics.TestFunction(self.V)
        if self.det_dic['name'] == "lgad3D":
            if self.det_dic['part'] == 2:
                bond = self.det_dic['bond1']
                doping_avalanche = self.f_value(my_d, self.det_dic['doping1'])
                doping = self.f_value(my_d, self.det_dic['doping2'])
                f = fenics.Expression('x[2] < width ? doping1 : doping2',
                                      degree=1,
                                      width=bond,
                                      doping1=doping_avalanche,
                                      doping2=doping)
            elif self.det_dic['part'] == 3:
                bond1 = self.det_dic['bond1']
                bond2 = self.det_dic['bond2']
                doping1 = self.f_value(my_d, self.det_dic['doping1'])
                doping2 = self.f_value(my_d, self.det_dic['doping2'])
                doping3 = self.f_value(my_d, self.det_dic['doping3'])
                f = fenics.Expression(
                    'x[2] < bonda ? dopinga : x[2] > bondb ? dopingc : dopingb',
                    degree=1,
                    bonda=bond1,
                    bondb=bond2,
                    dopinga=doping1,
                    dopingb=doping2,
                    dopingc=doping3)
            else:
                print("The structure of lgad is wrong.")
        else:
            f = fenics.Constant(self.f_value(my_d))
        a = fenics.dot(fenics.grad(u), fenics.grad(v)) * fenics.dx
        L = f * v * fenics.dx
        # Compute solution
        self.u = fenics.Function(self.V)
        fenics.solve(a == L,
                     self.u,
                     bc_l,
                     solver_parameters=dict(linear_solver='gmres',
                                            preconditioner='ilu'))
        #calculate electric field
        W = fenics.VectorFunctionSpace(self.mesh3D, 'P', 1)
        self.E_field = fenics.project(
            fenics.as_vector((self.u.dx(0), self.u.dx(1), self.u.dx(2))), W)
Example #23
0
    def coarsen(self):
        """ Remesh and refine the new mesh until the interpolation error tolerance is met. 
    
        For simplicity, for now we'll consider only one scalar component of the solution.
        """
        time = 0. + self.old_state.time

        fine_solution = self.state.solution.copy(deepcopy=True)

        self.setup_coarse_mesh()

        for refinement_cycle in range(
                self.coarsening_maximum_refinement_cycles):

            self.setup_function_space()

            self.setup_states()

            self.old_state.time = 0. + time

            self.old_state.solution = fenics.project(
                fine_solution.leaf_node(), self.function_space.leaf_node())

            if refinement_cycle == self.coarsening_maximum_refinement_cycles:

                break

            exceeds_tolerance = fenics.MeshFunction("bool",
                                                    self.mesh.leaf_node(),
                                                    self.mesh.topology().dim(),
                                                    False)

            exceeds_tolerance.set_all(False)

            for cell in fenics.cells(self.mesh.leaf_node()):

                coarse_value = self.old_state.solution.leaf_node()(cell.midpoint())\
                    [self.coarsening_scalar_solution_component_index]

                fine_value = fine_solution.leaf_node()(cell.midpoint())\
                    [self.coarsening_scalar_solution_component_index]

                if (abs(coarse_value - fine_value) >
                        self.coarsening_absolute_tolerance):

                    exceeds_tolerance[cell] = True

            if any(exceeds_tolerance):

                self.mesh = fenics.refine(self.mesh, exceeds_tolerance)

            else:

                break

        self.setup_problem_and_solver()  # We broke important references.
Example #24
0
 def _compute_effective_diffusion(self):
     # Uses the solutions of the cell problems to compute the harmonic average of the diffusivity
     for i, j in np.ndindex((self.dim, self.dim)):
         integrand_ij = fe.project(
             self.a_y *
             fe.dot(self.e_is[i] + fe.grad(self.cell_solutions[i]),
                    self.e_is[j] + fe.grad(self.cell_solutions[j])),
             self.function_space)
         self.eff_diff[i, j] = fe.assemble(integrand_ij * fe.dx)
     return fe.Constant(self.eff_diff)
 def save_strain_and_params(self, iteration: int) -> None:
     strain_vec = fenics.project(fenics.sym(fenics.grad(self.fields.u_new)),
                                 V=self.tensor_space).vector()[:]
     strain_tens = np.reshape(strain_vec,
                              newshape=(self.number_of_vertices, 2, 2))
     parameters = fenics.project(
         self.fields.new_constitutive_relation_multiplicative_parameters,
         V=self.fields.new_constitutive_relation_multiplicative_parameters.
         function_space()).vector()[:]
     if self.fields.new_constitutive_relation_multiplicative_parameters.function_space(
     ).dim() == self.fields.tensor_space.dim():
         parameters = np.reshape(parameters,
                                 newshape=(self.number_of_vertices, 2, 2))
     self.np_files.write(prefix='strain',
                         iteration=iteration,
                         array=strain_tens)
     self.np_files.write(prefix='params',
                         iteration=iteration,
                         array=parameters)
Example #26
0
def mfem():
    files = glob.glob('data/pvd/mfem/*')
    for f in files:
        try:
            os.remove(f)
        except Exception as e:
            print('Failed to delete {}, reason: {}' % (f, e))

    plate = mshr.Rectangle(fe.Point(0, 0), fe.Point(100, 100))
    mesh = mshr.generate_mesh(plate, 50)
    # mesh = fe.RectangleMesh(fe.Point(-2, -2), fe.Point(2, 2), 50, 50)

    x_hat = fe.SpatialCoordinate(mesh)

    U = fe.VectorFunctionSpace(mesh, 'CG', 2)
    V = fe.FunctionSpace(mesh, "CG", 1)
    W = fe.FunctionSpace(mesh, "DG", 0)

    # n = 21
    # control_points = np.stack((np.linspace(0, 1, n), np.linspace(0, 1, n)), axis=1)
    # impact_radii = np.linspace(0., 0.5, n)

    rho_default = 25. / np.sqrt(5) * 2
    control_points = np.array([[50., 50.], [62.5, 25.]])
    impact_radii = np.array([rho_default, rho_default])

    mid_point = np.array([75., 0.])
    mid_point1 = np.array([100., 0.])
    mid_point2 = np.array([50., 0.])
    points = [mid_point, mid_point1, mid_point2]
    direct_vec = np.array([1., -2])
    rotated_vec = np.array([2., 1.])

    direct_vec /= np.linalg.norm(direct_vec)
    rotated_vec /= np.linalg.norm(rotated_vec)

    directions = [direct_vec, rotated_vec]
    boundary_info = [points, directions, rho_default]

    # df, xi = distance_function_segments_ufl(x_hat, control_points, impact_radii)
    # d = fe.project(df, V)

    delta_x = map_function_ufl(x_hat, control_points, impact_radii,
                               boundary_info) - x_hat
    u = fe.project(delta_x, U)

    # e = fe.Function(U)
    # int_exp = InterpolateExpression(u, control_points, impact_radii)
    # e = fe.interpolate(int_exp, U)
    # int_exp = InterpolateExpression(e, control_points, impact_radii)
    # e = fe.project(int_exp, U)

    vtkfile_u = fe.File('data/pvd/mfem/u.pvd')
    u.rename("u", "u")
    vtkfile_u << u
Example #27
0
def extrapolate_list_forward(
    space: Space,
    space_timeline: TimeLine,
    space_interface: Space,
    space_interface_timeline: TimeLine,
    function_name,
    param: Parameters,
    transfer_function,
    subspace_index,
):
    array = []
    space_macrotimestep = space_timeline.head
    space_interface_macrotimestep = space_interface_timeline.head
    function = space_interface_macrotimestep.head.functions[function_name]
    array.append(
        transfer_function(function, space, space_interface, param,
                          subspace_index))
    global_size = space_timeline.size
    for n in range(global_size):

        space_microtimestep = space_macrotimestep.head
        local_size = space_macrotimestep.size - 1
        for m in range(local_size):

            extrapolation_proportion = (
                space_macrotimestep.tail.point -
                space_microtimestep.after.point) / space_macrotimestep.dt
            function_old = space_interface_macrotimestep.head.functions[
                function_name]
            function_new = space_interface_macrotimestep.tail.functions[
                function_name]
            array.append(
                project(
                    extrapolation_proportion * transfer_function(
                        function_old,
                        space,
                        space_interface,
                        param,
                        subspace_index,
                    ) + (1.0 - extrapolation_proportion) * transfer_function(
                        function_new,
                        space,
                        space_interface,
                        param,
                        subspace_index,
                    ),
                    space.function_space_split[subspace_index],
                ))
            space_microtimestep = space_microtimestep.after

        space_macrotimestep = space_macrotimestep.after
        space_interface_macrotimestep = space_interface_macrotimestep.after

    return array
Example #28
0
    def increase_conductivity(self, cond, e):
        # Set up the three way choice function
        intermediate = e * self.k + self.h
        not_less_than = ufl.conditional(ufl.gt(e, self.threshold_irreversible),
                                        self.sigma_end, intermediate)
        cond_expression = ufl.conditional(ufl.lt(e, self.threshold_reversible),
                                          self.sigma_start, not_less_than)

        # Project this onto the function space
        cond_function = d.project(ufl.Max(cond_expression, cond),
                                  cond.function_space())
        cond.assign(cond_function)
Example #29
0
    def project_function(self, v, **kwargs):
        """
        Project v onto the job's element, V.

        Args:
            v (?): The function to project.
            **kwargs: Valid `fenics.project` kwargs (except `V`, which is provided automatically).

        Returns:
            (?): Projected function.
        """
        return FEN.project(v, V=self.V, **kwargs)
Example #30
0
def compute_errors(u_approx, u_ref, V, total_error_tol=10**-4):
    error_normalized = (u_ref - u_approx) / u_ref  # compute pointwise L2 error
    error_pointwise = project(abs(error_normalized),
                              V)  # project onto function space
    error_total = sqrt(
        assemble(inner(error_pointwise, error_pointwise) *
                 dx))  # determine L2 norm to estimate total error
    error_pointwise.rename("error", " ")

    assert (error_total < total_error_tol)

    return error_total, error_pointwise
Example #31
0
def postprocess(fname, field, cond):
    """Postprocessing of the simulation."""
    func_space = field.function_space()
    mesh = func_space.mesh()
    degree = func_space.ufl_element().degree()
    vec_func_space = fe.VectorFunctionSpace(mesh, INPUTS['element_type'],
                                            degree)
    flux = fe.project(-cond * fe.grad(field), vec_func_space)
    divergence = fe.project(-fe.div(cond * fe.grad(field)), func_space)
    flux_x, flux_y, flux_z = flux.split()
    av_flux = fe.assemble(flux_z * fe.dx) / ((XMAX - XMIN) * (YMAX - YMIN))
    keff = av_flux * (ZMAX - ZMIN) / (INPUTS['boundary_conditions']['top'] -
                                      INPUTS['boundary_conditions']['bottom'])
    print('Effective conductivity: {0}'.format(keff))
    with open(fname + "_keff.csv", 'w') as textfile:
        textfile.write('keff\n')
        textfile.write('{0}\n'.format(keff))
    fe.File(fname + "_solution.pvd") << field
    if INPUTS['saving']['flux']:
        fe.File(fname + "_flux.pvd") << flux
    if INPUTS['saving']['flux_divergence']:
        fe.File(fname + "_flux_divergence.pvd") << divergence
    if INPUTS['saving']['flux_components']:
        fe.File(fname + "_flux_x.pvd") << flux_x
        fe.File(fname + "_flux_y.pvd") << flux_y
        fe.File(fname + "_flux_z.pvd") << flux_z
    if INPUTS['plotting']['solution']:
        fe.plot(field, title="Solution")
    if INPUTS['plotting']['flux']:
        fe.plot(flux, title="Flux")
    if INPUTS['plotting']['flux_divergence']:
        fe.plot(divergence, title="Divergence")
    if INPUTS['plotting']['flux_components']:
        fe.plot(flux_x, title='x-component of flux (-kappa*grad(u))')
        fe.plot(flux_y, title='y-component of flux (-kappa*grad(u))')
        fe.plot(flux_z, title='z-component of flux (-kappa*grad(u))')
    if True in INPUTS['plotting'].values():
        fe.interactive()
Example #32
0
def Vufl(soln, t):
    "Make a UFL object for plotting V"
    split = fe.split(soln.function)
    irho = split[0]
    iUs = split[1:]
    soln.ksdg.set_time(t)
    V = (soln.V(iUs, irho, params=soln.ksdg.iparams) /
         (soln.ksdg.iparams['sigma']**2 / 2))
    fs = soln.function.function_space()
    cell = fs.ufl_cell()
    CE = fe.FiniteElement('CG', cell, soln.degree)
    CS = fe.FunctionSpace(fs.mesh(), CE)
    pV = fe.project(V, CS, solver_type='petsc')
    return pV
Example #33
0
    def plot_result(self):
        # Calculate preliminary relationships
        dofmap = self.DV.dofmap()
        cell_dofs = N.array([dofmap.cell_dofs(c)[0] for c in N.arange(self.mesh.num_cells()) if (self.subdomains[c] in v.tissues["tumour"]["indices"])])
        volumes = N.array([d.Cell(self.mesh, c).volume() for c in N.arange(self.mesh.num_cells()) if (self.subdomains[c] in v.tissues["tumour"]["indices"])])

        # Create a horizontal axis
        cc_haxis = N.linspace(5000, 1e5, 200)

        # Calculate the tumour volume; this is what we will compare against
        tumour_volume = self.get_tumour_volume()

        # Initialize the output_arrays vector a rescale the x to V/cm
        output_arrays = [cc_haxis / 100]

        # Loop through the electrode pairs
        for i, triple in enumerate(v.electrode_triples):
            # Project the max e values for this triple to DG0 - this forces an evaluation of the function at the mid-point of each tet, DG0's only DOF
            e_dg = d.project(self.max_es[i], self.DV)

            # Calculate the "max e" contribution for each cell
            contributor = N.vectorize(lambda c: e_dg.vector()[c])
            contributions = contributor(cell_dofs)

            # Sum the tet volumes for tets with a midpoint value greater than x, looping over x as e-norm thresholds (also scale to tumour volume)
            elim = N.vectorize(lambda x: volumes[contributions > x].sum() / tumour_volume)
            output_arrays.append(elim(cc_haxis))

        # Compile into a convenient array
        output = N.array(zip(*output_arrays))

        # Output cumulative coverage curves as CSV
        N.savetxt('results/%s-coverage_curves.csv' % input_mesh, output)

        # Plot the coverage curves
        for (anode, cathode, voltage), a in zip(v.electrode_triples, output_arrays[1:]):
            P.plot(output_arrays[0], a, label="%d - %d" % (anode, cathode))

        # Draw the plot
        P.draw()
        P.xlabel(r"Threshold level of $|E|$ ($\mathrm{J}$)")
        P.ylabel(r"Fraction of tumour beneath level")

        # Show a legend for the plot
        P.legend(loc=3)

        # Display the plot
        P.savefig('%s-coverage_curves' % input_mesh)
Example #34
0
    def solve(self):
        # TODO: when FEniCS ported to Python3, this should be exist_ok
        try:
            os.makedirs('results')
        except OSError:
            pass

        z, w = (self.z, self.w)
        u0 = d.Constant(0.0)

        # Define the linear and bilinear forms
        L = u0 * w * dx

        # Define useful functions
        cond = d.Function(self.DV)
        U = d.Function(self.V)

        # Initialize the max_e vector, that will store the cumulative max e values
        max_e = d.Function(self.V)
        max_e.vector()[:] = 0.0
        max_e.rename("max_E", "Maximum energy deposition by location")
        max_e_file = d.File("results/%s-max_e.pvd" % input_mesh)
        max_e_per_step = d.Function(self.V)
        max_e_per_step_file = d.File("results/%s-max_e_per_step.pvd" % input_mesh)

        self.es = {}
        self.max_es = {}
        fi = d.File("results/%s-cond.pvd" % input_mesh)

        potential_file = d.File("results/%s-potential.pvd" % input_mesh)

        # Loop through the voltages and electrode combinations
        for i, (anode, cathode, voltage) in enumerate(v.electrode_triples):
            print("Electrodes %d (%lf) -> %d (0)" % (anode, voltage, cathode))

            cond = d.project(self.sigma_start, V=self.DV)

            # Define the Dirichlet boundary conditions on the active needles
            uV = d.Constant(voltage)
            term1_bc = d.DirichletBC(self.V, uV, self.patches, v.needles[anode])
            term2_bc = d.DirichletBC(self.V, u0, self.patches, v.needles[cathode])

            e = d.Function(self.V)
            e.vector()[:] = max_e.vector()

            # Re-evaluate conductivity
            self.increase_conductivity(cond, e)

            for j in range(v.max_restarts):
                # Update the bilinear form
                a = d.inner(d.nabla_grad(z), cond * d.nabla_grad(w)) * dx

                # Solve again
                print(" [solving...")
                d.solve(a == L, U, bcs=[term1_bc, term2_bc])
                print("  ....solved]")

                # Extract electric field norm
                for k in range(len(U.vector())):
                    if N.isnan(U.vector()[k]):
                        U.vector()[k] = 1e5

                e_new = d.project(d.sqrt(d.dot(d.grad(U), d.grad(U))), self.V)

                # Take the max of the new field and the established electric field
                e.vector()[:] = N.array([max(*X) for X in zip(e.vector(), e_new.vector())])

                # Re-evaluate conductivity
                fi << cond
                self.increase_conductivity(cond, e)

            potential_file << U

            # Save the max e function to a VTU
            max_e_per_step.vector()[:] = e.vector()[:]
            max_e_per_step_file << max_e_per_step

            # Store this electric field norm, for this triple, for later reference
            self.es[i] = e

            # Store the max of this electric field norm and that for all previous triples
            max_e_array = N.array([max(*X) for X in zip(max_e.vector(), e.vector())])
            max_e.vector()[:] = max_e_array

            # Create a new max_e function for storage, or it will be overwritten by the next iteration
            max_e_new = d.Function(self.V)
            max_e_new.vector()[:] = max_e_array

            # Store this max e function for the cumulative coverage curve calculation later
            self.max_es[i] = max_e_new

            # Save the max e function to a VTU
            max_e_file << max_e
            self.max_e_count = i
Example #35
0
def main():
    """Main function. Organizes workflow."""
    fname = str(INPUTS['filename'])
    term = Terminal()
    print(
        term.yellow
        + "Working on file {}.".format(fname)
        + term.normal
    )
    # Load mesh and physical domains from file.
    mesh = fe.Mesh(fname + ".xml")
    if INPUTS['saving']['mesh']:
        fe.File(fname + "_mesh.pvd") << mesh
    if INPUTS['plotting']['mesh']:
        fe.plot(mesh, title='Mesh')
    subdomains = fe.MeshFunction(
        'size_t', mesh, fname + '_physical_region.xml')
    if INPUTS['saving']['subdomains']:
        fe.File(fname + "_subdomains.pvd") << subdomains
    if INPUTS['plotting']['subdomains']:
        fe.plot(subdomains, title='Subdomains')
    # function space for temperature/concentration
    func_space = fe.FunctionSpace(
        mesh,
        INPUTS['element_type'],
        INPUTS['element_degree'],
        constrained_domain=PeriodicDomain()
    )
    # discontinuous function space for visualization
    dis_func_space = fe.FunctionSpace(
        mesh,
        'DG',
        INPUTS['element_degree'],
        constrained_domain=PeriodicDomain()
    )
    if ARGS['--verbose']:
        print('Number of cells:', mesh.num_cells())
        print('Number of faces:', mesh.num_faces())
        print('Number of edges:', mesh.num_edges())
        print('Number of vertices:', mesh.num_vertices())
        print('Number of DOFs:', len(func_space.dofmap().dofs()))
    # temperature/concentration field
    field = fe.TrialFunction(func_space)
    # test function
    test_func = fe.TestFunction(func_space)
    # function, which is equal to 1 everywhere
    unit_function = fe.Function(func_space)
    unit_function.assign(fe.Constant(1.0))
    # assign material properties to each domain
    if INPUTS['mode'] == 'conductivity':
        mat_prop = SubdomainConstant(
            subdomains,
            fe.Constant(INPUTS['conductivity']['gas']),
            fe.Constant(INPUTS['conductivity']['solid']),
            degree=0
        )
    elif INPUTS['mode'] == 'diffusivity':
        mat_prop = SubdomainConstant(
            subdomains,
            fe.Constant(INPUTS['diffusivity']['gas']),
            fe.Constant(INPUTS['diffusivity']['solid']) *
            fe.Constant(INPUTS['solubility'] *
                        gas_constant * INPUTS['temperature']),
            degree=0
        )
    # assign 1 to gas domain, and 0 to solid domain
    gas_content = SubdomainConstant(
        subdomains,
        fe.Constant(1.0),
        fe.Constant(0.0),
        degree=0
    )
    # define structure of foam over whole domain
    structure = fe.project(unit_function * gas_content, dis_func_space)
    # calculate porosity and wall thickness
    porosity = fe.assemble(structure *
                           fe.dx) / ((XMAX - XMIN) * (YMAX - YMIN) * (ZMAX - ZMIN))
    print('Porosity: {0}'.format(porosity))
    dwall = wall_thickness(
        porosity, INPUTS['morphology']['cell_size'],
        INPUTS['morphology']['strut_content'])
    print('Wall thickness: {0} m'.format(dwall))
    # calculate effective conductivity/diffusivity by analytical model
    if INPUTS['mode'] == 'conductivity':
        eff_prop = analytical_conductivity(
            INPUTS['conductivity']['gas'], INPUTS['conductivity']['solid'],
            porosity, INPUTS['morphology']['strut_content'])
        print('Analytical model: {0} W/(mK)'.format(eff_prop))
    elif INPUTS['mode'] == 'diffusivity':
        eff_prop = analytical_diffusivity(
            INPUTS['diffusivity']['solid'] *
            INPUTS['solubility'], INPUTS['solubility'],
            porosity, INPUTS['morphology']['cell_size'], dwall,
            INPUTS['temperature'], INPUTS['morphology']['enhancement_par'])
        print('Analytical model: {0} m^2/s'.format(eff_prop))
    # create system matrix
    system_matrix = -mat_prop * \
        fe.inner(fe.grad(field), fe.grad(test_func)) * fe.dx
    left_side, right_side = fe.lhs(system_matrix), fe.rhs(system_matrix)
    # define boundary conditions
    bcs = [
        fe.DirichletBC(func_space, fe.Constant(
            INPUTS['boundary_conditions']['top']), top_bc),
        fe.DirichletBC(func_space, fe.Constant(
            INPUTS['boundary_conditions']['bottom']), bottom_bc)
    ]
    # compute solution
    field = fe.Function(func_space)
    fe.solve(left_side == right_side, field, bcs)
    # output temperature/concentration at the boundaries
    if ARGS['--verbose']:
        print('Checking periodicity:')
        print('Value at XMIN:', field(XMIN, (YMIN + YMAX) / 3, (ZMIN + ZMAX) / 3))
        print('Value at XMAX:', field(XMAX, (YMIN + YMAX) / 3, (ZMIN + ZMAX) / 3))
        print('Value at YMIN:', field((XMIN + XMAX) / 3, YMIN, (ZMIN + ZMAX) / 3))
        print('Value at YMAX:', field((XMIN + XMAX) / 3, YMAX, (ZMIN + ZMAX) / 3))
        print('Value at ZMIN:', field((XMIN + XMAX) / 3, (YMIN + YMAX) / 3, ZMIN))
        print('Value at ZMAX:', field((XMIN + XMAX) / 3, (YMIN + YMAX) / 3, ZMAX))
    # calculate flux, and effective properties
    vec_func_space = fe.VectorFunctionSpace(
        mesh,
        INPUTS['element_type'],
        INPUTS['element_degree']
    )
    flux = fe.project(-mat_prop * fe.grad(field), vec_func_space)
    divergence = fe.project(-fe.div(mat_prop * fe.grad(field)), func_space)
    flux_x, flux_y, flux_z = flux.split()
    av_flux = fe.assemble(flux_z * fe.dx) / ((XMAX - XMIN) * (YMAX - YMIN))
    eff_prop = av_flux * (ZMAX - ZMIN) / (
        INPUTS['boundary_conditions']['top']
        - INPUTS['boundary_conditions']['bottom']
    )
    if INPUTS['mode'] == 'conductivity':
        print('Numerical model: {0} W/(mK)'.format(eff_prop))
    elif INPUTS['mode'] == 'diffusivity':
        print('Numerical model: {0} m^2/s'.format(eff_prop))
    # projection of concentration has to be in discontinuous function space
    if INPUTS['mode'] == 'diffusivity':
        sol_field = SubdomainConstant(
            subdomains,
            fe.Constant(1.0),
            fe.Constant(INPUTS['solubility'] *
                        gas_constant * INPUTS['temperature']),
            degree=0
        )
        field = fe.project(field * sol_field, dis_func_space)
    # save results
    with open(fname + "_eff_prop.csv", 'w') as textfile:
        textfile.write('eff_prop\n')
        textfile.write('{0}\n'.format(eff_prop))
    fe.File(fname + "_solution.pvd") << field
    fe.File(fname + "_structure.pvd") << structure
    if INPUTS['saving']['flux']:
        fe.File(fname + "_flux.pvd") << flux
    if INPUTS['saving']['flux_divergence']:
        fe.File(fname + "_flux_divergence.pvd") << divergence
    if INPUTS['saving']['flux_components']:
        fe.File(fname + "_flux_x.pvd") << flux_x
        fe.File(fname + "_flux_y.pvd") << flux_y
        fe.File(fname + "_flux_z.pvd") << flux_z
    # plot results
    if INPUTS['plotting']['solution']:
        fe.plot(field, title="Solution")
    if INPUTS['plotting']['flux']:
        fe.plot(flux, title="Flux")
    if INPUTS['plotting']['flux_divergence']:
        fe.plot(divergence, title="Divergence")
    if INPUTS['plotting']['flux_components']:
        fe.plot(flux_x, title='x-component of flux (-kappa*grad(u))')
        fe.plot(flux_y, title='y-component of flux (-kappa*grad(u))')
        fe.plot(flux_z, title='z-component of flux (-kappa*grad(u))')
    if True in INPUTS['plotting'].values():
        fe.interactive()
    print(
        term.yellow
        + "End."
        + term.normal
    )