Exemple #1
0
def test_krylov_solver_options_prefix(pushpop_parameters):
    "Test set/get PETScKrylov solver prefix option"

    # Set backend
    parameters["linear_algebra_backend"] = "PETSc"

    # Prefix
    prefix = "test_foo_"

    # Create solver and set prefix
    solver = PETScKrylovSolver()
    solver.set_options_prefix(prefix)

    # Check prefix (pre solve)
    assert solver.get_options_prefix() == prefix

    # Solve a system of equations
    mesh = UnitSquareMesh(4, 4)
    V = FunctionSpace(mesh, "Lagrange", 1)
    u, v = TrialFunction(V), TestFunction(V)
    a, L = u*v*dx, Constant(1.0)*v*dx
    A, b = assemble(a), assemble(L)
    solver.set_operator(A)
    solver.solve(b.copy(), b)

    # Check prefix (post solve)
    assert solver.get_options_prefix() == prefix
Exemple #2
0
    def _assemble(self):
        # Get input:
        self.gamma = self.Parameters['gamma']
        if self.Parameters.has_key('beta'): self.beta = self.Parameters['beta']
        else: self.beta = 0.0
        self.Vm = self.Parameters['Vm']
        self.m0 = Function(self.Vm)
        if self.Parameters.has_key('m0'):
            setfct(self.m0, self.Parameters['m0'])
        self.mtrial = TrialFunction(self.Vm)
        self.mtest = TestFunction(self.Vm)
        self.mysample = Function(self.Vm)
        self.draw = Function(self.Vm)
        # Assemble:
        self.R = assemble(inner(nabla_grad(self.mtrial), \
        nabla_grad(self.mtest))*dx)
        self.M = assemble(inner(self.mtrial, self.mtest) * dx)

        self.Msolver = PETScKrylovSolver('cg', 'jacobi')
        self.Msolver.parameters["maximum_iterations"] = 2000
        self.Msolver.parameters["relative_tolerance"] = 1e-24
        self.Msolver.parameters["absolute_tolerance"] = 1e-24
        self.Msolver.parameters["error_on_nonconvergence"] = True
        self.Msolver.parameters["nonzero_initial_guess"] = False
        self.Msolver.set_operator(self.M)

        # preconditioner is Gamma^{-1}:
        if self.beta > 1e-10:
            self.precond = self.gamma * self.R + self.beta * self.M
        else:
            self.precond = self.gamma * self.R + (1e-10) * self.M
        # Minvprior is M.A^2 (if you use M inner-product):
        self.Minvprior = self.gamma * self.R + self.beta * self.M
Exemple #3
0
def test_krylov_solver_options_prefix(pushpop_parameters):
    "Test set/get PETScKrylov solver prefix option"

    # Set backend
    parameters["linear_algebra_backend"] = "PETSc"

    # Prefix
    prefix = "test_foo_"

    # Create solver and set prefix
    solver = PETScKrylovSolver()
    solver.set_options_prefix(prefix)

    # Check prefix (pre solve)
    assert solver.get_options_prefix() == prefix

    # Solve a system of equations
    mesh = UnitSquareMesh(4, 4)
    V = FunctionSpace(mesh, "Lagrange", 1)
    u, v = TrialFunction(V), TestFunction(V)
    a, L = u*v*dx, Constant(1.0)*v*dx
    A, b = assemble(a), assemble(L)
    solver.set_operator(A)
    solver.solve(b.copy(), b)

    # Check prefix (post solve)
    assert solver.get_options_prefix() == prefix
Exemple #4
0
    def set_solver(self):
        """Reset solver for fwd operator"""

        #self.solver = LUSolver()
        #self.solver.parameters['reuse_factorization'] = True
        self.solver = PETScKrylovSolver("cg", "amg")
        self.solver.parameters["maximum_iterations"] = 1000
        self.solver.parameters["relative_tolerance"] = 1e-12
        self.solver.parameters["error_on_nonconvergence"] = True
        self.solver.parameters["nonzero_initial_guess"] = False
        self.solver.set_operator(self.A)
Exemple #5
0
def amg_method():
    """
    Determine which AMG preconditioner to use.
    If avaialable use ML, which is faster than the PETSc one.
    """
    S = PETScKrylovSolver()
    for pp in S.preconditioners():
        if pp[0] == 'ml_amg':
            return 'ml_amg'

    return 'petsc_amg'
Exemple #6
0
def amg_method(amg_type="ml_amg"):
    """
    Determine which AMG preconditioner to use.
    If available use the preconditioner suggested by the user (ML is default).
    If not available use  petsc_amg.
    """
    S = PETScKrylovSolver()
    for pp in S.preconditioners():
        if pp[0] == amg_type:
            return amg_type

    return 'petsc_amg'
Exemple #7
0
    def _assemble_solverM(self, Vm):

        self.MM = assemble(inner(self.mtrial, self.mtest) * dx)
        self.solverM = PETScKrylovSolver('cg', 'jacobi')
        self.solverM.parameters["maximum_iterations"] = 1000
        self.solverM.parameters["relative_tolerance"] = 1e-12
        self.solverM.parameters["error_on_nonconvergence"] = True
        self.solverM.parameters["nonzero_initial_guess"] = False
        #        self.solverM = LUSolver()
        #        self.solverM.parameters['reuse_factorization'] = True
        #        self.solverM.parameters['symmetric'] = True
        self.solverM.set_operator(self.MM)
Exemple #8
0
def amg_method(amg_type="ml_amg"):
    """
    Determine which AMG preconditioner to use.
    If available use the preconditioner suggested by the user (ML is default).
    If not available use  petsc_amg.
    """
    S = PETScKrylovSolver()
    for pp in S.preconditioners():
        if pp[0] == amg_type:
            return amg_type
        
    return 'petsc_amg'
Exemple #9
0
    def __missing__(self, key):
        assert len(key) == 4
        form, bcs, solver_type, preconditioner_type = key
        prec = PETScPreconditioner(preconditioner_type)
        sol = PETScKrylovSolver(solver_type, prec)

        #sol.parameters["preconditioner"]["structure"] = "same"
        sol.parameters["error_on_nonconvergence"] = False
        sol.parameters["monitor_convergence"] = False
        sol.parameters["report"] = False
        self[key] = sol

        return self[key]
 def getprecond(self):
     """ precondition by TV + small fraction of mass matrix """
     solver = PETScKrylovSolver('cg', self.amgprecond)
     solver.parameters["maximum_iterations"] = 2000
     solver.parameters["relative_tolerance"] = 1e-24
     solver.parameters["absolute_tolerance"] = 1e-24
     solver.parameters["error_on_nonconvergence"] = True
     solver.parameters["nonzero_initial_guess"] = False
     solver.set_operator(self.H + self.sMass)
     return solver
Exemple #11
0
    def __missing__(self, key):
        assert len(key) == 4
        form, bcs, solver_type, preconditioner_type = key
        prec = PETScPreconditioner(preconditioner_type)
        sol = PETScKrylovSolver(solver_type, prec)
        #sol.prec = prec
        #sol = KrylovSolver(solver_type, preconditioner_type)

        #sol.parameters["preconditioner"]["structure"] = "same"
        sol.parameters["error_on_nonconvergence"] = False
        sol.parameters["monitor_convergence"] = False
        sol.parameters["report"] = False
        self[key] = sol
        return self[key]
