Example #1
0
def weighted_H1_norm(w, vec, piecewise=False):
    if piecewise:
        DG = FunctionSpace(vec.basis.mesh, "DG", 0)
        s = TestFunction(DG)
        ae = assemble(w * inner(nabla_grad(vec._fefunc), nabla_grad(vec._fefunc)) * s * dx)
        norm_vec = np.array([sqrt(e) for e in ae])
        # map DG dofs to cell indices
        dofs = [DG.dofmap().cell_dofs(c.index())[0] for c in cells(vec.basis.mesh)]
        norm_vec = norm_vec[dofs]
    else:
        ae = assemble(w * inner(nabla_grad(vec._fefunc), nabla_grad(vec._fefunc)) * dx)
        norm_vec = sqrt(ae)
    return norm_vec
Example #2
0
V = FunctionSpace(mesh, 'Lagrange', 2)
# Vplot = FunctionSpace(mesh_plot, 'Lagrange', 1)
Vplot = V
Q = FunctionSpace(mesh, 'Lagrange', 1)

# BC conditions, nullspace
v_in_expr = Constant(v_in)
plt = plot(interpolate(v_in_expr, V), range_min=0., range_max=2*v_in, window_width= width, window_height= height)
plt.write_png('%s/correct' % dir)
# v_in_expr = Expression('(t<1.0)?t*v:v', v=Constant(v_in), t=0.0)
# v_in_expr = Expression('(t<1.0)?(1-cos(pi*t))*v*0.5:v', v=Constant(v_in), t=0.0)
bcp = DirichletBC(Q, Constant(0.0), boundary_parts, 2)
bcu = DirichletBC(V, v_in_expr, boundary_parts, 1)
foo = Function(Q)
null_vec = Vector(foo.vector())
Q.dofmap().set(null_vec, 1.0)
null_vec *= 1.0/null_vec.norm('l2')
# print(null_vec.array())
null_space = VectorSpaceBasis([null_vec])

ds = Measure("ds", subdomain_data=boundary_parts)

# Define forms (dont redefine functions used here)
# step 1
u0 = Function(V)
u1 = Function(V)
p0 = Function(Q)
u_tent = TrialFunction(V)
v = TestFunction(V)
# U_ = 1.5*u0 - 0.5*u1
# nonlinearity = inner(dot(0.5 * (u_tent.dx(0) + u0.dx(0)), U_), v) * dx
Example #3
0
    def _evaluateLocalEstimator(cls, mu, w, coeff_field, pde, f, quadrature_degree, epsilon=1e-5):
        """Evaluation of patch local equilibrated estimator."""

        # prepare numerical flux and f
        sigma_mu, f_mu = evaluate_numerical_flux(w, mu, coeff_field, f)

        # ###################
        # ## MIXED PROBLEM ##
        # ###################

        # get setup data for mixed problem
        V = w[mu]._fefunc.function_space()
        mesh = V.mesh()
        mesh.init()
        degree = element_degree(w[mu]._fefunc)

        # data for nodal bases
        V_dm = V.dofmap()
        V_dofs = dict([(i, V_dm.cell_dofs(i)) for i in range(mesh.num_cells())])
        V1 = FunctionSpace(mesh, 'CG', 1)   # V1 is to define nodal base functions
        phi_z = Function(V1)
        phi_coeffs = np.ndarray(V1.dim())
        vertex_dof_map = V1.dofmap().vertex_to_dof_map(mesh)
        # vertex_dof_map = vertex_to_dof_map(V1)
        dof_list = vertex_dof_map.tolist()
        # DG0 localisation
        DG0 = FunctionSpace(mesh, 'DG', 0)
        DG0_dofs = dict([(c.index(),DG0.dofmap().cell_dofs(c.index())[0]) for c in cells(mesh)])
        dg0 = TestFunction(DG0)
        # characteristic function of patch
        xi_z = Function(DG0)
        xi_coeffs = np.ndarray(DG0.dim())
        # mesh data
        h = CellSize(mesh)
        n = FacetNormal(mesh)
        cf = CellFunction('size_t', mesh)
        # setup error estimator vector
        eq_est = np.zeros(DG0.dim())

        # setup global equilibrated flux vector
        DG = VectorFunctionSpace(mesh, "DG", degree)
        DG_dofmap = DG.dofmap()

        # define form functions
        tau = TrialFunction(DG)
        v = TestFunction(DG)

        # define global tau
        tau_global = Function(DG)
        tau_global.vector()[:] = 0.0

        # iterate vertices
        for vertex in vertices(mesh):
            # get patch cell indices
            vid = vertex.index()
            patch_cid, FF_inner, FF_boundary = get_vertex_patch(vid, mesh, layers=1)

            # set nodal base function
            phi_coeffs[:] = 0
            phi_coeffs[dof_list.index(vid)] = 1
            phi_z.vector()[:] = phi_coeffs

            # set characteristic function and mark patch
            cf.set_all(0)
            xi_coeffs[:] = 0
            for cid in patch_cid:
                xi_coeffs[DG0_dofs[int(cid)]] = 1
                cf[int(cid)] = 1
            xi_z.vector()[:] = xi_coeffs

            # determine local dofs
            lDG_cell_dofs = dict([(cid, DG_dofmap.cell_dofs(cid)) for cid in patch_cid])
            lDG_dofs = [cd.tolist() for cd in lDG_cell_dofs.values()]
            lDG_dofs = list(iter.chain(*lDG_dofs))

            # print "\nlocal DG subspace has dimension", len(lDG_dofs), "degree", degree, "cells", len(patch_cid), patch_cid
            # print "local DG_cell_dofs", lDG_cell_dofs
            # print "local DG_dofs", lDG_dofs

            # create patch measures
            dx = Measure('dx')[cf]
            dS = Measure('dS')[FF_inner]

            # define forms
            alpha = Constant(1 / epsilon) / h
            a = inner(tau,v) * phi_z * dx(1) + alpha * div(tau) * div(v) * dx(1) + avg(alpha) * jump(tau,n) * jump(v,n) * dS(1)\
                + avg(alpha) * jump(xi_z * tau,n) * jump(v,n) * dS(2)
            L = -alpha * (div(sigma_mu) + f) * div(v) * phi_z * dx(1)\
                - avg(alpha) * jump(sigma_mu,n) * jump(v,n) * avg(phi_z)*dS(1)

    #        print "L2 f + div(sigma)", assemble((f + div(sigma)) * (f + div(sigma)) * dx(0))

            # assemble forms
            lhs = assemble(a, form_compiler_parameters={'quadrature_degree': quadrature_degree})
            rhs = assemble(L, form_compiler_parameters={'quadrature_degree': quadrature_degree})

            # convert DOLFIN representation to scipy sparse arrays
            rows, cols, values = lhs.data()
            lhsA = sps.csr_matrix((values, cols, rows)).tocoo()

            # slice sparse matrix and solve linear problem
            lhsA = coo_submatrix_pull(lhsA, lDG_dofs, lDG_dofs)
            lx = spsolve(lhsA, rhs.array()[lDG_dofs])
            # print ">>> local solution lx", type(lx), lx
            local_tau = Function(DG)
            local_tau.vector()[lDG_dofs] = lx
            # print "div(tau)", assemble(inner(div(local_tau),div(local_tau))*dx(1))

            # add up local fluxes
            tau_global.vector()[lDG_dofs] += lx

        # evaluate estimator
        # maybe TODO: re-define measure dx
        eq_est = assemble( inner(tau_global, tau_global) * dg0 * (dx(0)+dx(1)),\
                           form_compiler_parameters={'quadrature_degree': quadrature_degree})

        # reorder according to cell ids
        eq_est = eq_est[DG0_dofs.values()].array()
        global_est = np.sqrt(np.sum(eq_est))
        # eq_est_global = assemble( inner(tau_global, tau_global) * (dx(0)+dx(1)), form_compiler_parameters={'quadrature_degree': quadrature_degree} )
        # global_est2 = np.sqrt(np.sum(eq_est_global))
        return global_est, FlatVector(np.sqrt(eq_est))#, tau_global
Example #4
0
    def _evaluateGlobalMixedEstimator(cls, mu, w, coeff_field, pde, f, quadrature_degree, vectorspace_type='BDM'):
        """Evaluation of global mixed equilibrated estimator."""
        # set quadrature degree
#        quadrature_degree_old = parameters["form_compiler"]["quadrature_degree"]
#        parameters["form_compiler"]["quadrature_degree"] = quadrature_degree
#        logger.debug("residual quadrature order = " + str(quadrature_degree))

        # prepare numerical flux and f
        sigma_mu, f_mu = evaluate_numerical_flux(w, mu, coeff_field, f)

        # ###################
        # ## MIXED PROBLEM ##
        # ###################

        # get setup data for mixed problem
        V = w[mu]._fefunc.function_space()
        mesh = V.mesh()
        degree = element_degree(w[mu]._fefunc)

        # create function spaces
        DG0 = FunctionSpace(mesh, 'DG', 0)
        DG0_dofs = [DG0.dofmap().cell_dofs(c.index())[0] for c in cells(mesh)]
        RT = FunctionSpace(mesh, vectorspace_type, degree)
        W = RT * DG0

        # setup boundary conditions
#        bcs = pde.create_dirichlet_bcs(W.sub(1))

        # debug ===
        # from dolfin import DOLFIN_EPS, DirichletBC
        # def boundary(x):
        #     return x[0] < DOLFIN_EPS or x[0] > 1.0 + DOLFIN_EPS or x[1] < DOLFIN_EPS or x[1] > 1.0 + DOLFIN_EPS
        # bcs = [DirichletBC(W.sub(1), Constant(0.0), boundary)]
        # === debug

        # create trial and test functions
        (sigma, u) = TrialFunctions(W)
        (tau, v) = TestFunctions(W)

        # define variational form
        a_eq = (dot(sigma, tau) + div(tau) * u + div(sigma) * v) * dx
        L_eq = (- f_mu * v + dot(sigma_mu, tau)) * dx

        # compute solution
        w_eq = Function(W)
        solve(a_eq == L_eq, w_eq)
        (sigma_mixed, u_mixed) = w_eq.split()

        # #############################
        # ## EQUILIBRATION ESTIMATOR ##
        # #############################

        # evaluate error estimator
        dg0 = TestFunction(DG0)
        eta_mu = inner(sigma_mu, sigma_mu) * dg0 * dx
        eta_T = assemble(eta_mu, form_compiler_parameters={'quadrature_degree': quadrature_degree})
        eta_T = np.array([sqrt(e) for e in eta_T])

        # evaluate global error
        eta = sqrt(sum(i**2 for i in eta_T))
        # reorder array entries for local estimators
        eta_T = eta_T[DG0_dofs]

        # restore quadrature degree
#        parameters["form_compiler"]["quadrature_degree"] = quadrature_degree_old

        return eta, FlatVector(eta_T)
Example #5
0
    def _evaluateResidualEstimator(cls, mu, w, coeff_field, pde, f, quadrature_degree):
        """Evaluate the residual error according to EGSZ (5.7) which consists of volume terms (5.3) and jump terms (5.5).

            .. math:: \eta_{\mu,T}(w_N) &:= h_T || \overline{a}^{-1/2} (f\delta_{\mu,0} + \nabla\overline{a}\cdot\nabla w_{N,\mu}
                                + \sum_{m=1}^\infty \nabla a_m\cdot\nabla( \alpha^m_{\mu_m+1}\Pi_\mu^{\mu+e_m} w_{N,\mu+e_m}
                                - \alpha_{\mu_m}^m w_{N,\mu} + \alpha_{\mu_m-1}^m\Pi_\mu^{\mu_m-e_m} w_{N,\mu-e_m} ||_{L^2(T)}\\
          \eta_{\mu,S}(w_N) &:= h_S^{-1/2} || \overline{a}^{-1/2} [(\overline{a}\nabla w_{N,\mu} + \sum_{m=1}^\infty a_m\nabla
                                  ( \alpha_{\mu_m+1}^m\Pi_\mu^{\mu+e_m} w_{N,\mu+e_m} - \alpha_{\mu_m}^m w_{N,\mu}
                                  + \alpha_{\mu_m-1}^m\Pi_\mu^{\mu-e_m} w_{N,\mu-e_m})\cdot\nu] ||_{L^2(S)}\\
        """
        # set quadrature degree
        quadrature_degree_old = parameters["form_compiler"]["quadrature_degree"]
        parameters["form_compiler"]["quadrature_degree"] = quadrature_degree
        logger.debug("residual quadrature order = " + str(quadrature_degree))
    
        # get pde residual terms
        r_T = pde.volume_residual
        r_E = pde.edge_residual
        r_Nb = pde.neumann_residual
        
        # get mean field of coefficient
        a0_f = coeff_field.mean_func

        # prepare some FEM variables
        V = w[mu]._fefunc.function_space()
        mesh = V.mesh()
        nu = FacetNormal(mesh)

        # initialise volume and edge residual with deterministic part
#        R_T = dot(nabla_grad(a0_f), nabla_grad(w[mu]._fefunc))
        R_T = r_T(a0_f, w[mu]._fefunc)
        if not mu:
            R_T = R_T + f
#        R_E = a0_f * dot(nabla_grad(w[mu]._fefunc), nu)
        R_E = r_E(a0_f, w[mu]._fefunc, nu)
        # get Neumann residual
        homogeneousNBC = False if mu.order == 0 else True
        R_Nb = r_Nb(a0_f, w[mu]._fefunc, nu, mesh, homogeneous=homogeneousNBC)

        # iterate m
        Lambda = w.active_indices()
        maxm = w.max_order
        if len(coeff_field) < maxm:
            logger.warning("insufficient length of coefficient field for MultiVector (%i < %i)", len(coeff_field), maxm)
            maxm = len(coeff_field)
            #        assert coeff_field.length >= maxm        # ensure coeff_field expansion is sufficiently long
        for m in range(maxm):
            am_f, am_rv = coeff_field[m]

            # prepare polynom coefficients
            beta = am_rv.orth_polys.get_beta(mu[m])

            # mu
            res = -beta[0] * w[mu]

            # mu+1
            mu1 = mu.inc(m)
            if mu1 in Lambda:
                w_mu1 = w.get_projection(mu1, mu)
                res += beta[1] * w_mu1

            # mu-1
            mu2 = mu.dec(m)
            if mu2 in Lambda:
                w_mu2 = w.get_projection(mu2, mu)
                res += beta[-1] * w_mu2

            # add volume contribution for m
#            r_t = dot(nabla_grad(am_f), nabla_grad(res._fefunc))
            R_T = R_T + r_T(am_f, res._fefunc)
            # add edge contribution for m
