def compute_static_deformation(self):

        assert self.mesh is not None

        # now we define subdomains on the mesh

        bottom = fe.CompiledSubDomain('near(x[2], 0) && on_boundary')
        top = fe.CompiledSubDomain('near(x[2], 1) && on_boundary')
        # middle = fe.CompiledSubDomain('x[2] > 0.3 && x[2] < 0.7')

        # Initialize mesh function for interior domains
        self.domains = fe.MeshFunction('size_t', self.mesh, 3)
        self.domains.set_all(0)
        # middle.mark(self.domains, 1)

        # Initialize mesh function for boundary domains
        self.boundaries = fe.MeshFunction('size_t', self.mesh, 2)
        self.boundaries.set_all(0)
        bottom.mark(self.boundaries, 1)
        top.mark(self.boundaries, 2)

        # Define new measures associated with the interior domains and
        # exterior boundaries

        self.dx = fe.Measure('dx',
                             domain=self.mesh,
                             subdomain_data=self.domains)
        self.ds = fe.Measure('ds',
                             domain=self.mesh,
                             subdomain_data=self.boundaries)

        # define function spaces
        V = fe.VectorFunctionSpace(self.mesh, "Lagrange", 1)
        # now we define subdomains on the mesh

        bottom = fe.CompiledSubDomain('near(x[2], 0) && on_boundary')
        top = fe.CompiledSubDomain('near(x[2], 1) && on_boundary')
        # middle = fe.CompiledSubDomain('x[2] > 0.3 && x[2] < 0.7')

        d = self.mesh.geometry().dim()

        # Initialize mesh function for interior domains
        self.domains = fe.MeshFunction('size_t', self.mesh, d)
        self.domains.set_all(0)
        # middle.mark(self.domains, 1)

        # Initialize mesh function for boundary domains
        self.boundaries = fe.MeshFunction('size_t', self.mesh, d - 1)
        self.boundaries.set_all(0)
        bottom.mark(self.boundaries, 1)
        top.mark(self.boundaries, 2)

        # Define new measures associated with the interior domains and
        # exterior boundaries

        self.dx = fe.Measure('dx',
                             domain=self.mesh,
                             subdomain_data=self.domains)
        self.ds = fe.Measure('ds',
                             domain=self.mesh,
                             subdomain_data=self.boundaries)

        c_zero = fe.Constant((0, 0, 0))

        # define boundary conditions
        bc_bottom = fe.DirichletBC(V, c_zero, bottom)
        bc_top = fe.DirichletBC(V, c_zero, top)

        bcs = [bc_bottom]  # , bc_top]

        # define functions
        du = TrialFunction(V)
        v = TestFunction(V)
        u = Function(V)
        B = fe.Constant((0., 2.0, 0.))
        T = fe.Constant((0.0, 0.0, 0.0))

        d = u.geometric_dimension()
        I = fe.Identity(d)
        F = I + grad(u)
        C = F.T * F

        I_1 = tr(C)
        J = det(F)

        E, mu = 10., 0.3
        mu, lmbda = fe.Constant(E / (2 * (1 + mu))), fe.Constant(
            E * mu / ((1 + mu) * (1 - 2 * mu)))

        # stored energy (comp. neo-hookean model)
        psi = (mu / 2.) * (I_1 - 3) - mu * fe.ln(J) + (lmbda /
                                                       2.) * (fe.ln(J))**2

        dx = self.dx
        ds = self.ds

        Pi = psi * fe.dx - dot(B, u) * fe.dx - dot(T, u) * fe.ds

        F = fe.derivative(Pi, u, v)
        J = fe.derivative(F, u, du)

        fe.solve(F == 0, u, bcs, J=J)

        # save results
        self.u = u

        # write to disk
        output = fe.File("/tmp/static.pvd")
        output << u