Exemple #12
0
	def initializeLinearSystem(self, properties, dt, u, w, p, q, bcs):
		self.stiffnessBlock = self.stiffnessBlock(properties, u, w)
		self.porePressureBlock = self.porePressureBlock(properties, p, w)
		self.solidVelocityBlock = self.solidVelocityBlock(properties, dt, u, q)
		self.storageBlock = self.storageBlock(properties, dt, p, q)
		self.solidPressureBlock = self.solidPressureBlock(properties, dt, p, q)
		self.fluidFlowBlock = self.fluidFlowBlock(properties, p, q)
		self.bcs = bcs
		if self.split:
			self.geoSolver = PETScKrylovSolver("gmres", "hypre_amg")
			self.geoSolver.parameters['relative_tolerance'] = self.tol
			self.flowSolver = PETScKrylovSolver("bicgstab", "sor")
			self.flowSolver.parameters['relative_tolerance'] = self.tol

		"""
Exemple #13
0
class LaplacianPrior(GaussianPrior):
    """Gaussian prior
    Parameters must be a dictionary containing:
        gamma = multiplicative factor applied to <Grad u, Grad v> term
        beta = multiplicative factor applied to <u,v> term (default=0.0)
        m0 = mean (or reference parameter when used as regularization)
        Vm = function space for parameter
    cost = 1/2 * (m-m0)^T.R.(m-m0)"""
    def _assemble(self):
        # Get input:
        self.gamma = self.Parameters['gamma']
        if self.Parameters.has_key('beta'): self.beta = self.Parameters['beta']
        else: self.beta = 0.0
        self.Vm = self.Parameters['Vm']
        self.m0 = Function(self.Vm)
        if self.Parameters.has_key('m0'):
            setfct(self.m0, self.Parameters['m0'])
        self.mtrial = TrialFunction(self.Vm)
        self.mtest = TestFunction(self.Vm)
        self.mysample = Function(self.Vm)
        self.draw = Function(self.Vm)
        # Assemble:
        self.R = assemble(inner(nabla_grad(self.mtrial), \
        nabla_grad(self.mtest))*dx)
        self.M = assemble(inner(self.mtrial, self.mtest) * dx)

        self.Msolver = PETScKrylovSolver('cg', 'jacobi')
        self.Msolver.parameters["maximum_iterations"] = 2000
        self.Msolver.parameters["relative_tolerance"] = 1e-24
        self.Msolver.parameters["absolute_tolerance"] = 1e-24
        self.Msolver.parameters["error_on_nonconvergence"] = True
        self.Msolver.parameters["nonzero_initial_guess"] = False
        self.Msolver.set_operator(self.M)

        # preconditioner is Gamma^{-1}:
        if self.beta > 1e-10:
            self.precond = self.gamma * self.R + self.beta * self.M
        else:
            self.precond = self.gamma * self.R + (1e-10) * self.M
        # Minvprior is M.A^2 (if you use M inner-product):
        self.Minvprior = self.gamma * self.R + self.beta * self.M
        # L is used to sample

    def Minvpriordot(self, vect):
        return self.Minvprior * vect

    def init_vector(self, u, dim):
        self.R.init_vector(u, dim)
 def getprecond(self):
     """ precondition by TV + small fraction of mass matrix """
     #TODO: does not appear to be a great way to apply preconditioner (DIVERGED_ITS)
     solver = PETScKrylovSolver('cg', self.amgprecond)
     solver.parameters["maximum_iterations"] = 3000
     solver.parameters["relative_tolerance"] = 1e-24
     solver.parameters["absolute_tolerance"] = 1e-24
     solver.parameters["error_on_nonconvergence"] = True
     solver.parameters["nonzero_initial_guess"] = False
     self.precond = self.H + self.sMass
     solver.set_operator(self.precond)
     return solver
 def getprecond(self):
     #        if self.coeff_cg + self.coeff_ncg > 0.0:
     #            solver = PETScKrylovSolver('gmres', self.amgprecond)
     #            solver.parameters["maximum_iterations"] = 10000
     #        else:
     solver = PETScKrylovSolver('cg', self.amgprecond)
     solver.parameters["maximum_iterations"] = 2000
     solver.parameters["absolute_tolerance"] = 1e-24
     solver.parameters["relative_tolerance"] = 1e-24
     solver.parameters["error_on_nonconvergence"] = True
     solver.parameters["nonzero_initial_guess"] = False
     solver.set_operator(self.precond)
     return solver
Exemple #16
0
 def getprecond(self):
     """
     solver = PETScKrylovSolver("richardson", "amg")
     solver.parameters["maximum_iterations"] = 1
     solver.parameters["error_on_nonconvergence"] = False
     solver.parameters["nonzero_initial_guess"] = False
     """
     solver = PETScKrylovSolver('cg', self.amgsolver)
     solver.parameters["maximum_iterations"] = 1000
     solver.parameters["relative_tolerance"] = 1e-24
     solver.parameters["absolute_tolerance"] = 1e-24
     solver.parameters["error_on_nonconvergence"] = True
     solver.parameters["nonzero_initial_guess"] = False
     solver.set_operator(self.precond)
     return solver
    def set_coefficients(self):
        self.T = 1

        self.alpha_range = [0, 2*np.pi]
        self.alpha_size = 10

        self.beta_range = [0, 2*np.pi]
        self.beta_size = 10

        self.howmaxit = 3  # maximum number of iterations of Howard's algorithm
        self.solver = PETScKrylovSolver('gmres', 'sor')
        self.solver.parameters['report'] = False
        self.solver.parameters['absolute_tolerance'] = 1E-12
        self.solver.parameters['relative_tolerance'] = 1E-8
        self.solver.parameters['monitor_convergence'] = False
        self.solver.parameters['nonzero_initial_guess'] = True

        def diffusion(alpha, beta):
            return Expression('x[1]<0.0?0.1:x[1]', degree=1)
        self.__dict__['diffusion'] = diffusion

        velocity_evader_hor = 0.5
        velocity_pursuer_hor = 4

        velocity_evader_ver = 1
        velocity_pursuer_ver = 1

        def adv_x(alpha, beta):
            exp = '(vP*sin(beta)-vE*sin(alpha))'
            return Expression(exp, degree=1,
                              alpha=alpha, beta=beta,
                              vE=velocity_evader_hor, vP=velocity_pursuer_hor)
        self.__dict__['adv_x'] = adv_x

        def adv_y(alpha, beta):
            exp = '(vP*cos(beta)-vE*cos(alpha))'
            return Expression(exp, degree=1,
                              alpha=alpha, beta=beta,
                              vE=velocity_evader_ver, vP=velocity_pursuer_ver)
        self.__dict__['adv_y'] = adv_y

        def lin(alpha, beta):
            return Constant('0.0')
        self.__dict__['lin'] = lin

        self.small_radius = 1
        self.large_radius = 4

        self.ft = Expression(
            'abs(x[0]*x[0] + x[1]*x[1] - r) < 1E-10 ? 0: 1.0',
            degree=1, r=self.small_radius)

        def RHSt(alpha, beta):
            return Constant('0.0')
        self.__dict__['RHSt'] = RHSt
 def getprecond(self):
     Prec = PETScKrylovSolver("richardson", "amg")
     Prec.parameters["maximum_iterations"] = 1
     Prec.parameters["error_on_nonconvergence"] = False
     Prec.parameters["nonzero_initial_guess"] = False
     Prec.set_operator(self.Regul.get_precond())
     return Prec
    def getprecond(self):
        """ Precondition by inverting the TV Hessian """

        solver = PETScKrylovSolver('cg', self.amgprecond)
        solver.parameters["maximum_iterations"] = 2000
        solver.parameters["relative_tolerance"] = 1e-24
        solver.parameters["absolute_tolerance"] = 1e-24
        solver.parameters["error_on_nonconvergence"] = True
        solver.parameters["nonzero_initial_guess"] = False

        # used to compare iterative application of preconditioner
        # with exact application of preconditioner:
        #solver = PETScLUSolver("petsc")
        #solver.parameters['symmetric'] = True
        #solver.parameters['reuse_factorization'] = True

        solver.set_operator(self.precond)
        return solver
Exemple #20
0
    def _set_fluid_default_parameters(self):
        fluid = df.Parameters(Physics.FLUID.value)
        fluid.add("dt", 1e-3)
        fluid.add("dummy_parameter", False)

        # Add default parameters from both LU and Krylov solvers
        fluid.add(LUSolver.default_parameters())
        fluid.add(PETScKrylovSolver.default_parameters())

        # Add solver parameters
        fluid.add(df.Parameters("Solver"))
        fluid["Solver"].add("dummy_parameter", False)

        self.add(fluid)
Exemple #21
0
    def _set_porous_default_parameters(self):
        porous = df.Parameters(Physics.POROUS.value)
        porous.add("dt", 1e-3)
        porous.add("dummy_parameter", False)

        # Add default parameters from both LU and Krylov solvers
        porous.add(LUSolver.default_parameters())
        porous.add(PETScKrylovSolver.default_parameters())

        # Add solver parameters
        porous.add(df.Parameters("Solver"))
        porous["Solver"].add("dummy_parameter", False)

        self.add(porous)
Exemple #22
0
    def _set_solid_default_parameters(self):
        solid = df.Parameters(Physics.SOLID.value)
        solid.add("dt", 1e-3)
        solid.add("dummy_parameter", False)

        # Add boundary condtion parameters
        solid.add(df.Parameters("BoundaryConditions"))
        solid["BoundaryConditions"].add("base_bc", "fixed")
        solid["BoundaryConditions"].add("lv_pressure", 10.0)
        solid["BoundaryConditions"].add("rv_pressure", 0.0)
        solid["BoundaryConditions"].add("pericardium_spring", 0.0)
        solid["BoundaryConditions"].add("base_spring", 0.0)

        # Add default parameters from both LU and Krylov solvers
        solid.add(NonlinearVariationalSolver.default_parameters())
        solid.add(LUSolver.default_parameters())
        solid.add(PETScKrylovSolver.default_parameters())

        # Add solver parameters
        solid.add(df.Parameters("Solver"))
        solid["Solver"].add("dummy_parameter", False)

        self.add(solid)
class TVPD():
    """ Total variation using primal-dual Newton """
    def __init__(self, parameters):
        """ 
        TV regularization in primal-dual format
        Input parameters:
            * k = regularization parameter
            * eps = regularization constant (see above)
            * rescaledradiusdual = radius of dual set
            * exact = use full TV (bool)
            * PCGN = use GN Hessian to precondition (bool); 
            not recommended for performance but can help avoid num.instability
            * print (bool)
        """

        self.parameters = {}
        self.parameters['k'] = 1.0
        self.parameters['eps'] = 1e-2
        self.parameters['rescaledradiusdual'] = 1.0
        self.parameters['exact'] = False
        self.parameters['PCGN'] = False
        self.parameters['print'] = False
        self.parameters['correctcost'] = True
        self.parameters['amg'] = 'default'

        assert parameters.has_key('Vm')
        self.parameters.update(parameters)
        self.Vm = self.parameters['Vm']
        k = self.parameters['k']
        eps = self.parameters['eps']
        exact = self.parameters['exact']
        amg = self.parameters['amg']

        self.m = Function(self.Vm)
        testm = TestFunction(self.Vm)
        trialm = TrialFunction(self.Vm)

        # WARNING: should not be changed.
        # As it is, code only works with DG0
        if self.parameters.has_key('Vw'):
            Vw = self.parameters['Vw']
        else:
            Vw = FunctionSpace(self.Vm.mesh(), 'DG', 0)
        self.wx = Function(Vw)
        self.wxrs = Function(Vw)  # re-scaled dual variable
        self.wxhat = Function(Vw)
        self.gwx = Function(Vw)
        self.wy = Function(Vw)
        self.wyrs = Function(Vw)  # re-scaled dual variable
        self.wyhat = Function(Vw)
        self.gwy = Function(Vw)
        self.wxsq = Vector()
        self.wysq = Vector()
        self.normw = Vector()
        self.factorw = Vector()
        testw = TestFunction(Vw)
        trialw = TrialFunction(Vw)

        normm = inner(nabla_grad(self.m), nabla_grad(self.m))
        TVnormsq = normm + Constant(eps)
        TVnorm = sqrt(TVnormsq)
        if self.parameters['correctcost']:
            meshtmp = UnitSquareMesh(self.Vm.mesh().mpi_comm(), 10, 10)
            Vtmp = FunctionSpace(meshtmp, 'CG', 1)
            x = SpatialCoordinate(meshtmp)
            correctioncost = 1. / assemble(sqrt(4.0 * x[0] * x[0]) * dx)
        else:
            correctioncost = 1.0
        self.wkformcost = Constant(k * correctioncost) * TVnorm * dx
        if exact:
            sys.exit(1)
#            self.w = nabla_grad(self.m)/TVnorm # full Hessian
#            self.Htvw = inner(Constant(k) * nabla_grad(testm), self.w) * dx

        self.misfitwx = inner(testw, self.wx * TVnorm - self.m.dx(0)) * dx
        self.misfitwy = inner(testw, self.wy * TVnorm - self.m.dx(1)) * dx

        self.Htvx = assemble(inner(Constant(k) * testm.dx(0), trialw) * dx)
        self.Htvy = assemble(inner(Constant(k) * testm.dx(1), trialw) * dx)

        self.massw = inner(TVnorm * testw, trialw) * dx

        mpicomm = self.Vm.mesh().mpi_comm()
        invMwMat, VDM, VDM = setupPETScmatrix(Vw, Vw, 'aij', mpicomm)
        for ii in VDM.dofs():
            invMwMat[ii, ii] = 1.0
        invMwMat.assemblyBegin()
        invMwMat.assemblyEnd()
        self.invMwMat = PETScMatrix(invMwMat)
        self.invMwd = Vector()
        self.invMwMat.init_vector(self.invMwd, 0)
        self.invMwMat.init_vector(self.wxsq, 0)
        self.invMwMat.init_vector(self.wysq, 0)
        self.invMwMat.init_vector(self.normw, 0)
        self.invMwMat.init_vector(self.factorw, 0)

        u = Function(Vw)
        uflrank = len(u.ufl_shape)
        if uflrank == 0:
            ones = ("1.0")
        elif uflrank == 1:
            ones = (("1.0", "1.0"))
        else:
            sys.exit(1)
        u = interpolate(Constant(ones), Vw)
        self.one = u.vector()

        self.wkformAx = inner(testw, trialm.dx(0) - \
        self.wx * inner(nabla_grad(self.m), nabla_grad(trialm)) / TVnorm) * dx
        self.wkformAxrs = inner(testw, trialm.dx(0) - \
        self.wxrs * inner(nabla_grad(self.m), nabla_grad(trialm)) / TVnorm) * dx
        self.wkformAy = inner(testw, trialm.dx(1) - \
        self.wy * inner(nabla_grad(self.m), nabla_grad(trialm)) / TVnorm) * dx
        self.wkformAyrs = inner(testw, trialm.dx(1) - \
        self.wyrs * inner(nabla_grad(self.m), nabla_grad(trialm)) / TVnorm) * dx

        kovsq = Constant(k) / TVnorm
        self.wkformGNhess = kovsq * inner(nabla_grad(trialm),
                                          nabla_grad(testm)) * dx
        factM = 1e-2 * k
        M = assemble(inner(testm, trialm) * dx)
        self.sMass = M * factM

        self.Msolver = PETScKrylovSolver('cg', 'jacobi')
        self.Msolver.parameters["maximum_iterations"] = 2000
        self.Msolver.parameters["relative_tolerance"] = 1e-24
        self.Msolver.parameters["absolute_tolerance"] = 1e-24
        self.Msolver.parameters["error_on_nonconvergence"] = True
        self.Msolver.parameters["nonzero_initial_guess"] = False
        self.Msolver.set_operator(M)

        if amg == 'default': self.amgprecond = amg_solver()
        else: self.amgprecond = amg

        if self.parameters['print']:
            print '[TVPD] TV regularization -- primal-dual method',
            if self.parameters['PCGN']:
                print ' -- PCGN',
            print ' -- k={}, eps={}'.format(k, eps)
            print '[TVPD] preconditioner = {}'.format(self.amgprecond)
            print '[TVPD] correction cost with factor={}'.format(
                correctioncost)

    def isTV(self):
        return True

    def isPD(self):
        return True

    def cost(self, m):
        """ evaluate the cost functional at m """
        setfct(self.m, m)
        return assemble(self.wkformcost)

    def costvect(self, m_in):
        return self.cost(m_in)

    def _assemble_invMw(self):
        """ Assemble inverse of matrix Mw,
        weighted mass matrix in dual space """
        # WARNING: only works if Mw is diagonal (e.g, DG0)
        Mw = assemble(self.massw)
        Mwd = get_diagonal(Mw)
        as_backend_type(self.invMwd).vec().pointwiseDivide(\
            as_backend_type(self.one).vec(),\
            as_backend_type(Mwd).vec())
        self.invMwMat.set_diagonal(self.invMwd)

    def grad(self, m):
        """ compute the gradient at m """
        setfct(self.m, m)
        self._assemble_invMw()

        self.gwx.vector().zero()
        self.gwx.vector().axpy(1.0, assemble(self.misfitwx))
        normgwx = norm(self.gwx.vector())

        self.gwy.vector().zero()
        self.gwy.vector().axpy(1.0, assemble(self.misfitwy))
        normgwy = norm(self.gwy.vector())

        if self.parameters['print']:
            print '[TVPD] |gw|={}'.format(np.sqrt(normgwx**2 + normgwy**2))

        return self.Htvx*(self.wx.vector() - self.invMwd*self.gwx.vector()) \
        + self.Htvy*(self.wy.vector() - self.invMwd*self.gwy.vector())
        #return assemble(self.Htvw) - self.Htv*(self.invMwd*self.gw.vector())

    def gradvect(self, m_in):
        return self.grad(m_in)

    def assemble_hessian(self, m):
        """ build Hessian matrix at given point m """
        setfct(self.m, m)
        self._assemble_invMw()

        self.Ax = assemble(self.wkformAx)
        Hxasym = MatMatMult(self.Htvx, MatMatMult(self.invMwMat, self.Ax))
        Hx = (Hxasym + Transpose(Hxasym)) * 0.5
        Axrs = assemble(self.wkformAxrs)
        Hxrsasym = MatMatMult(self.Htvx, MatMatMult(self.invMwMat, Axrs))
        Hxrs = (Hxrsasym + Transpose(Hxrsasym)) * 0.5

        self.Ay = assemble(self.wkformAy)
        Hyasym = MatMatMult(self.Htvy, MatMatMult(self.invMwMat, self.Ay))
        Hy = (Hyasym + Transpose(Hyasym)) * 0.5
        Ayrs = assemble(self.wkformAyrs)
        Hyrsasym = MatMatMult(self.Htvy, MatMatMult(self.invMwMat, Ayrs))
        Hyrs = (Hyrsasym + Transpose(Hyrsasym)) * 0.5

        self.H = Hx + Hy
        self.Hrs = Hxrs + Hyrs

        PCGN = self.parameters['PCGN']
        if PCGN:
            HGN = assemble(self.wkformGNhess)
            self.precond = HGN + self.sMass
        else:
            self.precond = self.Hrs + self.sMass

    def hessian(self, mhat):
        return self.Hrs * mhat

    def compute_what(self, mhat):
        """ Compute update direction for what, given mhat """
        self.wxhat.vector().zero()
        self.wxhat.vector().axpy(
            1.0, self.invMwd * (self.Ax * mhat - self.gwx.vector()))
        normwxhat = norm(self.wxhat.vector())

        self.wyhat.vector().zero()
        self.wyhat.vector().axpy(
            1.0, self.invMwd * (self.Ay * mhat - self.gwy.vector()))
        normwyhat = norm(self.wyhat.vector())

        if self.parameters['print']:
            print '[TVPD] |what|={}'.format(
                np.sqrt(normwxhat**2 + normwyhat**2))

    def update_w(self, mhat, alphaLS, compute_what=True):
        """ update dual variable in direction what 
        and update re-scaled version """
        if compute_what: self.compute_what(mhat)
        self.wx.vector().axpy(alphaLS, self.wxhat.vector())
        self.wy.vector().axpy(alphaLS, self.wyhat.vector())

        # rescaledradiusdual=1.0: checked empirically to be max radius acceptable
        rescaledradiusdual = self.parameters['rescaledradiusdual']
        # wx**2
        as_backend_type(self.wxsq).vec().pointwiseMult(\
            as_backend_type(self.wx.vector()).vec(),\
            as_backend_type(self.wx.vector()).vec())
        # wy**2
        as_backend_type(self.wysq).vec().pointwiseMult(\
            as_backend_type(self.wy.vector()).vec(),\
            as_backend_type(self.wy.vector()).vec())
        # |w|
        self.normw = self.wxsq + self.wysq
        as_backend_type(self.normw).vec().sqrtabs()
        # |w|/r
        as_backend_type(self.normw).vec().pointwiseDivide(\
            as_backend_type(self.normw).vec(),\
            as_backend_type(self.one*rescaledradiusdual).vec())
        # max(1.0, |w|/r)
        #        as_backend_type(self.factorw).vec().pointwiseMax(\
        #            as_backend_type(self.one).vec(),\
        #            as_backend_type(self.normw).vec())
        count = pointwiseMaxCount(self.factorw, self.normw, 1.0)
        # rescale wx and wy
        as_backend_type(self.wxrs.vector()).vec().pointwiseDivide(\
            as_backend_type(self.wx.vector()).vec(),\
            as_backend_type(self.factorw).vec())
        as_backend_type(self.wyrs.vector()).vec().pointwiseDivide(\
            as_backend_type(self.wy.vector()).vec(),\
            as_backend_type(self.factorw).vec())

        minf = self.factorw.min()
        maxf = self.factorw.max()
        if self.parameters['print']:
            #            print 'min(factorw)={}, max(factorw)={}'.format(minf, maxf)
            print '[TVPD] perc. dual entries rescaled={:.2f} %, min(factorw)={}, max(factorw)={}'.format(\
            100.*float(count)/self.factorw.size(), minf, maxf)

    def getprecond(self):
        """ Precondition by inverting the TV Hessian """

        solver = PETScKrylovSolver('cg', self.amgprecond)
        solver.parameters["maximum_iterations"] = 2000
        solver.parameters["relative_tolerance"] = 1e-24
        solver.parameters["absolute_tolerance"] = 1e-24
        solver.parameters["error_on_nonconvergence"] = True
        solver.parameters["nonzero_initial_guess"] = False

        # used to compare iterative application of preconditioner
        # with exact application of preconditioner:
        #solver = PETScLUSolver("petsc")
        #solver.parameters['symmetric'] = True
        #solver.parameters['reuse_factorization'] = True

        solver.set_operator(self.precond)

        return solver

    def init_vector(self, u, dim):
        self.sMass.init_vector(u, dim)
def solve(W, P,
          mu,
          u_bcs, p_bcs,
          f,
          verbose=True,
          tol=1.0e-10
          ):
    # Some initial sanity checks.
    assert mu > 0.0

    WP = MixedFunctionSpace([W, P])

    # Translate the boundary conditions into the product space.
    # This conditional loop is able to deal with conditions of the kind
    #
    #     DirichletBC(W.sub(1), 0.0, right_boundary)
    #
    new_bcs = []
    for k, bcs in enumerate([u_bcs, p_bcs]):
        for bc in bcs:
            space = bc.function_space()
            C = space.component()
            if len(C) == 0:
                new_bcs.append(DirichletBC(WP.sub(k),
                                           bc.value(),
                                           bc.domain_args[0]))
            elif len(C) == 1:
                new_bcs.append(DirichletBC(WP.sub(k).sub(int(C[0])),
                                           bc.value(),
                                           bc.domain_args[0]))
            else:
                raise RuntimeError('Illegal number of subspace components.')

    # Define variational problem
    (u, p) = TrialFunctions(WP)
    (v, q) = TestFunctions(WP)

    # Build system.
    # The sign of the div(u)-term is somewhat arbitrary since the right-hand
    # side is 0 here. We can either make the system symmetric or positive-
    # definite.
    # On a second note, we have
    #
    #    \int grad(p).v = - \int p * div(v) + \int_\Gamma p n.v.
    #
    # Since, we have either p=0 or n.v=0 on the boundary, we could as well
    # replace the term dot(grad(p), v) by -p*div(v).
    #
    a = mu * inner(grad(u), grad(v))*dx \
      - p * div(v) * dx \
      - q * div(u) * dx
    #a = mu * inner(grad(u), grad(v))*dx + dot(grad(p), v) * dx \
    #  - div(u) * q * dx
    L = dot(f, v)*dx
    A, b = assemble_system(a, L, new_bcs)

    if has_petsc():
        # For an assortment of preconditioners, see
        #
        #     Performance and analysis of saddle point preconditioners
        #     for the discrete steady-state Navier-Stokes equations;
        #     H.C. Elman, D.J. Silvester, A.J. Wathen;
        #     Numer. Math. (2002) 90: 665-688;
        #     <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.145.3554>.
        #
        # Set up field split.
        W = SubSpace(WP, 0)
        P = SubSpace(WP, 1)
        u_dofs = W.dofmap().dofs()
        p_dofs = P.dofmap().dofs()
        prec = PETScPreconditioner()
        prec.set_fieldsplit([u_dofs, p_dofs], ['u', 'p'])

        PETScOptions.set('pc_type', 'fieldsplit')
        PETScOptions.set('pc_fieldsplit_type', 'additive')
        PETScOptions.set('fieldsplit_u_pc_type', 'lu')
        PETScOptions.set('fieldsplit_p_pc_type', 'jacobi')

        ## <http://scicomp.stackexchange.com/questions/7288/which-preconditioners-and-solver-in-petsc-for-indefinite-symmetric-systems-sho>
        #PETScOptions.set('pc_type', 'fieldsplit')
        ##PETScOptions.set('pc_fieldsplit_type', 'schur')
        ##PETScOptions.set('pc_fieldsplit_schur_fact_type', 'upper')
        #PETScOptions.set('pc_fieldsplit_detect_saddle_point')
        ##PETScOptions.set('fieldsplit_u_pc_type', 'lsc')
        ##PETScOptions.set('fieldsplit_u_ksp_type', 'preonly')

        #PETScOptions.set('pc_type', 'fieldsplit')
        #PETScOptions.set('fieldsplit_u_pc_type', 'hypre')
        #PETScOptions.set('fieldsplit_u_ksp_type', 'preonly')
        #PETScOptions.set('fieldsplit_p_pc_type', 'jacobi')
        #PETScOptions.set('fieldsplit_p_ksp_type', 'preonly')

        ## From PETSc/src/ksp/ksp/examples/tutorials/ex42-fsschur.opts:
        #PETScOptions.set('pc_type', 'fieldsplit')
        #PETScOptions.set('pc_fieldsplit_type', 'SCHUR')
        #PETScOptions.set('pc_fieldsplit_schur_fact_type', 'UPPER')
        #PETScOptions.set('fieldsplit_p_ksp_type', 'preonly')
        #PETScOptions.set('fieldsplit_u_pc_type', 'bjacobi')

        ## From
        ##
        ##     Composable Linear Solvers for Multiphysics;
        ##     J. Brown, M. Knepley, D.A. May, L.C. McInnes, B. Smith;
        ##     <http://www.computer.org/csdl/proceedings/ispdc/2012/4805/00/4805a055-abs.html>;
        ##     <http://www.mcs.anl.gov/uploads/cels/papers/P2017-0112.pdf>.
        ##
        #PETScOptions.set('pc_type', 'fieldsplit')
        #PETScOptions.set('pc_fieldsplit_type', 'schur')
        #PETScOptions.set('pc_fieldsplit_schur_factorization_type', 'upper')
        ##
        #PETScOptions.set('fieldsplit_u_ksp_type', 'cg')
        #PETScOptions.set('fieldsplit_u_ksp_rtol', 1.0e-6)
        #PETScOptions.set('fieldsplit_u_pc_type', 'bjacobi')
        #PETScOptions.set('fieldsplit_u_sub_pc_type', 'cholesky')
        ##
        #PETScOptions.set('fieldsplit_p_ksp_type', 'fgmres')
        #PETScOptions.set('fieldsplit_p_ksp_constant_null_space')
        #PETScOptions.set('fieldsplit_p_pc_type', 'lsc')
        ##
        #PETScOptions.set('fieldsplit_p_lsc_ksp_type', 'cg')
        #PETScOptions.set('fieldsplit_p_lsc_ksp_rtol', 1.0e-2)
        #PETScOptions.set('fieldsplit_p_lsc_ksp_constant_null_space')
        ##PETScOptions.set('fieldsplit_p_lsc_ksp_converged_reason')
        #PETScOptions.set('fieldsplit_p_lsc_pc_type', 'bjacobi')
        #PETScOptions.set('fieldsplit_p_lsc_sub_pc_type', 'icc')

        # Create Krylov solver with custom preconditioner.
        solver = PETScKrylovSolver('gmres', prec)
        solver.set_operator(A)
    else:
        # Use the preconditioner as recommended in
        # <http://fenicsproject.org/documentation/dolfin/dev/python/demo/pde/stokes-iterative/python/documentation.html>,
        #
        #     prec = inner(grad(u), grad(v))*dx - p*q*dx
        #
        # although it doesn't seem to be too efficient.
        # The sign on the last term doesn't matter.
        prec = mu * inner(grad(u), grad(v))*dx \
             - p*q*dx
        M, _ = assemble_system(prec, L, new_bcs)
        #solver = KrylovSolver('tfqmr', 'amg')
        solver = KrylovSolver('gmres', 'amg')
        solver.set_operators(A, M)

    solver.parameters['monitor_convergence'] = verbose
    solver.parameters['report'] = verbose
    solver.parameters['absolute_tolerance'] = 0.0
    solver.parameters['relative_tolerance'] = tol
    solver.parameters['maximum_iterations'] = 500

    # Solve
    up = Function(WP)
    solver.solve(up.vector(), b)

    # Get sub-functions
    u, p = up.split()

    return u, p
    def _pressure_poisson(self, p1, p0,
                          mu, ui,
                          u,
                          p_bcs=None,
                          rotational_form=False,
                          tol=1.0e-10,
                          verbose=True
                          ):
        '''Solve the pressure Poisson equation
            -1/r \div(r \nabla (p1-p0)) = -1/r div(r*u),
            boundary conditions,
        for
            \nabla p = u.
        '''
        r = Expression('x[0]', degree=1, domain=self.W.mesh())

        Q = p1.function_space()

        p = TrialFunction(Q)
        q = TestFunction(Q)
        a2 = dot(r * grad(p), grad(q)) * 2 * pi * dx
        # The boundary conditions
        #     n.(p1-p0) = 0
        # are implicitly included.
        #
        # L2 = -div(r*u) * q * 2*pi*dx
        div_u = 1/r * (r * u[0]).dx(0) + u[1].dx(1)
        L2 = -div_u * q * 2*pi*r*dx
        if p0:
            L2 += r * dot(grad(p0), grad(q)) * 2*pi*dx

        # In the Cartesian variant of the rotational form, one makes use of the
        # fact that
        #
        #     curl(curl(u)) = grad(div(u)) - div(grad(u)).
        #
        # The same equation holds true in cylindrical form. Hence, to get the
        # rotational form of the splitting scheme, we need to
        #
        # rotational form
        if rotational_form:
            # If there is no dependence of the angular coordinate, what is
            # div(grad(div(u))) in Cartesian coordinates becomes
            #
            #     1/r div(r * grad(1/r div(r*u)))
            #
            # in cylindrical coordinates (div and grad are in cylindrical
            # coordinates). Unfortunately, we cannot write it down that
            # compactly since u_phi is in the game.
            # When using P2 elements, this value will be 0 anyways.
            div_ui = 1/r * (r * ui[0]).dx(0) + ui[1].dx(1)
            grad_div_ui = as_vector((div_ui.dx(0), div_ui.dx(1)))
            L2 -= r * mu * dot(grad_div_ui, grad(q)) * 2*pi*dx
            #div_grad_div_ui = 1/r * (r * grad_div_ui[0]).dx(0) \
            #    + (grad_div_ui[1]).dx(1)
            #L2 += mu * div_grad_div_ui * q * 2*pi*r*dx
            #n = FacetNormal(Q.mesh())
            #L2 -= mu * (n[0] * grad_div_ui[0] + n[1] * grad_div_ui[1]) \
            #    * q * 2*pi*r*ds

        if p_bcs:
            solve(
                a2 == L2, p1,
                bcs=p_bcs,
                solver_parameters={
                    'linear_solver': 'iterative',
                    'symmetric': True,
                    'preconditioner': 'amg',
                    'krylov_solver': {'relative_tolerance': tol,
                                      'absolute_tolerance': 0.0,
                                      'maximum_iterations': 100,
                                      'monitor_convergence': verbose}
                    }
                )
        else:
            # If we're dealing with a pure Neumann problem here (which is the
            # default case), this doesn't hurt CG if the system is consistent,
            # cf. :cite:`vdV03`. And indeed it is consistent if and only if
            #
            #   \int_\Gamma r n.u = 0.
            #
            # This makes clear that for incompressible Navier-Stokes, one
            # either needs to make sure that inflow and outflow always add up
            # to 0, or one has to specify pressure boundary conditions.
            #
            # If the right-hand side is very small, round-off errors may impair
            # the consistency of the system. Make sure the system we are
            # solving remains consistent.
            A = assemble(a2)
            b = assemble(L2)
            # Assert that the system is indeed consistent.
            e = Function(Q)
            e.interpolate(Constant(1.0))
            evec = e.vector()
            evec /= norm(evec)
            alpha = b.inner(evec)
            normB = norm(b)
            # Assume that in every component of the vector, a round-off error
            # of the magnitude DOLFIN_EPS is present. This leads to the
            # criterion
            #    |<b,e>| / (||b||*||e||) < DOLFIN_EPS
            # as a check whether to consider the system consistent up to
            # round-off error.
            #
            # TODO think about condition here
            #if abs(alpha) > normB * DOLFIN_EPS:
            if abs(alpha) > normB * 1.0e-12:
                divu = 1 / r * (r * u[0]).dx(0) + u[1].dx(1)
                adivu = assemble(((r * u[0]).dx(0) + u[1].dx(1)) * 2 * pi * dx)
                info('\int 1/r * div(r*u) * 2*pi*r  =  %e' % adivu)
                n = FacetNormal(Q.mesh())
                boundary_integral = assemble((n[0] * u[0] + n[1] * u[1])
                                             * 2 * pi * r * ds)
                info('\int_Gamma n.u * 2*pi*r = %e' % boundary_integral)
                message = ('System not consistent! '
                           '<b,e> = %g, ||b|| = %g, <b,e>/||b|| = %e.') \
                           % (alpha, normB, alpha / normB)
                info(message)
                # Plot the stuff, and project it to a finer mesh with linear
                # elements for the purpose.
                plot(divu, title='div(u_tentative)')
                #Vp = FunctionSpace(Q.mesh(), 'CG', 2)
                #Wp = MixedFunctionSpace([Vp, Vp])
                #up = project(u, Wp)
                fine_mesh = Q.mesh()
                for k in range(1):
                    fine_mesh = refine(fine_mesh)
                V = FunctionSpace(fine_mesh, 'CG', 1)
                W = V * V
                #uplot = Function(W)
                #uplot.interpolate(u)
                uplot = project(u, W)
                plot(uplot[0], title='u_tentative[0]')
                plot(uplot[1], title='u_tentative[1]')
                #plot(u, title='u_tentative')
                interactive()
                exit()
                raise RuntimeError(message)
            # Project out the roundoff error.
            b -= alpha * evec

            #
            # In principle, the ILU preconditioner isn't advised here since it
            # might destroy the semidefiniteness needed for CG.
            #
            # The system is consistent, but the matrix has an eigenvalue 0.
            # This does not harm the convergence of CG, but when
            # preconditioning one has to make sure that the preconditioner
            # preserves the kernel.  ILU might destroy this (and the
            # semidefiniteness). With AMG, the coarse grid solves cannot be LU
            # then, so try Jacobi here.
            # <http://lists.mcs.anl.gov/pipermail/petsc-users/2012-February/012139.html>
            #
            prec = PETScPreconditioner('hypre_amg')
            from dolfin import PETScOptions
            PETScOptions.set('pc_hypre_boomeramg_relax_type_coarse', 'jacobi')
            solver = PETScKrylovSolver('cg', prec)
            solver.parameters['absolute_tolerance'] = 0.0
            solver.parameters['relative_tolerance'] = tol
            solver.parameters['maximum_iterations'] = 100
            solver.parameters['monitor_convergence'] = verbose
            # Create solver and solve system
            A_petsc = as_backend_type(A)
            b_petsc = as_backend_type(b)
            p1_petsc = as_backend_type(p1.vector())
            solver.set_operator(A_petsc)
            solver.solve(p1_petsc, b_petsc)
            # This would be the stump for Epetra:
            #solve(A, p.vector(), b, 'cg', 'ml_amg')
        return
Exemple #26
0
class ObjectiveFunctional(LinearOperator):
    """
    Provides data misfit, gradient and Hessian information for the data misfit
    part of a time-independent symmetric inverse problem.
    """
    __metaclass__ = abc.ABCMeta

    # Instantiation
    def __init__(self, V, Vm, bc, bcadj, \
    RHSinput=[], ObsOp=[], UD=[], Regul=[], Data=[], plot=False, \
    mycomm=None):
        # Define test, trial and all other functions
        self.trial = TrialFunction(V)
        self.test = TestFunction(V)
        self.mtrial = TrialFunction(Vm)
        self.mtest = TestFunction(Vm)
        self.rhs = Function(V)
        self.m = Function(Vm)
        self.mcopy = Function(Vm)
        self.srchdir = Function(Vm)
        self.delta_m = Function(Vm)
        self.MG = Function(Vm)
        self.MGv = self.MG.vector()
        self.Grad = Function(Vm)
        self.Gradnorm = 0.0
        self.lenm = len(self.m.vector().array())
        self.u = Function(V)
        self.ud = Function(V)
        self.diff = Function(V)
        self.p = Function(V)
        # Store other info:
        self.ObsOp = ObsOp
        self.UD = UD
        self.reset()  # Initialize U, C and E to []
        self.Data = Data
        self.GN = 1.0  # GN = 0.0 => GN Hessian; = 1.0 => full Hessian
        # Define weak forms to assemble A, C and E
        self._wkforma()
        self._wkformc()
        self._wkforme()
        # Operators and bc
        LinearOperator.__init__(self, self.delta_m.vector(), \
        self.delta_m.vector())
        self.bc = bc
        self.bcadj = bcadj
        self._assemble_solverM(Vm)
        self.assemble_A()
        self.assemble_RHS(RHSinput)
        self.Regul = Regul
        self.regparam = 1.0
        if Regul != []:
            self.PD = self.Regul.isPD()
        # Counters, tolerances and others
        self.nbPDEsolves = 0  # Updated when solve_A called
        self.nbfwdsolves = 0  # Counter for plots
        self.nbadjsolves = 0  # Counter for plots
        # MPI:
        self.mycomm = mycomm

    def copy(self):
        """Define a copy method"""

        V = self.trial.function_space()
        Vm = self.mtrial.function_space()
        newobj = self.__class__(V, Vm, self.bc, self.bcadj, [], self.ObsOp, \
        self.UD, self.Regul, self.Data, False)
        newobj.RHS = self.RHS
        newobj.update_m(self.m)
        return newobj

    def mult(self, mhat, y):
        """mult(self, mhat, y): do y = Hessian * mhat
        member self.GN sets full Hessian (=1.0) or GN Hessian (=0.0)"""

        N = self.Nbsrc  # Number of sources
        y[:] = np.zeros(self.lenm)

        for C, E in zip(self.C, self.E):
            C.transpmult(mhat, self.rhs.vector())
            if self.bcadj is not None:
                self.bcadj.apply(self.rhs.vector())
            self.solve_A(self.u.vector(), -self.rhs.vector())

            E.transpmult(mhat, self.rhs.vector())
            Etmhat = self.rhs.vector().array()
            self.rhs.vector().axpy(1.0, self.ObsOp.incradj(self.u))
            if self.bcadj is not None:
                self.bcadj.apply(self.rhs.vector())
            self.solve_A(self.p.vector(), -self.rhs.vector())

            y.axpy(1.0 / N, C * self.p.vector())
            y.axpy(self.GN / N, E * self.u.vector())

        y.axpy(self.regparam, self.Regul.hessian(mhat))

    # Getters
    def getm(self):
        return self.m

    def getmarray(self):
        return self.m.vector().array()

    def getmcopyarray(self):
        return self.mcopy.vector().array()

    def getVm(self):
        return self.mtrial.function_space()

    def getMGarray(self):
        return self.MG.vector().array()

    def getMGvec(self):
        return self.MGv

    def getGradarray(self):
        return self.Grad.vector().array()

    def getGradnorm(self):
        return self.Gradnorm

    def getsrchdirarray(self):
        return self.srchdir.vector().array()

    def getsrchdirvec(self):
        return self.srchdir.vector()

    def getsrchdirnorm(self):
        return np.sqrt(
            (self.MM * self.getsrchdirvec()).inner(self.getsrchdirvec()))

    def getgradxdir(self):
        return self.gradxdir

    def getcost(self):
        return self.cost, self.misfit, self.regul

    def getprecond(self):
        return self.Regul.getprecond()
#        Prec = PETScKrylovSolver("richardson", "amg")
#        Prec.parameters["maximum_iterations"] = 1
#        Prec.parameters["error_on_nonconvergence"] = False
#        Prec.parameters["nonzero_initial_guess"] = False
#        Prec.set_operator(self.Regul.get_precond())
#        return Prec

    def getMass(self):
        return self.MM

    # Setters
    def setsrchdir(self, arr):
        self.srchdir.vector()[:] = arr

    def setgradxdir(self, valueloc):
        """Sum all local results for Grad . Srch_dir"""
        try:
            valueglob = MPI.sum(self.mycomm, valueloc)
        except:
            valueglob = valueloc
        self.gradxdir = valueglob

    # Solve
    def solvefwd(self, cost=False):
        """Solve fwd operators for given RHS"""

        self.nbfwdsolves += 1
        if cost: self.misfit = 0.0
        self.U = []
        self.C = []
        for ii, rhs in enumerate(self.RHS):
            self.solve_A(self.u.vector(), rhs)
            u_obs, noiselevel = self.ObsOp.obs(self.u)
            self.U.append(u_obs)
            if cost:
                self.misfit += self.ObsOp.costfct(u_obs, self.UD[ii])
            self.C.append(assemble(self.c))
        if cost:
            self.misfit /= len(self.U)
            self.regul = self.Regul.cost(self.m)
            self.cost = self.misfit + self.regparam * self.regul

    def solvefwd_cost(self):
        """Solve fwd operators for given RHS and compute cost fct"""

        self.solvefwd(True)

    def solveadj(self, grad=False):
        """Solve adj operators"""

        self.nbadjsolves += 1
        self.Nbsrc = len(self.UD)
        if grad:
            self.MG.vector().zero()
        self.E = []

        for ii, C in enumerate(self.C):
            self.ObsOp.assemble_rhsadj(self.U[ii], self.UD[ii], \
            self.rhs, self.bcadj)
            self.solve_A(self.p.vector(), self.rhs.vector())
            self.E.append(assemble(self.e))
            if grad:
                self.MG.vector().axpy(1.0 / self.Nbsrc, C * self.p.vector())

        if grad:
            self.MG.vector().axpy(self.regparam, self.Regul.grad(self.m))
            self.solverM.solve(self.Grad.vector(), self.MG.vector())
            self.Gradnorm = np.sqrt(self.Grad.vector().inner(self.MG.vector()))

    def solveadj_constructgrad(self):
        """Solve adj operators and assemble gradient"""

        self.solveadj(True)

    # Assembler
    def assemble_A(self):
        """Assemble operator A(m)"""

        self.A = assemble(self.a)
        if self.bc is not None:
            self.bc.apply(self.A)
        compute_eigfenics(self.A, 'eigA.txt')
        self.set_solver()

    def solve_A(self, b, f):
        """Solve system of the form A.b = f, 
        with b and f in form to be used in solver."""

        self.solver.solve(b, f)
        self.nbPDEsolves += 1

    def assemble_RHS(self, RHSin):
        """Assemble RHS for fwd solve"""

        if RHSin == []: self.RHS = None
        else:
            self.RHS = []
            for rhs in RHSin:
                if isinstance(rhs, Expression):
                    L = rhs * self.test * dx
                    b = assemble(L)
                    if self.bc is not None:
                        self.bc.apply(b)
                    self.RHS.append(b)
                elif isinstance(rhs, GenericVector):
                    self.RHS.append(rhs)
                else:
                    raise WrongInstanceError(
                        "rhs should be an Expression or a GenericVector")

    def _assemble_solverM(self, Vm):

        self.MM = assemble(inner(self.mtrial, self.mtest) * dx)
        self.solverM = PETScKrylovSolver('cg', 'jacobi')
        self.solverM.parameters["maximum_iterations"] = 1000
        self.solverM.parameters["relative_tolerance"] = 1e-12
        self.solverM.parameters["error_on_nonconvergence"] = True
        self.solverM.parameters["nonzero_initial_guess"] = False
        #        self.solverM = LUSolver()
        #        self.solverM.parameters['reuse_factorization'] = True
        #        self.solverM.parameters['symmetric'] = True
        self.solverM.set_operator(self.MM)

    # Update param
    def update_Data(self, Data):
        """Update Data member"""

        self.Data = Data
        self.assemble_A()
        self.reset()

    def update_m(self, m):
        """Update values of parameter m"""

        if isinstance(m, np.ndarray):
            self.m.vector()[:] = m
        elif isinstance(m, Function):
            self.m.assign(m)
        elif isinstance(m, float):
            self.m.vector()[:] = m
        elif isinstance(m, int):
            self.m.vector()[:] = float(m)
        else:
            raise WrongInstanceError('Format for m not accepted')
        self.assemble_A()
        self.reset()

    def backup_m(self):
        self.mcopy.assign(self.m)

    def restore_m(self):
        self.update_m(self.mcopy)

    def reset(self):
        """Reset U, C and E"""
        self.U = []
        self.C = []
        self.E = []

    def set_solver(self):
        """Reset solver for fwd operator"""

        #self.solver = LUSolver()
        #self.solver.parameters['reuse_factorization'] = True
        self.solver = PETScKrylovSolver("cg", "amg")
        self.solver.parameters["maximum_iterations"] = 1000
        self.solver.parameters["relative_tolerance"] = 1e-12
        self.solver.parameters["error_on_nonconvergence"] = True
        self.solver.parameters["nonzero_initial_guess"] = False
        self.solver.set_operator(self.A)

    def addPDEcount(self, increment=1):
        """Increase 'nbPDEsolves' by 'increment'"""
        self.nbPDEsolves += increment

    def resetPDEsolves(self):
        self.nbPDEsolves = 0

    # Additional methods for compatibility with CG solver:
    def init_vector(self, x, dim):
        """Initialize vector x to be compatible with parameter
         Does not work in dolfin 1.3.0"""
        self.MM.init_vector(x, 0)

    def init_vector130(self):
        """Initialize vector x to be compatible with parameter"""
        return Vector(Function(self.mcopy.function_space()).vector())

    # Abstract methods
    @abc.abstractmethod
    def _wkforma(self):
        self.a = []

    @abc.abstractmethod
    def _wkformc(self):
        self.c = []

    @abc.abstractmethod
    def _wkforme(self):
        self.e = []


    def inversion(self, initial_medium, target_medium, mpicomm, \
    parameters_in=[], myplot=None):
        """ solve inverse problem with that objective function """

        parameters = {'tolgrad':1e-10, 'tolcost':1e-14, 'maxnbNewtiter':50, \
        'maxtolcg':0.5}
        parameters.update(parameters_in)
        maxnbNewtiter = parameters['maxnbNewtiter']
        tolgrad = parameters['tolgrad']
        tolcost = parameters['tolcost']
        tolcg = parameters['maxtolcg']
        mpirank = MPI.rank(mpicomm)

        self.update_m(initial_medium)
        self._plotm(myplot, 'init')

        if mpirank == 0:
            print '\t{:12s} {:10s} {:12s} {:12s} {:12s} {:10s} \t{:10s} {:12s} {:12s}'.format(\
            'iter', 'cost', 'misfit', 'reg', '|G|', 'medmisf', 'a_ls', 'tol_cg', 'n_cg')
        dtruenorm = np.sqrt(target_medium.vector().\
        inner(self.MM*target_medium.vector()))

        self.solvefwd_cost()
        for it in xrange(maxnbNewtiter):
            self.solveadj_constructgrad()  # compute gradient

            if it == 0: gradnorm0 = self.Gradnorm
            diff = self.m.vector() - target_medium.vector()
            medmisfit = np.sqrt(diff.inner(self.MM * diff))
            if mpirank == 0:
                print '{:12d} {:12.4e} {:12.2e} {:12.2e} {:11.4e} {:10.2e} ({:4.2f})'.\
                format(it, self.cost, self.misfit, self.regul, \
                self.Gradnorm, medmisfit, medmisfit/dtruenorm),
            self._plotm(myplot, str(it))
            self._plotgrad(myplot, str(it))

            if self.Gradnorm < gradnorm0 * tolgrad or self.Gradnorm < 1e-12:
                if mpirank == 0:
                    print '\nGradient sufficiently reduced -- optimization stopped'
                break

            # Compute search direction:
            tolcg = min(tolcg, np.sqrt(self.Gradnorm / gradnorm0))
            self.assemble_hessian()  # for regularization
            cgiter, cgres, cgid, tolcg = compute_searchdirection(
                self, 'Newt', tolcg)
            self._plotsrchdir(myplot, str(it))

            # Line search:
            cost_old = self.cost
            statusLS, LScount, alpha = bcktrcklinesearch(self, 12)
            if mpirank == 0:
                print '{:11.3f} {:12.2e} {:10d}'.format(alpha, tolcg, cgiter)
            if self.PD: self.Regul.update_w(self.srchdir.vector(), alpha)

            if np.abs(self.cost - cost_old) / np.abs(cost_old) < tolcost:
                if mpirank == 0:
                    if tolcg < 1e-14:
                        print 'Cost function stagnates -- optimization aborted'
                        break
                    tolcg = 0.001 * tolcg

    def assemble_hessian(self):
        self.Regul.assemble_hessian(self.m)

    def _plotm(self, myplot, index):
        """ plot media during inversion """
        if not myplot == None:
            myplot.set_varname('m' + index)
            myplot.plot_vtk(self.m)

    def _plotgrad(self, myplot, index):
        """ plot grad during inversion """
        if not myplot == None:
            myplot.set_varname('Grad_m' + index)
            myplot.plot_vtk(self.Grad)

    def _plotsrchdir(self, myplot, index):
        """ plot srchdir during inversion """
        if not myplot == None:
            myplot.set_varname('srchdir_m' + index)
            myplot.plot_vtk(self.srchdir)
    def _pressure_poisson(self,
                          p1, p0,
                          mu, ui,
                          divu,
                          p_bcs=None,
                          p_n=None,
                          rotational_form=False,
                          tol=1.0e-10,
                          verbose=True
                          ):
        '''Solve the pressure Poisson equation

            - \Delta phi = -div(u),
            boundary conditions,

        for

            \nabla p = u.
        '''
        P = p1.function_space()
        p = TrialFunction(P)
        q = TestFunction(P)

        a2 = dot(grad(p), grad(q)) * dx
        L2 = -divu * q * dx
        if p0:
            L2 += dot(grad(p0), grad(q)) * dx
        if p_n:
            n = FacetNormal(P.mesh())
            L2 += dot(n, p_n) * q * ds

        if rotational_form:
            L2 -= mu * dot(grad(div(ui)), grad(q)) * dx

        if p_bcs:
            solve(a2 == L2, p1,
                  bcs=p_bcs,
                  solver_parameters={
                      'linear_solver': 'iterative',
                      'symmetric': True,
                      'preconditioner': 'hypre_amg',
                      'krylov_solver': {'relative_tolerance': tol,
                                        'absolute_tolerance': 0.0,
                                        'maximum_iterations': 100,
                                        'monitor_convergence': verbose}
                  })
        else:
            # If we're dealing with a pure Neumann problem here (which is the
            # default case), this doesn't hurt CG if the system is consistent,
            # cf.
            #
            #    Iterative Krylov methods for large linear systems,
            #    Henk A. van der Vorst.
            #
            # And indeed, it is consistent: Note that
            #
            #    <1, rhs> = \sum_i 1 * \int div(u) v_i
            #             = 1 * \int div(u) \sum_i v_i
            #             = \int div(u).
            #
            # With the divergence theorem, we have
            #
            #    \int div(u) = \int_\Gamma n.u.
            #
            # The latter term is 0 iff inflow and outflow are exactly the same
            # at any given point in time. This corresponds with the
            # incompressibility of the liquid.
            #
            # In turn, this hints towards penetrable boundaries to require
            # Dirichlet conditions on the pressure.
            #
            A = assemble(a2)
            b = assemble(L2)
            #
            # In principle, the ILU preconditioner isn't advised here since it
            # might destroy the semidefiniteness needed for CG.
            #
            # The system is consistent, but the matrix has an eigenvalue 0.
            # This does not harm the convergence of CG, but when
            # preconditioning one has to take care that the preconditioner
            # preserves the kernel.  ILU might destroy this (and the
            # semidefiniteness). With AMG, the coarse grid solves cannot be LU
            # then, so try Jacobi here.
            # <http://lists.mcs.anl.gov/pipermail/petsc-users/2012-February/012139.html>
            #
            prec = PETScPreconditioner('hypre_amg')
            PETScOptions.set('pc_hypre_boomeramg_relax_type_coarse', 'jacobi')
            solver = PETScKrylovSolver('cg', prec)
            solver.parameters['absolute_tolerance'] = 0.0
            solver.parameters['relative_tolerance'] = tol
            solver.parameters['maximum_iterations'] = 100
            solver.parameters['monitor_convergence'] = verbose
            # Create solver and solve system
            A_petsc = as_backend_type(A)
            b_petsc = as_backend_type(b)
            p1_petsc = as_backend_type(p1.vector())
            solver.set_operator(A_petsc)
            try:
                solver.solve(p1_petsc, b_petsc)
            except RuntimeError as error:
                info('')
                # Check if the system is indeed consistent.
                #
                # If the right hand side is flawed (e.g., by round-off errors),
                # then it may have a component b1 in the direction of the null
                # space, orthogonal the image of the operator:
                #
                #     b = b0 + b1.
                #
                # When starting with initial guess x0=0, the minimal achievable
                # relative tolerance is then
                #
                #    min_rel_tol = ||b1|| / ||b||.
                #
                # If ||b|| is very small, which is the case when ui is almost
                # divergence-free, then min_rel_to may be larger than the
                # prescribed relative tolerance tol.
                #
                # Use this as a consistency check, i.e., bail out if
                #
                #     tol < min_rel_tol = ||b1|| / ||b||.
                #
                # For computing ||b1||, we use the fact that the null space is
                # one-dimensional, i.e.,  b1 = alpha e,  and
                #
                #     e.b = e.(b0 + b1) = e.b1 = alpha ||e||^2,
                #
                # so  alpha = e.b/||e||^2  and
                #
                #     ||b1|| = |alpha| ||e|| = e.b / ||e||
                #
                e = Function(P)
                e.interpolate(Constant(1.0))
                evec = e.vector()
                evec /= norm(evec)
                alpha = b.inner(evec)
                normB = norm(b)
                info('Linear system convergence failure.')
                info(error.message)
                message = ('Linear system not consistent! '
                           '<b,e> = %g, ||b|| = %g, <b,e>/||b|| = %e, tol = %e.') \
                           % (alpha, normB, alpha/normB, tol)
                info(message)
                if tol < abs(alpha) / normB:
                    info('\int div(u)  =  %e' % assemble(divu * dx))
                    #n = FacetNormal(Q.mesh())
                    #info('\int_Gamma n.u = %e' % assemble(dot(n, u)*ds))
                    #info('\int_Gamma u[0] = %e' % assemble(u[0]*ds))
                    #info('\int_Gamma u[1] = %e' % assemble(u[1]*ds))
                    ## Now plot the faulty u on a finer mesh (to resolve the
                    ## quadratic trial functions).
                    #fine_mesh = Q.mesh()
                    #for k in range(1):
                    #    fine_mesh = refine(fine_mesh)
                    #V1 = FunctionSpace(fine_mesh, 'CG', 1)
                    #W1 = V1*V1
                    #uplot = project(u, W1)
                    ##uplot = Function(W1)
                    ##uplot.interpolate(u)
                    #plot(uplot, title='u_tentative')
                    #plot(uplot[0], title='u_tentative[0]')
                    #plot(uplot[1], title='u_tentative[1]')
                    plot(divu, title='div(u_tentative)')
                    interactive()
                    exit()
                    raise RuntimeError(message)
                else:
                    exit()
                    raise RuntimeError('Linear system failed to converge.')
            except:
                exit()
        return
bc = DirichletBC(V, u0, u0_boundary)

# Define target medium and rhs:
mtrue_exp = Expression('1 + 7*(pow(pow(x[0] - 0.5,2) +' + \
' pow(x[1] - 0.5,2),0.5) > 0.2)')
#mtrue = interpolate(mtrue_exp, Vme)
mtrue = interpolate(mtrue_exp, Vm)
f = Expression("1.0")

# Assemble weak form
trial = TrialFunction(V)
test = TestFunction(V)
a_true = inner(mtrue*nabla_grad(trial), nabla_grad(test))*dx
A_true = assemble(a_true)
bc.apply(A_true)
solver = PETScKrylovSolver('cg')    # doesn't work with ilu preconditioner
#solver = LUSolver()    # doesn't work in parallel !?
#solver.parameters['reuse_factorization'] = True
solver.set_operator(A_true)
# Assemble rhs
L = f*test*dx
b = assemble(L)
bc.apply(b)
# Solve:
u_true = Function(V)

"""
solver.solve(u_true.vector(), b)
if myrank == 0: print 'By hand:\n'
print 'P{0}: max(u)={1}\n'.format(myrank, max(u_true.vector().array()))
Exemple #29
0
    def solve(self, F, u, grad=None, H=None):

        if grad is None:
            print("Using Symbolic Differentiation to compute the gradient")
            grad = derivative(F, u)

        if H is None:
            print("Using Symbolic Differentiation to compute the Hessian")
            H = derivative(grad, u)

        rtol = self.parameters["rel_tolerance"]
        atol = self.parameters["abs_tolerance"]
        gdu_tol = self.parameters["gdu_tolerance"]
        max_iter = self.parameters["max_iter"]
        c_armijo = self.parameters["c_armijo"]
        max_backtrack = self.parameters["max_backtracking_iter"]
        prt_level = self.parameters["print_level"]
        cg_coarsest_tol = self.parameters["cg_coarse_tolerance"]

        Fn = assemble(F)
        gn = assemble(grad)
        g0_norm = gn.norm("l2")
        gn_norm = g0_norm
        tol = max(g0_norm * rtol, atol)
        du = Vector()

        self.converged = False
        self.reason = 0

        if prt_level > 0:
            print(
                "{0:>3} {1:>15} {2:>15} {3:>15} {4:>15} {5:>15} {6:>7}".format(
                    "It", "Energy", "||g||", "(g,du)", "alpha", "tol_cg",
                    "cg_it"))

        for self.it in range(max_iter):
            Hn = assemble(H)

            Hn.init_vector(du, 1)
            solver = PETScKrylovSolver("cg", "petsc_amg")
            solver.set_operator(Hn)
            solver.parameters["nonzero_initial_guess"] = False
            cg_tol = min(cg_coarsest_tol, math.sqrt(gn_norm / g0_norm))
            solver.parameters["relative_tolerance"] = cg_tol
            lin_it = solver.solve(du, gn)

            self.total_cg_iter += lin_it

            du_gn = -du.inner(gn)

            if (-du_gn < gdu_tol):
                self.converged = True
                self.reason = 3
                break

            u_backtrack = u.copy(deepcopy=True)
            alpha = 1.
            bk_converged = False

            #Backtrack
            for j in range(max_backtrack):
                u.assign(u_backtrack)
                u.vector().axpy(-alpha, du)
                Fnext = assemble(F)
                if Fnext < Fn + alpha * c_armijo * du_gn:
                    Fn = Fnext
                    bk_converged = True
                    break

                alpha = alpha / 2.

            if not bk_converged:
                self.reason = 2
                break

            gn = assemble(grad)
            gn_norm = gn.norm("l2")

            if prt_level > 0:
                print("{0:3d} {1:15e} {2:15e} {3:15e} {4:15e} {5:15e} {6:7d}".
                      format(self.it, Fn, gn_norm, du_gn, alpha, cg_tol,
                             lin_it))

            if gn_norm < tol:
                self.converged = True
                self.reason = 1
                break

        self.final_grad_norm = gn_norm

        if prt_level > -1:
            print(self.termination_reasons[self.reason])
            if self.converged:
                print( "Inexact Newton CG converged in ", self.it, \
                "nonlinear iterations and ", self.total_cg_iter, "linear iterations." )
            else:
                print( "Inexact Newton CG did NOT converge after ", self.it, \
                "nonlinear iterations and ", self.total_cg_iter, "linear iterations.")
            print("Final norm of the gradient", self.final_grad_norm)
            print("Value of the cost functional", Fn)