#            r_e = am_f * dot(nabla_grad(res._fefunc), nu)
            R_E = R_E + r_E(am_f, res._fefunc, nu)

        # prepare more FEM variables for residual assembly
        DG = FunctionSpace(mesh, "DG", 0)
        s = TestFunction(DG)
        h = CellSize(mesh)

        # scaling of residual terms and definition of residual form
        a0_s = a0_f[0] if isinstance(a0_f, tuple) else a0_f     # required for elasticity parameters
        res_form = (h ** 2 * (1 / a0_s) * dot(R_T, R_T) * s * dx
                    + avg(h) * dot(avg(R_E) / avg(a0_s), avg(R_E)) * 2 * avg(s) * dS)
        
        resT = h ** 2 * (1 / a0_s) * dot(R_T, R_T) * s * dx
        resE = 0 * s * dx + avg(h) * dot(avg(R_E) / avg(a0_s), avg(R_E)) * 2 * avg(s) * dS
        resNb = 0 * s * dx
        
        # add Neumann residuals
        if R_Nb is not None:
            for rj, dsj in R_Nb:
                res_form = res_form + h * (1 / a0_s) * dot(rj, rj) * s * dsj
                resNb += h * (1 / a0_s) * dot(rj, rj) * s * dsj

        # FEM evaluate residual on mesh
        eta = assemble(res_form)
        eta_indicator = np.array([sqrt(e) for e in eta])
        # map DG dofs to cell indices
        dofs = [DG.dofmap().cell_dofs(c.index())[0] for c in cells(mesh)]
        eta_indicator = eta_indicator[dofs]
        global_error = sqrt(sum(e for e in eta))

        # debug ---
        if False:
            etaT = assemble(resT)
            etaT_indicator = etaT #np.array([sqrt(e) for e in etaT])
            etaT = sqrt(sum(e for e in etaT))
            etaE = assemble(resE)
            etaE_indicator = etaE #np.array([sqrt(e) for e in etaE])
            etaE = sqrt(sum(e for e in etaE))
            etaNb = assemble(resNb)
            etaNb_indicator = etaNb #np.array([sqrt(e) for e in etaNb])
            etaNb = sqrt(sum(e for e in etaNb))
        
            print "==========RESIDUAL ESTIMATOR============"
            print "eta", eta
            print "eta_indicator", eta_indicator
            print "global =", global_error
            print "volume =", etaT
            print "edge =", etaE
            print "Neumann =", etaNb

            if False:        
                plot_indicators(((eta, "overall residual"), (etaT_indicator, "volume residual"), (etaE_indicator, "edge residual"), (etaNb_indicator, "Neumann residual")), mesh)
        # ---debug
        
        # restore quadrature degree
        parameters["form_compiler"]["quadrature_degree"] = quadrature_degree_old

        return (FlatVector(eta_indicator), global_error)
