ds = df.Measure("ds", domain=mesh, subdomain_data=subd) F_chi = (n[0] * psi * ds(1) + df.inner(df.grad(chi), df.grad(psi)) * df.dx + Pe * psi * df.dot(U_, df.grad(chi)) * df.dx + Pe * (U_[0] - df.Constant(1.)) * psi * df.dx) a_chi, L_chi = df.lhs(F_chi), df.rhs(F_chi) solver_chi = df.PETScKrylovSolver("gmres") nullvec = df.Vector(chi_.vector()) S.dofmap().set(nullvec, 1.0) nullvec *= 1.0 / nullvec.norm("l2") nullspace = df.VectorSpaceBasis([nullvec]) problem_chi2 = df.LinearVariationalProblem(a_chi, L_chi, chi_, bcs=[]) solver_chi2 = df.LinearVariationalSolver(problem_chi2) solver_chi2.parameters["krylov_solver"]["absolute_tolerance"] = 1e-15 logPe_arr = np.linspace(args.logPe_min, args.logPe_max, args.logPe_N) if rank == 0: data = np.zeros((len(logPe_arr), 3)) for iPe, logPe in enumerate(logPe_arr): Pe_loc = 10**logPe Pe.assign(Pe_loc) solver_chi2.solve()
#time derivative of Brenner B_CN = 0.5 * (B + B_1) #calculate mean velocity ux_CN_mean = df.Expression("ux", degree=1, ux=0.) F_chi = ((B - B_1) * psi / dt * df.dx + D * df.inner(df.grad(B_CN), df.grad(psi)) * df.dx + psi * df.dot(u_CN_, df.grad(B_CN)) * df.dx - (u_CN_[0] - ux_CN_mean) * psi * df.dx - n[0] * psi * ds(1)) #define linear and bi-linear form a_chi, L_chi = df.lhs(F_chi), df.rhs(F_chi) #define problem for u and P problem = df.LinearVariationalProblem(a, L, w_, bcs=bcs) solver = df.LinearVariationalSolver(problem) #define problem for B, with no BCs problem_B = df.LinearVariationalProblem(a_chi, L_chi, B_, bcs=[]) solver_B = df.LinearVariationalSolver(problem_B) solver_B.parameters["krylov_solver"]["absolute_tolerance"] = 1e-14 one = df.interpolate(df.Constant(1.), S) V_Omega = df.assemble( one * df.dx) # calculate unit cell volume (should always be 2*lambda) r = df.SpatialCoordinate(mesh) #write xdmf xdmf_u = df.XDMFFile(mesh.mpi_comm(), "{}/u.xdmf".format(folder)) xdmf_B = df.XDMFFile(mesh.mpi_comm(), "{}/B.xdmf".format(folder))
def __init__(self, J, U, u, m, bcs, dx_m=None, dims_m=None, use_nonlinear_solver=False): ''' Parameters ---------- J : ufl.Form Objective functional to maximize, e.g. linear-elastic strain energy. U : ufl.Form Linear-elastic strain energy. u : dolfin.Function Displacement field. m : dolfin.Function Vector-valued "body force" like traction field. bcs : (list of) dolfin.DirichletBC('s) Dirichlet boundary conditions. dx_m : dolfin.Measure or None (optional) "Body force" integration measure. Can be of cell type or exterior facet type; the default is cell type. Note, the integration measure can be defined on a subdomain (not necessarily on the whole domain). kappa : float or None (optional) Diffusion-like constant for smoothing the solution (`m`) increment. Notes ----- If the "body force" integration measure `dx_m` concerns cells (as opposed to exterior facets), the gradient smoothing constant `kappa` can be `None`. Usually, `kappa` will need to be some small positive value if `dx_m` concerns exterior facets. ''' if not isinstance(J, ufl_form_t): raise TypeError('Parameter `J`') if not isinstance(U, ufl_form_t): raise TypeError('Parameter `U`') if not isinstance(u, Function): raise TypeError('Parameter `u`') if not isinstance(m, Function): raise TypeError('Parameter `m`') if len(u) != len(m): raise ValueError( 'Functions `u` and `m` must live in the same dimension space') if bcs is None: bcs = () elif not isinstance(bcs, (list, tuple)): bcs = (bcs, ) if not all(isinstance(bc, DirichletBC) for bc in bcs): raise TypeError('Parameter `bcs` must contain ' 'homogenized `DirichletBC`(\'s)') if dims_m is not None: if not isinstance(bcs, (list, tuple, range)): dims_m = (dims_m, ) if not all(isinstance(dim_i, int) for dim_i in dims_m): raise TypeError('Parameter `dims_m`') if not all(0 <= dim_i < len(m) for dim_i in dims_m): raise ValueError('Parameter `dims_m`') if dx_m is None: dx_m = dx elif not isinstance(dx_m, dolfin.Measure): raise TypeError('Parameter `dx_m`') bcs_zro = [] for bc in bcs: bc_zro = DirichletBC(bc) bc_zro.homogenize() bcs_zro.append(bc_zro) V_u = u.function_space() V_m = m.function_space() v0_u = dolfin.TestFunction(V_u) v0_m = dolfin.TestFunction(V_m) v1_u = dolfin.TrialFunction(V_u) v1_m = dolfin.TrialFunction(V_m) self._u = u self._m = m self._z = z = Function(V_u) # Adjoint solution self._J = J dJ_u = derivative(J, u, v0_u) self._dJ_m = derivative(J, m, v0_m) dU_u = derivative(U, u, v0_u) # Internal force d2U_uu = a = derivative(dU_u, u, v1_u) # Stiffness if dims_m is None: dW_u = L = dot(v0_u, m) * dx_m else: dW_u = L = sum(v0_u[i] * m[i] for i in dims_m) * dx_m self._ufl_norm_m = dolfin.sqrt(m**2) * dx_m # equiv. L1-norm self._assembled_adj_dW_um = assemble(dot(v0_m, v1_u) * dx_m) self._dimdofs_V_m = tuple( np.array(V_m_sub_i.dofmap().dofs()) for V_m_sub_i in V_m.split()) if use_nonlinear_solver: self._equilibrium_solver = dolfin.NonlinearVariationalSolver( dolfin.NonlinearVariationalProblem(dU_u - dW_u, u, bcs, d2U_uu)) self._adjoint_solver = dolfin.LinearVariationalSolver( dolfin.LinearVariationalProblem(d2U_uu, dJ_u, z, bcs_zro)) # NOTE: `d2U_uu` is equivalent to `adjoint(d2U_uu)` due to symmetry self._equilibrium_solver.parameters["symmetric"] = True self._adjoint_solver.parameters["symmetric"] = True else: self._equilibrium_solver = LinearEquilibriumSolver(a, L, u, bcs) self._adjoint_solver = LinearAdjointSolver(None, dJ_u, z, bcs_zro) self._adjoint_solver._solver = self._equilibrium_solver.solver self._equilibrium_solver.parameters['symmetric'] = True # NOTE: `a` is equivalent to `adjoint(a)` due to symmetry; however, # the LU factorization can be reused in the adjoint solver. if dx_m.integral_type() == 'cell': kappa = None else: # Compute scale factor for `kappa` based on domain size mesh = V_m.mesh() xs = mesh.coordinates() domain_volume = assemble(1.0 * dx(mesh)) domain_length = (xs.max(0) - xs.min(0)).max() scale_factor = domain_volume / domain_length kappa = Constant(scale_factor * SMOOTHING_SOLVER_KAPPA) self._smoothing_solver = SmoothingSolver(V_m, kappa)
def linear_solver(self, u_k): r""" Solves the (linear) Newton iteration Eq. :eq:`Eq_linear_solver` for the UV theory. For the UV theory, the form of the Newton iteration is: .. math:: & \int \hat{Y} v_1 \hat{r}^2 d\hat{r} - \left(\frac{m}{M_n}\right)^2 \int \hat{\phi} v_1 \hat{r}^2 d\hat{r} - \alpha \int \hat{Z} v_1 \hat{r}^2 d\hat{r} + & + \int \hat{Z} v_2 \hat{r}^2 d\hat{r} - \left(\frac{M}{M_n}\right)^2 \int \hat{H} v_2 \hat{r}^2 d\hat{r} - \alpha \int \hat{Y} v_2 \hat{r}^2 d\hat{r} - \frac{\lambda}{2} \int {\hat{H}_k}^2 \hat{H} v_2 \hat{r}^2 d\hat{r} + & - \int \hat{\nabla}\hat{\phi} \cdot \hat{\nabla} v_3 \hat{r}^2 d\hat{r} - \int \hat{Y} v_3 \hat{r}^2 d\hat{r} - \int \hat{\nabla}\hat{H} \cdot \hat{\nabla} v_4 \hat{r}^2 d\hat{r} - \int \hat{Z} v_4 \hat{r}^2 d\hat{r} = & = \frac{M_n}{M_{f1}} \int \frac{\hat{\rho}}{M_P} v_1 \hat{r}^2 d\hat{r} - \int \frac{\lambda}{3} {\hat{H}_k}^3 v_2 \hat{r}^2 d\hat{r} *Arguments* u_k solution at the previous iteration """ # get the boundary conditions Dirichlet_bc = self.get_Dirichlet_bc() # create a vector (phi,h) with the two trial functions for the fields u = d.TrialFunction(self.V) # ... and split it into phi and h phi, h, y, z = d.split(u) # define test functions over the function space v1, v2, v3, v4 = d.TestFunctions(self.V) # split solution at current iteration into phi_k, h_k, y_k, z_k phi_k, h_k, y_k, z_k = d.split(u_k) # cast params as constant functions so that, if they are set to 0, # fenics still understand what it is integrating m, M, Mp = Constant(self.fields.m), Constant(self.fields.M), Constant( self.fields.Mp) alpha, lam = Constant(self.fields.alpha), Constant(self.fields.lam) Mn, Mf1, Mf2 = Constant(self.Mn), Constant(self.Mf1), Constant( self.Mf2) # r^2 r2 = Expression('pow(x[0],2)', degree=self.fem.func_degree) # define bilinear form # Eq.1 a1 = y * v1 * r2 * dx - ( m / Mn )**2 * phi * v1 * r2 * dx \ - alpha * ( Mf2/Mf1 ) * z * v1 * r2 * dx # Eq.2 a2 = z * v2 * r2 * dx - ( M / Mn )**2 * h * v2 * r2 * dx \ - alpha * ( Mf1/Mf2 ) * y * v2 * r2 * dx \ - lam/2. * ( Mf2 / Mn )**2 * h_k**2 * h * v2 * r2 * dx # Laplacian of phi a3 = -inner(grad(phi), grad(v3)) * r2 * dx - y * v3 * r2 * dx # Laplacian of H a4 = -inner(grad(h), grad(v4)) * r2 * dx - z * v4 * r2 * dx # all equations a = a1 + a2 + a3 + a4 # define linear form L1 = self.source.rho / Mp * Mn / Mf1 * v1 * r2 * dx # Eq.1 L2 = -lam / 3. * (Mf2 / Mn)**2 * h_k**3 * v2 * r2 * dx # Eq.2 L = L1 + L2 # all equations # define a vector with the solution sol = d.Function(self.V) # solve linearised system pde = d.LinearVariationalProblem(a, L, sol, Dirichlet_bc) solver = d.LinearVariationalSolver(pde) solver.solve() return sol
def setup_NS(w_NS, u, p, v, q, p0, q0, dx, ds, normal, dirichlet_bcs, neumann_bcs, boundary_to_mark, u_1, phi_, rho_, rho_1, g_, M_, mu_, rho_e_, c_, V_, c_1, V_1, dbeta, solutes, per_tau, drho, sigma_bar, eps, dveps, grav, enable_PF, enable_EC, use_iterative_solvers, use_pressure_stabilization, p_lagrange, q_rhs): """ Set up the Navier-Stokes subproblem. """ # F = ( # per_tau * rho_ * df.dot(u - u_1, v)*dx # + rho_*df.inner(df.grad(u), df.outer(u_1, v))*dx # + 2*mu_*df.inner(df.sym(df.grad(u)), df.grad(v))*dx # - p * df.div(v)*dx # + df.div(u)*q*dx # - df.dot(rho_*grav, v)*dx # ) mom_1 = rho_1 * u_1 if enable_PF: mom_1 += -M_ * drho * df.nabla_grad(g_) F = (per_tau * rho_1 * df.dot(u - u_1, v) * dx + 2 * mu_ * df.inner(df.sym(df.nabla_grad(u)), df.sym(df.nabla_grad(v))) * dx - p * df.div(v) * dx - q * df.div(u) * dx + df.inner(df.nabla_grad(u), df.outer(mom_1, v)) * dx + 0.5 * (per_tau * (rho_ - rho_1) * df.dot(u, v) - df.dot(mom_1, df.nabla_grad(df.dot(u, v)))) * dx - rho_ * df.dot(grav, v) * dx) for boundary_name, slip_length in neumann_bcs["u"].iteritems(): F += 1./slip_length * \ df.dot(u, v) * ds(boundary_to_mark[boundary_name]) for boundary_name, pressure in neumann_bcs["p"].iteritems(): F += pressure * df.inner(normal, v) * ds( boundary_to_mark[boundary_name]) if enable_PF: F += phi_ * df.dot(df.grad(g_), v) * dx if enable_EC: for ci_, ci_1, dbetai, solute in zip(c_, c_1, dbeta, solutes): zi = solute[1] F += df.dot(df.grad(ci_), v)*dx \ + zi*ci_1*df.dot(df.grad(V_), v)*dx if enable_PF: F += ci_ * dbetai * df.dot(df.grad(phi_), v) * dx if p_lagrange: F += (p * q0 + q * p0) * dx if "u" in q_rhs: F += -df.dot(q_rhs["u"], v) * dx a, L = df.lhs(F), df.rhs(F) problem = df.LinearVariationalProblem(a, L, w_NS, dirichlet_bcs) solver = df.LinearVariationalSolver(problem) if use_iterative_solvers and use_pressure_stabilization: solver.parameters["linear_solver"] = "gmres" # solver.parameters["preconditioner"] = "ilu" return solver
# geometric objects n = dlfn.FacetNormal(mesh) P = dlfn.Identity(dim) - dlfn.outer(n, n) # Dirichlet boundary condition on exterior surface bcA = dlfn.DirichletBC(Vh.sub(0), dlfn.Constant((0.0, 0.0, 0.0)), facetIds, bndryId) bcPhi = dlfn.DirichletBC(Vh.sub(1), dlfn.Constant(0.0), facetIds, bndryId) # bilinear form a = dlfn.dot(dlfn.curl(A), dlfn.curl(delA)) * dV() \ + dlfn.dot(A, dlfn.grad(psi)) * dV() \ + dlfn.dot(dlfn.grad(phi), delA) * dV() # rhs form l = dlfn.dot(dlfn.cross(n("+"), jumpM("+")), delA("+")) * dA(intrfcId) # compute solution sol = dlfn.Function(Vh) lin_problem = dlfn.LinearVariationalProblem(a, l, sol, bcs=[bcA, bcPhi]) lin_solver = dlfn.LinearVariationalSolver(lin_problem) lin_solver_parameters = lin_solver.parameters lin_solver.solve() # sub solutions solA = sol.sub(0) solPhi = sol.sub(1) # output to pvd pvd_A = dlfn.File("solution-A.pvd") pvd_A << solA # solving for magnetic field Wh = dlfn.VectorFunctionSpace(mesh, "CG", 1) curlA = dlfn.project(dlfn.curl(solA), Wh) # output to pvd pvd_H = dlfn.File("solution-curlA.pvd") pvd_H << curlA
def __init__(self, V, viscosity=1e-2, penalty=1e5): ''' Parameters ---------- V : dolfin.FunctionSpace Function space for the distance function. viscosity: float or dolfin.Constant Stabilization for the unique solution of the distance problem. penalty: float or dolfin.Constant Penalty for weakly enforcing the zero-distance boundary conditions. ''' if not isinstance(V, dolfin.FunctionSpace): raise TypeError('Parameter `V` must be a `dolfin.FunctionSpace`') if not isinstance(viscosity, Constant): if not isinstance(viscosity, (float, int)): raise TypeError('`Parameter `viscosity`') viscosity = Constant(viscosity) if not isinstance(penalty, Constant): if not isinstance(penalty, (float, int)): raise TypeError('Parameter `penalty`') penalty = Constant(penalty) self._viscosity = viscosity self._penalty = penalty mesh = V.mesh() xs = mesh.coordinates() l0 = (xs.max(0) - xs.min(0)).min() self._d = d = Function(V) self._Q = dolfin.FunctionSpace(mesh, 'DG', 0) self._mf = dolfin.MeshFunction('size_t', mesh, mesh.geometric_dimension()) self._dx_penalty = dx(subdomain_id=1, subdomain_data=self._mf, domain=mesh) v0 = dolfin.TestFunction(V) v1 = dolfin.TrialFunction(V) target_gradient = Constant(1.0) scaled_penalty = penalty / mesh.hmax() lhs_F0 = l0*dot(grad(v0), grad(v1))*dx \ + scaled_penalty*v0*v1*self._dx_penalty rhs_F0 = v0 * target_gradient * dx problem = dolfin.LinearVariationalProblem(lhs_F0, rhs_F0, d) self._linear_solver = dolfin.LinearVariationalSolver(problem) self._linear_solver.parameters["symmetric"] = True F = v0*(grad(d)**2-target_gradient)*dx \ + viscosity*l0*dot(grad(v0), grad(d))*dx \ + scaled_penalty*v0*d*self._dx_penalty J = dolfin.derivative(F, d, v1) problem = dolfin.NonlinearVariationalProblem(F, d, bcs=None, J=J) self._nonlinear_solver = dolfin.NonlinearVariationalSolver(problem) self._nonlinear_solver.parameters['nonlinear_solver'] = 'newton' self._nonlinear_solver.parameters['symmetric'] = False self._solve_initdist_problem = self._linear_solver.solve self._solve_distance_problem = self._nonlinear_solver.solve
def test_scipy_uprime_integration_with_fenics(): iterations = 0 NB_OF_CELLS_X = NB_OF_CELLS_Y = 2 mesh = df.UnitSquare(NB_OF_CELLS_X, NB_OF_CELLS_Y) V = df.FunctionSpace(mesh, 'CG', 1) u0 = df.Constant('1.0') uprime = df.TrialFunction(V) uprev = df.interpolate(u0, V) v = df.TestFunction(V) #ODE is du/dt= uprime = -2*u, exact solution is u(t)=exp(-2*t) a = uprime * v * dx L = -2 * uprev * v * dx uprime_solution = df.Function(V) uprime_problem = df.LinearVariationalProblem(a, L, uprime_solution) uprime_solver = df.LinearVariationalSolver(uprime_problem) def rhs_fenics(y, t): """A somewhat strange case where the right hand side is constant and thus we don't need to use the information in y.""" #print "time: ",t uprev.vector()[:] = y uprime_solver.solve() return uprime_solution.vector().array() def rhs(y, t): """ dy/dt = f(y,t) with y(0)=1 dy/dt = -2y -> solution y(t) = c * exp(-2*t)""" print "time: %g, y=%.10g" % (t, y) tmp = iterations + 1 return -2 * y T_MAX = 2 ts = numpy.arange(0, T_MAX + 0.1, 0.5) ysfenics, stat = scipy.integrate.odeint(rhs_fenics, uprev.vector().array(), ts, printmessg=True, full_output=True) def exact(t, y0=1): return y0 * numpy.exp(-2 * t) print "With fenics:" err_abs = abs(ysfenics[-1][0] - exact(ts[-1])) #use value at mesh done 0 for check print "Error: abs=%g, rel=%g, y_exact=%g" % (err_abs, err_abs / exact(ts[-1]), exact(ts[-1])) fenics_error = err_abs print "Without fenics:" ys = scipy.integrate.odeint(rhs, 1, ts) err_abs = abs(ys[-1] - exact(ts[-1])) print "Error: abs=%g, rel=%g, y_exact=%g" % (err_abs, err_abs / exact(ts[-1]), exact(ts[-1])) non_fenics_error = float(err_abs) print("Difference between fenics and non-fenics calculation: %g" % abs(fenics_error - non_fenics_error)) assert abs(fenics_error - non_fenics_error) < 7e-16 #should also check that solution is the same on all mesh points for i in range(ysfenics.shape[0]): #for all result rows #each row contains the data at all mesh points for one t in ts row = ysfenics[i, :] number_range = abs(row.min() - row.max()) print "row: %d, time %f, range %g" % (i, ts[i], number_range) assert number_range < 10e-16 if False: from IPython.Shell import IPShellEmbed ipshell = IPShellEmbed() ipshell() if False: import pylab pylab.plot(ts, ys, 'o') pylab.plot(ts, numpy.exp(-2 * ts), '-') pylab.show() return stat
# linear forms in interior domain from dolfin import curl, inner, dot, grad a = dlfn.Constant(1. / dt) * dot(A, B) * dV(intId) \ + dlfn.Constant(0.5 * eta) * inner(curl(A), curl(B)) * dV(intId) \ + inner(curl(A), curl(B)) * dV(extId) \ + dot(A, grad(psi)) * dV \ + dot(grad(phi), B) * dV l = dlfn.Constant(1. / dt) * dot(sol_A0, B) * dV(intId) \ - dlfn.Constant(0.5 * eta) * inner(curl(sol_A0), curl(B)) * dV(intId) # boundary condition bcA = dlfn.DirichletBC(Wh.sub(0), dlfn.Constant((0.0, 0.0, 0.0)), facetMeshFun, bndryId) #------------------------------------------------------------------------------# # linear solver #------------------------------------------------------------------------------# problem = dlfn.LinearVariationalProblem(a, l, sol, bcs=bcA) solver = dlfn.LinearVariationalSolver(problem) #------------------------------------------------------------------------------# # initial conditions #------------------------------------------------------------------------------# class ExactVectorPotential(dlfn.Expression): def __init__(self, **kwargs): # user input check assert isinstance(kwargs["cell_data"], dlfn.MeshFunctionSizet) self._cell_data = kwargs["cell_data"] assert isinstance(kwargs["interior_id"], int) assert isinstance(kwargs["exterior_id"], int) self._interior_id = kwargs["interior_id"] self._exterior_id = kwargs["exterior_id"]
def linear_solver(self, u_k): r""" Solves the (linear) Newton iteration Eq. :eq:`Eq_linear_solver` for the IR theory. For the IR theory, the form of the Newton iterations is: .. math:: & - \int \hat{\nabla}\hat{\pi} \cdot \hat{\nabla} v_1 \hat{r}^2 d\hat{r} - \int \hat{Y} v_1 \hat{r}^2 d\hat{r} + & + \int \hat{W} v_2 \hat{r}^2 d\hat{r} -n \int {\hat{Y}_k}^{n-1} \hat{Y} v_2 \hat{r}^2 d\hat{r} + & + \int \hat{Y} v_3 \hat{r}^2 d\hat{r} - \int \left( \frac{m}{M_n} \right)^2 \hat{\pi} v_3 \hat{r}^2 d\hat{r} + \epsilon \left( \frac{M_n}{\Lambda} \right)^{3n-1} \left( \frac{M_{f1}}{M_n} \right)^{n-1} \int \nabla\hat{W} \cdot \nabla v_3 \hat{r}^2 d\hat{r} & = (1-n) \int {\hat{Y}_k}^n v_2 \hat{r}^2 d\hat{r} + \int \frac{\hat{\rho}}{M_P} \frac{M_n}{M_{f1}} v_3 \hat{r}^2 d\hat{r} *Arguments* u_k solution at the previous iteration """ # get the boundary conditions Dirichlet_bc = self.get_Dirichlet_bc() # create a vector (pi,w,y) with the three trial functions for the fields u = d.TrialFunction(self.V) # ... and split it into pi, w, y pi, w, y = d.split(u) # define test functions over the function space v1, v2, v3 = d.TestFunctions(self.V) # split solution at current iteration into pi_k, w_k, y_k - this is only really useful for y_k pi_k, w_k, y_k = d.split(u_k) # cast params as constant functions so that, if they are set to 0, FEniCS still understand # what is being integrated m, Lambda, Mp = Constant(self.fields.m), Constant( self.fields.Lambda), Constant(self.fields.Mp) epsilon = Constant(self.fields.epsilon) Mn, Mf1 = Constant(self.Mn), Constant(self.Mf1) n = self.fields.n # r^2 r2 = Expression('pow(x[0],2)', degree=self.fem.func_degree) # define bilinear form a1 = -inner(grad(pi), grad(v1)) * r2 * dx - y * v1 * r2 * dx a2 = w * v2 * r2 * dx - n * y_k**(n - 1) * y * v2 * r2 * dx a3 = y * v3 * r2 * dx - ( m / Mn )**2 * pi * v3 * r2 * dx + \ epsilon * ( Mn / Lambda )**(3*n-1) * ( Mf1 / Mn )**(n-1) * inner( grad(w), grad(v3) ) * r2 * dx a = a1 + a2 + a3 # define linear form # we have L1 = 0. L2 = (1 - n) * y_k**n * v2 * r2 * dx L3 = self.source.rho / Mp * Mn / Mf1 * v3 * r2 * dx L = L2 + L3 # define a vector with the solution sol = d.Function(self.V) # solve linearised system pde = d.LinearVariationalProblem(a, L, sol, Dirichlet_bc) solver = d.LinearVariationalSolver(pde) solver.solve() return sol
def NL_initial_guess(self, y_method='vector'): r""" Obtains an initial guess for the Galileon equation of motion by assuming the nonlinear term is dominant, i.e.: .. math:: -\frac{\epsilon}{\Lambda^{3n-1}}\nabla^2(\nabla^2\pi^n) \approx \frac{\rho}{M_P} The initial guess is computed by first solving the Poisson equation: .. math:: -\hat{\nabla}\hat{W} =\left( \frac{\Lambda}{M_n} \right)^{3n-1} \left(\frac{M_n}{M_{f1}}\right)^n \frac{\hat{\rho}}{\epsilon M_P} and then obtaining :math:`\hat{Y}=\sqrt[n]{\hat{W}}` through one of three methods explained below. Finally, :math:`\hat{\pi}` is computed by solving the Poisson equation .. math:: \hat{\nabla}\hat{\pi} = \hat{Y}. The main methods to obtain :math:`\hat{Y}` from :math:`\hat{Z}` are interpolation and projection: the standard FEniCS implementation for both can be chosen by setting `y\_method='interpolate'` and `y\_method='project'`. A third method (`y\_method='vector'`, default), formally identical to interpolation, consists in assigning :math:`\hat{Y}`'s value at all nodes through e.g.: .. code-block:: python y.vector().set_local( np.sqrt( np.abs( w.vector().get_local() ) ) ) However, because of differences in the implementations of :math:`\sqrt[n]{\cdot}` called by the two methods, the latter generally gives better results compared to `'interpolate'`. *Arguments* y_method `'vector'` (default), `'interpolate'` or `'project'` """ # cast params as constant functions so that, if they are set to 0, FEniCS still understand # what is being integrated m, Lambda, Mp = Constant(self.fields.m), Constant( self.fields.Lambda), Constant(self.fields.Mp) epsilon = Constant(self.fields.epsilon) Mn, Mf1 = Constant(self.Mn), Constant(self.Mf1) n = self.fields.n # get the boundary conditions for w only def boundary(x): return self.fem.mesh.r_max - x[0] < d.DOLFIN_EPS wD = Constant(0.) w_Dirichlet_bc = d.DirichletBC(self.fem.S, wD, boundary, method='pointwise') # define trial and test function w_ = d.TrialFunction(self.fem.S) v_ = d.TestFunction(self.fem.S) # for the measure r2 = Expression('pow(x[0],2)', degree=self.fem.func_degree) # bilinear and linear forms w_a = inner(grad(w_), grad(v_)) * r2 * dx w_L = (Lambda / Mn)**(3 * n - 1) * ( Mn / Mf1)**n / epsilon * self.source.rho / Mp * v_ * r2 * dx # define a function for the solution w = d.Function(self.fem.S) # solve w_pde = d.LinearVariationalProblem(w_a, w_L, w, w_Dirichlet_bc) w_solver = d.LinearVariationalSolver(w_pde) print('Getting NL initial guess...') w_solver.solve() # now we have w. we can obtain y by projection or interpolation if y_method == 'interpolate': # I use the functions sqrt and cbrt because they're more precise than pow(w,1/n) if n == 2: code = "sqrt(fabs(w))" elif n == 3: code = "cbrt(w)" else: if n % 2 == 0: # even power code = "pow(fabs(w),1./n)" else: # odd power code = "pow(w,1./n)" y_expression = Expression(code, w=w, n=n, degree=self.fem.func_degree) y = d.interpolate(y_expression, self.fem.S) elif y_method == 'vector': # this should formally be identical to 'interpolate', but it's a workaround to this # potential FEniCS bug which occurs in the previous code block: # https://bitbucket.org/fenics-project/dolfin/issues/1079/interpolated-expression-gives-wrong-result y = d.Function(self.fem.S) if n == 2: y.vector().set_local(np.sqrt(np.abs(w.vector().get_local()))) elif n == 3: y.vector().set_local(np.cbrt(np.abs(w.vector().get_local()))) else: if n % 2 == 0: # even power y.vector().set_local( np.abs(w.vector().get_local())**(1. / self.fields.n)) else: # odd power y.vector().set_local( w.vector().get_local()**(1. / self.fields.n)) elif y_method == 'project': y = w**(1. / n) y = project(y, self.fem.S, self.fem.func_degree) # we obtain pi by solving Del pi = y piD = Constant(0.) pi_Dirichlet_bc = d.DirichletBC(self.fem.S, piD, boundary, method='pointwise') pi_ = d.TrialFunction(self.fem.S) v_ = d.TestFunction(self.fem.S) pi_a = -inner(grad(pi_), grad(v_)) * r2 * dx pi_L = y * v_ * r2 * dx pi = d.Function(self.fem.S) pi_pde = d.LinearVariationalProblem(pi_a, pi_L, pi, pi_Dirichlet_bc) pi_solver = d.LinearVariationalSolver(pi_pde) pi_solver.solve() # now let's pack pi, w, y into a single function guess = d.Function(self.V) fa = d.FunctionAssigner(self.V, [self.fem.S, self.fem.S, self.fem.S]) fa.assign(guess, [pi, w, y]) return guess
def KG_initial_guess(self): r""" Obtains an initial guess for the Galileon equation of motion by assuming the nonlinear term is subdominant, i.e.: .. math:: \Box\pi - m^2\pi \approx \frac{\rho}{Mp} The initial guess is computed by first solving the system of equations: .. math:: & \hat{\nabla}^2\hat{\pi} = \hat{Y} \\ & \hat{Y} - \left( \frac{m}{M_n} \right)^2\pi = \frac{\hat{\rho}}{M_p} and then obtaining :math:`\hat{W}=\hat{Y}^n` by projection. """ # define a function space for (pi, y) only piy_E = d.MixedElement([self.fem.Pn, self.fem.Pn]) piy_V = d.FunctionSpace(self.fem.mesh.mesh, piy_E) # get the boundary conditions for pi and y only piD, yD = Constant(0.), Constant(0.) def boundary(x): return self.fem.mesh.r_max - x[0] < d.DOLFIN_EPS bc_pi = d.DirichletBC(piy_V.sub(0), piD, boundary, method='pointwise') bc_y = d.DirichletBC(piy_V.sub(1), yD, boundary, method='pointwise') Dirichlet_bc = [bc_pi, bc_y] # Trial functions for pi and y u = d.TrialFunction(piy_V) pi, y = d.split(u) # test functions for the two equations v1, v3 = d.TestFunctions(piy_V) # cast params as constant functions so that, if they are set to 0, FEniCS still understand # what is being integrated m, Mp, Mn, Mf1 = Constant(self.fields.m), Constant( self.fields.Mp), Constant(self.Mn), Constant(self.Mf1) n = self.fields.n # r^2 r2 = Expression('pow(x[0],2)', degree=self.fem.func_degree) # bilinear form a1 = -inner(grad(pi), grad(v1)) * r2 * dx - y * v1 * r2 * dx a3 = y * v3 * r2 * dx - (m / Mn)**2 * pi * v3 * r2 * dx a = a1 + a3 # linear form (L1=0) L3 = self.source.rho / Mp * Mn / Mf1 * v3 * r2 * dx L = L3 # solve system sol = d.Function(piy_V) pde = d.LinearVariationalProblem(a, L, sol, Dirichlet_bc) solver = d.LinearVariationalSolver(pde) print('Getting KG initial guess...') solver.solve() # split solution into pi and y - cast as independent functions, not components of a vector function pi, y = sol.split(deepcopy=True) # obtain w by projecting y**n w = y**n w = project(w, self.fem.S, self.fem.func_degree) # and now pack pi, w, y into one function... guess = d.Function(self.V) # this syntax is because, even though pi and y are effectively defined on fem.S, from fenics point # of view, they are obtained as splits of a function fa = d.FunctionAssigner( self.V, [pi.function_space(), self.fem.S, y.function_space()]) fa.assign(guess, [pi, w, y]) return guess