Exemple #30
0
def create_solver(solver, preconditioner="default"):
    """Create solver from arguments. Should be flexible to handle
    
    - strings specifying the solver and preconditioner types
    - PETScKrylovSolver/PETScPreconditioner objects
    - petsc4py.PETSC.KSP/petsc4py.PETSC.pc objects
    
    or any combination of the above
    """
    # Create solver
    if isinstance(solver, str):
        try:
            linear_solvers = set(dict(linear_solver_methods()).keys())
            krylov_solvers = set(dict(krylov_solver_methods()).keys())
        except:
            linear_solvers = set(linear_solver_methods())
            krylov_solvers = set(krylov_solver_methods())
        direct_solvers = linear_solvers - krylov_solvers

        if solver in direct_solvers:
            s = LinearSolver(solver)
            return s
        elif solver in krylov_solvers:
            s = PETScKrylovSolver(solver)
        else:
            s = PETScKrylovSolver()
            s.ksp().setType(solver)
            if s.ksp().getNormType() == petsc4py.PETSc.KSP.NormType.NONE:
                s.ksp().setNormType(petsc4py.PETSc.KSP.NormType.PRECONDITIONED)
            #raise RuntimeError("Don't know how to handle solver %s" %solver)
    elif isinstance(solver, PETScKrylovSolver):
        s = solver
    elif isinstance(solver, petsc4py.PETSc.KSP):
        s = PETScKrylovSolver(solver)
    else:
        raise ValueError("Unable to create solver from argument of type %s" %
                         type(solver))

    assert isinstance(s, PETScKrylovSolver)
    if preconditioner == "default":
        return s

    # Create preconditioner
    if preconditioner in [None, "none", "None"]:
        pc = PETScPreconditioner("none")
        pc.set(s)
        return s
    elif isinstance(preconditioner, str):
        if preconditioner in krylov_solver_preconditioners():
            pc = PETScPreconditioner(preconditioner)
            pc.set(s)
            return s
        elif preconditioner in ["additive_schwarz", "bjacobi", "jacobi"]:
            if preconditioner == "additive_schwarz":
                pc_type = "asm"
            else:
                pc_type = preconditioner
            ksp = s.ksp()
            pc = ksp.pc
            pc.setType(pc_type)
            return s
    elif isinstance(preconditioner, PETScPreconditioner):
        pc = preconditioner
        pc.set(s)
        return s
    elif isinstance(preconditioner, petsc4py.PETSc.PC):
        ksp = s.ksp()
        ksp.setPC(preconditioner)
        return s
    else:
        raise ValueError(
            "Unable to create preconditioner from argument of type %s" %
            type(solver))

    raise RuntimeError(
        "Should not reach this code. The solver/preconditioner (%s/%s) failed to return a valid solver."
        % (str(solver), str(preconditioner)))