Example #6
0
class Solver(gs.GeneralSolver):
    def __init__(self, args, tc, metadata):
        gs.GeneralSolver.__init__(self, args, tc, metadata)
        self.metadata['hasTentativeV'] = True

        self.solver_vel_tent = None
        self.solver_vel_cor = None
        self.solver_p = None
        self.solver_rot = None
        self.null_space = None

        # input parameters
        self.bc = args.bc
        self.forceOutflow = args.fo
        self.useLaplace = args.laplace
        self.use_full_SUPG = args.cs
        self.bcv = 'NOT' if self.useLaplace else args.bcv
        if self.bcv == 'CDN':
            info('Using classical do nothing condition (Tn=0).')
        if self.bcv == 'DDN':
            info('Using directional do nothing condition (Tn=0.5*negative(u.n)u).')
        if self.bcv == 'LAP':
            info('Using laplace neutral condition (grad(u)n=0).')
        self.stabCoef = args.stab
        self.stabilize = (args.stab > DOLFIN_EPS)
        if self.stabilize:
            if self.use_full_SUPG:
                info('Used consistent streamline-diffusion stabilization with coef.: %f' % args.stab)
            else:
                info('Used non-consistent streamline-diffusion stabilization with coef.: %f' % args.stab)
        else:
            info('No stabilization used.')
        self.solvers = args.solvers
        self.useRotationScheme = args.r
        self.metadata['hasTentativeP'] = self.useRotationScheme

        self.B = args.B
        self.use_ema = args.ema
        self.cbcDelta = args.cbcDelta
        self.prec_v = args.precV
        self.prec_p = args.precP
        self.precision_rel_v_tent = args.prv1
        self.precision_abs_v_tent = args.pav1
        self.precision_p = args.pp

    def __str__(self):
        return 'ipcs1 - incremental pressure correction scheme with nonlinearity treated by Adam-Bashword + ' \
               'Crank-Nicolson and viscosity term treated semi-explicitly (Crank-Nicholson)'

    @staticmethod
    def setup_parser_options(parser):
        gs.GeneralSolver.setup_parser_options(parser)
        parser.add_argument('-s', '--solvers', help='Solvers', choices=['direct', 'krylov'], default='krylov')
        parser.add_argument('--prv1', help='relative tentative velocity Krylov solver precision', type=int, default=6)
        parser.add_argument('--pav1', help='absolute tentative velocity Krylov solver precision', type=int, default=10)
        parser.add_argument('--pp', help='pressure Krylov solver precision', type=int, default=10)
        parser.add_argument('-b', '--bc', help='Pressure boundary condition mode',
                            choices=['outflow', 'nullspace', 'nullspace_s', 'lagrange'], default='outflow')
        parser.add_argument('--precV', help='Preconditioner for tentative velocity solver', type=str, default='ilu')
        parser.add_argument('--precP', help='Preconditioner for pressure solver', choices=['hypre_amg', 'ilu'],
                            default='hypre_amg')
        parser.add_argument('-r', help='Use rotation scheme', action='store_true')
        parser.add_argument('-B', help='Use no BC in correction step', action='store_true')
        parser.add_argument('--fo', help='Force Neumann outflow boundary for pressure', action='store_true')
        parser.add_argument('--laplace', help='Use laplace(u) instead of div(symgrad(u))', action='store_true')
        parser.add_argument('--stab', help='Use stabilization (positive constant)', type=float, default=0.)
        parser.add_argument('--bcv', help='Oufflow BC for velocity (with stress formulation)',
                            choices=['CDN', 'DDN', 'LAP'], default='CDN')
        parser.add_argument('--ema', help='Use EMA conserving scheme for convection term', action='store_true')
        # described in Charnyi, Heister, Olshanskii, Rebholz:
        # "On conservation laws of Navier-Stokes Galerkin discretizations" (2016)
        parser.add_argument('--cs', help='Use consistent SUPG stabilisation.', action='store_true')
        parser.add_argument('--cbcDelta', help='Use simpler cbcflow parameter for SUPG', action='store_true')

    def solve(self, problem):
        self.problem = problem
        doSave = problem.doSave
        save_this_step = False
        onlyVel = problem.saveOnlyVel
        dt = self.metadata['dt']

        nu = Constant(self.problem.nu)
        # TODO check proper use of watches
        self.tc.init_watch('init', 'Initialization', True, count_to_percent=False)
        self.tc.init_watch('rhs', 'Assembled right hand side', True, count_to_percent=True)
        self.tc.init_watch('updateBC', 'Updated velocity BC', True, count_to_percent=True)
        self.tc.init_watch('applybc1', 'Applied velocity BC 1st step', True, count_to_percent=True)
        self.tc.init_watch('applybc3', 'Applied velocity BC 3rd step', True, count_to_percent=True)
        self.tc.init_watch('applybcP', 'Applied pressure BC or othogonalized rhs', True, count_to_percent=True)
        self.tc.init_watch('assembleMatrices', 'Initial matrix assembly', False, count_to_percent=True)
        self.tc.init_watch('solve 1', 'Running solver on 1st step', True, count_to_percent=True)
        self.tc.init_watch('solve 2', 'Running solver on 2nd step', True, count_to_percent=True)
        self.tc.init_watch('solve 3', 'Running solver on 3rd step', True, count_to_percent=True)
        self.tc.init_watch('solve 4', 'Running solver on 4th step', True, count_to_percent=True)
        self.tc.init_watch('assembleA1', 'Assembled A1 matrix (without stabiliz.)', True, count_to_percent=True)
        self.tc.init_watch('assembleA1stab', 'Assembled A1 stabilization', True, count_to_percent=True)
        self.tc.init_watch('next', 'Next step assignments', True, count_to_percent=True)
        self.tc.init_watch('saveVel', 'Saved velocity', True)

        self.tc.start('init')

        # Define function spaces (P2-P1)
        mesh = self.problem.mesh
        self.V = VectorFunctionSpace(mesh, "Lagrange", 2)  # velocity
        self.Q = FunctionSpace(mesh, "Lagrange", 1)  # pressure
        self.PS = FunctionSpace(mesh, "Lagrange", 2)  # partial solution (must be same order as V)
        self.D = FunctionSpace(mesh, "Lagrange", 1)   # velocity divergence space
        if self.bc == 'lagrange':
            L = FunctionSpace(mesh, "R", 0)
            QL = self.Q*L

        problem.initialize(self.V, self.Q, self.PS, self.D)

        # Define trial and test functions
        u = TrialFunction(self.V)
        v = TestFunction(self.V)
        if self.bc == 'lagrange':
            (pQL, rQL) = TrialFunction(QL)
            (qQL, lQL) = TestFunction(QL)
        else:
            p = TrialFunction(self.Q)
            q = TestFunction(self.Q)

        n = FacetNormal(mesh)
        I = Identity(u.geometric_dimension())

        # Initial conditions: u0 velocity at previous time step u1 velocity two time steps back p0 previous pressure
        [u1, u0, p0] = self.problem.get_initial_conditions([{'type': 'v', 'time': -dt},
                                                          {'type': 'v', 'time': 0.0},
                                                          {'type': 'p', 'time': 0.0}])

        if doSave:
            problem.save_vel(False, u0, 0.0)
            problem.save_vel(True, u0, 0.0)

        u_ = Function(self.V)         # current tentative velocity
        u_cor = Function(self.V)         # current corrected velocity
        if self.bc == 'lagrange':
            p_QL = Function(QL)    # current pressure or pressure help function from rotation scheme
            pQ = Function(self.Q)     # auxiliary function for conversion between QL.sub(0) and Q
        else:
            p_ = Function(self.Q)         # current pressure or pressure help function from rotation scheme
        p_mod = Function(self.Q)      # current modified pressure from rotation scheme

        # Define coefficients
        k = Constant(self.metadata['dt'])
        f = Constant((0, 0, 0))

        # Define forms
        # step 1: Tentative velocity, solve to u_
        u_ext = 1.5*u0 - 0.5*u1  # extrapolation for convection term

        # Stabilisation
        h = CellSize(mesh)
        # CBC delta:
        if self.cbcDelta:
            delta = Constant(self.stabCoef)*h/(sqrt(inner(u_ext, u_ext))+h)
        else:
            delta = Constant(self.stabCoef)*h**2/(2*nu*k + k*h*inner(u_ext, u_ext)+h**2)

        if self.use_full_SUPG:
            v1 = v + delta*0.5*k*dot(grad(v), u_ext)
            parameters['form_compiler']['quadrature_degree'] = 6
        else:
            v1 = v

        def nonlinearity(function):
            if self.use_ema:
               return 2*inner(dot(sym(grad(function)), u_ext), v1) * dx + inner(div(function)*u_ext, v1) * dx
                # return 2*inner(dot(sym(grad(function)), u_ext), v) * dx + inner(div(u_ext)*function, v) * dx
                # QQ implement this way?
            else:
                return inner(dot(grad(function), u_ext), v1) * dx

        def diffusion(fce):
            if self.useLaplace:
                return nu*inner(grad(fce), grad(v1)) * dx
            else:
                form = inner(nu * 2 * sym(grad(fce)), sym(grad(v1))) * dx
                if self.bcv == 'CDN':
                    # IMP will work only if p=0 on output, or we must add term
                    # inner(p0*n, v)*problem.get_outflow_measure_form() to avoid boundary layer
                    return form
                if self.bcv == 'LAP':
                    return form - inner(nu*dot(grad(fce).T, n), v1)  * problem.get_outflow_measure_form()
                if self.bcv == 'DDN':
                    # IMP will work only if p=0 on output, or we must add term
                    # inner(p0*n, v)*problem.get_outflow_measure_form() to avoid boundary layer
                    return form  # additional term must be added to non-constant part

        def pressure_rhs():
            if self.useLaplace or self.bcv == 'LAP':
                return inner(p0, div(v1)) * dx - inner(p0*n, v1) * problem.get_outflow_measure_form()
                # NT term inner(inner(p, n), v) is 0 when p=0 on outflow
            else:
                return inner(p0, div(v1)) * dx

        a1_const = (1./k)*inner(u, v1)*dx + diffusion(0.5*u)
        a1_change = nonlinearity(0.5*u)
        if self.bcv == 'DDN':
            # IMP Problem: Does not penalize influx for current step, only for the next one
            # IMP this can lead to oscilation: DDN correct next step, but then u_ext is OK so in next step DDN is not used, leading to new influx...
            # u and u_ext cannot be switched, min_value is nonlinear function
            a1_change += -0.5*min_value(Constant(0.), inner(u_ext, n))*inner(u, v1)*problem.get_outflow_measure_form()
            # IMP works only with uflacs compiler

        L1 = (1./k)*inner(u0, v1)*dx - nonlinearity(0.5*u0) - diffusion(0.5*u0) + pressure_rhs()
        if self.bcv == 'DDN':
            L1 += 0.5*min_value(0., inner(u_ext, n))*inner(u0, v1)*problem.get_outflow_measure_form()

        # Non-consistent SUPG stabilisation
        if self.stabilize and not self.use_full_SUPG:
            # a1_stab = delta*inner(dot(grad(u), u_ext), dot(grad(v), u_ext))*dx
            a1_stab = 0.5*delta*inner(dot(grad(u), u_ext), dot(grad(v), u_ext))*dx(None, {'quadrature_degree': 6})
            # NT optional: use Crank Nicolson in stabilisation term: change RHS
            # L1 += -0.5*delta*inner(dot(grad(u0), u_ext), dot(grad(v), u_ext))*dx(None, {'quadrature_degree': 6})

        outflow_area = Constant(problem.outflow_area)
        need_outflow = Constant(0.0)
        if self.useRotationScheme:
            # Rotation scheme
            if self.bc == 'lagrange':
                F2 = inner(grad(pQL), grad(qQL))*dx + (1./k)*qQL*div(u_)*dx + pQL*lQL*dx + qQL*rQL*dx
            else:
                F2 = inner(grad(p), grad(q))*dx + (1./k)*q*div(u_)*dx
        else:
            # Projection, solve to p_
            if self.bc == 'lagrange':
                F2 = inner(grad(pQL - p0), grad(qQL))*dx + (1./k)*qQL*div(u_)*dx + pQL*lQL*dx + qQL*rQL*dx
            else:
                if self.forceOutflow and problem.can_force_outflow:
                    info('Forcing outflow.')
                    F2 = inner(grad(p - p0), grad(q))*dx + (1./k)*q*div(u_)*dx
                    for m in problem.get_outflow_measures():
                        F2 += (1./k)*(1./outflow_area)*need_outflow*q*m
                else:
                    F2 = inner(grad(p - p0), grad(q))*dx + (1./k)*q*div(u_)*dx
        a2, L2 = system(F2)

        # step 3: Finalize, solve to u_
        if self.useRotationScheme:
            # Rotation scheme
            if self.bc == 'lagrange':
                F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_QL.sub(0)), v)*dx
            else:
                F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_), v)*dx
        else:
            if self.bc == 'lagrange':
                F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_QL.sub(0) - p0), v)*dx
            else:
                F3 = (1./k)*inner(u - u_, v)*dx + inner(grad(p_ - p0), v)*dx
        a3, L3 = system(F3)

        if self.useRotationScheme:
            # Rotation scheme: modify pressure
            if self.bc == 'lagrange':
                pr = TrialFunction(self.Q)
                qr = TestFunction(self.Q)
                F4 = (pr - p0 - p_QL.sub(0) + nu*div(u_))*qr*dx
            else:
                F4 = (p - p0 - p_ + nu*div(u_))*q*dx
            # TODO zkusit, jestli to nebude rychlejsi? nepocitat soustavu, ale p.assign(...), nutno project(div(u),Q) coz je pocitani podobne soustavy
            # TODO zkusit v project zadat solver_type='lu' >> primy resic by mel byt efektivnejsi
            a4, L4 = system(F4)

        # Assemble matrices
        self.tc.start('assembleMatrices')
        A1_const = assemble(a1_const)  # need to be here, so A1 stays one Python object during repeated assembly
        A1_change = A1_const.copy()  # copy to get matrix with same sparse structure (data will be overwriten)
        if self.stabilize and not self.use_full_SUPG:
            A1_stab = A1_const.copy()  # copy to get matrix with same sparse structure (data will be overwriten)
        A2 = assemble(a2)
        A3 = assemble(a3)
        if self.useRotationScheme:
            A4 = assemble(a4)
        self.tc.end('assembleMatrices')

        if self.solvers == 'direct':
            self.solver_vel_tent = LUSolver('mumps')
            self.solver_vel_cor = LUSolver('mumps')
            self.solver_p = LUSolver('umfpack')
            if self.useRotationScheme:
                self.solver_rot = LUSolver('umfpack')
        else:
            # NT not needed, chosen not to use hypre_parasails
            # if self.prec_v == 'hypre_parasails':  # in FEniCS 1.6.0 inaccessible using KrylovSolver class
            #     self.solver_vel_tent = PETScKrylovSolver('gmres')   # PETSc4py object
            #     self.solver_vel_tent.ksp().getPC().setType('hypre')
            #     PETScOptions.set('pc_hypre_type', 'parasails')
            #     # this is global setting, but preconditioners for pressure solvers are set by their constructors
            # else:
            self.solver_vel_tent = KrylovSolver('gmres', self.prec_v)   # nonsymetric > gmres
            # IMP cannot use 'ilu' in parallel (choose different default option)
            self.solver_vel_cor = KrylovSolver('cg', 'hypre_amg')   # nonsymetric > gmres
            self.solver_p = KrylovSolver('cg', self.prec_p)          # symmetric > CG
            if self.useRotationScheme:
                self.solver_rot = KrylovSolver('cg', self.prec_p)

        solver_options = {'monitor_convergence': True, 'maximum_iterations': 1000, 'nonzero_initial_guess': True}
        # 'nonzero_initial_guess': True   with  solver.solbe(A, u, b) means that
        # Solver will use anything stored in u as an initial guess

        # Get the nullspace if there are no pressure boundary conditions
        foo = Function(self.Q)     # auxiliary vector for setting pressure nullspace
        if self.bc in ['nullspace', 'nullspace_s']:
            null_vec = Vector(foo.vector())
            self.Q.dofmap().set(null_vec, 1.0)
            null_vec *= 1.0/null_vec.norm('l2')
            self.null_space = VectorSpaceBasis([null_vec])
            if self.bc == 'nullspace':
                as_backend_type(A2).set_nullspace(self.null_space)

        # apply global options for Krylov solvers
        self.solver_vel_tent.parameters['relative_tolerance'] = 10 ** (-self.precision_rel_v_tent)
        self.solver_vel_tent.parameters['absolute_tolerance'] = 10 ** (-self.precision_abs_v_tent)
        self.solver_vel_cor.parameters['relative_tolerance'] = 10E-12
        self.solver_vel_cor.parameters['absolute_tolerance'] = 10E-4
        self.solver_p.parameters['relative_tolerance'] = 10**(-self.precision_p)
        self.solver_p.parameters['absolute_tolerance'] = 10E-10
        if self.useRotationScheme:
            self.solver_rot.parameters['relative_tolerance'] = 10**(-self.precision_p)
            self.solver_rot.parameters['absolute_tolerance'] = 10E-10

        if self.solvers == 'krylov':
            for solver in [self.solver_vel_tent, self.solver_vel_cor, self.solver_p, self.solver_rot] if \
                    self.useRotationScheme else [self.solver_vel_tent, self.solver_vel_cor, self.solver_p]:
                for key, value in solver_options.items():
                    try:
                        solver.parameters[key] = value
                    except KeyError:
                        info('Invalid option %s for KrylovSolver' % key)
                        return 1
                solver.parameters['preconditioner']['structure'] = 'same'
                # matrices A2-A4 do not change, so we can reuse preconditioners

        self.solver_vel_tent.parameters['preconditioner']['structure'] = 'same_nonzero_pattern'
        # matrix A1 changes every time step, so change of preconditioner must be allowed

        if self.bc == 'lagrange':
            fa = FunctionAssigner(self.Q, QL.sub(0))

        # boundary conditions
        bcu, bcp = problem.get_boundary_conditions(self.bc == 'outflow', self.V, self.Q)
        self.tc.end('init')
        # Time-stepping
        info("Running of Incremental pressure correction scheme n. 1")
        ttime = self.metadata['time']
        t = dt
        step = 1
        while t < (ttime + dt/2.0):
            info("t = %f" % t)
            self.problem.update_time(t, step)
            if self.MPI_rank == 0:
                problem.write_status_file(t)

            if doSave:
                save_this_step = problem.save_this_step

            # DDN debug
            # u_ext_in = assemble(inner(u_ext, n)*problem.get_outflow_measure_form())
            # DDN_triggered = assemble(min_value(Constant(0.), inner(u_ext, n))*problem.get_outflow_measure_form())
            # print('DDN: u_ext*n dSout = ', u_ext_in)
            # print('DDN: negative part of u_ext*n dSout = ', DDN_triggered)

            # assemble matrix (it depends on solution)
            self.tc.start('assembleA1')
            assemble(a1_change, tensor=A1_change)  # assembling into existing matrix is faster than assembling new one
            A1 = A1_const.copy()  # we dont want to change A1_const
            A1.axpy(1, A1_change, True)
            self.tc.end('assembleA1')
            self.tc.start('assembleA1stab')
            if self.stabilize and not self.use_full_SUPG:
                assemble(a1_stab, tensor=A1_stab)  # assembling into existing matrix is faster than assembling new one
                A1.axpy(1, A1_stab, True)
            self.tc.end('assembleA1stab')

            # Compute tentative velocity step
            begin("Computing tentative velocity")
            self.tc.start('rhs')
            b = assemble(L1)
            self.tc.end('rhs')
            self.tc.start('applybc1')
            [bc.apply(A1, b) for bc in bcu]
            self.tc.end('applybc1')
            try:
                self.tc.start('solve 1')
                self.solver_vel_tent.solve(A1, u_.vector(), b)
                self.tc.end('solve 1')
                if save_this_step:
                    self.tc.start('saveVel')
                    problem.save_vel(True, u_, t)
                    self.tc.end('saveVel')
                if save_this_step and not onlyVel:
                    problem.save_div(True, u_)
                problem.compute_err(True, u_, t)
                problem.compute_div(True, u_)
            except RuntimeError as inst:
                problem.report_fail(t)
                return 1
            end()

            # DDN debug
            # u_ext_in = assemble(inner(u_, n)*problem.get_outflow_measure_form())
            # DDN_triggered = assemble(min_value(Constant(0.), inner(u_, n))*problem.get_outflow_measure_form())
            # print('DDN: u_tent*n dSout = ', u_ext_in)
            # print('DDN: negative part of u_tent*n dSout = ', DDN_triggered)

            if self.useRotationScheme:
                begin("Computing tentative pressure")
            else:
                begin("Computing pressure")
            if self.forceOutflow and problem.can_force_outflow:
                out = problem.compute_outflow(u_)
                info('Tentative outflow: %f' % out)
                n_o = -problem.last_inflow-out
                info('Needed outflow: %f' % n_o)
                need_outflow.assign(n_o)
            self.tc.start('rhs')
            b = assemble(L2)
            self.tc.end('rhs')
            self.tc.start('applybcP')
            [bc.apply(A2, b) for bc in bcp]
            if self.bc in ['nullspace', 'nullspace_s']:
                self.null_space.orthogonalize(b)
            self.tc.end('applybcP')
            try:
                self.tc.start('solve 2')
                if self.bc == 'lagrange':
                    self.solver_p.solve(A2, p_QL.vector(), b)
                else:
                    self.solver_p.solve(A2, p_.vector(), b)
                self.tc.end('solve 2')
            except RuntimeError as inst:
                problem.report_fail(t)
                return 1
            if self.useRotationScheme:
                foo = Function(self.Q)
                if self.bc == 'lagrange':
                    fa.assign(pQ, p_QL.sub(0))
                    foo.assign(pQ + p0)
                else:
                    foo.assign(p_+p0)
                problem.averaging_pressure(foo)
                if save_this_step and not onlyVel:
                    problem.save_pressure(True, foo)
            else:
                if self.bc == 'lagrange':
                    fa.assign(pQ, p_QL.sub(0))
                    problem.averaging_pressure(pQ)
                    if save_this_step and not onlyVel:
                        problem.save_pressure(False, pQ)
                else:
                    # we do not want to change p=0 on outflow, it conflicts with do-nothing conditions
                    foo = Function(self.Q)
                    foo.assign(p_)
                    problem.averaging_pressure(foo)
                    if save_this_step and not onlyVel:
                        problem.save_pressure(False, foo)
            end()

            begin("Computing corrected velocity")
            self.tc.start('rhs')
            b = assemble(L3)
            self.tc.end('rhs')
            if not self.B:
                self.tc.start('applybc3')
                [bc.apply(A3, b) for bc in bcu]
                self.tc.end('applybc3')
            try:
                self.tc.start('solve 3')
                self.solver_vel_cor.solve(A3, u_cor.vector(), b)
                self.tc.end('solve 3')
                problem.compute_err(False, u_cor, t)
                problem.compute_div(False, u_cor)
            except RuntimeError as inst:
                problem.report_fail(t)
                return 1
            if save_this_step:
                self.tc.start('saveVel')
                problem.save_vel(False, u_cor, t)
                self.tc.end('saveVel')
            if save_this_step and not onlyVel:
                problem.save_div(False, u_cor)
            end()

            # DDN debug
            # u_ext_in = assemble(inner(u_cor, n)*problem.get_outflow_measure_form())
            # DDN_triggered = assemble(min_value(Constant(0.), inner(u_cor, n))*problem.get_outflow_measure_form())
            # print('DDN: u_cor*n dSout = ', u_ext_in)
            # print('DDN: negative part of u_cor*n dSout = ', DDN_triggered)

            if self.useRotationScheme:
                begin("Rotation scheme pressure correction")
                self.tc.start('rhs')
                b = assemble(L4)
                self.tc.end('rhs')
                try:
                    self.tc.start('solve 4')
                    self.solver_rot.solve(A4, p_mod.vector(), b)
                    self.tc.end('solve 4')
                except RuntimeError as inst:
                    problem.report_fail(t)
                    return 1
                problem.averaging_pressure(p_mod)
                if save_this_step and not onlyVel:
                    problem.save_pressure(False, p_mod)
                end()

            # compute functionals (e. g. forces)
            problem.compute_functionals(u_cor,
                                        p_mod if self.useRotationScheme else (pQ if self.bc == 'lagrange' else p_), t)

            # Move to next time step
            self.tc.start('next')
            u1.assign(u0)
            u0.assign(u_cor)
            u_.assign(u_cor)  # use corretced velocity as initial guess in first step

            if self.useRotationScheme:
                p0.assign(p_mod)
            else:
                if self.bc == 'lagrange':
                    p0.assign(pQ)
                else:
                    p0.assign(p_)

            t = round(t + dt, 6)  # round time step to 0.000001
            step += 1
            self.tc.end('next')

        info("Finished: Incremental pressure correction scheme n. 1")
        problem.report()
        return 0
