def solve(self, **arguments): t_start = time.clock() # definig Function space on this mesh using Lagrange #polynoimals of degree 1. H = FunctionSpace(self.mesh, "CG", 1) # Setting up the variational problem v = TrialFunction(H) w = TestFunction(H) epsilon = Constant(arguments[Components().Diffusion]) f = Constant(0) a = (epsilon * inner(grad(v), grad(w)) + inner(v, w)) * dx #Still have to figure it how to use a Neumann Condition here L = f * w * dx # solving the variational problem. v = Function(H) solve(a == L, v) self.solution.extend(v.vector().array()) return [self.solution, time.clock() - t_start]
def solve(self, **arguments): t_start = time.clock() # definig Function space on this mesh using Lagrange #polynoimals of degree 1. H = FunctionSpace(self.mesh, "CG", 1) # Setting up the variational problem v = TrialFunction(H) w = TestFunction(H) epsilon = Constant(arguments[Components().Diffusion]) f = Expression("(1 - epsilon*4*pow(pi,2))*cos(2*pi*x[0])",\ epsilon=epsilon, degree=1) a = (epsilon * inner(grad(v), grad(w)) + inner(v, w)) * dx L = f * w * dx # solving the variational problem. v = Function(H) solve(a == L, v) self.solution.extend(v.vector().array()) return [self.solution, time.clock() - t_start]
def solve(self, **arguments): t_start = time.clock() # definig Function space on this mesh using Lagrange #polynoimals of degree 1. H = FunctionSpace(self.mesh, "CG", 1) # Setting up the variational problem v = TrialFunction(H) w = TestFunction(H) coeff_dx2 = Constant(1) coeff_v = Constant(1) f = Expression("(4*pow(pi,2))*exp(-(1/coeff_v)*t)*sin(2*pi*x[0])", {'coeff_v': coeff_v}, degree=2) v0 = Expression("sin(2*pi*x[0])", degree=2) f.t = 0 def boundary(x, on_boundary): return on_boundary bc = DirichletBC(H, v0, boundary) v1 = interpolate(v0, H) dt = self.steps.time a = (dt * inner(grad(v), grad(w)) + dt * coeff_v * inner(v, w)) * dx L = (f * dt - coeff_v * v1) * w * dx A = assemble(a) v = Function(H) T = self.domain.time[-1] t = dt # solving the variational problem. while t <= T: b = assemble(L, tensor=b) vo.t = t bc.apply(A, b) solve(A, v.vector(), b) t += dt v1.assign(v) self.solution.extend(v.vector().array()) return [self.solution, time.clock() - t_start]
def project_gradient_neumann( f0, degree=None, mesh=None, solver_type='gmres', preconditioner_type='default' ): """Find an approximation to f0 that has the same gradient The resulting function also satisfies homogeneous Neumann boundary conditions. Parameters: f0: the function to approximate mesh=None: the mesh on which to approximate it If not provided, the mesh is extracted from f0. degree=None: degree of the polynomial approximation. extracted from f0 if not provided. solver_type='gmres': The linear solver type to use. preconditioner_type='default': Preconditioner type to use """ if not mesh: mesh = f0.function_space().mesh() element = f0.ufl_element() if not degree: degree = element.degree() CE = FiniteElement('CG', mesh.ufl_cell(), degree) CS = FunctionSpace(mesh, CE) DE = FiniteElement('DG', mesh.ufl_cell(), degree) DS = FunctionSpace(mesh, DE) CVE = VectorElement('CG', mesh.ufl_cell(), degree - 1) CV = FunctionSpace(mesh, CVE) RE = FiniteElement('R', mesh.ufl_cell(), 0) R = FunctionSpace(mesh, RE) CRE = MixedElement([CE, RE]) CR = FunctionSpace(mesh, CRE) f = fe.project(f0, CS, solver_type=solver_type, preconditioner_type=preconditioner_type) g = fe.project(fe.grad(f), CV, solver_type=solver_type, preconditioner_type=preconditioner_type) lf = fe.project(fe.nabla_div(g), CS, solver_type=solver_type, preconditioner_type=preconditioner_type) tf, tc = TrialFunction(CR) wf, wc = TestFunctions(CR) dx = Measure('dx', domain=mesh, metadata={'quadrature_degree': min(degree, 10)}) a = (fe.dot(fe.grad(tf), fe.grad(wf)) + tc * wf + tf * wc) * dx L = (f * wc - lf * wf) * dx igc = Function(CR) fe.solve(a == L, igc, solver_parameters={'linear_solver': solver_type, 'preconditioner': preconditioner_type} ) ig, c = igc.sub(0), igc.sub(1) igd = fe.project(ig, DS, solver_type=solver_type, preconditioner_type=preconditioner_type) return igd
def run_fokker_planck(nx, num_steps, t_0 = 0, t_final=10): # define mesh mesh = IntervalMesh(nx, -200, 200) # define function space. V = FunctionSpace(mesh, "Lagrange", 1) # Homogenous Neumann BCs don't have to be defined as they are the default in dolfin # define parameters. dt = (t_final-t_0) / num_steps # set mu and sigma mu = Constant(-1) D = Constant(1) # define initial conditions u_0 u_0 = Expression('x[0]', degree=1) # set U_n to be the interpolant of u_0 over the function space V. Note that # u_n is the value of u at the previous timestep, while u is the current value. u_n = interpolate(u_0, V) # Define variational problem u = TrialFunction(V) v = TestFunction(V) F = u*v*dx + dt*inner(D*grad(u), grad(v))*dx + dt*mu*grad(u)[0]*v*dx - inner(u_n, v)*dx # isolate the bilinear and linear forms. a, L = lhs(F), rhs(F) # initialize function to capture solution. t = 0 u_h = Function(V) plt.figure(figsize=(15, 15)) # time-stepping section for n in range(num_steps): t += dt # compute solution solve(a == L, u_h) u_n.assign(u_h) # Plot solutions intermittently if n % (num_steps // 10) == 0 and num_steps > 0: plot(u_h, label='t = %s' % t) plt.legend() plt.grid() plt.title("Finite Element Solutions to Fokker-Planck Equation with $\mu(x, t) = -(x+1)$ , $D(x, t) = e^t x^2$, $t_n$ = %s" % t_final) plt.ylabel("$u(x, t)$") plt.xlabel("x") plt.savefig("fpe/fokker-planck-solutions-mu.png") plt.clf() # return the approximate solution evaluated on the coordinates, and the actual coordinates. return u_n.compute_vertex_values(), mesh.coordinates()
def dual_error_estimates(resolution): mesh = UnitSquareMesh(resolution, resolution) def all_boundary(_, on_boundary): return on_boundary zero = Constant(0.0) def a(u, v): return inner(grad(u), grad(v)) * dx def L(f, v): return f * v * dx # Primal problem f = Expression("32.*x[0]*(1. - x[0])+32.*x[1]*(1. - x[1])", domain=mesh, degree=5) ue = Expression("16.*x[0]*(1. - x[0])*x[1]*(1. - x[1])", domain=mesh, degree=5) Qp = FunctionSpace(mesh, 'CG', 1) bcp = DirichletBC(Qp, zero, all_boundary) u = TrialFunction(Qp) v = TestFunction(Qp) U = Function(Qp) solve(a(u, v) == L(f, v), U, bcp) # Dual problem Qd = FunctionSpace(mesh, 'CG', 2) psi = Constant(1.0) bcd = DirichletBC(Qd, zero, all_boundary) w = TestFunction(Qd) phi = TrialFunction(Qd) Phi = Function(Qd) solve(a(w, phi) == L(psi, w), Phi, bcd) # Compute errors e1 = compute_error(ue, U) e2 = assemble((inner(grad(U), grad(Phi)) - f * Phi) * dx) print("e1 = {}".format(e1)) print("e2 = {}".format(e2))
def test_multiplication(self): print( '\n== 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=2) # 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=2) # 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, "CG", 2 * pol_order) # double-grid space print('assembling local matrices for DoGIP...') Bhat = get_Bhat( dim, pol_order, problem=0) # projection between V on W on a reference element AT_dogip = get_A_T(m, V, W, problem=0) dofmapV = V.dofmap() def system_multiplication_DoGIP(AT_dogip, Bhat, u_vec): # mutliplication with DoGIP decomposition Au = np.zeros_like(u_vec) for ii, cell in enumerate(cells(mesh)): ind = dofmapV.cell_dofs(ii) # local to global map Au[ind] += Bhat.T.dot(AT_dogip[ii] * Bhat.dot(u_vec[ind])) return Au print('assembling FEM sparse matrix') u, v = TrialFunction(V), TestFunction(V) Asp = assemble(m * u * v * dx, tensor=EigenMatrix()) # Asp = Asp.sparray() print('multiplication...') ur = Function(V) # creating random vector ur_vec = 5 * 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')
def problem_mix(T, dt, E, coupling, VV, boundaries, rho_s, lambda_, mu_s, f, bcs, **Solid_namespace): # Temporal parameters t = 0 k = Constant(dt) # Split problem to two 1.order differential equations psi, phi = TestFunctions(VV) # Functions, wd is for holding the solution d_ = {} w_ = {} wd_ = {} for time in ["n", "n-1", "n-2", "n-3"]: if time == "n" and E not in [None, reference]: tmp_wd = Function(VV) wd_[time] = tmp_wd wd = TrialFunction(VV) w, d = split(wd) else: wd = Function(VV) wd_[time] = wd w, d = split(wd) d_[time] = d w_[time] = w # Time derivative if coupling == "center": G = rho_s / (2 * k) * inner(w_["n"] - w_["n-2"], psi) * dx else: G = rho_s / k * inner(w_["n"] - w_["n-1"], psi) * dx # Stress tensor G += inner(Piola2(d_, w_, k, lambda_, mu_s, E_func=E), grad(psi)) * dx # External forces, like gravity G -= rho_s * inner(f, psi) * dx # d-w coupling if coupling == "CN": G += inner(d_["n"] - d_["n-1"] - k * 0.5 * (w_["n"] + w_["n-1"]), phi) * dx elif coupling == "imp": G += inner(d_["n"] - d_["n-1"] - k * w_["n"], phi) * dx elif coupling == "exp": G += inner(d_["n"] - d_["n-1"] - k * w_["n-1"], phi) * dx elif coupling == "center": G += innter(d_["n"] - d_["n-2"] - 2 * k * w["n-1"], phi) * dx else: print "The coupling %s is not implemented, 'CN', 'imp', and 'exp' are the only valid choices." sys.exit(0) # Solve if E in [None, reference]: solver_nonlinear(G, d_, w_, wd_, bcs, T, dt, **Solid_namespace) else: solver_linear(G, d_, w_, wd_, bcs, T, dt, **Solid_namespace)
def test_DoGIP_vs_FEniCS(self): print( '\n== testing DoGIP vs. FEniCS for problem of weighted projection ====' ) for dim, pol_order in itertools.product([2, 3], [1, 2]): print('dim={}; pol_order={}'.format(dim, pol_order)) N = 2 # creating MESH, defining MATERIAL and SOURCE if dim == 2: mesh = UnitSquareMesh(N, N) m = Expression("1+10*16*x[0]*(1-x[0])*x[1]*(1-x[1])", degree=3) # material coefficients f = Expression("x[0]*x[0]*x[1]", degree=2) elif dim == 3: mesh = UnitCubeMesh(N, N, N) m = Expression("1+100*x[0]*(1-x[0])*x[1]*x[2]", degree=2) # material coefficients f = Expression("(1-x[0])*x[1]*x[2]", degree=2) mesh.coordinates()[:] += 0.1 * np.random.random( mesh.coordinates().shape) # mesh perturbation ## standard approach with FEniCS ############################################# V = FunctionSpace(mesh, "CG", pol_order) # original FEM space u, v = TrialFunction(V), TestFunction(V) u_fenics = Function(V) solve(m * u * v * dx == m * f * v * dx, u_fenics) ## DoGIP - double-grid integration with interpolation-projection ############# W = FunctionSpace(mesh, "CG", 2 * pol_order) # double-grid space w = TestFunction(W) A_dogip = assemble( m * w * dx).get_local() # diagonal matrix of material coefficients b = assemble(m * f * v * dx) # vector of right-hand side # assembling interpolation-projection matrix B B = get_B(V, W, problem=0) # # linear solver on double grid, standard Afun = lambda x: B.T.dot(A_dogip * B.dot(x)) Alinoper = linalg.LinearOperator((V.dim(), V.dim()), matvec=Afun, dtype=np.float) x, info = linalg.cg(Alinoper, b.get_local(), x0=np.zeros(V.dim()), tol=1e-10, maxiter=1e3, callback=None) # testing the difference between DoGIP and FEniCS self.assertAlmostEqual( 0, np.linalg.norm(u_fenics.vector().get_local() - x)) print('...ok')
def __init__(self, *, V: Optional[FunctionSpace], mesh: Mesh, dt: float) -> None: V = V or FunctionSpace(mesh, "Lagrange", 1) self.V = V self.T = TrialFunction(V) self.v = TestFunction(V) self.T_prev = Function(V) self.P_s = Constant(1.0) self.c_s = Constant(1.0) self.k_e = Constant(1.0) self.Q_pc = Constant(1.0) self.Q_sw = Constant(1.0) self.Q_mm = Constant(1.0) self.dt = Constant(dt)
def determine_gradient(V_g, u, flux): """ compute flux following http://hplgit.github.io/INF5620/doc/pub/fenics_tutorial1.1/tu2.html#tut-poisson-gradu :param mesh :param u: solution where gradient is to be determined :return: """ w = TrialFunction(V_g) v = TestFunction(V_g) a = inner(w, v) * dx L = inner(grad(u), v) * dx solve(a == L, flux)
def _solve(self, z, x=None): # problem variables du = TrialFunction(self.V) # incremental displacement v = TestFunction(self.V) # test function u = Function(self.V) # displacement from previous iteration # kinematics ii = Identity(3) # identity tensor dimension 3 f = ii + grad(u) # deformation gradient c = f.T * f # right Cauchy-Green tensor # invariants of deformation tensors ic = tr(c) j = det(f) # elasticity parameters if type(z) in [list, np.ndarray]: param = self.param_remapper(z[0]) if self.param_remapper is not None else z[0] else: param = self.param_remapper(z) if self.param_remapper is not None else z e_var = variable(Constant(param)) # Young's modulus nu = Constant(.3) # Shear modulus (Lamè's second parameter) mu, lmbda = e_var / (2 * (1 + nu)), e_var * nu / ((1 + nu) * (1 - 2 * nu)) # strain energy density, total potential energy psi = (mu / 2) * (ic - 3) - mu * ln(j) + (lmbda / 2) * (ln(j)) ** 2 pi = psi * dx - self.time * dot(self.f, u) * self.ds(3) ff = derivative(pi, u, v) # compute first variation of pi jj = derivative(ff, u, du) # compute jacobian of f # solving if x is not None: numeric_evals = np.zeros(shape=(x.shape[1], len(self.times))) evals = np.zeros(shape=(x.shape[1], len(self.eval_times))) else: numeric_evals = None evals = None for it, t in enumerate(self.times): self.time.t = t self.solver(ff == 0, u, self.bcs, J=jj, bcs=self.bcs, solver_parameters=self.solver_parameters) if x is not None: numeric_evals[:, it] = np.log(np.array([-u(x_)[2] for x_ in x.T]).T) # time-interpolation if x is not None: for i in range(evals.shape[0]): evals[i, :] = np.interp(self.eval_times, self.times, numeric_evals[i, :]) return (evals, u) if x is not None else u
def determine_gradient(V_g, u, flux): """ compute flux following http://hplgit.github.io/INF5620/doc/pub/fenics_tutorial1.1/tu2.html#tut-poisson-gradu :param V_g: Vector function space :param u: solution where gradient is to be determined :param flux: returns calculated flux into this value """ w = TrialFunction(V_g) v = TestFunction(V_g) a = inner(w, v) * dx L = inner(grad(u), v) * dx solve(a == L, flux)
def __init__(self, grid_shape, f, init_z, dirichlet, degree=1, polynomial_type='P', reparam=True): """Parameters ---------- grid_shape : numpy.array or list Defines the grid dimensions of the mesh used to solve the problem. f : str Source term of the Poisson equation in a form accepted by FEniCS (C++ style string) init_z : numpy.ndarray Placeholder value(s) for parameters of the model. dirichlet : str Dirichlet boundary conditions in string form accepted by FEniCS. degree : int, default 1 Polynomial degree for the functional space. polynomial_type : str, default 'P' String encoding the type of polynomials in the functional space, according to FEniCS conventions (defaults to Lagrange polynomials). reparam: bool, default True Boolean indicating whether input parameters are to be reparametrized according to an inverse-logit transform. """ def boundary(x, on_boundary): return on_boundary self.grid_shape = grid_shape self.mesh = UnitSquareMesh(*grid_shape) self.V = FunctionSpace(self.mesh, polynomial_type, degree) self.dirichlet = DirichletBC(self.V, Expression(dirichlet, degree=degree + 3), boundary) self._paramnames = ['param{}'.format(i) for i in range(len(init_z))] self.f = Expression(f, degree=degree, **dict(zip(self._paramnames, init_z))) u = TrialFunction(self.V) v = TestFunction(self.V) self.a = dot(grad(u), grad(v)) * dx self.L = self.f * v * dx self.u = Function(self.V) self.reparam = reparam self.solver = CountIt(solve)
def local_project(v, V, u=None): """Element-wise projection using LocalSolver""" dv = TrialFunction(V) v_ = TestFunction(V) a_proj = inner(dv, v_) * dx b_proj = inner(v, v_) * dx solver = LocalSolver(a_proj, b_proj) solver.factorize() if u is None: u = Function(V) solver.solve_local_rhs(u) return u else: solver.solve_local_rhs(u) return
def compute_steady_state(self): names = {'Cl', 'Na', 'K'} P1 = FiniteElement('P', fe.triangle, 1) element = MixedElement([P1, P1, P1]) V = FunctionSpace(self.mesh, element) self.V_conc = V (u_cl, u_na, u_k) = TrialFunction(V) (v_cl, v_na, v_k) = TestFunction(V) assert (self.flow is not None) n = fe.FacetNormal(self.mesh) # F = ( self.F_diff_conv(u_cl, v_cl, n, grad(self.phi), 1. ,1., 0.) # + self.F_diff_conv(u_na, v_na, n, grad(self.phi), 1. ,1., 0.) # + self.F_diff_conv(u_k , v_k , n, grad(self.phi), 1. ,1., 0.) ) dx, ds = self.dx, self.ds flow = self.flow F = inner(grad(u_cl), grad(v_cl)) * dx \ + inner(flow, grad(u_cl)) * v_cl * dx \ + inner(grad(u_na), grad(v_na)) * dx \ + inner(flow, grad(u_na)) * v_na * dx \ + inner(grad(u_k), grad(v_k)) * dx \ + inner(flow, grad(u_k)) * v_k * dx a, L = fe.lhs(F), fe.rhs(F) a_mat = fe.assemble(a) L_vec = fe.assemble(L) # solve u = Function(V) fe.solve(a_mat, u.vector(), L_vec) u_cl, u_na, u_k = u.split() output1 = fe.File('/tmp/steady_state_cl.pvd') output1 << u_cl output2 = fe.File('/tmp/steady_state_na.pvd') output2 << u_na output3 = fe.File('/tmp/steady_state_k.pvd') output3 << u_k self.u_cl = u_cl self.u_na = u_na self.u_k = u_k
def __init__(self, *, V: Optional[FunctionSpace], mesh: Mesh, dt: float): V = V or FunctionSpace(mesh, "Lagrange", 1) self.V = V self.u = TrialFunction(V) self.v = TestFunction(V) self.P_prev = Function(V) self.theta_a = Constant(1.0) self.D_e = Constant(1.0) self.P_d = Constant(1.0) self.my_v = Constant(1.0) self.my_d = Constant(1.0) self.alpha_th = Constant(1.0) self.M_mm = Constant(1.0) self.q_h = Constant(1.0) self.T_s = project(Constant(-5.0), V) self.dt = Constant(dt) self.mesh = mesh
def __init__(self, V): u = TrialFunction(V) v = TestFunction(V) A = assemble(inner(u, v) * dx, tensor=EigenMatrix()) self.V = V self.v = v try: import sksparse from sksparse.cholmod import cholesky # sparse Cholesky decomposition print('...projection with cholesky of version {}'.format( sksparse.__version__)) self.solve = cholesky(A.sparray().tocsc()) except: print('...projection with LU decomposition') invA = scipy.sparse.linalg.splu( A.sparray().tocsc()) # sparse LU decomposition self.solve = invA.solve
def solve_wave_equation(u0, u1, u_boundary, f, domain, mesh, degree): """Solving the wave equation using CG-CG method. Args: u0: Initial data. u1: Initial velocity. u_boundary: Dirichlet boundary condition. f: Right-hand side. domain: Space-time domain. mesh: Computational mesh. degree: CG(degree) will be used as the finite element. Outputs: uh: Numerical solution. """ # Element V = FunctionSpace(mesh, "CG", degree) # Measures on the initial and terminal slice mask = MeshFunction("size_t", mesh, mesh.topology().dim() - 1, 0) domain.get_initial_slice().mark(mask, 1) ends = ds(subdomain_data=mask) # Form g = Constant(((-1.0, 0.0), (0.0, 1.0))) u = TrialFunction(V) v = TestFunction(V) a = dot(grad(v), dot(g, grad(u))) * dx L = f * v * dx + u1 * v * ends(1) # Assembled matrices A = assemble(a, keep_diagonal=True) b = assemble(L, keep_diagonal=True) # Spatial boundary condition bc = DirichletBC(V, u_boundary, domain.get_spatial_boundary()) bc.apply(A, b) # Temporal boundary conditions (by hand) (A, b) = apply_time_boundary_conditions(domain, V, u0, A, b) # Solve solver = LUSolver() solver.set_operator(A) uh = Function(V) solver.solve(uh.vector(), b) return uh
def compute_static_deformation(self): assert self.mesh is not None # now we define subdomains on the mesh bottom = fe.CompiledSubDomain('near(x[2], 0) && on_boundary') top = fe.CompiledSubDomain('near(x[2], 1) && on_boundary') # middle = fe.CompiledSubDomain('x[2] > 0.3 && x[2] < 0.7') # Initialize mesh function for interior domains self.domains = fe.MeshFunction('size_t', self.mesh, 3) self.domains.set_all(0) # middle.mark(self.domains, 1) # Initialize mesh function for boundary domains self.boundaries = fe.MeshFunction('size_t', self.mesh, 2) self.boundaries.set_all(0) bottom.mark(self.boundaries, 1) top.mark(self.boundaries, 2) # Define new measures associated with the interior domains and # exterior boundaries self.dx = fe.Measure('dx', domain=self.mesh, subdomain_data=self.domains) self.ds = fe.Measure('ds', domain=self.mesh, subdomain_data=self.boundaries) # define function spaces V = fe.VectorFunctionSpace(self.mesh, "Lagrange", 1) # now we define subdomains on the mesh bottom = fe.CompiledSubDomain('near(x[2], 0) && on_boundary') top = fe.CompiledSubDomain('near(x[2], 1) && on_boundary') # middle = fe.CompiledSubDomain('x[2] > 0.3 && x[2] < 0.7') d = self.mesh.geometry().dim() # Initialize mesh function for interior domains self.domains = fe.MeshFunction('size_t', self.mesh, d) self.domains.set_all(0) # middle.mark(self.domains, 1) # Initialize mesh function for boundary domains self.boundaries = fe.MeshFunction('size_t', self.mesh, d - 1) self.boundaries.set_all(0) bottom.mark(self.boundaries, 1) top.mark(self.boundaries, 2) # Define new measures associated with the interior domains and # exterior boundaries self.dx = fe.Measure('dx', domain=self.mesh, subdomain_data=self.domains) self.ds = fe.Measure('ds', domain=self.mesh, subdomain_data=self.boundaries) c_zero = fe.Constant((0, 0, 0)) # define boundary conditions bc_bottom = fe.DirichletBC(V, c_zero, bottom) bc_top = fe.DirichletBC(V, c_zero, top) bcs = [bc_bottom] # , bc_top] # define functions du = TrialFunction(V) v = TestFunction(V) u = Function(V) B = fe.Constant((0., 2.0, 0.)) T = fe.Constant((0.0, 0.0, 0.0)) d = u.geometric_dimension() I = fe.Identity(d) F = I + grad(u) C = F.T * F I_1 = tr(C) J = det(F) E, mu = 10., 0.3 mu, lmbda = fe.Constant(E / (2 * (1 + mu))), fe.Constant( E * mu / ((1 + mu) * (1 - 2 * mu))) # stored energy (comp. neo-hookean model) psi = (mu / 2.) * (I_1 - 3) - mu * fe.ln(J) + (lmbda / 2.) * (fe.ln(J))**2 dx = self.dx ds = self.ds Pi = psi * fe.dx - dot(B, u) * fe.dx - dot(T, u) * fe.ds F = fe.derivative(Pi, u, v) J = fe.derivative(F, u, du) fe.solve(F == 0, u, bcs, J=J) # save results self.u = u # write to disk output = fe.File("/tmp/static.pvd") output << u
def solve_flem(model_space, physical_space, flow, u_n, mesh, V, bc, dt, num_steps, out_time, plot, statistics, name): """ Solve for landscape evolution This function does hte hard work. First the model domain is created. Then we loop through time and solve the diffusion equation to solve for landscape evolution. Output can be saved as vtk files at every "out_time" specified. Plots using fenics inbuilt library can be visualised at every "plot_time" This function returns a 1d numpy array of time, sediment flux and if statistics is turned on a 2d numpy array of the final wavelength of the landscape. :param model_space: list of domain variables, [lx,ly,res] :param physical_space: list of physical parameters, [kappa, c, nexp, alpha, U] :param flow: 0 = MFD node-to-node; 1 = MFD cell-to-cell; 2 = SD node-to-node; 3 = SD cell-to-cell :param u_n: elevation function :param mesh: dolphyn mesh :param V: fenics functionspace :param bc: boundary conditions :param dt: time step size in years :param num_steps: number of time steps :param out_time: time steps to output vtk files (0=none) :param plot: plot sediment flux (0=off,1=on) :param statistics: output statistics of landscape (0=off,1=on) :param name: directory name for output vtk files :return: sed_flux, time, wavelength """ # Domain dimensions lx = model_space[0] ly = model_space[1] # Physical parameters kappa = physical_space[0] # diffusion coefficient c = physical_space[1] # discharge transport coefficient nexp = physical_space[2] # discharge exponent alpha = physical_space[3] # precipitation rate De = c * pow(alpha * ly, nexp) / kappa uamp = physical_space[4] * ly / kappa # uplift dt = dt * kappa / (ly * ly) # time step size sed_flux = np.zeros(num_steps) # array to store sediment flux time = np.zeros(num_steps) # Define variational problem u = TrialFunction(V) v = TestFunction(V) f = Constant(uamp) # 0 = MFD node-to-node; 1 = MFD cell-to-cell; 2 = SD node-to-node; 3 = SD cell-to-cell if flow == 0: q_n = mfd_nodenode(mesh, V, u_n, De, nexp) if flow == 1: q_n = mfd_cellcell(mesh, V, u_n, De, nexp) if flow == 2: q_n = sd_nodenode(mesh, V, u_n, De, nexp) if flow == 3: q_n = sd_cellcell(mesh, V, u_n, De, nexp) F = u * v * dx + dt * q_n * dot(grad(u), grad(v)) * dx - (u_n + dt * f) * v * dx a, L = lhs(F), rhs(F) # Solution and sediment flux u = Function(V) q_s = Expression('u0 + displ - u1', u0=u_n, displ=Constant(uamp * dt), u1=u, degree=2) # Iterate t = 0 i = 0 for n in range(num_steps): # This needs to become an option! # Double rain fall # if n == 501: # alpha = 2 # De = c*pow(alpha*ly,nexp)/kappa # Update current time t += dt # Compute solution solve(a == L, u, bc) # Calculate sediment flux sed_flux[i] = assemble(q_s * dx(mesh)) time[i] = t i += 1 # Update previous solution u_n.assign(u) # Update flux # 0 = MFD node-to-node; 1 = MFD cell-to-cell; 2 = SD node-to-node; 3 = SD cell-to-cell if flow == 0: q = mfd_nodenode(mesh, V, u_n, De, nexp) if flow == 1: q = mfd_cellcell(mesh, V, u_n, De, nexp) if flow == 2: q = sd_nodenode(mesh, V, u_n, De, nexp) if flow == 3: q = sd_cellcell(mesh, V, u_n, De, nexp) q_n.assign(q) # Output solutions if out_time != 0: if np.mod(n, out_time) == 0: filename = '%s/u_solution_%d.pvd' % (name, n) vtkfile = File(filename) vtkfile << u filename = '%s/q_solution_%d.pvd' % (name, n) vtkfile = File(filename) vtkfile << q # Post processing if plot != 0: plt.plot(time * 1e-6 * ly * ly / kappa, sed_flux / dt * kappa, 'k', linewidth=2) plt.xlabel('Time (Myr)') plt.ylabel('Sediment Flux (m^2/yr)') sedname = '%s/sed_flux_%d.svg' % (name, model_space[2]) plt.savefig(sedname, format='svg') plt.clf() if out_time != 0: # Output last elevation filename = '%s/u_solution_%d_%d.pvd' % (name, model_space[2], n) vtkfile = File(filename) u.rename("elv", "elevation") vtkfile << u # Output last water flux filename = '%s/q_solution_%d_%d.pvd' % (name, model_space[2], n) vtkfile = File(filename) q.rename("flx", "flux") vtkfile << q # Calculate valley spacing from peak to peak in water flux tol = 0.001 # avoid hitting points outside the domain y = np.linspace(0 + tol, 1 - tol, 100) x = np.linspace(0.01, lx / ly - 0.01, 20) wavelength = np.zeros(len(x)) if statistics != 0: i = 0 for ix in x: points = [(ix, y_) for y_ in y] # 2D points q_line = np.array([q(point) for point in points]) indexes = peakutils.indexes(q_line, thres=0.05, min_dist=5) if len(indexes) > 1: wavelength[i] = sum(np.diff(y[indexes])) / (len(indexes) - 1) else: wavelength[i] = 0 i += 1 if plot != 0: plt.plot(y * 1e-3 * ly, q_line * kappa / ly, 'k', linewidth=2) plt.plot(y[indexes] * 1e-3 * ly, q_line[indexes] * kappa / ly, '+r') plt.xlabel('Distance (km)') plt.ylabel('Water Flux (m/yr)') watername = '%s/water_flux_spacing_%d.svg' % (name, model_space[2]) plt.savefig(watername, format='svg') plt.clf() return sed_flux, time, wavelength
def solve_linear_pde( u_D_array, T, D=1, C1=0, num_r=100, min_r=0.001, tol=1e-14, degree=1, ): # disable logging set_log_active(False) num_t = len(u_D_array) dt = T / num_t # time step size mesh = IntervalMesh(num_r, min_r, 1) r = mesh.coordinates().flatten() r_args = np.argsort(r) V = FunctionSpace(mesh, "P", 1) # Define boundary conditions # Dirichlet condition at R def boundary_at_R(x, on_boundary): return on_boundary and near(x[0], 1, tol) D = Constant(D) u_D = Constant(u_D_array[0]) bc_at_R = DirichletBC(V, u_D, boundary_at_R) # Define initial values for free c c_0 = Expression("C1", C1=C1, degree=degree) c_n = interpolate(c_0, V) # Define variational problem c = TrialFunction(V) v = TestFunction(V) # define Constants r_squ = Expression("4*pi*pow(x[0],2)", degree=degree) F_tmp = (D * dt * inner(grad(c), grad(v)) * r_squ * dx + c * v * r_squ * dx - c_n * v * r_squ * dx) a, L = lhs(F_tmp), rhs(F_tmp) u = Function(V) data_c = np.zeros((num_t, len(r)), dtype=np.double) for n in range(num_t): u_D.assign(u_D_array[n]) # Compute solution solve(a == L, u, bc_at_R) data_c[n, :] = u.vector().vec().array c_n.assign(u) data_c = data_c[:, r_args[::-1]] r = r[r_args] return data_c, r
# Define boundary condition u_D = Expression('1 + x[0]*x[0] + 2*x[1]*x[1]', degree=poly_degree + 3) # the expression accepts a string with commands in C++ style, that is then compiled for efficiency # the degree parameter specifies the polynomial degree for interpolation of the solution # if using the exact solution, the order should be at least a few units more than the functional space's elements def boundary(x, on_boundary): return on_boundary bc = DirichletBC(V, u_D, boundary) # Define variational problem u = TrialFunction(V) v = TestFunction(V) f = Constant(-6.0) a = dot(grad(u), grad(v)) * dx L = f * v * dx # Compute solution u = Function(V) solve(a == L, u, bc) # since u is created as a Function object, it can be evaluated at any new point - although # this is an expensive operation! # Compute error in L2 norm error_L2 = errornorm(u_D, u, 'L2') # Compute maximum error at vertices
def fluid_problem(u_f, v_f, u_s, v_s, fluid, solid, param, t, save=False): # Store old solutions u_f_n_M = Function(fluid.V_split[0]) v_f_n_M = Function(fluid.V_split[1]) u_f_n_M.assign(u_f.old) v_f_n_M.assign(v_f.old) # Store old boundary values u_f_n_i_M = Function(fluid.V_split[0]) v_f_n_i_M = Function(fluid.V_split[1]) u_f_n_i_M.assign(u_f.i_old) v_f_n_i_M.assign(v_f.i_old) # Initialize interface values u_f_i = Function(fluid.V_split[0]) v_f_i = Function(fluid.V_split[1]) # Define Dirichlet boundary conditions bc_u_f_0 = DirichletBC(fluid.V.sub(0), Constant(0.0), boundary) bc_v_f_0 = DirichletBC(fluid.V.sub(1), Constant(0.0), boundary) bcs_f = [bc_u_f_0, bc_v_f_0] # Compute fractional steps for fluid problem for m in range(param.M): # Update boundary values u_f_i.assign( project((param.M - m - 1.0) / param.M * u_f.i_old + (m + 1.0) / param.M * solid_to_fluid(u_s.new, fluid, solid, param, 0), fluid.V_split[0])) v_f_i.assign( project((param.M - m - 1.0) / param.M * v_f.i_old + (m + 1.0) / param.M * solid_to_fluid(v_s.new, fluid, solid, param, 1), fluid.V_split[1])) # Define trial and test functions U_f_new = TrialFunction(fluid.V) (u_f_new, v_f_new) = split(U_f_new) Phi_f = TestFunction(fluid.V) (phi_f, psi_f) = split(Phi_f) # Define scheme A_f = (v_f_new * phi_f * fluid.dx + 0.5 * param.dt / param.M * a_f(u_f_new, v_f_new, phi_f, psi_f, fluid, param)) L_f = (v_f_n_M * phi_f * fluid.dx - 0.5 * param.dt / param.M * a_f(u_f_n_M, v_f_n_M, phi_f, psi_f, fluid, param) + 0.5 * param.dt / param.M * param.gamma / fluid.h * u_f_i * psi_f * fluid.ds + 0.5 * param.dt / param.M * param.gamma / fluid.h * v_f_i * phi_f * fluid.ds + 0.5 * param.dt / param.M * param.gamma / fluid.h * u_f_n_i_M * psi_f * fluid.ds + 0.5 * param.dt / param.M * param.gamma / fluid.h * v_f_n_i_M * phi_f * fluid.ds + param.dt / param.M * f(t) * phi_f * fluid.dx) # Solve fluid problem U_f_new = Function(fluid.V) solve(A_f == L_f, U_f_new, bcs_f) (u_f_new, v_f_new) = U_f_new.split(U_f_new) # Append solutions to the arrays if save: u_f.array.append(u_f_new.copy(deepcopy=True)) v_f.array.append(v_f_new.copy(deepcopy=True)) # Update fluid solution u_f_n_M.assign(u_f_new) v_f_n_M.assign(v_f_new) # Update boundary conditions u_f_n_i_M.assign(project(u_f_i, fluid.V_split[0])) v_f_n_i_M.assign(project(v_f_i, fluid.V_split[1])) # Save final values u_f.new.assign(u_f_new) v_f.new.assign(v_f_new) u_f.i_new.assign(u_f_i) v_f.i_new.assign(v_f_i) return
def theta_method(A, f, ah, bh, θ, k, T): """θ-method time-stepper. Abstract problem: u'' + Au = f, u(0) = ah, u'(0) = bh is formulated as a first-order system: u' - v = 0, v' + Au = f, u(0) = ah, v(0) = bh. Args: A (Function -> Function -> Form): Possibly nonlinear functional A(u, v) f (float -> Function): Right-hand side ah (Function): Initial value bh (Function): Initial velocity k (float): Time step T (float): Max time Returns: list(float): Times list(Function): Solution at each time """ # pylint: disable=invalid-name, too-many-arguments, too-many-locals # Basic setup V = ah.function_space() u = TrialFunction(V) w = TestFunction(V) v = TrialFunction(V) y = TestFunction(V) α = k * θ β = k * (1.0 - θ) # Prepare initial condition u0 = Function(V) u0.vector()[:] = ah.vector() v0 = Function(V) v0.vector()[:] = bh.vector() # Initialize time stepper t = k u1 = Function(V) u1.vector()[:] = u0.vector() v1 = Function(V) v1.vector()[:] = v0.vector() ts = [0] uh = [interpolate(u0, V)] progress = -1 print("Progress: ", end="") while t < T: # Print progress pct = int(t / T * 100) // 10 * 10 if pct > progress: print("{}%..".format(pct), end="") progress = pct # Solve for the next u (nonlinear) lhs = (dot(u, w) + α**2 * A(u, w)) * dx rhs = (dot(u0, w) - α * β * A(u0, w) + k * dot(v0, w) + α**2 * f(t + k, w) + α * β * f(t, w)) * dx act = action(lhs - rhs, u1) J = derivative(act, u1) problem = NonlinearVariationalProblem(act, u1, [], J) solver = NonlinearVariationalSolver(problem) solver.parameters["newton_solver"]["linear_solver"] = "gmres" solver.parameters["newton_solver"]["preconditioner"] = "ilu" try: solver.solve() except RuntimeError: print("blowup at t={}.".format(t)) return (ts, uh) # Solve for the next v (linear) lhs = dot(v, y) * dx rhs = (dot(u1 - u0, y) - β * dot(v0, w)) / α * dx problem = LinearVariationalProblem(lhs, rhs, v1) solver = LinearVariationalSolver(problem) solver.parameters["linear_solver"] = "cg" solver.parameters["preconditioner"] = "hypre_amg" solver.solve() # Update t += k u0.vector()[:] = u1.vector() v0.vector()[:] = v1.vector() # Record result ts.append(t) uh.append(interpolate(u1, V)) print("done") return (ts, uh)
mesh=mesh, read_field=u_D_function, write_field=f_N_function, u_n=u_n) elif problem is ProblemType.NEUMANN: precice_dt = precice.initialize(coupling_subdomain=coupling_boundary, mesh=mesh, read_field=f_N_function, write_field=u_D_function, u_n=u_n) dt = Constant(0) dt.assign(np.min([fenics_dt, precice_dt])) # Define variational problem u = TrialFunction(V) v = TestFunction(V) f = Expression( 'beta + gamma * x[0] * x[0] - 2 * gamma * t - 2 * (1-gamma) - 2 * alpha', degree=2, alpha=alpha, beta=beta, gamma=gamma, t=0) F = u * v / dt * dx + dot(grad(u), grad(v)) * dx - (u_n / dt + f) * v * dx if problem is ProblemType.DIRICHLET: # apply Dirichlet boundary condition on coupling interface bcs.append(precice.create_coupling_dirichlet_boundary_condition(V)) if problem is ProblemType.NEUMANN: # apply Neumann boundary condition on coupling interface, modify weak form correspondingly
def __init__( self, mesh=None, width=1.0, dim=1, nelements=8, degree=2, parameters={}, V=(lambda U: U), U0=None, rho0=None, t0=0.0, debug=False, solver_type = 'lu', preconditioner_type = 'default', periodic=True, ligands=None ): """DG solver for the periodic Keller-Segel PDE system Keyword parameters: mesh=None: the mesh on which to solve the problem width=1.0: the width of the domain dim=1: # of spatial dimensions. nelements=8: If mesh is not supplied, one will be contructed using UnitIntervalMesh, UnitSquareMesh, or UnitCubeMesh (depending on dim). dim and nelements are not needed if mesh is supplied. degree=2: degree of the polynomial approximation parameters={}: a dict giving the values of scalar parameters of .V, U0, and rho0 Expressions. This dict needs to also define numerical parameters that appear in the PDE. Some of these have defaults: dim = dim: # of spatial dimensions sigma: organism movement rate s: attractant secretion rate gamma: attractant decay rate D: attractant diffusion constant rho_min=10.0**-7: minimum feasible worm density U_min=10.0**-7: minimum feasible attractant concentration rhopen=10: penalty for discontinuities in rho Upen=1: penalty for discontinuities in U grhopen=1, gUpen=1: penalties for discontinuities in gradients V=(lambda U: U): a callable taking two numerical arguments, U and rho, or a single argument, U, and returning a single number, V, the potential corresponding to U. Use fenics versions of mathematical functions, e.g. fe.ln, abs, fe.exp. U0, rho0: Expressions, Functions, or strs specifying the initial condition. t0=0.0: initial time solver_type='lu' preconditioner_type='default' periodic=True: Allowed for compatibility, but ignored ligands=None: ignored for compatibility """ logPERIODIC('creating KSDGSolverPeriodic') self.args = dict( mesh=mesh, width=width, dim=dim, nelements=nelements, degree=degree, parameters=parameters, V=V, U0=U0, rho0=rho0, t0=t0, debug=debug, solver_type = solver_type, preconditioner_type = preconditioner_type, periodic=True, ligands=ligands ) self.debug = debug self.solver_type = solver_type self.preconditioner_type = preconditioner_type self.periodic = True self.params = self.default_params.copy() # # Store the original mesh in self.omesh. self.mesh will be the # corner mesh. # if (mesh): self.omesh = mesh else: self.omesh = box_mesh(width=width, dim=dim, nelements=nelements) self.nelements = nelements try: comm = self.omesh.mpi_comm().tompi4py() except AttributeError: comm = self.omesh.mpi_comm() self.lmesh = gather_mesh(self.omesh) omeshstats = mesh_stats(self.omesh) logPERIODIC('omeshstats', omeshstats) self.xmin = omeshstats['xmin'] self.xmax = omeshstats['xmax'] self.xmid = omeshstats['xmid'] self.delta_ = omeshstats['dx'] self.mesh = corner_submesh(self.lmesh) meshstats = mesh_stats(self.mesh) logPERIODIC('meshstats', meshstats) logPERIODIC('self.omesh', self.omesh) logPERIODIC('self.mesh', self.mesh) logPERIODIC('self.mesh.mpi_comm().size', self.mesh.mpi_comm().size) self.nelements = nelements self.degree = degree self.dim = self.mesh.geometry().dim() self.params['dim'] = self.dim self.params.update(parameters) # # Solution spaces and Functions # # The solution function space is a vector space with # 2*(2**dim) elements. The first 2**dim components are even # and odd parts of rho; These are followed by even and # odd parts of U. The array self.evenodd identifies even # and odd components. Each row is a length dim sequence 0s and # 1s and represnts one component. For instance, if evenodd[i] # is [0, 1, 0], then component i of the vector space is even # in dimensions 0 and 2 (x and z conventionally) and off in # dimension 1 (y). # self.symmetries = evenodd_symmetries(self.dim) self.signs = [fe.as_matrix(np.diagflat(1.0 - 2.0*eo)) for eo in self.symmetries] self.eomat = evenodd_matrix(self.symmetries) fss = self.make_function_space() (self.SE, self.SS, self.VE, self.VS) = [ fss[fs] for fs in ('SE', 'SS', 'VE', 'VS') ] (self.SE, self.SS, self.VE, self.VS) = self.make_function_space() self.sol = Function(self.VS) # sol, current soln logPERIODIC('self.sol', self.sol) # srhos and sUs are fcuntions defiend on subspaces self.srhos = self.sol.split()[:2**self.dim] self.sUs = self.sol.split()[2**self.dim:] # irhos and iUs are Indexed UFL expressions self.irhos = fe.split(self.sol)[:2**self.dim] self.iUs = fe.split(self.sol)[2**self.dim:] self.wrhos = TestFunctions(self.VS)[: 2**self.dim] self.wUs = TestFunctions(self.VS)[2**self.dim :] self.tdsol = TrialFunction(self.VS) # time derivatives self.tdrhos = fe.split(self.tdsol)[: 2**self.dim] self.tdUs = fe.split(self.tdsol)[2**self.dim :] bc_method = 'geometric' if self.dim > 1 else 'pointwise' rhobcs = [DirichletBC( self.VS.sub(i), Constant(0), FacesDomain(self.mesh, self.symmetries[i]), method=bc_method ) for i in range(2**self.dim) if np.any(self.symmetries[i] != 0.0)] Ubcs = [DirichletBC( self.VS.sub(i + 2**self.dim), Constant(0), FacesDomain(self.mesh, self.symmetries[i]), method=bc_method ) for i in range(2**self.dim) if np.any(self.symmetries[i] != 0.0)] self.bcs = rhobcs + Ubcs self.n = FacetNormal(self.mesh) self.h = CellDiameter(self.mesh) self.havg = fe.avg(self.h) self.dx = fe.dx self.dS = fe.dS # # record initial state # if not U0: U0 = Constant(0.0) if isinstance(U0, ufl.coefficient.Coefficient): self.U0 = U0 else: self.U0 = Expression(U0, **self.params, degree=self.degree, domain=self.mesh) if not rho0: rho0 = Constant(0.0) if isinstance(rho0, ufl.coefficient.Coefficient): self.rho0 = rho0 else: self.rho0 = Expression(rho0, **self.params, degree=self.degree, domain=self.mesh) try: V(self.U0, self.rho0) def realV(U, rho): return V(U, rho) except TypeError: def realV(U, rho): return V(U) self.V = realV self.t0 = t0 # # initialize state # # cache assigners logPERIODIC('restarting') self.restart() logPERIODIC('restart returned') return(None)
def __init__(self, mesh=None, width=1.0, dim=1, nelements=8, degree=2, parameters={}, V=(lambda U: U), U0=None, rho0=None, t0=0.0, debug=False, solver_type='gmres', preconditioner_type='default', periodic=False, ligands=None): """Discontinuous Galerkin solver for the Keller-Segel PDE system Keyword parameters: mesh=None: the mesh on which to solve the problem width=1.0: the width of the domain dim=1: # of spatial dimensions. nelements=8: If mesh is not supplied, one will be contructed using UnitIntervalMesh, UnitSquareMesh, or UnitCubeMesh (depending on dim). dim and nelements are not needed if mesh is supplied. degree=2: degree of the polynomial approximation parameters={}: a dict giving the values of scalar parameters of .V, U0, and rho0 Expressions. This dict needs to also define numerical parameters that appear in the PDE. Some of these have defaults: dim = dim: # of spatial dimensions sigma: organism movement rate s: attractant secretion rate gamma: attractant decay rate D: attractant diffusion constant rho_min=10.0**-7: minimum feasible worm density U_min=10.0**-7: minimum feasible attractant concentration rhopen=10: penalty for discontinuities in rho Upen=1: penalty for discontinuities in U grhopen=1, gUpen=1: penalties for discontinuities in gradients V=(lambda U: U): a callable taking two numerical arguments, U and rho, or a single argument, U, and returning a single number, V, the potential corresponding to U. Use fenics versions of mathematical functions, e.g. ufl.ln, abs, ufl.exp. U0, rho0: Expressions, Functions, or strs specifying the initial condition. t0=0.0: initial time solver_type='gmres' preconditioner_type='default' periodic, ligands: ignored for caompatibility """ logSOLVER('creating KSDGSolver') self.args = dict(mesh=mesh, width=width, dim=dim, nelements=nelements, degree=degree, parameters=parameters, V=V, U0=U0, rho0=rho0, t0=t0, debug=debug, solver_type=solver_type, preconditioner_type=preconditioner_type, periodic=periodic, ligands=ligands) self.debug = debug self.solver_type = solver_type self.preconditioner_type = preconditioner_type self.periodic = False self.ligands = ligands self.params = self.default_params.copy() if (mesh): self.omesh = self.mesh = mesh else: self.omesh = self.mesh = box_mesh(width=width, dim=dim, nelements=nelements) self.nelements = nelements logSOLVER('self.mesh', self.mesh) logSOLVER('self.mesh.mpi_comm().size', self.mesh.mpi_comm().size) self.nelements = nelements self.degree = degree self.dim = self.mesh.geometry().dim() self.params['dim'] = self.dim self.params.update(parameters) # # Solution spaces and Functions # fss = self.make_function_space() (self.SE, self.SS, self.VE, self.VS) = [fss[fs] for fs in ('SE', 'SS', 'VE', 'VS')] logSOLVER('self.VS', self.VS) self.sol = Function(self.VS) # sol, current soln logSOLVER('self.sol', self.sol) self.srho, self.sU = self.sol.sub(0), self.sol.sub(1) self.irho, self.iU = fe.split(self.sol) self.wrho, self.wU = TestFunctions(self.VS) self.tdsol = TrialFunction(self.VS) self.tdrho, self.tdU = fe.split(self.tdsol) self.n = FacetNormal(self.mesh) self.h = CellDiameter(self.mesh) self.havg = fe.avg(self.h) self.dx = fe.dx # self.dx = fe.dx(metadata={'quadrature_degree': min(degree, 10)}) self.dS = fe.dS # self.dS = fe.dS(metadata={'quadrature_degree': min(degree, 10)}) # # record initial state # try: V(self.iU, self.irho) def realV(U, rho): return V(U, rho) except TypeError: def realV(U, rho): return V(U) self.V = realV if not U0: U0 = Constant(0.0) if isinstance(U0, ufl.coefficient.Coefficient): self.U0 = U0 else: self.U0 = Expression(U0, **self.params, degree=self.degree, domain=self.mesh) if not rho0: rho0 = Constant(0.0) if isinstance(rho0, ufl.coefficient.Coefficient): self.rho0 = rho0 else: self.rho0 = Expression(rho0, **self.params, degree=self.degree, domain=self.mesh) self.t0 = t0 # # initialize state # # cache assigners logSOLVER('restarting') self.restart() logSOLVER('restart returned') return (None)
def solid_problem(u_f, v_f, u_s, v_s, fluid, solid, param, save = False): # Store old solutions u_s_n_K = Function(solid.V_split[0]) v_s_n_K = Function(solid.V_split[1]) u_s_n_K.assign(u_s.old) v_s_n_K.assign(v_s.old) # Store old boundary values v_s_n_i_K = Function(solid.V_split[1]) v_s_n_i_K.assign(v_s.i_old) # Initialize interface values v_s_i = Function(solid.V_split[1]) # Define Dirichlet boundary conditions bc_u_s_0 = DirichletBC(solid.V.sub(0), Constant(0.0), boundary) bc_v_s_0 = DirichletBC(solid.V.sub(1), Constant(0.0), boundary) bcs_s = [bc_u_s_0, bc_v_s_0] # Compute fractional steps for solid problem for k in range(param.K): # Update boundary values v_s_i.assign(project((param.K - k - 1.0)/param.K*v_s.i_old + + (k + 1.0)/param.K*fluid_to_solid(v_f.new, fluid, solid, param, 1), solid.V_split[1])) # Define trial and test functions U_s_new = TrialFunction(solid.V) (u_s_new, v_s_new) = split(U_s_new) Phi_s = TestFunction(solid.V) (phi_s, psi_s) = split(Phi_s) # Define scheme A_s = (v_s_new*phi_s*solid.dx + u_s_new*psi_s*solid.dx + 0.5*param.dt/param.K*a_s(u_s_new, v_s_new, phi_s, psi_s, solid, param)) L_s = (v_s_n_K*phi_s*solid.dx + u_s_n_K*psi_s*solid.dx - 0.5*param.dt/param.K*a_s(u_s_n_K, v_s_n_K, phi_s, psi_s, solid, param) + 0.5*param.dt/param.K*param.nu*dot(grad(v_s_i), solid.n)*phi_s*solid.ds + 0.5*param.dt/param.K*param.nu*dot(grad(v_s_n_i_K), solid.n)*phi_s*solid.ds) # Solve solid problem U_s_new = Function(solid.V) solve(A_s == L_s, U_s_new, bcs_s) (u_s_new, v_s_new) = U_s_new.split(U_s_new) # Append solutions to the arrays if save: u_s.array.append(u_s_new.copy(deepcopy = True)) v_s.array.append(v_s_new.copy(deepcopy = True)) # Update solid solution u_s_n_K.assign(u_s_new) v_s_n_K.assign(v_s_new) # Update boundary condition v_s_n_i_K.assign(project(v_s_i, solid.V_split[1])) # Save final values u_s.new.assign(u_s_new) v_s.new.assign(v_s_new) v_s.i_new.assign(v_s_i) return
def leapfrog(A, f, ah, bh, k, T): """Leapfrog time-stepper. Abstract problem: u'' + Au = f, u(0) = ah, u'(0) = bh Args: A (Function -> Function -> Form): Possibly nonlinear functional A(u, v) f (float -> Function): Right-hand side ah (Function): Initial value bh (Function): Initial velocity k (float): Time step T (float): Max time Returns: list(float): Times list(Function): Solution at each time """ # pylint: disable=invalid-name, too-many-arguments, too-many-locals # Basic setup V = ah.function_space() u = TrialFunction(V) v = TestFunction(V) # Prepare initial condition u0 = Function(V) u0.vector()[:] = ah.vector() u1 = Function(V) u1.vector()[:] = ah.vector() + k * bh.vector() # Initialize time stepper t = k u2 = Function(V) u2.vector()[:] = u1.vector() ts = [0, t] uh = [interpolate(u0, V), interpolate(u1, V)] progress = -1 print("Progress: ", end="") while t < T: # Print progress pct = int(t / T * 100) // 10 * 10 if pct > progress: print("{}%..".format(pct), end="") progress = pct # Solve lhs = dot(u, v) * dx rhs = (dot(2 * u1 - u0, v) + k * k * (f(t, v) - A(u1, v))) * dx problem = LinearVariationalProblem(lhs, rhs, u2) solver = LinearVariationalSolver(problem) solver.parameters["linear_solver"] = "cg" solver.parameters["preconditioner"] = "hypre_amg" try: solver.solve() except RuntimeError: print("blowup at t={}.".format(t)) return (ts, uh) # Update t += k u0.vector()[:] = u1.vector() u1.vector()[:] = u2.vector() # Record result ts.append(t) uh.append(interpolate(u1, V)) print("done") return (ts, uh)