Exemple #31
0
def test_krylov_solver_norm_type():
    """Check setting of norm type used in testing for convergence by
    PETScKrylovSolver

    """

    norm_type = (PETScKrylovSolver.norm_type.default_norm,
                 PETScKrylovSolver.norm_type.natural,
                 PETScKrylovSolver.norm_type.preconditioned,
                 PETScKrylovSolver.norm_type.none,
                 PETScKrylovSolver.norm_type.unpreconditioned)

    for norm in norm_type:
        # Solve a system of equations
        mesh = UnitSquareMesh(4, 4)
        V = FunctionSpace(mesh, "Lagrange", 1)
        u, v = TrialFunction(V), TestFunction(V)
        a = u*v*dx
        L = Constant(1.0)*v*dx
        A, b = assemble(a), assemble(L)

        solver = PETScKrylovSolver("cg")
        solver.parameters["maximum_iterations"] = 2
        solver.parameters["error_on_nonconvergence"] = False
        solver.set_norm_type(norm)
        solver.set_operator(A)
        solver.solve(b.copy(), b)
        solver.get_norm_type()

        if norm is not PETScKrylovSolver.norm_type.default_norm:
            assert solver.get_norm_type() == norm
class TV():
    """
    Define Total Variation regularization
    """
    def __init__(self, parameters=[]):
        """
        TV regularization in primal form:
                |f|_TV = int k(x) sqrt{|grad f|^2 + eps} dx 
        Input parameters:
            * k = regularization parameter
            * eps = regularization constant (see above)
            * GNhessian = use GN format (aka, lagged diffusivity) (bool)
            * PCGN = use GN Hessian to precondition (bool); only used if 'GNhessian = False'
            * print (bool)
        """

        self.parameters = {}
        self.parameters['k'] = 1.0
        self.parameters['eps'] = 1e-2
        self.parameters['GNhessian'] = False
        self.parameters['PCGN'] = False
        self.parameters['print'] = False
        self.parameters['correctcost'] = True
        self.parameters['amg'] = 'default'

        assert parameters.has_key('Vm')
        self.parameters.update(parameters)
        GN = self.parameters['GNhessian']
        self.Vm = self.parameters['Vm']
        eps = self.parameters['eps']
        k = self.parameters['k']
        isprint = self.parameters['print']
        amg = self.parameters['amg']

        self.m = Function(self.Vm)
        test, trial = TestFunction(self.Vm), TrialFunction(self.Vm)
        factM = 1e-2 * k
        M = assemble(inner(test, trial) * dx)
        self.sMass = M * factM

        self.Msolver = PETScKrylovSolver('cg', 'jacobi')
        self.Msolver.parameters["maximum_iterations"] = 2000
        self.Msolver.parameters["relative_tolerance"] = 1e-24
        self.Msolver.parameters["absolute_tolerance"] = 1e-24
        self.Msolver.parameters["error_on_nonconvergence"] = True
        self.Msolver.parameters["nonzero_initial_guess"] = False
        self.Msolver.set_operator(M)

        self.fTV = inner(nabla_grad(self.m), nabla_grad(
            self.m)) + Constant(eps)
        self.kovsq = Constant(k) / sqrt(self.fTV)

        if self.parameters['correctcost']:
            meshtmp = UnitSquareMesh(self.Vm.mesh().mpi_comm(), 10, 10)
            Vtmp = FunctionSpace(meshtmp, 'CG', 1)
            x = SpatialCoordinate(meshtmp)
            correctioncost = 1. / assemble(sqrt(4.0 * x[0] * x[0]) * dx)
        else:
            correctioncost = 1.0
        self.wkformcost = Constant(k * correctioncost) * sqrt(self.fTV) * dx

        self.wkformgrad = self.kovsq * inner(nabla_grad(self.m),
                                             nabla_grad(test)) * dx

        self.wkformGNhess = self.kovsq * inner(nabla_grad(trial),
                                               nabla_grad(test)) * dx
        self.wkformFhess = self.kovsq*( \
        inner(nabla_grad(trial), nabla_grad(test)) - \
        inner(nabla_grad(self.m), nabla_grad(test))*\
        inner(nabla_grad(trial), nabla_grad(self.m))/self.fTV )*dx
        if GN:
            self.wkformhess = self.wkformGNhess
        else:
            self.wkformhess = self.wkformFhess

        if amg == 'default': self.amgprecond = amg_solver()
        else: self.amgprecond = amg

        if isprint:
            print '[TV] TV regularization',
            if GN:
                print ' -- GN Hessian',
            else:
                print ' -- full Hessian',
            if self.parameters['PCGN']:
                print ' -- PCGN',
            print ' -- k={}, eps={}'.format(k, eps)
            print '[TV] preconditioner = {}'.format(self.amgprecond)
            print '[TV] correction cost with factor={}'.format(correctioncost)

    def isTV(self):
        return True

    def isPD(self):
        return False

    def cost(self, m_in):
        """ returns the cost functional for self.m=m_in """
        setfct(self.m, m_in)
        return assemble(self.wkformcost)

    def costvect(self, m_in):
        return self.cost(m_in)

    def grad(self, m_in):
        """ returns the gradient (in vector format) evaluated at self.m = m_in """
        setfct(self.m, m_in)
        return assemble(self.wkformgrad)

    def gradvect(self, m_in):
        return self.grad(m_in)

    def assemble_hessian(self, m_in):
        """ Assemble the Hessian of TV at m_in """
        setfct(self.m, m_in)
        self.H = assemble(self.wkformhess)
        PCGN = self.parameters['PCGN']
        if PCGN:
            HGN = assemble(self.wkformGNhess)
            self.precond = HGN + self.sMass
        else:
            self.precond = self.H + self.sMass

    def assemble_GNhessian(self, m_in):
        """ Assemble the Gauss-Newton Hessian at m_in 
        Not used anymore (wkformhess selects GN Hessian if needed)
        Left here for back-compatibility """
        setfct(self.m, m_in)
        self.H = assemble(self.wkformGNhess)
        self.precond = self.H + self.sMass

    def hessian(self, mhat):
        """ returns the Hessian applied along a direction mhat """
        isVector(mhat)
        return self.H * mhat

    def getprecond(self):
        """ Precondition by inverting the TV Hessian """

        solver = PETScKrylovSolver('cg', self.amgprecond)
        solver.parameters["maximum_iterations"] = 2000
        solver.parameters["relative_tolerance"] = 1e-24
        solver.parameters["absolute_tolerance"] = 1e-24
        solver.parameters["error_on_nonconvergence"] = True
        solver.parameters["nonzero_initial_guess"] = False

        # used to compare iterative application of preconditioner
        # with exact application of preconditioner:
        #solver = PETScLUSolver("petsc")
        #solver.parameters['symmetric'] = True
        #solver.parameters['reuse_factorization'] = True

        solver.set_operator(self.precond)
        return solver

    def init_vector(self, u, dim):
        self.sMass.init_vector(u, dim)