Example #7
0
class Solver(object):

    """
    First order FEM with CR elements.

    Nonconforming CR elements give lower bounds for eigenvalues
    after apropriate postprocessing is applied.
    """

    def __init__(self, mesh, num, denom, method="CR"):
        """ Assemble matrices and cache matrices. """
        self.V = FunctionSpace(mesh, method, 1)
        # save coordinates of DOFs
        self.dofs = np.reshape(self.V.dofmap().tabulate_all_coordinates(mesh),
                               (-1, 2))
        u = TrialFunction(self.V)
        v = TestFunction(self.V)
        self.boundary = MeshFunction("size_t", mesh, 1)
        # assemble matrices
        a = inner(grad(u), grad(v)) * dx
        b = u * v * dx
        self.A = uBLASSparseMatrix()
        self.A = assemble(a, tensor=self.A)
        self.A.compress()
        self.B = uBLASSparseMatrix()
        self.B = assemble(b, tensor=self.B)
        self.B.compress()
        size = mesh.size(2)
        # cell diameter calculation
        self.H2 = (num ** 2 + denom ** 2) * 1.0 / size
        # print "Theoretical cell diameter: ", sqrt(self.H2)
        # print "FEniCS calculated: ", mesh.hmax()

        # Matrices have rational entries. We can rescale to get integers
        # and save as scipy matrices
        scaleB = 6.0*size/num/denom
        self.scaleB = scaleB
        self.B *= scaleB
        r, c, val = self.B.data()
        val = np.round(val)
        self.B = sps.csr_matrix((val, c, r))
        # check if B is diagonal
        assert len(self.B.diagonal()) == self.B.nnz
        # find B inverse
        self.Binv = sps.csr_matrix((1.0/val, c, r))
        scaleA = 1.0*num*denom/2
        self.scaleA = scaleA
        self.A *= scaleA
        r, c, val = self.A.data()
        self.A = sps.csr_matrix((np.round(val), c, r))

        self.scale = scaleA/scaleB
        print 'scaling A by: ', scaleA
        print 'scaling B by: ', scaleB
        print 'eigenvalues scale by:', self.scale

    def solve(self, dirichlet=lambda x: False, plotted=None):
        """
        Find eigenvalues given a boundary condition function.

        dirichlet is a function returning True if x is on Dirichlet BC.
        """
        self.boundary.set_all(0)
        d = Dirichlet()
        d.init(dirichlet)
        d.mark(self.boundary, 1)
        if plotted is not None:
            plotted.plot(self.boundary)
        #    plotted.write_png()

        # indices for non-Dirichlet rows/columns
        indices = np.nonzero(np.apply_along_axis(
            lambda x: not dirichlet(x), 1, self.dofs))[0]
        # remove Dirichlet rows and columns from A
        self.AA = (self.A[indices, :]).tocsc()[:, indices]
        # remove Dirichlet rows and columns from B
        self.BB = (self.B[indices, :]).tocsc()[:, indices]
        self.BBinv = (self.Binv[indices, :]).tocsc()[:, indices]
        # solve using scipy
        eigs, eigfs = ssl.eigsh(self.AA, k=2, M=self.BB, sigma=0, which='LM')
        self.raweigs = [eigs[0], eigs[1]]
        eig = eigs[0]
        # turn eigf into a function
        eigf = np.array(eigfs[:, 0]).flatten()
        # we were solving only for some dofs, rest is 0
        # u = Function(self.V)
        # u.vector()[:] = 0
        # u.vector()[indices] = eigf
        # find algebraic residual
        # both L2 norm of u and B norm of eigf should equal 1
        # print assemble(u*u*dx), self.BB.dot(eigf).dot(eigf)
        res = self.AA.dot(eigf) - eig * self.BB.dot(eigf)
        resnorm = np.sqrt(self.BBinv.dot(res).dot(res))
        self.residual = [resnorm, 0]
        # apply Carstensen-Gedicke transformations to eig
        #
        # kappa^2 less than 0.1932
        eig = (eig - resnorm) / (1 + 0.1932 * (eig - resnorm) *
                                 self.H2 / self.scale)
        # scale back the eigenvalue
        eig = eig/self.scale
        # find residual for the second eigenvalue (for gap calculations)
        eigf = np.array(eigfs[:, 1]).flatten()
        res = self.AA.dot(eigf) - eigs[1] * self.BB.dot(eigf)
        resnorm = np.sqrt(self.BBinv.dot(res).dot(res))
        self.residual[1] = resnorm
        # return (eig, u)  # pair (eigenvalue,eigenfunctions)
        return (eig, None)  # pair (eigenvalue,eigenfunctions)
Example #8
0
def test_block_size_real(mesh):
    mesh = UnitIntervalMesh(12)
    V = FiniteElement('DG', mesh.ufl_cell(), 0)
    R = FiniteElement('R', mesh.ufl_cell(), 0)
    X = FunctionSpace(mesh, V * R)
    assert X.dofmap().index_map.block_size == 1
Example #9
0
def test_entity_dofs(mesh):
    """Test that num entity dofs is correctly wrapped to dolfin::DofMap"""
    V = FunctionSpace(mesh, ("CG", 1))
    assert V.dofmap().num_entity_dofs(0) == 1
    assert V.dofmap().num_entity_dofs(1) == 0
    assert V.dofmap().num_entity_dofs(2) == 0

    V = VectorFunctionSpace(mesh, ("CG", 1))
    assert V.dofmap().num_entity_dofs(0) == 2
    assert V.dofmap().num_entity_dofs(1) == 0
    assert V.dofmap().num_entity_dofs(2) == 0

    V = FunctionSpace(mesh, ("CG", 2))
    assert V.dofmap().num_entity_dofs(0) == 1
    assert V.dofmap().num_entity_dofs(1) == 1
    assert V.dofmap().num_entity_dofs(2) == 0

    V = FunctionSpace(mesh, ("CG", 3))
    assert V.dofmap().num_entity_dofs(0) == 1
    assert V.dofmap().num_entity_dofs(1) == 2
    assert V.dofmap().num_entity_dofs(2) == 1

    V = FunctionSpace(mesh, ("DG", 0))
    assert V.dofmap().num_entity_dofs(0) == 0
    assert V.dofmap().num_entity_dofs(1) == 0
    assert V.dofmap().num_entity_dofs(2) == 1

    V = FunctionSpace(mesh, ("DG", 1))
    assert V.dofmap().num_entity_dofs(0) == 0
    assert V.dofmap().num_entity_dofs(1) == 0
    assert V.dofmap().num_entity_dofs(2) == 3

    V = VectorFunctionSpace(mesh, ("CG", 1))

    # Note this numbering is dependent on FFC and can change This test
    # is here just to check that we get correct numbers mapped from ufc
    # generated code to dolfin
    for i, cdofs in enumerate([[0, 3], [1, 4], [2, 5]]):
        dofs = V.dofmap().tabulate_entity_dofs(0, i)
        assert all(d == cd for d, cd in zip(dofs, cdofs))
Example #10
0
def visualize_form(A, b, c):
    '''
    Study the quadratic form phi(x) = 0.5*x*A*x - b*x + c
        - definiteness
        - extrema
        - constraints
    '''

    # Iput check
    assert isinstance(A, np.ndarray)
    assert A.shape == (2, 2)
    assert isinstance(b, np.ndarray)
    assert b.shape == (2, )
    assert isinstance(c, (float, int))

    # Decide symmetry
    is_sym = la.norm(A - A.T) < _TOL_
    is_skew = la.norm(A + A.T) < _TOL_

    print 'A is symmetric', is_sym, 'A is skew', is_skew

    # Decide definitness from eigenvalues
    vals, vecs = la.eig(A)
    vecs = vecs.T        # Eigenvectors are now row, easies for iter access
    print 'A eigenvalues', vals
    print 'A eigenvectors', vecs[0], vecs[1]

    is_singular, is_posdef, is_negdef, is_indef = False, False, False, False
    # Don't know about definiteness of forms from A with complex eigenvalues
    if not vals.dtype == 'complex128':
        if np.any(np.abs(vals) < _TOL_):
            is_singular = True
        else:
            if np.all(vals > 0):
                is_posdef = True
            elif np.all(vals < 0):
                is_negdef = True
            elif abs(np.sum(np.sign(vals))) < _TOL_:
                is_indef = True

        print 'A is singular', is_singular,\
              'A is posdef', is_posdef,\
              'A is negdef', is_negdef,\
              'A is indef', is_indef

    # lambda for computing the quadratic form value, and the gradient
    phi = lambda x: 0.5*x.dot(A.dot(x)) - b.dot(x) + c
    grad_phi = lambda x: A.dot(x) - b

    # Width for grids
    w = 3

    # If the system is not singular we can compute Ax=b (straight forward)
    # and use x to decide visualiz. grid width
    if not is_singular:
        if is_sym:
            x = la.solve(A, b)
        elif not is_skew:
            # If the system is not symmetric then the minima is sought
            # via the `symmetrized` system
            x = la.solve(0.5*(A + A.T), b)
        else:
            # For the skew system the above gives zero matrix and the gradient
            # is just b -- the quadratic surface is just a plane perpend to b
            x = b

    else:
        # For the singular system only constrained minimization makes sense
        # The functional is modified to include costraint that x is
        # perpendicular to nullspace vector

        # Get eigenvalues and eigenvectors of A
        # Find the eigenvector corresponding to 0. Is in the nullspace
        z = None
        for vec in vecs:
            if abs(vec.dot(A.dot(vec.T))) < _TOL_:
                z = vec

        # Build the system that is yields extrema of
        # phi(x) + constraint of orthogonality to z
        AA = np.zeros((3, 3))
        AA[:2, :2] = A
        AA[2, :2] = z
        AA[:2, 2] = z.T

        bb = np.zeros(3)
        bb[:2] = b

        xx = la.solve(AA, bb)

        # Extract x and the multiplier
        x = xx[:2]
        lmbda = xx[-1]
        print 'Multiplier', lmbda

        # lambda for computing surface whose contours are lines that satisfy
        # the constraint
        psi = lambda x: x.dot(z)

        # These are poits on the line that satisfies the constraint and the
        # solution passed through it
        cs = lambda s: x + np.array([z[1], -z[0]])*s

        # Perperndicular line to cs
        cs_perp = lambda s: x + z*s

        # Chi is the constrained functional whose value we want to plot
        chi = lambda x: phi(x[:2]) + x[-1]*psi(x[:2])

        mesh = BoxMesh(x[0]-w, x[1]-w, lmbda-w, x[0]+w, x[1]+w, lmbda+w,
                       10, 10, 10)
        V = FunctionSpace(mesh, 'CG', 1)
        dofs_x = V.dofmap().tabulate_all_coordinates(mesh).reshape((-1, 3))

        values = np.array([chi(dof_x) for dof_x in dofs_x])

        # The plot should show that the solution has chosen a plane at certain
        # height = lagrange multiplier, that has normal = z
        chi_f = Function(V)
        chi_f.vector().set_local(values)
        chi_f.vector().apply('')
        plot(chi_f, interactive=True)

        print 'Saved constrained functional to chi.pvd'
        File('chi.pvd') << chi_f

        # Generate data for plot showing the quadratic function values over
        # cs line
        s_array = np.linspace(-2, 2, 1000)
        PHI_cs = np.zeros_like(s_array)
        for i, s in enumerate(s_array):
            P = cs(s)
            PHI_cs[i] = phi(P)

        # Add three contour plot x, y @ a*
        X, Y = np.meshgrid(np.linspace(x[0]-w, x[0]+w, 100),
                           np.linspace(x[1]-w, x[1]+w, 100))
        PHI_a = np.zeros_like(X)
        for i in range(PHI_a.shape[0]):
            for j in range(PHI_a.shape[1]):
                P = np.array([X[i, j], Y[i, j], lmbda])
                PHI_a[i, j] = chi(P)

        plt.figure()
        plt.suptitle('lambda constant')
        plt.pcolor(X, Y, PHI_a)
        plt.contour(X, Y, PHI_a, 10, colors='k')
        plt.plot(x[0], x[1], 'rx')

        # #                        x, a @ y* eval chi
        X, LMBDA = np.meshgrid(np.linspace(x[0]-w, x[0]+w, 100),
                               np.linspace(lmbda-w, lmbda+w, 100))
        PHI_y = np.zeros_like(X)
        for i in range(PHI_y.shape[0]):
            for j in range(PHI_y.shape[1]):
                P = np.array([X[i, j], x[1], LMBDA[i, j]])
                PHI_y[i, j] = chi(P)

        plt.figure()
        plt.suptitle('x[1] constant')
        plt.pcolor(X, LMBDA, PHI_y)
        plt.contour(X, LMBDA, PHI_y, 10, colors='k')
        plt.plot(x[0], lmbda, 'rx')

        #                        y, a @ x*
        Y, LMBDA = np.meshgrid(np.linspace(x[1]-w, x[1]+w, 100),
                               np.linspace(lmbda-w, lmbda+w, 100))
        PHI_x = np.zeros_like(Y)
        for i in range(PHI_x.shape[0]):
            for j in range(PHI_x.shape[1]):
                P = np.array([x[0], Y[i, j], LMBDA[i, j]])
                PHI_x[i, j] = chi(P)

        plt.figure()
        plt.suptitle('x[0] constant')
        plt.pcolor(Y, LMBDA, PHI_x)
        plt.contour(Y, LMBDA, PHI_x, 10, colors='k')
        plt.plot(x[1], lmbda, 'rx')

        # Slice through selected nullspace line and perpr to it
        s = np.linspace(-2, 2, 100)
        S, T = np.meshgrid(s, s)
        Z = np.zeros_like(S)
        Z_perp = np.zeros_like(S)
        for i in range(len(s)):
            for j in range(len(s)):
                P = np.r_[cs(s[j]), lmbda+s[i]]
                # print P, psi(cs(s[j])), phi(cs(s[j])), chi(cs(s[j]))
                # On the line phi is chi!
                Z[i, j] = chi(P)

                P = np.r_[cs_perp(s[j]), lmbda+s[i]]
                Z_perp[i, j] = chi(P)

        plt.figure()
        plt.suptitle('Nullspace slice')
        pcolor = plt.pcolor(S, T, Z)
        plt.colorbar(pcolor)
        plt.contour(S, T, Z, 10, colors='k')

        plt.figure()
        plt.suptitle('Nullspace perp. slice')
        plt.pcolor(S, T, Z_perp)
        plt.contour(S, T, Z_perp, 10, colors='k')


    print 'Potential extremum', x

    X, Y = np.meshgrid(np.linspace(x[0]-w, x[0]+w, 100),
                       np.linspace(x[1]-w, x[1]+w, 100))

    PHI = np.zeros_like(X)
    GRAD_PHI = np.zeros((X.shape[0], X.shape[1], 2))

    # Generate values of qudratic function and its gradient
    for i in range(PHI.shape[0]):
        for j in range(PHI.shape[1]):
            P = np.array([X[i, j], Y[i, j]])
            PHI[i, j] = phi(P)
            GRAD_PHI[i, j, :] = grad_phi(P)

    # Plot the quadratic form as 3d + the extreme point
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    ax.plot_surface(X, Y, PHI)
    ax.plot([x[0]], [x[1]], [phi(x)], 'rx')
    # ------------------------------------------------------------------------

    # Plot contours of the quadratic function
    plt.figure()
    plt.pcolor(X, Y, PHI)
    plt.contour(X, Y, PHI, 10, colors='k')
    plt.plot(x[0], x[1], 'rx')

    # Plot eigenvectors
    e0 = x + vecs[0]
    plt.plot(np.linspace(x[0], e0[0], 10), np.linspace(x[1], e0[1], 10), 'k')
    plt.text(0.5*x[0] + 0.5*e0[0], 0.5*x[1] + 0.5*e0[1], str(vals[0]))
    e1 = x + vecs[1]
    plt.plot(np.linspace(x[0], e1[0], 10), np.linspace(x[1], e1[1], 10), 'k')
    plt.text(0.5*x[0] + 0.5*e1[0], 0.5*x[1] + 0.5*e1[1], str(vals[1]))

    # For singular system add 'constraint lines'
    if is_singular:
        PSI = np.zeros_like(X)
        for i in range(PSI.shape[0]):
            for j in range(PSI.shape[1]):
                P = np.array([X[i, j], Y[i, j]])
                PSI[i, j] = psi(P)

        plt.contour(X, Y, PSI, 10, colors='g')
    # ------------------------------------------------------------------------

    # Finally plot curves tangent to gradient (better for  visualiz. than
    # quiver)
    plt.figure()
    plt.streamplot(X, Y, GRAD_PHI[:, :, 0], GRAD_PHI[:, :, 1])
    plt.plot(x[0], x[1], 'rx')
    if is_singular:
        plt.contour(X, Y, PSI, 10, colors='g')
    # ------------------------------------------------------------------------

    # Add the plot of phi(cs) to check that found point is the extremum
    if is_singular:
        plt.figure()
        plt.plot(s_array, PHI_cs, 'b')
        plt.plot([0], [phi(cs(0))], 'rx')

        plt.figure()
        plt.plot(s_array, np.array([chi(np.array([x[0], x[1], s])) for s
                                    in s_array]), label='via min')
        plt.plot(s_array, np.array([chi(np.array([x[0]+0.01, x[1]-0.01, s])) for s
                                    in s_array]), label='no min')
        plt.legend()
    # ------------------------------------------------------------------------

    plt.show()

    # For positve def and negdef forms see that we found minima and maxima
    # respectively
    # Something similar could be done for cs with singular system but the plot
    # tells the same story
    if is_posdef or is_negdef or is_indef:
        phi_s = np.array([PHI[random_index(PHI.shape[0], PHI.shape[1])]
                          for i in range(1000)])
        phi_s -= phi(x)

        if is_posdef:
            assert np.all(phi_s > 0)
        elif is_negdef:
            assert np.all(phi_s < 0)