class PorousProblem(object):
    """
    Boundary marker labels:
    - inflow (Neumann BC in fluid mass increase)
    - outflow (Neumann BC in fluid mass increase)
    """
    def __init__(self,
                 geometry,
                 material,
                 parameters=None,
                 solver_parameters=None,
                 **kwargs):
        self.geometry = geometry
        self.material = material
        self.mesh = geometry.mesh
        self.markers = self.geometry.markers
        self.isbiv = False
        if 'ENDO_RV' in self.markers.keys():
            self.isbiv = True

        # Set parameters
        self.parameters = PorousProblem.default_parameters()
        if parameters is not None:
            self.parameters.update(parameters)

        if self.parameters['N'] > 1 and\
                            len(self.parameters['K']) != self.parameters['N']:
            N = self.parameters['N']
            K = self.parameters['K']
            msg = "Parameter N is {}, but K contains {} elements."\
                    "K has to contain exactly N elements.".format(N, len(K))
            raise ValueError(msg)

        # Create function spaces
        self._init_spaces()
        self._init_form()
        self.t = 0.0

        # Set up solver
        self.newton_steps = 5000
        self.solver_parameters = PorousProblem.default_solver_parameters()
        if solver_parameters is not None:
            self.solver_parameters.update(solver_parameters)

    @staticmethod
    def default_parameters():
        """
        Default parameters for the porous problem.

        Taken from [Cookson2012, Michler2013]
        """

        return {
            'N': 1,
            'rho': 1.06,
            'K': [1e-2],
            'phi': [0.021],
            'beta': [0.02],
            'qi': 0.0,
            'qo': 0,
            'tf': 1.0,
            'dt': 1e-2,
            'steps': 10,
            'theta': 0.5,
            'mechanics': False
        }

    @staticmethod
    def default_solver_parameters():
        return df.LinearVariationalSolver.default_parameters()

    def _init_spaces(self):
        P2 = FiniteElement('P', self.mesh.ufl_cell(), 2)
        N = self.parameters['N']
        if N == 1:
            elem = P2
        else:
            elem = MixedElement([P2 for i in range(N)])
        v_elem = VectorElement('P', self.mesh.ufl_cell(), 1)

        mesh = self.mesh
        self.state_space = FunctionSpace(mesh, elem)
        self.vector_space = FunctionSpace(mesh, v_elem)
        self.pressure_space = FunctionSpace(mesh, P2)
        self.state = Function(self.state_space, name="m")
        self.state_previous = Function(self.state_space)
        self.state_test = TestFunction(self.state_space)
        self.displacement = Function(self.vector_space, name="du")
        self.mech_velocity = Function(self.vector_space)
        self.pressure = [
            Function(self.pressure_space, name="p{}".format(i))
            for i in range(N)
        ]
        self.darcy_flow = [
            Function(self.vector_space, name="w{}".format(i)) for i in range(N)
        ]

    def _init_form(self):
        m = TrialFunction(self.state_space)
        m_n = self.state_previous
        v = self.state_test
        u = self.displacement
        du = self.mech_velocity
        p = self.pressure

        N = self.parameters['N']

        # Get parameters
        rho = Constant(self.parameters['rho'])
        beta = [Constant(beta) for beta in self.parameters['beta']]
        if isinstance(self.parameters['K'], float):
            K = [self.parameters['K']]
        else:
            K = self.parameters['K']
        if self.geometry.f0 is not None:
            self.K = [self.permeability_tensor(k) for k in K]
        else:
            self.K = [Constant(k) for k in K]
        dt = self.parameters['dt'] / self.parameters['steps']
        self.qi = self.inflow_rate(self.parameters['qi'])
        self.qo = self.inflow_rate(self.parameters['qo'])
        k = Constant(1 / dt)
        theta = self.parameters['theta']

        # Crank-Nicolson time scheme
        M = Constant(theta) * m + Constant(1 - theta) * m_n

        # Mechanics
        from ufl import grad as ufl_grad
        dx = self.geometry.dx
        d = self.state.geometric_dimension()
        I = Identity(d)
        F = df.variable(kinematics.DeformationGradient(u))
        J = kinematics.Jacobian(F)

        if N == 1:
            self._form = k * (m - m_n) * v * dx
        else:
            self._form = sum(
                [k * (m[i] - m_n[i]) * v[i] * dx for i in range(N)])

        # porous dynamics
        if self.parameters['mechanics']:
            F = df.variable(kinematics.DeformationGradient(u))
            J = kinematics.Jacobian(F)
            A = [J * df.inv(F) * K * df.inv(F.T) for K in self.K]
        else:
            A = [Constant(1.0) * K for K in self.K]

        if N == 1:
            self._form += -rho * df.div(A[0] * df.grad(p[0])) * v * dx
        else:
            self._form += -rho * sum(
                [df.div(A[i] * df.grad(p[i])) * v[i] * dx for i in range(N)])

        # compartment coupling
        if N > 1:
            # forward
            self._form -= sum([
                -J * beta[i] * (p[i] - p[i + 1]) * v[i] * dx
                for i in range(N - 1)
            ])
            # backward
            self._form -= sum([
                -J * beta[i - 1] * (p[i] - p[i - 1]) * v[i] * dx
                for i in range(1, N)
            ])

        # add mechanics
        if self.parameters['mechanics']:
            if N == 1:
                self._form -= df.dot(df.grad(M), du) * v * dx
            else:
                self._form -= sum(
                    [df.dot(df.grad(M[i]), du) * v[i] * dx for i in range(N)])

        # Add inflow/outflow terms
        if N == 1:
            self._form -= rho * self.qi * v * dx + rho * self.qo * v * dx
        else:
            self._form -= rho * self.qi * v[0] * dx + rho * self.qo * v[-1] * dx

    def inflow_rate(self, rate):
        if isinstance(rate, (int, float)):
            rate = Constant(rate)
        elif isinstance(rate, df.function.expression.Expression):
            rate = rate
        return rate

    def permeability_tensor(self, K):
        FS = self.geometry.f0.function_space()
        TS = TensorFunctionSpace(self.geometry.mesh, 'P', 1)
        d = self.geometry.dim()
        fibers = Function(FS)
        fibers.vector()[:] = self.geometry.f0.vector().get_local()
        fibers.vector()[:] /= df.norm(self.geometry.f0)
        if self.geometry.s0 is not None:
            # normalize vectors
            sheet = Function(FS)
            sheet.vector()[:] = self.geometry.s0.vector().get_local()
            sheet.vector()[:] /= df.norm(self.geometry.s0)
            if d == 3:
                csheet = Function(FS)
                csheet.vector()[:] = self.geometry.n0.vector().get_local()
                csheet.vector()[:] /= df.norm(self.geometry.n0)
        else:
            return Constant(1)

        from ufl import diag
        factor = 10
        if d == 3:
            ftensor = df.as_matrix(((fibers[0], sheet[0], csheet[0]),
                                    (fibers[1], sheet[1], csheet[1]),
                                    (fibers[2], sheet[2], csheet[2])))
            ktensor = diag(df.as_vector([K, K / factor, K / factor]))
        else:
            ftensor = df.as_matrix(
                ((fibers[0], sheet[0]), (fibers[1], sheet[1])))
            ktensor = diag(df.as_vector([K, K / factor]))

        permeability = df.project(
            df.dot(df.dot(ftensor, ktensor), df.inv(ftensor)), TS)
        return permeability

    def update_mechanics(self, pressure, displacement, mech_velocity):
        N = self.parameters['N']
        phi = self.parameters['phi']
        for i in range(N):
            fluid_pressure = df.interpolate(pressure, self.pressure_space)
            self.pressure[i].vector()[:] = fluid_pressure.vector().get_local()
            self.pressure[i].vector()[:] *= phi[i]
        self.displacement.assign(displacement)
        self.mech_velocity.assign(mech_velocity)

    def prescribed_pressure(self, pe):
        N = self.parameters['N']
        phi = self.parameters['phi']
        for i in range(N):
            fluid_pressure = df.interpolate(pe, self.pressure_space)
            self.pressure[i].vector()[:] = fluid_pressure.vector().get_local()
            self.pressure[i].vector()[:] *= phi[i]

    def calculate_darcy_flow(self):
        # equation 5a
        du = self.mech_velocity
        p = self.pressure
        rho = Constant(self.parameters['rho'])
        phi = [Constant(phi) for phi in self.parameters['phi']]
        F = df.variable(kinematics.DeformationGradient(self.displacement))
        J = kinematics.Jacobian(F)
        dx = self.geometry.dx
        N = self.parameters['N']

        # Calculate endo to epi permeability gradient
        w = [TrialFunction(self.vector_space) for i in range(N)]
        v = [TestFunction(self.vector_space) for i in range(N)]
        a = [(1 / phi[i]) * df.inner(F * J * df.inv(F) * w[i], v[i]) * dx
             for i in range(N)]
        # a = [phi[i]*df.inner((w[i]-du), v[i])*dx for i in range(N)]
        # porous dynamics
        if self.parameters['mechanics']:
            A = [-J * self.K[i] * df.inv(F.T) for i in range(N)]
        else:
            A = [self.K[i] for i in range(N)]
        L = [-df.dot(A[i] * df.grad(p[i]), v[i]) * dx for i in range(N)]

        [
            df.solve(a[i] == L[i],
                     self.darcy_flow[i], [],
                     solver_parameters={
                         "linear_solver": "cg",
                         "preconditioner": "sor"
                     }) for i in range(N)
        ]

    def solve(self, bcs=[]):
        """
        Solve the variational problem

        """

        logger.debug("Solving porous problem")

        a = df.lhs(self._form)
        L = df.rhs(self._form)
        problem = LinearVariationalProblem(a, L, self.state, bcs=bcs)

        solver = LinearVariationalSolver(problem)
        solver.parameters.update(self.solver_parameters)
        solver.solve()

        for i in range(self.parameters['steps']):
            try:
                self.qi.t +=\
                                self.parameters['dt']/self.parameters['steps']
            except AttributeError:
                # If the Expression for qi does not have a time parameter
                # do nothing
                pass

            try:
                logger.debug("Trying...")
                solver.solve()
            except RuntimeError as ex:
                logger.debug("Failed")
                logger.debug("Solver did not converge...")
                break

            else:
                logger.debug("Solved")

        self.state_previous.assign(self.state)
        self.calculate_darcy_flow()