def test_poisson(k):
    # Polynomial order and mesh resolution
    nx_list = [4, 8, 16]

    # Error list
    error_u_l2, error_u_h1 = [], []

    for nx in nx_list:
        mesh = UnitSquareMesh(nx, nx)

        # Define FunctionSpaces and functions
        V = FunctionSpace(mesh, "DG", k)
        Vbar = FunctionSpace(mesh,
                             FiniteElement("CG", mesh.ufl_cell(), k)["facet"])

        u_soln = Expression("sin(pi*x[0])*sin(pi*x[1])",
                            degree=k + 1,
                            domain=mesh)
        f = Expression("2*pi*pi*sin(pi*x[0])*sin(pi*x[1])", degree=k + 1)
        u, v = Function(V), TestFunction(V)
        ubar, vbar = Function(Vbar), TestFunction(Vbar)

        n = FacetNormal(mesh)
        h = CellDiameter(mesh)
        alpha = Constant(6 * k * k)
        penalty = alpha / h

        def facet_integral(integrand):
            return integrand('-') * dS + integrand('+') * dS + integrand * ds

        u_flux = ubar
        F_v_flux = grad(u) + penalty * outer(u_flux - u, n)

        residual_local = inner(grad(u), grad(v)) * dx
        residual_local += facet_integral(inner(outer(u_flux - u, n), grad(v)))
        residual_local -= facet_integral(inner(F_v_flux, outer(v, n)))
        residual_local -= f * v * dx

        residual_global = facet_integral(inner(F_v_flux, outer(vbar, n)))

        a_ll = derivative(residual_local, u)
        a_lg = derivative(residual_local, ubar)
        a_gl = derivative(residual_global, u)
        a_gg = derivative(residual_global, ubar)

        l_l = -residual_local
        l_g = -residual_global

        bcs = [DirichletBC(Vbar, u_soln, "on_boundary")]

        # Initialize static condensation assembler
        assembler = AssemblerStaticCondensation(a_ll, a_lg, a_gl, a_gg, l_l,
                                                l_g, bcs)

        A_g, b_g = PETScMatrix(), PETScVector()
        assembler.assemble_global_lhs(A_g)
        assembler.assemble_global_rhs(b_g)

        for bc in bcs:
            bc.apply(A_g, b_g)

        solver = PETScKrylovSolver()
        solver.set_operator(A_g)
        PETScOptions.set("ksp_type", "preonly")
        PETScOptions.set("pc_type", "lu")
        PETScOptions.set("pc_factor_mat_solver_type", "mumps")
        solver.set_from_options()

        solver.solve(ubar.vector(), b_g)
        assembler.backsubstitute(ubar._cpp_object, u._cpp_object)

        # Compute L2 and H1 norms
        e_u_l2 = assemble((u - u_soln)**2 * dx)**0.5
        e_u_h1 = assemble(grad(u - u_soln)**2 * dx)**0.5

        if mesh.mpi_comm().rank == 0:
            error_u_l2.append(e_u_l2)
            error_u_h1.append(e_u_h1)

    if mesh.mpi_comm().rank == 0:
        iterator_list = [1.0 / float(nx) for nx in nx_list]
        conv_u_l2 = compute_convergence(iterator_list, error_u_l2)
        conv_u_h1 = compute_convergence(iterator_list, error_u_h1)

        # Optimal rate of k + 1 - tolerance
        assert np.all(conv_u_l2 >= (k + 1.0 - 0.15))
        # Optimal rate of k - tolerance
        assert np.all(conv_u_h1 >= (k - 0.1))
    def solve(self, F, u, grad = None, H = None):
        
        if grad is None:
            print "Using Automatic Differentiation to compute the gradient"
            grad = derivative(F,u)
            
        if H is None:
            print "Using Automatic Differentiation to compute the Hessian"
            H = derivative(grad, u)
        
        rtol = self.parameters["rel_tolerance"]
        atol = self.parameters["abs_tolerance"]
        gdu_tol = self.parameters["gdu_tolerance"]
        max_iter = self.parameters["max_iter"]
        c_armijo = self.parameters["c_armijo"] 
        max_backtrack = self.parameters["max_backtracking_iter"]
        prt_level =  self.parameters["print_level"]
        cg_coarsest_tol = self.parameters["cg_coarse_tolerance"]
        
        Fn = assemble(F)
        gn = assemble(grad)
        g0_norm = gn.norm("l2")
        gn_norm = g0_norm
        tol = max(g0_norm*rtol, atol)
        du = Vector()
        
        self.converged = False
        self.reason = 0
        
        if prt_level > 0:
            print "{0:3} {1:15} {2:15} {3:15} {4:15} {5:15} {6:5}".format(
                "It", "Energy", "||g||", "(g,du)", "alpha", "tol_cg", "cg_it")
        
        for self.it in range(max_iter):
            Hn = assemble(H)
            
            Hn.init_vector(du,1)
            solver = PETScKrylovSolver("cg", "petsc_amg")
            solver.set_operator(Hn)
            solver.parameters["nonzero_initial_guess"] = False
            cg_tol = min(cg_coarsest_tol, math.sqrt( gn_norm/g0_norm) )
            solver.parameters["relative_tolerance"] = cg_tol
            lin_it = solver.solve(du,gn)
            
            self.total_cg_iter += lin_it
            

            du_gn = -du.inner(gn)
            
            if(-du_gn < gdu_tol):
                self.converged=True
                self.reason = 3
                break
             
            u_backtrack = u.copy(deepcopy=True)
            alpha = 1.   
            bk_converged = False
            
            #Backtrack
            for j in range(max_backtrack):
                u.assign(u_backtrack)
                u.vector().axpy(-alpha, du)
                Fnext = assemble(F)
                if Fnext < Fn + alpha*c_armijo*du_gn:
                    Fn = Fnext
                    bk_converged = True
                    break
                
                alpha = alpha/2.
                
            if not bk_converged:
                self.reason = 2
                break
                   
            gn = assemble(grad)
            gn_norm = gn.norm("l2")
            
            if prt_level > 0:
                print "{0:3d} {1:15f} {2:15f} {3:15f} {4:15f} {5:15f} {6:5d}".format(
                        self.it, Fn, gn_norm, du_gn, alpha, cg_tol, lin_it)
                
            if gn_norm < tol:
                self.converged = True
                self.reason = 1
                break
            
        self.final_grad_norm = gn_norm
        
        if prt_level > -1:
            print self.termination_reasons[self.reason]
            if self.converged:
                print "Inexact Newton CG converged in ", self.it, \
                "nonlinear iterations and ", self.total_cg_iter, "linear iterations."
            else:
                print "Inexact Newton CG did NOT converge after ", self.it, \
                "nonlinear iterations and ", self.total_cg_iter, "linear iterations."
            print "Final norm of the gradient", self.final_grad_norm
            print "Value of the cost functional", Fn
    def __init__(self, parameters):
        """ 
        TV regularization in primal-dual format
        Input parameters:
            * k = regularization parameter
            * eps = regularization constant (see above)
            * rescaledradiusdual = radius of dual set
            * exact = use full TV (bool)
            * PCGN = use GN Hessian to precondition (bool); 
            not recommended for performance but can help avoid num.instability
            * print (bool)
        """

        self.parameters = {}
        self.parameters['k'] = 1.0
        self.parameters['eps'] = 1e-2
        self.parameters['rescaledradiusdual'] = 1.0
        self.parameters['exact'] = False
        self.parameters['PCGN'] = False
        self.parameters['print'] = False
        self.parameters['correctcost'] = True
        self.parameters['amg'] = 'default'

        assert parameters.has_key('Vm')
        self.parameters.update(parameters)
        self.Vm = self.parameters['Vm']
        k = self.parameters['k']
        eps = self.parameters['eps']
        exact = self.parameters['exact']
        amg = self.parameters['amg']

        self.m = Function(self.Vm)
        testm = TestFunction(self.Vm)
        trialm = TrialFunction(self.Vm)

        # WARNING: should not be changed.
        # As it is, code only works with DG0
        if self.parameters.has_key('Vw'):
            Vw = self.parameters['Vw']
        else:
            Vw = FunctionSpace(self.Vm.mesh(), 'DG', 0)
        self.wx = Function(Vw)
        self.wxrs = Function(Vw)  # re-scaled dual variable
        self.wxhat = Function(Vw)
        self.gwx = Function(Vw)
        self.wy = Function(Vw)
        self.wyrs = Function(Vw)  # re-scaled dual variable
        self.wyhat = Function(Vw)
        self.gwy = Function(Vw)
        self.wxsq = Vector()
        self.wysq = Vector()
        self.normw = Vector()
        self.factorw = Vector()
        testw = TestFunction(Vw)
        trialw = TrialFunction(Vw)

        normm = inner(nabla_grad(self.m), nabla_grad(self.m))
        TVnormsq = normm + Constant(eps)
        TVnorm = sqrt(TVnormsq)
        if self.parameters['correctcost']:
            meshtmp = UnitSquareMesh(self.Vm.mesh().mpi_comm(), 10, 10)
            Vtmp = FunctionSpace(meshtmp, 'CG', 1)
            x = SpatialCoordinate(meshtmp)
            correctioncost = 1. / assemble(sqrt(4.0 * x[0] * x[0]) * dx)
        else:
            correctioncost = 1.0
        self.wkformcost = Constant(k * correctioncost) * TVnorm * dx
        if exact:
            sys.exit(1)