Example #11
0
    def test_multiplication(self):
        print(
            '== testing multiplication of system matrix for problem of weighted projection ===='
        )

        for dim, pol_order in itertools.product([2, 3], [1, 2]):
            N = 2  # no. of elements
            print('dim={0}, pol_order={1}, N={2}'.format(dim, pol_order, N))

            # creating MESH and defining MATERIAL
            if dim == 2:
                mesh = UnitSquareMesh(N, N)
                m = Expression("1+10*16*x[0]*(1-x[0])*x[1]*(1-x[1])",
                               degree=4)  # material coefficients
            elif dim == 3:
                mesh = UnitCubeMesh(N, N, N)
                m = Expression("1+10*16*x[0]*(1-x[0])*(1-x[1])*x[2]",
                               degree=1)  # material coefficients

            mesh.coordinates()[:] += 0.1 * np.random.random(
                mesh.coordinates().shape)  # mesh perturbation

            V = FunctionSpace(mesh, "CG", pol_order)  # original FEM space
            W = FunctionSpace(mesh, "DG",
                              2 * (pol_order - 1))  # double-grid space

            print('assembling local matrices for DoGIP...')
            Bhat = get_Bhat(
                dim, pol_order, problem=1
            )  # interpolation between V on W on a reference element
            AT_dogip = get_A_T(m, V, W, problem=1)

            dofmapV = V.dofmap()

            def system_multiplication_DoGIP(AT_dogip, Bhat, u_vec):
                # matrix-vector mutliplication in DoGIP
                Au = np.zeros_like(u_vec)
                for ii, cell in enumerate(cells(mesh)):
                    ind = dofmapV.cell_dofs(ii)  # local to global map
                    Bu = Bhat.dot(u_vec[ind])
                    ABu = np.einsum('rsj,sj->rj', AT_dogip[ii], Bu)
                    Au[ind] += np.einsum('rjl,rj->l', Bhat, ABu)
                return Au

            print('assembling system matrix for FEM')
            u, v = TrialFunction(V), TestFunction(V)
            Asp = assemble(m * inner(grad(u), grad(v)) * dx,
                           tensor=EigenMatrix())
            Asp = Asp.sparray()  # sparse FEM matrix

            print('multiplication...')
            ur = Function(V)  # creating random vector
            ur_vec = 10 * np.random.random(V.dim())
            ur.vector().set_local(ur_vec)

            Au_DoGIP = system_multiplication_DoGIP(
                AT_dogip, Bhat, ur_vec)  # DoGIP multiplication
            Auex = Asp.dot(ur_vec)  # FEM multiplication with sparse matrix

            # testing the difference between DoGIP and FEniCS
            self.assertAlmostEqual(0, np.linalg.norm(Auex - Au_DoGIP))
            print('...ok')
Example #12
0
class Solver(gs.GeneralSolver):
    def __init__(self, args, tc, metadata):
        gs.GeneralSolver.__init__(self, args, tc, metadata)
        self.metadata['hasTentativeV'] = True

        self.solver_vel_tent = None
        self.solver_vel_cor = None
        self.solver_p = None
        self.solver_rot = None
        self.null_space = None

        # input parameters
        self.forceOutflow = args.fo
        self.useLaplace = args.laplace
        self.use_full_SUPG = args.cs
        self.bcv = 'NOT' if self.useLaplace else args.bcv
        if self.bcv == 'CDN':
            info('Using classical do nothing condition (Tn=0).')
        if self.bcv == 'DDN':
            info(
                'Using directional do nothing condition (Tn=0.5*negative(u.n)u).'
            )
        if self.bcv == 'LAP':
            info('Using laplace neutral condition (grad(u)n=0).')
        self.stabCoef = args.stab
        self.stabilize = (args.stab > DOLFIN_EPS)
        if self.stabilize:
            if self.use_full_SUPG:
                info(
                    'Used consistent streamline-diffusion stabilization with coef.: %f'
                    % args.stab)
            else:
                info(
                    'Used non-consistent streamline-diffusion stabilization with coef.: %f'
                    % args.stab)
        else:
            info('No stabilization used.')
        self.solvers = args.solvers
        self.useRotationScheme = args.r
        self.metadata['hasTentativeP'] = self.useRotationScheme

    def __str__(self):
        return 'ipcs1 - incremental pressure correction scheme with nonlinearity treated by Adam-Bashword + ' \
               'Crank-Nicolson and viscosity term treated semi-explicitly (Crank-Nicholson)'

    @staticmethod
    def setup_parser_options(parser):
        gs.GeneralSolver.setup_parser_options(parser)
        parser.add_argument('--stab',
                            help='Use stabilization (positive constant)',
                            type=float,
                            default=0.)
        parser.add_argument('--cs',
                            help='Use consistent SUPG stabilisation.',
                            action='store_true')
        parser.add_argument('--cbc_tau',
                            help='Use simpler cbcflow parameter for SUPG',
                            action='store_true')
        parser.add_argument('-r',
                            help='Use rotation scheme',
                            action='store_true')
        parser.add_argument('-B',
                            help='Use no BC in correction step',
                            action='store_true')
        parser.add_argument('-s',
                            '--solvers',
                            help='Solvers',
                            choices=['direct', 'krylov'],
                            default='krylov')
        parser.add_argument('-b',
                            '--bc',
                            help='Pressure boundary condition mode',
                            choices=['outflow', 'nullspace'],
                            default='outflow')
        # 'outflow' should be used with direct solvers (cannot LU-decompose singular matrix)
        parser.add_argument(
            '--prv1',
            help='relative tentative velocity Krylov solver precision',
            type=int,
            default=6)
        parser.add_argument(
            '--pav1',
            help='absolute tentative velocity Krylov solver precision',
            type=int,
            default=10)
        parser.add_argument('--pap',
                            help='pressure Krylov solver absolute precision',
                            type=int,
                            default=6)
        parser.add_argument('--prp',
                            help='pressure Krylov solver relative precision',
                            type=int,
                            default=10)
        parser.add_argument(
            '--precV',
            help='Preconditioner for tentative velocity GMRES solver',
            type=str,
            default='sor')
        parser.add_argument(
            '--precVC',
            help='Preconditioner for corrected velocity CG solver',
            type=str,
            default='sor')
        parser.add_argument(
            '--precP',
            help='Preconditioner for 2nd step solver (Poisson)',
            choices=['hypre_amg', 'ilu', 'sor'],
            default='sor')
        parser.add_argument('--solP',
                            help='2nd step solver (Poisson)',
                            type=str,
                            default='cg')
        # choices=['cg', 'gmres', 'richardson', 'tfqmr', ...]
        parser.add_argument('--Prestart',
                            help='2nd step solver GMRES restart',
                            type=int,
                            default=-1)
        parser.add_argument('--Vrestart',
                            help='1st step solver GMRES restart',
                            type=int,
                            default=-1)
        parser.add_argument('--fo',
                            help='Force Neumann outflow boundary for pressure',
                            action='store_true')
        parser.add_argument('--laplace',
                            help='Use laplace(u) instead of div(symgrad(u))',
                            action='store_true')
        parser.add_argument(
            '--bcv',
            help='Outflow BC for velocity (with stress formulation)',
            choices=['CDN', 'DDN', 'LAP'],
            default='CDN')
        parser.add_argument(
            '--ema',
            help='Use EMA conserving scheme for convection term',
            action='store_true')
        # described in Charnyi, Heister, Olshanskii, Rebholz:
        # "On conservation laws of Navier-Stokes Galerkin discretizations" (2016)
        # it seems that it does not work with projection methods (probably because of dropping div u = 0 constraint)
        # not documented in readme

    def solve(self, problem):
        self.problem = problem
        doSave = problem.doSave
        save_this_step = False
        onlyVel = problem.saveOnlyVel
        dt = self.metadata['dt']

        nu = Constant(self.problem.nu)
        self.tc.init_watch('init',
                           'Initialization',
                           True,
                           count_to_percent=False)
        self.tc.init_watch('rhs',
                           'Assembled right hand side',
                           True,
                           count_to_percent=True)
        self.tc.init_watch('applybc1',
                           'Applied velocity BC 1st step',
                           True,
                           count_to_percent=True)
        self.tc.init_watch('applybc3',
                           'Applied velocity BC 3rd step',
                           True,
                           count_to_percent=True)
        self.tc.init_watch('applybcP',
                           'Applied pressure BC or othogonalized rhs',
                           True,
                           count_to_percent=True)
        self.tc.init_watch('assembleMatrices',
                           'Initial matrix assembly',
                           False,
                           count_to_percent=True)
        self.tc.init_watch('solve 1',
                           'Running solver on 1st step',
                           True,
                           count_to_percent=True)
        self.tc.init_watch('solve 2',
                           'Running solver on 2nd step',
                           True,
                           count_to_percent=True)
        self.tc.init_watch('solve 3',
                           'Running solver on 3rd step',
                           True,
                           count_to_percent=True)
        self.tc.init_watch('solve 4',
                           'Running solver on 4th step',
                           True,
                           count_to_percent=True)
        self.tc.init_watch('assembleA1',
                           'Assembled A1 matrix (without stabiliz.)',
                           True,
                           count_to_percent=True)
        self.tc.init_watch('assembleA1stab',
                           'Assembled A1 stabilization',
                           True,
                           count_to_percent=True)
        self.tc.init_watch('next',
                           'Next step assignments',
                           True,
                           count_to_percent=True)
        self.tc.init_watch('saveVel', 'Saved velocity', True)

        self.tc.start('init')

        # Define function spaces (P2-P1)
        mesh = self.problem.mesh
        self.V = VectorFunctionSpace(mesh, "Lagrange", 2)  # velocity
        self.Q = FunctionSpace(mesh, "Lagrange", 1)  # pressure
        self.PS = FunctionSpace(
            mesh, "Lagrange", 2)  # partial solution (must be same order as V)
        self.D = FunctionSpace(mesh, "Lagrange",
                               1)  # velocity divergence space

        problem.initialize(self.V, self.Q, self.PS, self.D)

        # Define trial and test functions
        u = TrialFunction(self.V)
        v = TestFunction(self.V)
        p = TrialFunction(self.Q)
        q = TestFunction(self.Q)

        n = FacetNormal(mesh)
        I = Identity(find_geometric_dimension(u))

        # Initial conditions: u0 velocity at previous time step u1 velocity two time steps back p0 previous pressure
        [u1, u0, p0] = self.problem.get_initial_conditions([{
            'type': 'v',
            'time': -dt
        }, {
            'type': 'v',
            'time': 0.0
        }, {
            'type': 'p',
            'time': 0.0
        }])

        u_ = Function(self.V)  # current tentative velocity
        u_cor = Function(self.V)  # current corrected velocity
        p_ = Function(
            self.Q
        )  # current pressure or pressure help function from rotation scheme
        p_mod = Function(
            self.Q)  # current modified pressure from rotation scheme

        # Define coefficients
        k = Constant(self.metadata['dt'])
        f = Constant((0, 0, 0))

        # Define forms
        # step 1: Tentative velocity, solve to u_
        u_ext = 1.5 * u0 - 0.5 * u1  # extrapolation for convection term

        # Stabilisation
        h = CellSize(mesh)
        if self.args.cbc_tau:
            # used in Simula cbcflow project
            tau = Constant(self.stabCoef) * h / (sqrt(inner(u_ext, u_ext)) + h)
        else:
            # proposed in R. Codina: On stabilized finite element methods for linear systems of
            # convection-diffusion-reaction equations.
            tau = Constant(self.stabCoef) * k * h**2 / (
                2 * nu * k + k * h * sqrt(DOLFIN_EPS + inner(u_ext, u_ext)) +
                h**2)
            # DOLFIN_EPS is added because of FEniCS bug that inner(u_ext, u_ext) can be negative when u_ext = 0

        if self.use_full_SUPG:
            v1 = v + tau * 0.5 * dot(grad(v), u_ext)
            parameters['form_compiler']['quadrature_degree'] = 6
        else:
            v1 = v

        def nonlinearity(function):
            if self.args.ema:
                return 2 * inner(dot(sym(grad(function)), u_ext), v1
                                 ) * dx + inner(div(function) * u_ext, v1) * dx
            else:
                return inner(dot(grad(function), u_ext), v1) * dx

        def diffusion(fce):
            if self.useLaplace:
                return nu * inner(grad(fce), grad(v1)) * dx
            else:
                form = inner(nu * 2 * sym(grad(fce)), sym(grad(v1))) * dx
                if self.bcv == 'CDN':
                    return form
                if self.bcv == 'LAP':
                    return form - inner(nu * dot(grad(fce).T, n), v1
                                        ) * problem.get_outflow_measure_form()
                if self.bcv == 'DDN':
                    return form  # additional term must be added to non-constant part

        def pressure_rhs():
            if self.args.bc == 'outflow':
                return inner(p0, div(v1)) * dx
            else:
                return inner(p0, div(v1)) * dx - inner(
                    p0 * n, v1) * problem.get_outflow_measure_form()

        a1_const = (1. / k) * inner(u, v1) * dx + diffusion(0.5 * u)
        a1_change = nonlinearity(0.5 * u)
        if self.bcv == 'DDN':
            # does not penalize influx for current step, only for the next one
            # this can lead to oscilation:
            # DDN correct next step, but then u_ext is OK so in next step DDN is not used, leading to new influx...
            # u and u_ext cannot be switched, min_value is nonlinear function
            a1_change += -0.5 * min_value(Constant(0.), inner(
                u_ext, n)) * inner(u, v1) * problem.get_outflow_measure_form()
            # NT works only with uflacs compiler

        L1 = (1. / k) * inner(u0, v1) * dx - nonlinearity(
            0.5 * u0) - diffusion(0.5 * u0) + pressure_rhs()
        if self.bcv == 'DDN':
            L1 += 0.5 * min_value(0., inner(u_ext, n)) * inner(
                u0, v1) * problem.get_outflow_measure_form()

        # Non-consistent SUPG stabilisation
        if self.stabilize and not self.use_full_SUPG:
            # a1_stab = tau*inner(dot(grad(u), u_ext), dot(grad(v), u_ext))*dx
            a1_stab = 0.5 * tau * inner(dot(grad(u), u_ext), dot(
                grad(v), u_ext)) * dx(None, {'quadrature_degree': 6})
            # optional: to use Crank Nicolson in stabilisation term following change of RHS is needed:
            # L1 += -0.5*tau*inner(dot(grad(u0), u_ext), dot(grad(v), u_ext))*dx(None, {'quadrature_degree': 6})

        outflow_area = Constant(problem.outflow_area)
        need_outflow = Constant(0.0)
        if self.useRotationScheme:
            # Rotation scheme
            F2 = inner(grad(p), grad(q)) * dx + (1. / k) * q * div(u_) * dx
        else:
            # Projection, solve to p_
            if self.forceOutflow and problem.can_force_outflow:
                info('Forcing outflow.')
                F2 = inner(grad(p - p0),
                           grad(q)) * dx + (1. / k) * q * div(u_) * dx
                for m in problem.get_outflow_measures():
                    F2 += (1. / k) * (1. / outflow_area) * need_outflow * q * m
            else:
                F2 = inner(grad(p - p0),
                           grad(q)) * dx + (1. / k) * q * div(u_) * dx
        a2, L2 = system(F2)

        # step 3: Finalize, solve to u_
        if self.useRotationScheme:
            # Rotation scheme
            F3 = (1. / k) * inner(u - u_, v) * dx + inner(grad(p_), v) * dx
        else:
            F3 = (1. / k) * inner(u - u_, v) * dx + inner(grad(p_ - p0),
                                                          v) * dx
        a3, L3 = system(F3)

        if self.useRotationScheme:
            # Rotation scheme: modify pressure
            F4 = (p - p0 - p_ + nu * div(u_)) * q * dx
            a4, L4 = system(F4)

        # Assemble matrices
        self.tc.start('assembleMatrices')
        A1_const = assemble(
            a1_const
        )  # must be here, so A1 stays one Python object during repeated assembly
        A1_change = A1_const.copy(
        )  # copy to get matrix with same sparse structure (data will be overwritten)
        if self.stabilize and not self.use_full_SUPG:
            A1_stab = A1_const.copy(
            )  # copy to get matrix with same sparse structure (data will be overwritten)
        A2 = assemble(a2)
        A3 = assemble(a3)
        if self.useRotationScheme:
            A4 = assemble(a4)
        self.tc.end('assembleMatrices')

        if self.solvers == 'direct':
            self.solver_vel_tent = LUSolver('mumps')
            self.solver_vel_cor = LUSolver('mumps')
            self.solver_p = LUSolver('mumps')
            if self.useRotationScheme:
                self.solver_rot = LUSolver('mumps')
        else:
            # NT 2016-1  KrylovSolver >> PETScKrylovSolver

            # not needed, chosen not to use hypre_parasails:
            # if self.prec_v == 'hypre_parasails':  # in FEniCS 1.6.0 inaccessible using KrylovSolver class
            #     self.solver_vel_tent = PETScKrylovSolver('gmres')   # PETSc4py object
            #     self.solver_vel_tent.ksp().getPC().setType('hypre')
            #     PETScOptions.set('pc_hypre_type', 'parasails')
            #     # this is global setting, but preconditioners for pressure solvers are set by their constructors
            # else:
            self.solver_vel_tent = PETScKrylovSolver(
                'gmres', self.args.precV)  # nonsymetric > gmres
            # cannot use 'ilu' in parallel
            self.solver_vel_cor = PETScKrylovSolver('cg', self.args.precVC)
            self.solver_p = PETScKrylovSolver(
                self.args.solP,
                self.args.precP)  # almost (up to BC) symmetric > CG
            if self.useRotationScheme:
                self.solver_rot = PETScKrylovSolver('cg', 'hypre_amg')

        # setup Krylov solvers
        if self.solvers == 'krylov':
            # Get the nullspace if there are no pressure boundary conditions
            foo = Function(
                self.Q)  # auxiliary vector for setting pressure nullspace
            if self.args.bc == 'nullspace':
                null_vec = Vector(foo.vector())
                self.Q.dofmap().set(null_vec, 1.0)
                null_vec *= 1.0 / null_vec.norm('l2')
                self.null_space = VectorSpaceBasis([null_vec])
                as_backend_type(A2).set_nullspace(self.null_space)

            # apply global options for Krylov solvers
            solver_options = {
                'monitor_convergence': True,
                'maximum_iterations': 10000,
                'nonzero_initial_guess': True
            }
            # 'nonzero_initial_guess': True   with  solver.solve(A, u, b) means that
            # Solver will use anything stored in u as an initial guess
            for solver in [self.solver_vel_tent, self.solver_vel_cor, self.solver_rot, self.solver_p] if \
                    self.useRotationScheme else [self.solver_vel_tent, self.solver_vel_cor, self.solver_p]:
                for key, value in solver_options.items():
                    try:
                        solver.parameters[key] = value
                    except KeyError:
                        info('Invalid option %s for KrylovSolver' % key)
                        return 1

            if self.args.solP == 'richardson':
                self.solver_p.parameters['monitor_convergence'] = False

            self.solver_vel_tent.parameters['relative_tolerance'] = 10**(
                -self.args.prv1)
            self.solver_vel_tent.parameters['absolute_tolerance'] = 10**(
                -self.args.pav1)
            self.solver_vel_cor.parameters['relative_tolerance'] = 10E-12
            self.solver_vel_cor.parameters['absolute_tolerance'] = 10E-4
            self.solver_p.parameters['relative_tolerance'] = 10**(
                -self.args.prp)
            self.solver_p.parameters['absolute_tolerance'] = 10**(
                -self.args.pap)
            if self.useRotationScheme:
                self.solver_rot.parameters['relative_tolerance'] = 10E-10
                self.solver_rot.parameters['absolute_tolerance'] = 10E-10

            if self.args.Vrestart > 0:
                self.solver_vel_tent.parameters['gmres'][
                    'restart'] = self.args.Vrestart

            if self.args.solP == 'gmres' and self.args.Prestart > 0:
                self.solver_p.parameters['gmres'][
                    'restart'] = self.args.Prestart

        # boundary conditions
        bcu, bcp = problem.get_boundary_conditions(self.args.bc == 'outflow',
                                                   self.V, self.Q)
        self.tc.end('init')
        # Time-stepping
        info("Running of Incremental pressure correction scheme n. 1")
        ttime = self.metadata['time']
        t = dt
        step = 1

        # debug function
        if problem.args.debug_rot:
            plot_cor_v = Function(self.V)

        while t < (ttime + dt / 2.0):
            self.problem.update_time(t, step)
            if self.MPI_rank == 0:
                problem.write_status_file(t)

            if doSave:
                save_this_step = problem.save_this_step

            # assemble matrix (it depends on solution)
            self.tc.start('assembleA1')
            assemble(
                a1_change, tensor=A1_change
            )  # assembling into existing matrix is faster than assembling new one
            A1 = A1_const.copy()  # we dont want to change A1_const
            A1.axpy(1, A1_change, True)
            self.tc.end('assembleA1')
            self.tc.start('assembleA1stab')
            if self.stabilize and not self.use_full_SUPG:
                assemble(
                    a1_stab, tensor=A1_stab
                )  # assembling into existing matrix is faster than assembling new one
                A1.axpy(1, A1_stab, True)
            self.tc.end('assembleA1stab')

            # Compute tentative velocity step
            begin("Computing tentative velocity")
            self.tc.start('rhs')
            b = assemble(L1)
            self.tc.end('rhs')
            self.tc.start('applybc1')
            [bc.apply(A1, b) for bc in bcu]
            self.tc.end('applybc1')
            try:
                self.tc.start('solve 1')
                self.solver_vel_tent.solve(A1, u_.vector(), b)
                self.tc.end('solve 1')
                if save_this_step:
                    self.tc.start('saveVel')
                    problem.save_vel(True, u_)
                    self.tc.end('saveVel')
                if save_this_step and not onlyVel:
                    problem.save_div(True, u_)
                problem.compute_err(True, u_, t)
                problem.compute_div(True, u_)
            except RuntimeError as inst:
                problem.report_fail(t)
                return 1
            end()

            if self.useRotationScheme:
                begin("Computing tentative pressure")
            else:
                begin("Computing pressure")
            if self.forceOutflow and problem.can_force_outflow:
                out = problem.compute_outflow(u_)
                info('Tentative outflow: %f' % out)
                n_o = -problem.last_inflow - out
                info('Needed outflow: %f' % n_o)
                need_outflow.assign(n_o)
            self.tc.start('rhs')
            b = assemble(L2)
            self.tc.end('rhs')
            self.tc.start('applybcP')
            [bc.apply(A2, b) for bc in bcp]
            if self.args.bc == 'nullspace':
                self.null_space.orthogonalize(b)
            self.tc.end('applybcP')
            try:
                self.tc.start('solve 2')
                self.solver_p.solve(A2, p_.vector(), b)
                self.tc.end('solve 2')
            except RuntimeError as inst:
                problem.report_fail(t)
                return 1
            if self.useRotationScheme:
                foo = Function(self.Q)
                foo.assign(p_ + p0)
                if save_this_step and not onlyVel:
                    problem.averaging_pressure(foo)
                    problem.save_pressure(True, foo)
            else:
                foo = Function(self.Q)
                foo.assign(p_)  # we do not want to change p_ by averaging
                if save_this_step and not onlyVel:
                    problem.averaging_pressure(foo)
                    problem.save_pressure(False, foo)
            end()

            begin("Computing corrected velocity")
            self.tc.start('rhs')
            b = assemble(L3)
            self.tc.end('rhs')
            if not self.args.B:
                self.tc.start('applybc3')
                [bc.apply(A3, b) for bc in bcu]
                self.tc.end('applybc3')
            try:
                self.tc.start('solve 3')
                self.solver_vel_cor.solve(A3, u_cor.vector(), b)
                self.tc.end('solve 3')
                problem.compute_err(False, u_cor, t)
                problem.compute_div(False, u_cor)
            except RuntimeError as inst:
                problem.report_fail(t)
                return 1
            if save_this_step:
                self.tc.start('saveVel')
                problem.save_vel(False, u_cor)
                self.tc.end('saveVel')
            if save_this_step and not onlyVel:
                problem.save_div(False, u_cor)
            end()

            if self.useRotationScheme:
                begin("Rotation scheme pressure correction")
                self.tc.start('rhs')
                b = assemble(L4)
                self.tc.end('rhs')
                try:
                    self.tc.start('solve 4')
                    self.solver_rot.solve(A4, p_mod.vector(), b)
                    self.tc.end('solve 4')
                except RuntimeError as inst:
                    problem.report_fail(t)
                    return 1
                if save_this_step and not onlyVel:
                    problem.averaging_pressure(p_mod)
                    problem.save_pressure(False, p_mod)
                end()

                if problem.args.debug_rot:
                    # save applied pressure correction (expressed as a term added to RHS of next tentative vel. step)
                    # see comment next to argument definition
                    plot_cor_v.assign(project(k * grad(nu * div(u_)), self.V))
                    problem.fileDict['grad_cor']['file'].write(plot_cor_v, t)

            # compute functionals (e. g. forces)
            problem.compute_functionals(
                u_cor, p_mod if self.useRotationScheme else p_, t, step)

            # Move to next time step
            self.tc.start('next')
            u1.assign(u0)
            u0.assign(u_cor)
            u_.assign(
                u_cor)  # use corrected velocity as initial guess in first step

            if self.useRotationScheme:
                p0.assign(p_mod)
            else:
                p0.assign(p_)

            t = round(t + dt, 6)  # round time step to 0.000001
            step += 1
            self.tc.end('next')

        info("Finished: Incremental pressure correction scheme n. 1")
        problem.report()
        return 0