#            self.w = nabla_grad(self.m)/TVnorm # full Hessian
#            self.Htvw = inner(Constant(k) * nabla_grad(testm), self.w) * dx

        self.misfitwx = inner(testw, self.wx * TVnorm - self.m.dx(0)) * dx
        self.misfitwy = inner(testw, self.wy * TVnorm - self.m.dx(1)) * dx

        self.Htvx = assemble(inner(Constant(k) * testm.dx(0), trialw) * dx)
        self.Htvy = assemble(inner(Constant(k) * testm.dx(1), trialw) * dx)

        self.massw = inner(TVnorm * testw, trialw) * dx

        mpicomm = self.Vm.mesh().mpi_comm()
        invMwMat, VDM, VDM = setupPETScmatrix(Vw, Vw, 'aij', mpicomm)
        for ii in VDM.dofs():
            invMwMat[ii, ii] = 1.0
        invMwMat.assemblyBegin()
        invMwMat.assemblyEnd()
        self.invMwMat = PETScMatrix(invMwMat)
        self.invMwd = Vector()
        self.invMwMat.init_vector(self.invMwd, 0)
        self.invMwMat.init_vector(self.wxsq, 0)
        self.invMwMat.init_vector(self.wysq, 0)
        self.invMwMat.init_vector(self.normw, 0)
        self.invMwMat.init_vector(self.factorw, 0)

        u = Function(Vw)
        uflrank = len(u.ufl_shape)
        if uflrank == 0:
            ones = ("1.0")
        elif uflrank == 1:
            ones = (("1.0", "1.0"))
        else:
            sys.exit(1)
        u = interpolate(Constant(ones), Vw)
        self.one = u.vector()

        self.wkformAx = inner(testw, trialm.dx(0) - \
        self.wx * inner(nabla_grad(self.m), nabla_grad(trialm)) / TVnorm) * dx
        self.wkformAxrs = inner(testw, trialm.dx(0) - \
        self.wxrs * inner(nabla_grad(self.m), nabla_grad(trialm)) / TVnorm) * dx
        self.wkformAy = inner(testw, trialm.dx(1) - \
        self.wy * inner(nabla_grad(self.m), nabla_grad(trialm)) / TVnorm) * dx
        self.wkformAyrs = inner(testw, trialm.dx(1) - \
        self.wyrs * inner(nabla_grad(self.m), nabla_grad(trialm)) / TVnorm) * dx

        kovsq = Constant(k) / TVnorm
        self.wkformGNhess = kovsq * inner(nabla_grad(trialm),
                                          nabla_grad(testm)) * dx
        factM = 1e-2 * k
        M = assemble(inner(testm, trialm) * dx)
        self.sMass = M * factM

        self.Msolver = PETScKrylovSolver('cg', 'jacobi')
        self.Msolver.parameters["maximum_iterations"] = 2000
        self.Msolver.parameters["relative_tolerance"] = 1e-24
        self.Msolver.parameters["absolute_tolerance"] = 1e-24
        self.Msolver.parameters["error_on_nonconvergence"] = True
        self.Msolver.parameters["nonzero_initial_guess"] = False
        self.Msolver.set_operator(M)

        if amg == 'default': self.amgprecond = amg_solver()
        else: self.amgprecond = amg

        if self.parameters['print']:
            print '[TVPD] TV regularization -- primal-dual method',
            if self.parameters['PCGN']:
                print ' -- PCGN',
            print ' -- k={}, eps={}'.format(k, eps)
            print '[TVPD] preconditioner = {}'.format(self.amgprecond)
            print '[TVPD] correction cost with factor={}'.format(
                correctioncost)
    def __init__(self, parameters=[]):
        """
        TV regularization in primal form:
                |f|_TV = int k(x) sqrt{|grad f|^2 + eps} dx 
        Input parameters:
            * k = regularization parameter
            * eps = regularization constant (see above)
            * GNhessian = use GN format (aka, lagged diffusivity) (bool)
            * PCGN = use GN Hessian to precondition (bool); only used if 'GNhessian = False'
            * print (bool)
        """

        self.parameters = {}
        self.parameters['k'] = 1.0
        self.parameters['eps'] = 1e-2
        self.parameters['GNhessian'] = False
        self.parameters['PCGN'] = False
        self.parameters['print'] = False
        self.parameters['correctcost'] = True
        self.parameters['amg'] = 'default'

        assert parameters.has_key('Vm')
        self.parameters.update(parameters)
        GN = self.parameters['GNhessian']
        self.Vm = self.parameters['Vm']
        eps = self.parameters['eps']
        k = self.parameters['k']
        isprint = self.parameters['print']
        amg = self.parameters['amg']

        self.m = Function(self.Vm)
        test, trial = TestFunction(self.Vm), TrialFunction(self.Vm)
        factM = 1e-2 * k
        M = assemble(inner(test, trial) * dx)
        self.sMass = M * factM

        self.Msolver = PETScKrylovSolver('cg', 'jacobi')
        self.Msolver.parameters["maximum_iterations"] = 2000
        self.Msolver.parameters["relative_tolerance"] = 1e-24
        self.Msolver.parameters["absolute_tolerance"] = 1e-24
        self.Msolver.parameters["error_on_nonconvergence"] = True
        self.Msolver.parameters["nonzero_initial_guess"] = False
        self.Msolver.set_operator(M)

        self.fTV = inner(nabla_grad(self.m), nabla_grad(
            self.m)) + Constant(eps)
        self.kovsq = Constant(k) / sqrt(self.fTV)

        if self.parameters['correctcost']:
            meshtmp = UnitSquareMesh(self.Vm.mesh().mpi_comm(), 10, 10)
            Vtmp = FunctionSpace(meshtmp, 'CG', 1)
            x = SpatialCoordinate(meshtmp)
            correctioncost = 1. / assemble(sqrt(4.0 * x[0] * x[0]) * dx)
        else:
            correctioncost = 1.0
        self.wkformcost = Constant(k * correctioncost) * sqrt(self.fTV) * dx

        self.wkformgrad = self.kovsq * inner(nabla_grad(self.m),
                                             nabla_grad(test)) * dx

        self.wkformGNhess = self.kovsq * inner(nabla_grad(trial),
                                               nabla_grad(test)) * dx
        self.wkformFhess = self.kovsq*( \
        inner(nabla_grad(trial), nabla_grad(test)) - \
        inner(nabla_grad(self.m), nabla_grad(test))*\
        inner(nabla_grad(trial), nabla_grad(self.m))/self.fTV )*dx
        if GN:
            self.wkformhess = self.wkformGNhess
        else:
            self.wkformhess = self.wkformFhess

        if amg == 'default': self.amgprecond = amg_solver()
        else: self.amgprecond = amg

        if isprint:
            print '[TV] TV regularization',
            if GN:
                print ' -- GN Hessian',
            else:
                print ' -- full Hessian',
            if self.parameters['PCGN']:
                print ' -- PCGN',
            print ' -- k={}, eps={}'.format(k, eps)
            print '[TV] preconditioner = {}'.format(self.amgprecond)
            print '[TV] correction cost with factor={}'.format(correctioncost)
    def __init__(self, mpicomm_global, acousticwavePDE, sources, \
    sourcesindex, timestepsindex, \
    invparam='ab', regularization=None):
        """ 
        Input:
            acousticwavePDE should be an instantiation from class AcousticWave
        """
        self.mpicomm_global = mpicomm_global

        self.PDE = acousticwavePDE
        self.PDE.exact = None
        self.obsop = None   # Observation operator
        self.dd = None  # observations
        self.fwdsource = sources
        self.srcindex = sourcesindex
        self.tsteps = timestepsindex
        self.PDEcount = 0

        self.inverta = False
        self.invertb = False
        if 'a' in invparam:
            self.inverta = True
        if 'b' in invparam:
            self.invertb = True
        assert self.inverta + self.invertb > 0

        Vm = self.PDE.Vm
        V = self.PDE.V
        VmVm = createMixedFS(Vm, Vm)
        self.ab = Function(VmVm)   # used for conversion (Vm,Vm)->VmVm
        self.invparam = invparam
        self.MG = Function(VmVm)
        self.MGv = self.MG.vector()
        self.Grad = Function(VmVm)
        self.srchdir = Function(VmVm)
        self.delta_m = Function(VmVm)
        self.m_bkup = Function(VmVm)
        LinearOperator.__init__(self, self.MGv, self.MGv)
        self.GN = False

        if regularization == None:  
            print '[ObjectiveAcoustic] *** Warning: Using zero regularization'
            self.regularization = ZeroRegularization(Vm)
        else:   
            self.regularization = regularization
            self.PD = self.regularization.isPD()
        self.alpha_reg = 1.0

        self.p, self.q = Function(V), Function(V)
        self.phat, self.qhat = Function(V), Function(V)
        self.ahat, self.bhat = Function(Vm), Function(Vm)
        self.ptrial, self.ptest = TrialFunction(V), TestFunction(V)
        self.mtest, self.mtrial = TestFunction(Vm), TrialFunction(Vm)
        if self.PDE.parameters['lumpM']:
            self.Mprime = LumpedMassMatrixPrime(Vm, V, self.PDE.M.ratio)
            self.get_gradienta = self.get_gradienta_lumped
            self.get_hessiana = self.get_hessiana_lumped
            self.get_incra = self.get_incra_lumped
        else:
            self.wkformgrada = inner(self.mtest*self.p, self.q)*dx
            self.get_gradienta = self.get_gradienta_full
            self.wkformhessa = inner(self.phat*self.mtest, self.q)*dx \
            + inner(self.p*self.mtest, self.qhat)*dx
            self.wkformhessaGN = inner(self.p*self.mtest, self.qhat)*dx
            self.get_hessiana = self.get_hessiana_full
            self.wkformrhsincra = inner(self.ahat*self.ptrial, self.ptest)*dx
            self.get_incra = self.get_incra_full
        self.wkformgradb = inner(self.mtest*nabla_grad(self.p), nabla_grad(self.q))*dx
        self.wkformgradbout = assemble(self.wkformgradb)
        self.wkformrhsincrb = inner(self.bhat*nabla_grad(self.ptrial), nabla_grad(self.ptest))*dx
        self.wkformhessb = inner(nabla_grad(self.phat)*self.mtest, nabla_grad(self.q))*dx \
        + inner(nabla_grad(self.p)*self.mtest, nabla_grad(self.qhat))*dx
        self.wkformhessbGN = inner(nabla_grad(self.p)*self.mtest, nabla_grad(self.qhat))*dx

        # Mass matrix:
        self.mmtest, self.mmtrial = TestFunction(VmVm), TrialFunction(VmVm)
        weak_m =  inner(self.mmtrial, self.mmtest)*dx
        self.Mass = assemble(weak_m)
        self.solverM = PETScKrylovSolver("cg", "jacobi")
        self.solverM.parameters["maximum_iterations"] = 2000
        self.solverM.parameters["absolute_tolerance"] = 1e-24
        self.solverM.parameters["relative_tolerance"] = 1e-24
        self.solverM.parameters["report"] = False
        self.solverM.parameters["error_on_nonconvergence"] = True 
        self.solverM.parameters["nonzero_initial_guess"] = False # True?
        self.solverM.set_operator(self.Mass)

        # Time-integration factors
        self.factors = np.ones(self.PDE.times.size)
        self.factors[0], self.factors[-1] = 0.5, 0.5
        self.factors *= self.PDE.Dt
        self.invDt = 1./self.PDE.Dt

        # Absorbing BCs
        if self.PDE.parameters['abc']:
            assert not self.PDE.parameters['lumpD']

            self.wkformgradaABC = inner(
            self.mtest*sqrt(self.PDE.b/self.PDE.a)*self.p, 
            self.q)*self.PDE.ds(1)
            self.wkformgradbABC = inner(
            self.mtest*sqrt(self.PDE.a/self.PDE.b)*self.p, 
            self.q)*self.PDE.ds(1)
            self.wkformgradaABCout = assemble(self.wkformgradaABC)
            self.wkformgradbABCout = assemble(self.wkformgradbABC)

            self.wkformincrrhsABC = inner(
            (self.ahat*sqrt(self.PDE.b/self.PDE.a)
             + self.bhat*sqrt(self.PDE.a/self.PDE.b))*self.ptrial,
            self.ptest)*self.PDE.ds(1)

            self.wkformhessaABC = inner(
            (self.bhat/sqrt(self.PDE.a*self.PDE.b) - 
            self.ahat*sqrt(self.PDE.b/(self.PDE.a*self.PDE.a*self.PDE.a)))
            *self.p*self.mtest, self.q)*self.PDE.ds(1)
            self.wkformhessbABC = inner(
            (self.ahat/sqrt(self.PDE.a*self.PDE.b) - 
            self.bhat*sqrt(self.PDE.a/(self.PDE.b*self.PDE.b*self.PDE.b)))
            *self.p*self.mtest, self.q)*self.PDE.ds(1)