Example #13
0
class Solver(gs.GeneralSolver):
    def __init__(self, args, tc, metadata):
        gs.GeneralSolver.__init__(self, args, tc, metadata)
        self.metadata['hasTentativeV'] = True

        self.solver_vel_tent = None
        self.solver_vel_cor = None
        self.solver_p = None
        self.solver_rot = None
        self.null_space = None

        # input parameters
        self.forceOutflow = args.fo
        self.useLaplace = args.laplace
        self.use_full_SUPG = args.cs
        self.bcv = 'NOT' if self.useLaplace else args.bcv
        if self.bcv == 'CDN':
            info('Using classical do nothing condition (Tn=0).')
        if self.bcv == 'DDN':
            info('Using directional do nothing condition (Tn=0.5*negative(u.n)u).')
        if self.bcv == 'LAP':
            info('Using laplace neutral condition (grad(u)n=0).')
        self.stabCoef = args.stab
        self.stabilize = (args.stab > DOLFIN_EPS)
        if self.stabilize:
            if self.use_full_SUPG:
                info('Used consistent streamline-diffusion stabilization with coef.: %f' % args.stab)
            else:
                info('Used non-consistent streamline-diffusion stabilization with coef.: %f' % args.stab)
        else:
            info('No stabilization used.')
        self.solvers = args.solvers
        self.useRotationScheme = args.r
        self.metadata['hasTentativeP'] = self.useRotationScheme

    def __str__(self):
        return 'ipcs1 - incremental pressure correction scheme with nonlinearity treated by Adam-Bashword + ' \
               'Crank-Nicolson and viscosity term treated semi-explicitly (Crank-Nicholson)'

    @staticmethod
    def setup_parser_options(parser):
        gs.GeneralSolver.setup_parser_options(parser)
        parser.add_argument('--stab', help='Use stabilization (positive constant)', type=float, default=0.)
        parser.add_argument('--cs', help='Use consistent SUPG stabilisation.', action='store_true')
        parser.add_argument('--cbc_tau', help='Use simpler cbcflow parameter for SUPG', action='store_true')
        parser.add_argument('-r', help='Use rotation scheme', action='store_true')
        parser.add_argument('-B', help='Use no BC in correction step', action='store_true')
        parser.add_argument('-s', '--solvers', help='Solvers', choices=['direct', 'krylov'], default='krylov')
        parser.add_argument('-b', '--bc', help='Pressure boundary condition mode',
                            choices=['outflow', 'nullspace'], default='outflow')
        # 'outflow' should be used with direct solvers (cannot LU-decompose singular matrix)
        parser.add_argument('--prv1', help='relative tentative velocity Krylov solver precision', type=int, default=6)
        parser.add_argument('--pav1', help='absolute tentative velocity Krylov solver precision', type=int, default=10)
        parser.add_argument('--pap', help='pressure Krylov solver absolute precision', type=int, default=6)
        parser.add_argument('--prp', help='pressure Krylov solver relative precision', type=int, default=10)
        parser.add_argument('--precV', help='Preconditioner for tentative velocity GMRES solver', type=str,
                            default='sor')
        parser.add_argument('--precVC', help='Preconditioner for corrected velocity CG solver', type=str, default='sor')
        parser.add_argument('--precP', help='Preconditioner for 2nd step solver (Poisson)',
                            choices=['hypre_amg', 'ilu', 'sor'], default='sor')
        parser.add_argument('--solP', help='2nd step solver (Poisson)', type=str, default='cg')
        # choices=['cg', 'gmres', 'richardson', 'tfqmr', ...]
        parser.add_argument('--Prestart', help='2nd step solver GMRES restart', type=int, default=-1)
        parser.add_argument('--Vrestart', help='1st step solver GMRES restart', type=int, default=-1)
        parser.add_argument('--fo', help='Force Neumann outflow boundary for pressure', action='store_true')
        parser.add_argument('--laplace', help='Use laplace(u) instead of div(symgrad(u))', action='store_true')
        parser.add_argument('--bcv', help='Outflow BC for velocity (with stress formulation)',
                            choices=['CDN', 'DDN', 'LAP'], default='CDN')
        parser.add_argument('--ema', help='Use EMA conserving scheme for convection term', action='store_true')
        # described in Charnyi, Heister, Olshanskii, Rebholz:
        # "On conservation laws of Navier-Stokes Galerkin discretizations" (2016)
        # it seems that it does not work with projection methods (probably because of dropping div u = 0 constraint)
        # not documented in readme

    def solve(self, problem):
        self.problem = problem
        doSave = problem.doSave
        save_this_step = False
        onlyVel = problem.saveOnlyVel
        dt = self.metadata['dt']

        nu = Constant(self.problem.nu)
        self.tc.init_watch('init', 'Initialization', True, count_to_percent=False)
        self.tc.init_watch('rhs', 'Assembled right hand side', True, count_to_percent=True)
        self.tc.init_watch('applybc1', 'Applied velocity BC 1st step', True, count_to_percent=True)
        self.tc.init_watch('applybc3', 'Applied velocity BC 3rd step', True, count_to_percent=True)
        self.tc.init_watch('applybcP', 'Applied pressure BC or othogonalized rhs', True, count_to_percent=True)
        self.tc.init_watch('assembleMatrices', 'Initial matrix assembly', False, count_to_percent=True)
        self.tc.init_watch('solve 1', 'Running solver on 1st step', True, count_to_percent=True)
        self.tc.init_watch('solve 2', 'Running solver on 2nd step', True, count_to_percent=True)
        self.tc.init_watch('solve 3', 'Running solver on 3rd step', True, count_to_percent=True)
        self.tc.init_watch('solve 4', 'Running solver on 4th step', True, count_to_percent=True)
        self.tc.init_watch('assembleA1', 'Assembled A1 matrix (without stabiliz.)', True, count_to_percent=True)
        self.tc.init_watch('assembleA1stab', 'Assembled A1 stabilization', True, count_to_percent=True)
        self.tc.init_watch('next', 'Next step assignments', True, count_to_percent=True)
        self.tc.init_watch('saveVel', 'Saved velocity', True)

        self.tc.start('init')

        # Define function spaces (P2-P1)
        mesh = self.problem.mesh
        self.V = VectorFunctionSpace(mesh, "Lagrange", 2)  # velocity
        self.Q = FunctionSpace(mesh, "Lagrange", 1)  # pressure
        self.PS = FunctionSpace(mesh, "Lagrange", 2)  # partial solution (must be same order as V)
        self.D = FunctionSpace(mesh, "Lagrange", 1)  # velocity divergence space

        problem.initialize(self.V, self.Q, self.PS, self.D)

        # Define trial and test functions
        u = TrialFunction(self.V)
        v = TestFunction(self.V)
        p = TrialFunction(self.Q)
        q = TestFunction(self.Q)

        n = FacetNormal(mesh)
        I = Identity(find_geometric_dimension(u))

        # Initial conditions: u0 velocity at previous time step u1 velocity two time steps back p0 previous pressure
        [u1, u0, p0] = self.problem.get_initial_conditions([{'type': 'v', 'time': -dt},
                                                            {'type': 'v', 'time': 0.0},
                                                            {'type': 'p', 'time': 0.0}])

        u_ = Function(self.V)  # current tentative velocity
        u_cor = Function(self.V)  # current corrected velocity
        p_ = Function(self.Q)  # current pressure or pressure help function from rotation scheme
        p_mod = Function(self.Q)  # current modified pressure from rotation scheme

        # Define coefficients
        k = Constant(self.metadata['dt'])
        f = Constant((0, 0, 0))

        # Define forms
        # step 1: Tentative velocity, solve to u_
        u_ext = 1.5 * u0 - 0.5 * u1  # extrapolation for convection term

        # Stabilisation
        h = CellSize(mesh)
        if self.args.cbc_tau:
            # used in Simula cbcflow project
            tau = Constant(self.stabCoef) * h / (sqrt(inner(u_ext, u_ext)) + h)
        else:
            # proposed in R. Codina: On stabilized finite element methods for linear systems of
            # convection-diffusion-reaction equations.
            tau = Constant(self.stabCoef) * k * h ** 2 / (
            2 * nu * k + k * h * sqrt(DOLFIN_EPS + inner(u_ext, u_ext)) + h ** 2)
            # DOLFIN_EPS is added because of FEniCS bug that inner(u_ext, u_ext) can be negative when u_ext = 0

        if self.use_full_SUPG:
            v1 = v + tau * 0.5 * dot(grad(v), u_ext)
            parameters['form_compiler']['quadrature_degree'] = 6
        else:
            v1 = v

        def nonlinearity(function):
            if self.args.ema:
                return 2 * inner(dot(sym(grad(function)), u_ext), v1) * dx + inner(div(function) * u_ext, v1) * dx
            else:
                return inner(dot(grad(function), u_ext), v1) * dx

        def diffusion(fce):
            if self.useLaplace:
                return nu * inner(grad(fce), grad(v1)) * dx
            else:
                form = inner(nu * 2 * sym(grad(fce)), sym(grad(v1))) * dx
                if self.bcv == 'CDN':
                    return form
                if self.bcv == 'LAP':
                    return form - inner(nu * dot(grad(fce).T, n), v1) * problem.get_outflow_measure_form()
                if self.bcv == 'DDN':
                    return form  # additional term must be added to non-constant part

        def pressure_rhs():
            if self.args.bc == 'outflow':
                return inner(p0, div(v1)) * dx
            else:
                return inner(p0, div(v1)) * dx - inner(p0 * n, v1) * problem.get_outflow_measure_form()

        a1_const = (1. / k) * inner(u, v1) * dx + diffusion(0.5 * u)
        a1_change = nonlinearity(0.5 * u)
        if self.bcv == 'DDN':
            # does not penalize influx for current step, only for the next one
            # this can lead to oscilation:
            # DDN correct next step, but then u_ext is OK so in next step DDN is not used, leading to new influx...
            # u and u_ext cannot be switched, min_value is nonlinear function
            a1_change += -0.5 * min_value(Constant(0.), inner(u_ext, n)) * inner(u,
                                                                                 v1) * problem.get_outflow_measure_form()
            # NT works only with uflacs compiler

        L1 = (1. / k) * inner(u0, v1) * dx - nonlinearity(0.5 * u0) - diffusion(0.5 * u0) + pressure_rhs()
        if self.bcv == 'DDN':
            L1 += 0.5 * min_value(0., inner(u_ext, n)) * inner(u0, v1) * problem.get_outflow_measure_form()

        # Non-consistent SUPG stabilisation
        if self.stabilize and not self.use_full_SUPG:
            # a1_stab = tau*inner(dot(grad(u), u_ext), dot(grad(v), u_ext))*dx
            a1_stab = 0.5 * tau * inner(dot(grad(u), u_ext), dot(grad(v), u_ext)) * dx(None, {'quadrature_degree': 6})
            # optional: to use Crank Nicolson in stabilisation term following change of RHS is needed:
            # L1 += -0.5*tau*inner(dot(grad(u0), u_ext), dot(grad(v), u_ext))*dx(None, {'quadrature_degree': 6})

        outflow_area = Constant(problem.outflow_area)
        need_outflow = Constant(0.0)
        if self.useRotationScheme:
            # Rotation scheme
            F2 = inner(grad(p), grad(q)) * dx + (1. / k) * q * div(u_) * dx
        else:
            # Projection, solve to p_
            if self.forceOutflow and problem.can_force_outflow:
                info('Forcing outflow.')
                F2 = inner(grad(p - p0), grad(q)) * dx + (1. / k) * q * div(u_) * dx
                for m in problem.get_outflow_measures():
                    F2 += (1. / k) * (1. / outflow_area) * need_outflow * q * m
            else:
                F2 = inner(grad(p - p0), grad(q)) * dx + (1. / k) * q * div(u_) * dx
        a2, L2 = system(F2)

        # step 3: Finalize, solve to u_
        if self.useRotationScheme:
            # Rotation scheme
            F3 = (1. / k) * inner(u - u_, v) * dx + inner(grad(p_), v) * dx
        else:
            F3 = (1. / k) * inner(u - u_, v) * dx + inner(grad(p_ - p0), v) * dx
        a3, L3 = system(F3)

        if self.useRotationScheme:
            # Rotation scheme: modify pressure
            F4 = (p - p0 - p_ + nu * div(u_)) * q * dx
            a4, L4 = system(F4)

        # Assemble matrices
        self.tc.start('assembleMatrices')
        A1_const = assemble(a1_const)  # must be here, so A1 stays one Python object during repeated assembly
        A1_change = A1_const.copy()  # copy to get matrix with same sparse structure (data will be overwritten)
        if self.stabilize and not self.use_full_SUPG:
            A1_stab = A1_const.copy()  # copy to get matrix with same sparse structure (data will be overwritten)
        A2 = assemble(a2)
        A3 = assemble(a3)
        if self.useRotationScheme:
            A4 = assemble(a4)
        self.tc.end('assembleMatrices')

        if self.solvers == 'direct':
            self.solver_vel_tent = LUSolver('mumps')
            self.solver_vel_cor = LUSolver('mumps')
            self.solver_p = LUSolver('mumps')
            if self.useRotationScheme:
                self.solver_rot = LUSolver('mumps')
        else:
            # NT 2016-1  KrylovSolver >> PETScKrylovSolver

            # not needed, chosen not to use hypre_parasails:
            # if self.prec_v == 'hypre_parasails':  # in FEniCS 1.6.0 inaccessible using KrylovSolver class
            #     self.solver_vel_tent = PETScKrylovSolver('gmres')   # PETSc4py object
            #     self.solver_vel_tent.ksp().getPC().setType('hypre')
            #     PETScOptions.set('pc_hypre_type', 'parasails')
            #     # this is global setting, but preconditioners for pressure solvers are set by their constructors
            # else:
            self.solver_vel_tent = PETScKrylovSolver('gmres', self.args.precV)  # nonsymetric > gmres
            # cannot use 'ilu' in parallel
            self.solver_vel_cor = PETScKrylovSolver('cg', self.args.precVC)
            self.solver_p = PETScKrylovSolver(self.args.solP, self.args.precP)  # almost (up to BC) symmetric > CG
            if self.useRotationScheme:
                self.solver_rot = PETScKrylovSolver('cg', 'hypre_amg')

        # setup Krylov solvers
        if self.solvers == 'krylov':
            # Get the nullspace if there are no pressure boundary conditions
            foo = Function(self.Q)  # auxiliary vector for setting pressure nullspace
            if self.args.bc == 'nullspace':
                null_vec = Vector(foo.vector())
                self.Q.dofmap().set(null_vec, 1.0)
                null_vec *= 1.0 / null_vec.norm('l2')
                self.null_space = VectorSpaceBasis([null_vec])
                as_backend_type(A2).set_nullspace(self.null_space)

            # apply global options for Krylov solvers
            solver_options = {'monitor_convergence': True, 'maximum_iterations': 10000, 'nonzero_initial_guess': True}
            # 'nonzero_initial_guess': True   with  solver.solve(A, u, b) means that
            # Solver will use anything stored in u as an initial guess
            for solver in [self.solver_vel_tent, self.solver_vel_cor, self.solver_rot, self.solver_p] if \
                    self.useRotationScheme else [self.solver_vel_tent, self.solver_vel_cor, self.solver_p]:
                for key, value in solver_options.items():
                    try:
                        solver.parameters[key] = value
                    except KeyError:
                        info('Invalid option %s for KrylovSolver' % key)
                        return 1

            if self.args.solP == 'richardson':
                self.solver_p.parameters['monitor_convergence'] = False

            self.solver_vel_tent.parameters['relative_tolerance'] = 10 ** (-self.args.prv1)
            self.solver_vel_tent.parameters['absolute_tolerance'] = 10 ** (-self.args.pav1)
            self.solver_vel_cor.parameters['relative_tolerance'] = 10E-12
            self.solver_vel_cor.parameters['absolute_tolerance'] = 10E-4
            self.solver_p.parameters['relative_tolerance'] = 10 ** (-self.args.prp)
            self.solver_p.parameters['absolute_tolerance'] = 10 ** (-self.args.pap)
            if self.useRotationScheme:
                self.solver_rot.parameters['relative_tolerance'] = 10E-10
                self.solver_rot.parameters['absolute_tolerance'] = 10E-10

            if self.args.Vrestart > 0:
                self.solver_vel_tent.parameters['gmres']['restart'] = self.args.Vrestart

            if self.args.solP == 'gmres' and self.args.Prestart > 0:
                self.solver_p.parameters['gmres']['restart'] = self.args.Prestart

        # boundary conditions
        bcu, bcp = problem.get_boundary_conditions(self.args.bc == 'outflow', self.V, self.Q)
        self.tc.end('init')
        # Time-stepping
        info("Running of Incremental pressure correction scheme n. 1")
        ttime = self.metadata['time']
        t = dt
        step = 1

        # debug function
        if problem.args.debug_rot:
            plot_cor_v = Function(self.V)

        while t < (ttime + dt / 2.0):
            self.problem.update_time(t, step)
            if self.MPI_rank == 0:
                problem.write_status_file(t)

            if doSave:
                save_this_step = problem.save_this_step

            # assemble matrix (it depends on solution)
            self.tc.start('assembleA1')
            assemble(a1_change, tensor=A1_change)  # assembling into existing matrix is faster than assembling new one
            A1 = A1_const.copy()  # we dont want to change A1_const
            A1.axpy(1, A1_change, True)
            self.tc.end('assembleA1')
            self.tc.start('assembleA1stab')
            if self.stabilize and not self.use_full_SUPG:
                assemble(a1_stab, tensor=A1_stab)  # assembling into existing matrix is faster than assembling new one
                A1.axpy(1, A1_stab, True)
            self.tc.end('assembleA1stab')

            # Compute tentative velocity step
            begin("Computing tentative velocity")
            self.tc.start('rhs')
            b = assemble(L1)
            self.tc.end('rhs')
            self.tc.start('applybc1')
            [bc.apply(A1, b) for bc in bcu]
            self.tc.end('applybc1')
            try:
                self.tc.start('solve 1')
                self.solver_vel_tent.solve(A1, u_.vector(), b)
                self.tc.end('solve 1')
                if save_this_step:
                    self.tc.start('saveVel')
                    problem.save_vel(True, u_)
                    self.tc.end('saveVel')
                if save_this_step and not onlyVel:
                    problem.save_div(True, u_)
                problem.compute_err(True, u_, t)
                problem.compute_div(True, u_)
            except RuntimeError as inst:
                problem.report_fail(t)
                return 1
            end()

            if self.useRotationScheme:
                begin("Computing tentative pressure")
            else:
                begin("Computing pressure")
            if self.forceOutflow and problem.can_force_outflow:
                out = problem.compute_outflow(u_)
                info('Tentative outflow: %f' % out)
                n_o = -problem.last_inflow - out
                info('Needed outflow: %f' % n_o)
                need_outflow.assign(n_o)
            self.tc.start('rhs')
            b = assemble(L2)
            self.tc.end('rhs')
            self.tc.start('applybcP')
            [bc.apply(A2, b) for bc in bcp]
            if self.args.bc == 'nullspace':
                self.null_space.orthogonalize(b)
            self.tc.end('applybcP')
            try:
                self.tc.start('solve 2')
                self.solver_p.solve(A2, p_.vector(), b)
                self.tc.end('solve 2')
            except RuntimeError as inst:
                problem.report_fail(t)
                return 1
            if self.useRotationScheme:
                foo = Function(self.Q)
                foo.assign(p_ + p0)
                if save_this_step and not onlyVel:
                    problem.averaging_pressure(foo)
                    problem.save_pressure(True, foo)
            else:
                foo = Function(self.Q)
                foo.assign(p_)  # we do not want to change p_ by averaging
                if save_this_step and not onlyVel:
                    problem.averaging_pressure(foo)
                    problem.save_pressure(False, foo)
            end()

            begin("Computing corrected velocity")
            self.tc.start('rhs')
            b = assemble(L3)
            self.tc.end('rhs')
            if not self.args.B:
                self.tc.start('applybc3')
                [bc.apply(A3, b) for bc in bcu]
                self.tc.end('applybc3')
            try:
                self.tc.start('solve 3')
                self.solver_vel_cor.solve(A3, u_cor.vector(), b)
                self.tc.end('solve 3')
                problem.compute_err(False, u_cor, t)
                problem.compute_div(False, u_cor)
            except RuntimeError as inst:
                problem.report_fail(t)
                return 1
            if save_this_step:
                self.tc.start('saveVel')
                problem.save_vel(False, u_cor)
                self.tc.end('saveVel')
            if save_this_step and not onlyVel:
                problem.save_div(False, u_cor)
            end()

            if self.useRotationScheme:
                begin("Rotation scheme pressure correction")
                self.tc.start('rhs')
                b = assemble(L4)
                self.tc.end('rhs')
                try:
                    self.tc.start('solve 4')
                    self.solver_rot.solve(A4, p_mod.vector(), b)
                    self.tc.end('solve 4')
                except RuntimeError as inst:
                    problem.report_fail(t)
                    return 1
                if save_this_step and not onlyVel:
                    problem.averaging_pressure(p_mod)
                    problem.save_pressure(False, p_mod)
                end()

                if problem.args.debug_rot:
                    # save applied pressure correction (expressed as a term added to RHS of next tentative vel. step)
                    # see comment next to argument definition
                    plot_cor_v.assign(project(k * grad(nu * div(u_)), self.V))
                    problem.fileDict['grad_cor']['file'].write(plot_cor_v, t)

            # compute functionals (e. g. forces)
            problem.compute_functionals(u_cor, p_mod if self.useRotationScheme else p_, t, step)

            # Move to next time step
            self.tc.start('next')
            u1.assign(u0)
            u0.assign(u_cor)
            u_.assign(u_cor)  # use corrected velocity as initial guess in first step

            if self.useRotationScheme:
                p0.assign(p_mod)
            else:
                p0.assign(p_)

            t = round(t + dt, 6)  # round time step to 0.000001
            step += 1
            self.tc.end('next')

        info("Finished: Incremental pressure correction scheme n. 1")
        problem.report()
        return 0