Exemple #38
0
def compute_pressure(
    P,
    p0,
    mu,
    ui,
    u,
    my_dx,
    p_bcs=None,
    rotational_form=False,
    tol=1.0e-10,
    verbose=True,
):
    """Solve the pressure Poisson equation

    .. math::

        \\begin{align}
          -\\frac{1}{r} \\div(r \\nabla (p_1-p_0)) =
              -\\frac{1}{r} \\div(r u),\\\\
          \\text{(with boundary conditions)},
        \\end{align}

    for :math:`\\nabla p = u`.

    The pressure correction is based on the update formula

    .. math::
        \\frac{\\rho}{dt} (u_{n+1}-u^*)
            + \\begin{pmatrix}
                \\text{d}\\phi/\\text{d}r\\\\
                \\text{d}\\phi/\\text{d}z\\\\
                \\frac{1}{r} \\text{d}\\phi/\\text{d}\\theta
              \\end{pmatrix}
                = 0

    with :math:`\\phi = p_{n+1} - p^*` and

    .. math::

         \\frac{1}{r} \\frac{\\text{d}}{\\text{d}r} (r u_r^{(n+1)})
       + \\frac{\\text{d}}{\\text{d}z}  (u_z^{(n+1)})
       + \\frac{1}{r} \\frac{\\text{d}}{\\text{d}\\theta} (u_{\\theta}^{(n+1)})
           = 0

    With the assumption that u does not change in the direction
    :math:`\\theta`, one derives

    .. math::

       - \\frac{1}{r}   \\div(r \\nabla \\phi) =
           \\frac{1}{r} \\frac{\\rho}{dt}   \\div(r (u_{n+1} - u^*))\\\\
       - \\frac{1}{r} \\langle n, r \\nabla \\phi\\rangle =
           \\frac{1}{r} \\frac{\\rho}{dt} \\langle n, r (u_{n+1} - u^*)\\rangle

    In its weak form, this is

    .. math::

      \\int r \\langle\\nabla\\phi, \\nabla q\\rangle \\,2 \\pi =
           - \\frac{\\rho}{dt} \\int \\div(r u^*) q \\, 2 \\pi
           - \\frac{\\rho}{dt} \\int_{\\Gamma}
                 \\langle n,  r (u_{n+1}-u^*)\\rangle q \\, 2\\pi.

    (The terms :math:`1/r` cancel with the volume elements :math:`2\\pi r`.)
    If the Dirichlet boundary conditions are applied to both :math:`u^*` and
    :math:`u_n` (the latter in the velocity correction step), the boundary
    integral vanishes.

    If no Dirichlet conditions are given (which is the default case), the
    system has no unique solution; one eigenvalue is 0. This however, does not
    hurt CG convergence if the system is consistent, cf. :cite:`vdV03`. And
    indeed it is consistent if and only if

    .. math::
        \\int_\\Gamma r \\langle n, u\\rangle = 0.

    This condition makes clear that for incompressible Navier-Stokes, one
    either needs to make sure that inflow and outflow always add up to 0, or
    one has to specify pressure boundary conditions.

    Note that, when using a multigrid preconditioner as is done here, the
    coarse solver must be chosen such that it preserves the nullspace of the
    problem.
    """
    W = ui.function_space()
    r = SpatialCoordinate(W.mesh())[0]

    p = TrialFunction(P)
    q = TestFunction(P)
    a2 = dot(r * grad(p), grad(q)) * 2 * pi * my_dx
    # The boundary conditions
    #     n.(p1-p0) = 0
    # are implicitly included.
    #
    # L2 = -div(r*u) * q * 2*pi*my_dx
    div_u = 1 / r * (r * u[0]).dx(0) + u[1].dx(1)
    L2 = -div_u * q * 2 * pi * r * my_dx
    if p0:
        L2 += r * dot(grad(p0), grad(q)) * 2 * pi * my_dx

    # In the Cartesian variant of the rotational form, one makes use of the
    # fact that
    #
    #     curl(curl(u)) = grad(div(u)) - div(grad(u)).
    #
    # The same equation holds true in cylindrical form. Hence, to get the
    # rotational form of the splitting scheme, we need to
    #
    # rotational form
    if rotational_form:
        # If there is no dependence of the angular coordinate, what is
        # div(grad(div(u))) in Cartesian coordinates becomes
        #
        #     1/r div(r * grad(1/r div(r*u)))
        #
        # in cylindrical coordinates (div and grad are in cylindrical
        # coordinates). Unfortunately, we cannot write it down that
        # compactly since u_phi is in the game.
        # When using P2 elements, this value will be 0 anyways.
        div_ui = 1 / r * (r * ui[0]).dx(0) + ui[1].dx(1)
        grad_div_ui = as_vector((div_ui.dx(0), div_ui.dx(1)))
        L2 -= r * mu * dot(grad_div_ui, grad(q)) * 2 * pi * my_dx
        # div_grad_div_ui = 1/r * (r * grad_div_ui[0]).dx(0) \
        #     + (grad_div_ui[1]).dx(1)
        # L2 += mu * div_grad_div_ui * q * 2*pi*r*dx
        # n = FacetNormal(Q.mesh())
        # L2 -= mu * (n[0] * grad_div_ui[0] + n[1] * grad_div_ui[1]) \
        #     * q * 2*pi*r*ds

    p1 = Function(P)
    if p_bcs:
        solve(
            a2 == L2,
            p1,
            bcs=p_bcs,
            solver_parameters={
                "linear_solver": "iterative",
                "symmetric": True,
                "preconditioner": "hypre_amg",
                "krylov_solver": {
                    "relative_tolerance": tol,
                    "absolute_tolerance": 0.0,
                    "maximum_iterations": 100,
                    "monitor_convergence": verbose,
                },
            },
        )
    else:
        # If we're dealing with a pure Neumann problem here (which is the
        # default case), this doesn't hurt CG if the system is consistent,
        # cf. :cite:`vdV03`. And indeed it is consistent if and only if
        #
        #   \int_\Gamma r n.u = 0.
        #
        # This makes clear that for incompressible Navier-Stokes, one
        # either needs to make sure that inflow and outflow always add up
        # to 0, or one has to specify pressure boundary conditions.
        #
        # If the right-hand side is very small, round-off errors may impair
        # the consistency of the system. Make sure the system we are
        # solving remains consistent.
        A = assemble(a2)
        b = assemble(L2)
        # Assert that the system is indeed consistent.
        e = Function(P)
        e.interpolate(Constant(1.0))
        evec = e.vector()
        evec /= norm(evec)
        alpha = b.inner(evec)
        normB = norm(b)
        # Assume that in every component of the vector, a round-off error
        # of the magnitude DOLFIN_EPS is present. This leads to the
        # criterion
        #    |<b,e>| / (||b||*||e||) < DOLFIN_EPS
        # as a check whether to consider the system consistent up to
        # round-off error.
        #
        # TODO think about condition here
        # if abs(alpha) > normB * DOLFIN_EPS:
        if abs(alpha) > normB * 1.0e-12:
            # divu = 1 / r * (r * u[0]).dx(0) + u[1].dx(1)
            adivu = assemble(((r * u[0]).dx(0) + u[1].dx(1)) * 2 * pi * my_dx)
            info("\\int 1/r * div(r*u) * 2*pi*r  =  {:e}".format(adivu))
            n = FacetNormal(P.mesh())
            boundary_integral = assemble((n[0] * u[0] + n[1] * u[1]) * 2 * pi * r * ds)
            info("\\int_Gamma n.u * 2*pi*r = {:e}".format(boundary_integral))
            message = (
                "System not consistent! "
                "<b,e> = {:g}, ||b|| = {:g}, <b,e>/||b|| = {:e}.".format(
                    alpha, normB, alpha / normB
                )
            )
            info(message)
            # # Plot the stuff, and project it to a finer mesh with linear
            # # elements for the purpose.
            # plot(divu, title='div(u_tentative)')
            # # Vp = FunctionSpace(Q.mesh(), 'CG', 2)
            # # Wp = MixedFunctionSpace([Vp, Vp])
            # # up = project(u, Wp)
            # fine_mesh = Q.mesh()
            # for k in range(1):
            #     fine_mesh = refine(fine_mesh)
            # V = FunctionSpace(fine_mesh, 'CG', 1)
            # W = V * V
            # # uplot = Function(W)
            # # uplot.interpolate(u)
            # uplot = project(u, W)
            # plot(uplot[0], title='u_tentative[0]')
            # plot(uplot[1], title='u_tentative[1]')
            # # plot(u, title='u_tentative')
            # interactive()
            # exit()
            raise RuntimeError(message)
        # Project out the roundoff error.
        b -= alpha * evec

        #
        # In principle, the ILU preconditioner isn't advised here since it
        # might destroy the semidefiniteness needed for CG.
        #
        # The system is consistent, but the matrix has an eigenvalue 0.
        # This does not harm the convergence of CG, but when
        # preconditioning one has to make sure that the preconditioner
        # preserves the kernel. ILU might destroy this (and the
        # semidefiniteness). With AMG, the coarse grid solves cannot be LU
        # then, so try Jacobi here.
        # <http://lists.mcs.anl.gov/pipermail/petsc-users/2012-February/012139.html>
        #
        prec = PETScPreconditioner("hypre_amg")
        from dolfin import PETScOptions

        PETScOptions.set("pc_hypre_boomeramg_relax_type_coarse", "jacobi")
        solver = PETScKrylovSolver("cg", prec)
        solver.parameters["absolute_tolerance"] = 0.0
        solver.parameters["relative_tolerance"] = tol
        solver.parameters["maximum_iterations"] = 100
        solver.parameters["monitor_convergence"] = verbose
        # Create solver and solve system
        A_petsc = as_backend_type(A)
        b_petsc = as_backend_type(b)
        p1_petsc = as_backend_type(p1.vector())
        solver.set_operator(A_petsc)
        solver.solve(p1_petsc, b_petsc)
    return p1
Exemple #39
0
def test_krylov_solver_norm_type():
    """Check setting of norm type used in testing for convergence by
    PETScKrylovSolver

    """

    norm_type = (PETScKrylovSolver.norm_type_default_norm,
                 PETScKrylovSolver.norm_type_natural,
                 PETScKrylovSolver.norm_type_preconditioned,
                 PETScKrylovSolver.norm_type_none,
                 PETScKrylovSolver.norm_type_unpreconditioned)

    for norm in norm_type:
        # Solve a system of equations
        mesh = UnitSquareMesh(4, 4)
        V = FunctionSpace(mesh, "Lagrange", 1)
        u, v = TrialFunction(V), TestFunction(V)
        a = u*v*dx
        L = Constant(1.0)*v*dx
        A, b = assemble(a), assemble(L)

        solver = PETScKrylovSolver("cg")
        solver.parameters["maximum_iterations"] = 2
        solver.parameters["error_on_nonconvergence"] = False
        solver.set_norm_type(norm)
        solver.set_operator(A)
        solver.solve(b.copy(), b)
        solver.get_norm_type()

        if norm is not PETScKrylovSolver.norm_type_default_norm:
            assert solver.get_norm_type() == norm
Exemple #40
0
class LinearSystem(object):
	def __init__(self, grid, split=False, tol=1.e-6, maxIteration=100):
		self.dx = grid.dx
		self.ds = grid.ds
		self.dS = grid.dS
		self.tol = tol
		self.maxIteration = maxIteration
		self.split = split
	
	def stiffnessBlock(self, properties, u, w):
		return 2*properties.G*inner(sym(grad(u)), grad(w))*self.dx + properties.lamda*div(u)*div(w)*self.dx

	def porePressureBlock(self, properties, p, w):
		return -properties.alpha*p*div(w)*self.dx

	def solidVelocityBlock(self, properties, dt, u, q):
		return (properties.alpha/dt)*div(u)*q*self.dx

	def storageBlock(self, properties, dt, p, q):
		return (1./(properties.Q*dt))*p*q*self.dx

	def solidPressureBlock(self, properties, dt, p, q, delta=1):
		return (properties.alpha**2/(delta*properties.K*dt))*p*q*self.dx

	def fluidFlowBlock(self, properties, p, q):
		return (properties.k/properties.mu)*inner(grad(p), grad(q))*self.dx

	def forceVector(self, load, w, mark):
		return inner(load, w)*self.ds(mark)

	def apply(self, entity, bcs):
		if self.split:
			for bc in bcs:
				bc.apply(entity)
		else:
			bcs.apply(entity)
		return entity

	def assembly(self, entity, bcs=[]):
		if self.split:
			entity = assemble(entity)
		else:
			entity = block_assemble(entity)
		if bcs:
			self.apply(entity, bcs)
		return entity

	def initializeLinearSystem(self, properties, dt, u, w, p, q, bcs):
		self.stiffnessBlock = self.stiffnessBlock(properties, u, w)
		self.porePressureBlock = self.porePressureBlock(properties, p, w)
		self.solidVelocityBlock = self.solidVelocityBlock(properties, dt, u, q)
		self.storageBlock = self.storageBlock(properties, dt, p, q)
		self.solidPressureBlock = self.solidPressureBlock(properties, dt, p, q)
		self.fluidFlowBlock = self.fluidFlowBlock(properties, p, q)
		self.bcs = bcs
		if self.split:
			self.geoSolver = PETScKrylovSolver("gmres", "hypre_amg")
			self.geoSolver.parameters['relative_tolerance'] = self.tol
			self.flowSolver = PETScKrylovSolver("bicgstab", "sor")
			self.flowSolver.parameters['relative_tolerance'] = self.tol

		"""
		Solvers:
		'bicgstab'	Biconjugate gradient stabilized method
		'cg'	Conjugate gradient method
		'gmres'	Generalized minimal residual method
		'minres'	Minimal residual method
		'petsc'	PETSc built in LU solver
		'richardson'	Richardson method
		'superlu_dist'	Parallel SuperLU
		'tfqmr'	Transpose-free quasi-minimal residual method
		'umfpack'	UMFPACK

		PC:
		'icc'	Incomplete Cholesky factorization
		'ilu'	Incomplete LU factorization
		'petsc_amg'	PETSc algebraic multigrid
		'hypre_amg'	HYPRE algebraic multigrid
		'sor'	Successive over-relaxation
		"""

	def assemblyCoefficientsMatrix(self):
		if self.split:
			K = self.stiffnessBlock
			M = self.storageBlock + self.solidPressureBlock + self.fluidFlowBlock
			self.geoCoefficientsMatrix = self.assembly(K, self.bcs.uDirichlet)
			self.flowCoefficientsMatrix = self.assembly(M, self.bcs.pDirichlet)
		else:
			A = [[self.stiffnessBlock, 		self.porePressureBlock],
				 [self.solidVelocityBlock,	self.storageBlock + self.fluidFlowBlock]]
			self.coefficientsMatrix = self.assembly(A, self.bcs.dirichlet)

	def assemblyVector(self, forceVector, u0, p0):
		if self.split:
			self.u0 = u0
			self.p0 = p0
			self.fixedGeoVector = self.assembly(forceVector)
			self.fixedflowVector = self.assembly(self.storageBlock*p0) + self.assembly(self.solidVelocityBlock*u0)
		else:
			f = [forceVector,
			 	 0]
			f = self.assembly(f)
			m = [0,
				 self.storageBlock*p0]
			m = self.assembly(m)
			s = [0,
				 self.solidVelocityBlock*u0]
			s = self.assembly(s)
			self.vector = self.apply(f + m + s, self.bcs.dirichlet)

	def iterateGeoVector(self, p_hk):
		self.geoVector = self.fixedGeoVector + self.assembly(-self.porePressureBlock*p_hk)

	def iterateFlowVector(self, u_hk, p_hk):
		self.flowVector = self.fixedflowVector + self.assembly(-self.solidVelocityBlock*u_hk) + self.assembly(self.solidPressureBlock*p_hk)

	def getRelativeErrorNorm(self, p_h, p_hk):
		set_log_level(40)
		error = errornorm(p_h, p_hk)/norm(p_h)
		set_log_level(20)
		return error


	def solveProblem(self, space):
		X = space.function()
		if self.split:
			(u_h, p_h) = X
			(u_hk, p_hk) = space.assignInitialCondition(Constant((0.0, 0.0, 0.0)), Constant(0.0))
			u_hk.assign(self.u0)
			p_hk.assign(self.p0)
			error = 1e34
			iteration = 1
			while error > self.tol and iteration < self.maxIteration:
				print("Iteration = {:4d}".format(iteration), end="\t")
				self.iterateFlowVector(u_hk, p_hk)
				self.apply(self.flowVector, self.bcs.pDirichlet)
				self.flowSolver.solve(self.flowCoefficientsMatrix, p_h.vector(), self.flowVector)
				p_hk.assign(p_h)
				self.iterateGeoVector(p_hk)
				self.apply(self.geoVector, self.bcs.uDirichlet)
				self.geoSolver.solve(self.geoCoefficientsMatrix, u_h.vector(), self.geoVector)
				u_hk.assign(u_h)
				self.iterateFlowVector(u_hk, p_hk)
				self.apply(self.flowVector, self.bcs.pDirichlet)
				self.flowSolver.solve(self.flowCoefficientsMatrix, p_h.vector(), self.flowVector)
				error = self.getRelativeErrorNorm(p_h, p_hk)
				print("Error = {:.2E}".format(error), end="\r")
				p_hk.assign(p_h)
				iteration += 1
			print("\033[K", end="\r")
			print("Iteration = {:4d}".format(iteration), end="\t")
			print("Error = {:.2E}".format(error), end="\t")
			self.solution = (u_h, p_h)
		else:
			block_solve(self.coefficientsMatrix, X.block_vector(), self.vector, "mumps")
			self.solution = X.block_split()