Example #14
0
class Discretization(object):
  def __init__(self, problem, verbosity=0):
    """

    :param ProblemData problem:
    :param int verbosity:
    :return:
    """
    self.parameters = parameters["discretization"]

    self.verb = verbosity
    self.vis_folder = os.path.join(problem.out_folder, "MESH")
    self.core = problem.core
    self.G = problem.G

    if self.verb > 1: print pid+"Loading mesh"
        
    t_load = Timer("DD: Data loading")

    if not problem.mesh_module:
      if self.verb > 1: print pid + "  mesh data"
      self.mesh = Mesh(problem.mesh_files.mesh)

      if self.verb > 1: print pid + "  physical data"
      self.cell_regions_fun = MeshFunction("size_t", self.mesh, problem.mesh_files.physical_regions)

      if self.verb > 1: print pid + "  boundary data"
      self.boundaries = MeshFunction("size_t", self.mesh, problem.mesh_files.facet_regions)
    else:
      self.mesh = problem.mesh_module.mesh
      self.cell_regions_fun = problem.mesh_module.regions

      try:
        self.boundaries = problem.mesh_module.boundaries
      except AttributeError:
        self.boundaries = None

    assert self.mesh
    assert self.boundaries is None or self.boundaries.array().size > 0

    if self.verb > 2:
      print pid+"  mesh info: " + str(self.mesh)

    if self.verb > 1: print0("Defining function spaces" )

    self.t_spaces = Timer("DD: Function spaces construction")

    # Spaces that must be specified by the respective subclasses
    self.V = None     # solution space
    self.Vphi1 = None # 1-g scalar flux space
    
    # XS / TH space
    self.V0 = FunctionSpace(self.mesh, "DG", 0)
    self.ndof0 = self.V0.dim()

    dofmap = self.V0.dofmap()
    self.local_ndof0 = dofmap.local_dimension("owned")

    self.cell_regions = self.cell_regions_fun.array()
    assert self.cell_regions.size == self.local_ndof0

  def __create_cell_dof_mapping(self, dofmap):
    """
    Generate cell -> dof mapping for all cells of current partition.
    Note: in DG(0) space, there is one dof per element and no ghost cells.

    :param GenericDofMap dofmap: DG(0) dofmap
    """

    if self.verb > 2: print0("Constructing cell -> dof mapping")
    timer = Timer("DD: Cell->dof construction")

    code = \
    '''
      #include <dolfin/mesh/Cell.h>

      namespace dolfin
      {
        void fill_in(Array<int>& local_cell_dof_map, const Mesh& mesh, const GenericDofMap& dofmap)
        {
          std::size_t local_dof_range_start = dofmap.ownership_range().first;
          int* cell_dof_data = local_cell_dof_map.data();

          for (CellIterator c(mesh); !c.end(); ++c)
            *cell_dof_data++ = dofmap.cell_dofs(c->index())[0] - local_dof_range_start;
        }
      }
    '''

    cell_mapping_module = compile_extension_module(code)
    cell_dof_array = IntArray(self.local_ndof0)
    cell_mapping_module.fill_in(cell_dof_array, self.mesh, dofmap)
    self._local_cell_dof_map = cell_dof_array.array()

    timer.stop()

  def __create_cell_layers_mapping(self):
    """
    Generate a cell -> axial layer mapping for all cells of current partition. Note that keys are ordered by the
    associated DG(0) dof, not by the cell index in the mesh.
    """

    if self.verb > 2: print0("Constructing cell -> layer mapping")
    timer = Timer("DD: Cell->layer construction")

    code = \
    '''
      #include <dolfin/mesh/Cell.h>

      namespace dolfin
      {
        void fill_in(Array<int>& local_cell_layers,
                     const Mesh& mesh, const Array<int>& cell_dofs, const Array<double>& layer_boundaries)
        {
          std::size_t num_layers = layer_boundaries.size() - 1;
          unsigned int layer;

          for (CellIterator c(mesh); !c.end(); ++c)
          {
            double midz = c->midpoint().z();
            for (layer = 0; layer < num_layers; layer++)
              if (layer_boundaries[layer] <= midz && midz <= layer_boundaries[layer+1])
                break;

            int dof = cell_dofs[c->index()];
            local_cell_layers[dof] = layer;
          }
        }
      }
    '''

    cell_mapping_module = compile_extension_module(code)

    cell_layers_array =  IntArray(self.local_ndof0)
    cell_mapping_module.fill_in(cell_layers_array, self.mesh, self.local_cell_dof_map, self.core.layer_boundaries)
    self._local_cell_layers = cell_layers_array.array()

    timer.stop()

  def __create_cell_vol_mapping(self):
    """
    Generate cell -> volume mapping for all cells of current partition. Note that keys are ordered by the
    associated DG(0) dof, not by the cell index in the mesh.

    This map is required for calculating various densities from total region integrals (like cell power densities from
    cell-integrated powers).
    """

    if self.verb > 2: print0("Constructing cell -> volume mapping")
    timer = Timer("DD: Cell->vol construction")

    code = \
    '''
      #include <dolfin/mesh/Cell.h>

      namespace dolfin
      {
        void fill_in(Array<double>& cell_vols, const Mesh& mesh, const Array<int>& cell_dofs)
        {
          for (CellIterator c(mesh); !c.end(); ++c)
            cell_vols[cell_dofs[c->index()]] = c->volume();
        }
      }
    '''

    cell_mapping_module = compile_extension_module(code)
    cell_vol_array = DoubleArray(self.local_ndof0)
    cell_mapping_module.fill_in(cell_vol_array, self.mesh, self.local_cell_dof_map)
    self._local_cell_volumes = cell_vol_array.array()

    timer.stop()

  @property
  def local_cell_dof_map(self):
    try:
      self._local_cell_dof_map
    except AttributeError:
      self.__create_cell_dof_mapping(self.V0.dofmap())
    return self._local_cell_dof_map

  @property
  def local_cell_volumes(self):
    try:
      self._local_cell_volumes
    except AttributeError:
      self.__create_cell_vol_mapping()
    return self._local_cell_volumes

  @property
  def local_cell_layers(self):
    try:
      self._local_cell_layers
    except AttributeError:
      self.__create_cell_layers_mapping()
    return self._local_cell_layers

  def visualize_mesh_data(self):
    timer = Timer("DD: Mesh data visualization")
    if self.verb > 2: print0("Visualizing mesh data")

    File(os.path.join(self.vis_folder, "mesh.pvd"), "compressed") << self.mesh
    if self.boundaries:
      File(os.path.join(self.vis_folder, "boundaries.pvd"), "compressed") << self.boundaries
    File(os.path.join(self.vis_folder, "mesh_regions.pvd"), "compressed") << self.cell_regions_fun

    # Create MeshFunction to hold cell process rank
    processes = CellFunction('size_t', self.mesh, MPI.rank(comm))
    File(os.path.join(self.vis_folder, "mesh_partitioning.pvd"), "compressed") << processes

  def print_diagnostics(self):
    print "\nDiscretization diagnostics"

    print MPI.rank(comm), self.mesh.num_entities(self.mesh.topology().dim())

    dofmap = self.V0.dofmap()

    print MPI.rank(comm), dofmap.ownership_range()
    print MPI.rank(comm), numpy.min(dofmap.collapse(self.mesh)[1].values()), \
          numpy.max(dofmap.collapse(self.mesh)[1].values())

    print "#Owned by {}: {}".format(MPI.rank(comm), dofmap.local_dimension("owned"))
    print "#Unowned by {}: {}".format(MPI.rank(comm), dofmap.local_dimension("unowned"))
Example #15
0
# BC conditions, nullspace
v_in_expr = Constant(v_in)
plt = plot(interpolate(v_in_expr, V),
           range_min=0.,
           range_max=2 * v_in,
           window_width=width,
           window_height=height)
plt.write_png('%s/correct' % dir)
# v_in_expr = Expression('(t<1.0)?t*v:v', v=Constant(v_in), t=0.0)
# v_in_expr = Expression('(t<1.0)?(1-cos(pi*t))*v*0.5:v', v=Constant(v_in), t=0.0)
bcp = DirichletBC(Q, Constant(0.0), boundary_parts, 2)
bcu = DirichletBC(V, v_in_expr, boundary_parts, 1)
foo = Function(Q)
null_vec = Vector(foo.vector())
Q.dofmap().set(null_vec, 1.0)
null_vec *= 1.0 / null_vec.norm('l2')
# print(null_vec.array())
null_space = VectorSpaceBasis([null_vec])

ds = Measure("ds", subdomain_data=boundary_parts)

# Define forms (dont redefine functions used here)
# step 1
u0 = Function(V)
u1 = Function(V)
p0 = Function(Q)
u_tent = TrialFunction(V)
v = TestFunction(V)
# U_ = 1.5*u0 - 0.5*u1
# nonlinearity = inner(dot(0.5 * (u_tent.dx(0) + u0.dx(0)), U_), v) * dx