def initialise(dem, bounds, res): """ Function to initialise the model space :param dem: 0 = no input DEM; 1 = input DEM :param bounds: west, south, east, north - if no DEM [0, 0, lx, ly]; if DEM [west, south, east, north] :param res: model resolution along the y-axis. :return model_space, u_n, mesh, V, bc: """ if dem == 0: lx = bounds[1] ly = bounds[3] # Create mesh and define function space domain = Rectangle(Point(0, 0), Point(lx / ly, ly / ly)) mesh = generate_mesh(domain, res) V = FunctionSpace(mesh, 'P', 1) # Define initial value u_D = Constant(0) eps = 10 / ly u_n = interpolate(u_D, V) u_n.vector().set_local(u_n.vector().get_local() + eps * np.random.random(u_n.vector().size())) if dem == 1: u_n, lx, ly, mesh, V = read_dem(bounds, res) # boundary conditions class East(SubDomain): def inside(self, x, on_boundary): return near(x[0], lx / ly) class West(SubDomain): def inside(self, x, on_boundary): return near(x[0], 0.0) class North(SubDomain): def inside(self, x, on_boundary): return near(x[1], ly / ly) class South(SubDomain): def inside(self, x, on_boundary): return near(x[1], 0.0) # Should make this into an option! bc = [DirichletBC(V, u_n, West()), DirichletBC(V, u_n, East())] # def boundary(x, on_boundary): # return on_boundary # bc = DirichletBC(V, u_n, boundary) model_space = [lx, ly, res] return model_space, u_n, mesh, V, bc
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 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 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 __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)
#alpha method parameters alpha_m = Constant(0.2) alpha_f = Constant(0.4) gamma = Constant(0.5 + alpha_f - alpha_m) beta = Constant((gamma + 0.5)**2 / 4.) #Loading by an Expression: sinus loading dependend on x_0 #p = Expression(('1*sin(2*pi*t) * x[0]','0'),degree=1, t=0) #p = Expression( ('t<1 ? 0.01 : 0.01','0'),degree=1, t=0) #now precice coupling instead force_boundary = AutoSubDomain(Neumann_Boundary) # clamp the beam at the bottom bc = DirichletBC(V, Constant((0, 0)), clamped_boundary) #Define strain def epsilon(u): return 0.5 * (nabla_grad(u) + nabla_grad(u).T) # Define Stress tensor def sigma(u): return lambda_ * nabla_div(u) * Identity(d) + 2 * mu * epsilon(u) # Define Mass form def m(u, v): return rho * inner(u, v) * dx
precice = Adapter(adapter_config_filename="precice-adapter-config-N.json") precice_dt = precice.initialize(coupling_boundary, read_function_space=V_g, write_object=u_D_function) boundary_marker = False 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 bcs = [DirichletBC(V, u_D, remaining_boundary)] # Set boundary conditions at coupling interface once wrt to the coupling expression coupling_expression = precice.create_coupling_expression() if problem is ProblemType.DIRICHLET: # modify Dirichlet boundary condition on coupling interface bcs.append(DirichletBC(V, coupling_expression, coupling_boundary)) if problem is ProblemType.NEUMANN: # modify Neumann boundary condition on coupling interface, modify weak form correspondingly if not boundary_marker: # there is only 1 Neumann-BC which is at the coupling boundary -> integration over whole boundary if coupling_expression.is_scalar_valued(): F += v * coupling_expression * dolfin.ds # this term has to be added to weak form to add a Neumann BC (see e.g. p. 83ff Langtangen, Hans Petter, and Anders Logg. "Solving PDEs in Python The FEniCS Tutorial Volume I." (2016).) elif coupling_expression.is_vector_valued(): normal = FacetNormal(mesh) F += -v * dot(normal, coupling_expression) * dolfin.ds else:
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
def __init__( self, eval_times, n=10, lx=1., ly=.1, lz=.1, f=(0.0, 0.0, -50.), time=1., timestep=.1, tol=1e-5, max_iter=30, rel_tol=1e-10, param_remapper=None): """Parameters ---------- eval_times: numpy.ndarray Times at which evaluation (interpolation) of the solution is required. n: int, default 10 Dimension of the grid along the smallest side of the beam (the grid size along all other dimensions will be scaled proportionally. lx: float, default 1. Length of the beam along the x axis. ly: float, default .1 Length of the beam along the y axis. lz: float, default .1 Length of the beam along the z axis. f: tuple or numpy.ndarray, default (0.0, 0.0, -50.) Force per unit volume acting on the beam. time: float, default 1. Final time of the simulation. timestep: float, default 1. Time discretization step to solve the problem. tol: float, default 1e-5 Tolerance parameter to ensure the last time step is included in the solution. max_iter: int, default 30 Maximum iterations for the SNES solver. rel_tol: int, Relative tolerance for the convergence of the SNES solver. param_remapper: object, default None Either None (no remapping of the parameters), or a function remapping the parameter of the problem (Young's modulus) to values suitable for the definition of the solution. """ # solver parameters self.solver = CountIt(solve) self.solver_parameters = { 'nonlinear_solver': 'snes', 'snes_solver': { 'linear_solver': 'lu', 'line_search': 'basic', 'maximum_iterations': max_iter, 'relative_tolerance': rel_tol, 'report': False, 'error_on_nonconvergence': False}} self.param_remapper = param_remapper # mesh creation self.n = n self.lx = lx self.ly = ly self.lz = lz min_len = min(lx, ly, lz) mesh_dims = (int(n * lx / min_len), int(n * ly / min_len), int(n * lz / min_len)) self.mesh = BoxMesh(Point(0, 0, 0), Point(lx, ly, lz), *mesh_dims) self.V = VectorFunctionSpace(self.mesh, 'Lagrange', 1) # boundary conditions self.left = CompiledSubDomain('near(x[0], side) && on_boundary', side=0.0) self.right = CompiledSubDomain('near(x[0], side) && on_boundary', side=lx) self.top = CompiledSubDomain('near(x[2], side) && on_boundary', side=lz) self.boundaries = MeshFunction('size_t', self.mesh, self.mesh.topology().dim() - 1, 0) self.boundaries.set_all(0) self.left.mark(self.boundaries, 1) self.right.mark(self.boundaries, 2) self.top.mark(self.boundaries, 3) self.bcs1 = DirichletBC(self.V, Constant([0.0, 0.0, 0.0]), self.boundaries, 1) self.bcs2 = DirichletBC(self.V, Constant([0.0, 0.0, 0.0]), self.boundaries, 2) self.bcs = [self.bcs1, self.bcs2] # surface force self.f = Constant(f) self.ds = Measure('ds', domain=self.mesh, subdomain_data=self.boundaries) # evaluation times self.eval_times = eval_times self.dt = timestep self.T = time + tol self.times = np.arange(self.dt, self.T, self.dt) self.time = Expression('t', t=self.dt, degree=0)
inclusion = Inclusion_3d() point0 = "near(x[0], 0) && near(x[1], 0) && near(x[2], 0)" V = FunctionSpace(mesh, "CG", order, constrained_domain=PeriodicBoundary(dim)) # setting the elements that lies in inclusion and in matrix phase domains = MeshFunction("size_t", mesh, dim) domains.set_all(0) inclusion.mark(domains, 1) dx = Measure('dx', subdomain_data=domains) def bilf(up, vp): # bilinear form return inner(mat * up, vp) * dx(0) + inner(inc * up, vp) * dx(1) bc0 = DirichletBC(V, Constant(0.), point0, method='pointwise') # SOLVER u = TrialFunction(V) v = TestFunction(V) uE = Function(V) solve(bilf(grad(u), grad(v)) == -bilf(E, grad(v)), uE, bcs=[bc0]) # POSTPROCESSING evaluation of guaranteed bound AH11 = assemble(bilf(grad(uE) + E, grad(uE) + E)) print('homogenised component A11 = {} (FEM)'.format(AH11)) print('END')
def __init__(self, mesh=None, width=1.0, dim=1, nelements=8, degree=2, parameters={}, param_funcs={}, V=(lambda U, params={}: sum(U)), U0=[], rho0=None, t0=0.0, debug=False, solver_type='petsc', preconditioner_type='default', periodic=True, ligands=None): """Discontinuous Galerkin solver for the Keller-Segel PDE system Like KSDGSolverVariable, but with periodic boundary conditions. """ logVARIABLE('creating KSDGSolverVariablePeriodic') if not ligands: ligands = LigandGroups() else: ligands = copy.deepcopy(ligands) self.args = dict(mesh=mesh, width=width, dim=dim, nelements=nelements, degree=degree, parameters=parameters, param_funcs=param_funcs, V=V, U0=U0, rho0=rho0, t0=t0, debug=debug, solver_type=solver_type, preconditioner_type=preconditioner_type, periodic=True, ligands=ligands) self.t0 = t0 self.debug = debug self.solver_type = solver_type self.preconditioner_type = preconditioner_type self.periodic = True self.ligands = ligands self.nligands = ligands.nligands() self.init_params(parameters, param_funcs) if nelements is None: self.nelements = 8 else: self.nelements = nelements if (mesh): self.omesh = self.mesh = mesh else: self.omesh = self.mesh = box_mesh(width=width, dim=dim, nelements=self.nelements) self.nelements = nelements omeshstats = mesh_stats(self.omesh) try: comm = self.omesh.mpi_comm().tompi4py() except AttributeError: comm = self.omesh.mpi_comm() self.lmesh = gather_mesh(self.omesh) logVARIABLE('omeshstats', omeshstats) self.xmin = omeshstats['xmin'] self.xmax = omeshstats['xmax'] self.xmid = omeshstats['xmid'] self.delta_ = omeshstats['dx'] if nelements is None: self.nelements = (self.xmax - self.xmin) / self.delta_ self.mesh = corner_submesh(self.lmesh) meshstats = mesh_stats(self.mesh) self.degree = degree self.dim = self.mesh.geometry().dim() # # Solution spaces and Functions # 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')] logVARIABLE('self.VS', self.VS) self.sol = Function(self.VS) # sol, current soln logVARIABLE('self.sol', self.sol) splitsol = self.sol.split() self.srhos = splitsol[:2**self.dim] self.sUs = splitsol[2**self.dim:] splitsol = list(fe.split(self.sol)) self.irhos = splitsol[:2**self.dim] self.iUs = splitsol[2**self.dim:] self.iPs = list(fe.split(self.PSf)) self.iparams = collections.OrderedDict(zip(self.param_names, self.iPs)) self.iligands = copy.deepcopy(self.ligands) self.iligand_params = ParameterList( [p for p in self.iligands.params() if p[0] in self.param_numbers]) for k in self.iligand_params.keys(): i = self.param_numbers[k] self.iligand_params[k] = self.iPs[i] tfs = list(TestFunctions(self.VS)) self.wrhos, self.wUs = tfs[:2**self.dim], tfs[2**self.dim:] tfs = list(TrialFunctions(self.VS)) self.tdrhos, self.tdUs = tfs[:2**self.dim], tfs[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 = list( itertools.chain(*[[ DirichletBC(self.VS.sub(i + (lig + 1) * 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) ] for lig in range(self.nligands)])) 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)] * self.nligands self.U0s = [Constant(0.0)] * self.nligands for i, U0i in enumerate(U0): if isinstance(U0i, ufl.coefficient.Coefficient): self.U0s[i] = U0i else: self.U0s[i] = Expression(U0i, **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.set_time(t0) # # work out how to call V # try: V(self.U0s, self.rho0, params=self.iparams) def realV(Us, rho): return V(Us, rho, params=self.iparams) except TypeError: def realV(Us, rho): return V(Us, self.iparams) self.V = realV # # initialize state # self.restart() return None
mesh = UnitSquareMesh(*grid_shape) V = FunctionSpace(mesh, 'P', poly_degree) # creates a grid_shape[0+1 x grid_shape[1]+1 vertices mesh with Lagrange polynomials of order order_degree # 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
def make_bcs(cls, V, surface_val, ground_val) -> List[DirichletBC]: bc1 = DirichletBC(V, Constant(ground_val), cls.ground_boundary) bc2 = DirichletBC(V, Constant(surface_val), cls.surface_boundary) return [bc1, bc2]
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 problem_mix(T, dt, E, coupling, VV, boundaries, rho_s, lambda_, mu_s, f, **Solid_namespace): # Temporal parameters t = 0 k = Constant(dt) # Split problem to two 1.order differential equations psi, phi = TestFunctions(VV) # BCs bc1 = DirichletBC(VV.sub(0), ((0, 0)), boundaries, 1) bc2 = DirichletBC(VV.sub(1), ((0, 0)), boundaries, 1) bcs = [bc1, bc2] # 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 -= 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)
fixed_boundary = AutoSubDomain(clamped_boundary) precice = Adapter(adapter_config_filename="precice-adapter-config-fsi-s.json") # Initialize the coupling interface precice_dt = precice.initialize(coupling_boundary, read_function_space=V, write_object=V, fixed_boundary=fixed_boundary) fenics_dt = precice_dt # if fenics_dt == precice_dt, no subcycling is applied # fenics_dt = 0.02 # if fenics_dt < precice_dt, subcycling is applied dt = Constant(np.min([precice_dt, fenics_dt])) # clamp the beam at the bottom bc = DirichletBC(V, Constant((0, 0)), fixed_boundary) # alpha method parameters alpha_m = Constant(0) alpha_f = Constant(0) gamma = Constant(0.5 + alpha_f - alpha_m) beta = Constant((gamma + 0.5)**2 / 4.) # Define strain def epsilon(u): return 0.5 * (nabla_grad(u) + nabla_grad(u).T) # Define Stress tensor def sigma(u):
# Create a FEniCS Expression to define and control the coupling boundary values coupling_expression = precice.create_coupling_expression() # Assigning appropriate dt dt = Constant(0) dt.assign(np.min([fenics_dt, precice_dt])) # Define variational problem u = TrialFunction(V) v = TestFunction(V) F = u * v / dt * dx + alpha * dot(grad(u), grad(v)) * dx - u_n * v / dt * dx # apply constant Dirichlet boundary condition at bottom edge # apply Dirichlet boundary condition on coupling interface bcs = [ DirichletBC(V, coupling_expression, coupling_boundary), DirichletBC(V, u_D, bottom_boundary) ] a, L = lhs(F), rhs(F) # Time-stepping u_np1 = Function(V) t = 0 u_D.t = t + dt # mark mesh w.r.t ranks ranks = File("output/ranks%s.pvd.pvd" % precice.get_participant_name()) mesh_rank = MeshFunction("size_t", mesh, mesh.topology().dim()) mesh_rank.set_all(MPI.rank(MPI.comm_world)) mesh_rank.rename("myRank", "")
# namely we iterate over (small) timesteps, each time solving a Poisson equation via finite elements T = 2.0 # final time num_steps = 50 # number of time steps dt = T / num_steps # time step size # Create mesh and define function space nx = ny = 30 mesh = RectangleMesh(Point(-2, -2), Point(2, 2), nx, ny) V = FunctionSpace(mesh, 'P', 1) # Define boundary condition def boundary(x, on_boundary): return on_boundary bc = DirichletBC(V, Constant(0), boundary) # null Dirichlet conditions # Define initial value u_0 = Expression('exp(-a*pow(x[0], 2) - a*pow(x[1], 2))', degree=2, a=5) # the initial condition here is a "gaussian hill" of parameter alpha centered in the origin u_n = interpolate(u_0, V) # since we will be using iteratively the solution from the previous time step to compute the one # at the current time step, we need to convert the initial datum's expression to a Function object: # there are two ways to do this: either via the project() method or the interpolate() method; # projecy() is very popular, but since we have a closed form solution here we want to use interpolate() # in order to recover the exact solution within machine-error precision # Define variational problem u = TrialFunction(V) v = TestFunction(V) f = Constant(0)
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)
u_n=u_n, dimension=dim, dirichlet_boundary=clamped_boundary_domain) fenics_dt = precice_dt # if fenics_dt == precice_dt, no subcycling is applied #fenics_dt = 0.02 # if fenics_dt < precice_dt, subcycling is applied dt = Constant(np.min([precice_dt, fenics_dt])) # generalized alpha method (time stepping) parameters alpha_m = Constant(0.2) alpha_f = Constant(0.4) gamma = Constant(0.5 + alpha_f - alpha_m) beta = Constant((gamma + 0.5)**2 * 0.25) # clamp (u == 0) the beam at the left bc = DirichletBC(V, Constant((0, 0)), left_boundary) # Define strain def epsilon(u): return 0.5 * (nabla_grad(u) + nabla_grad(u).T) # Define Stress tensor def sigma(u): return lambda_ * nabla_div(u) * Identity(dim) + 2 * mu * epsilon(u) # Define Mass form def m(u, v): return rho * inner(u, v) * dx
args = parser.parse_args() if args.source: precice = Adapter(adapter_config_filename="precice-adapter-config-source.json") elif args.drain: precice = Adapter(adapter_config_filename="precice-adapter-config-drain.json") mesh = UnitSquareMesh(10, 10) V = FunctionSpace(mesh, "P", 1) u = TrialFunction(V) v = TestFunction(V) u_n = Function(V) if args.source: u_ini = Expression("1", degree=1) bc = DirichletBC(V, u_ini, AllBoundary()) elif args.drain: u_ini = Expression("0", degree=1) bc = DirichletBC(V, u_ini, RightBoundary()) u_n = interpolate(u_ini, V) dt = precice.initialize(AllDomain(), read_function_space=V, write_object=u_n) volume_term = precice.create_coupling_expression() f = Function(V) dt_inv = Constant(1 / dt)
f_N = Constant('0') f_N_function = interpolate(f_N, V) coupling_boundary = TopBoundary() bottom_boundary = BottomBoundary() # Define initial value u_n = interpolate(u_D, V) u_n.rename("T", "") #start preCICE adapter precice = Adapter() precice_dt = precice.initialize(coupling_subdomain=coupling_boundary, mesh=mesh, read_field=u_D_function, write_field=f_N_function, u_n=u_n) dt = np.min([fenics_dt, precice_dt]) # todo we could also consider deciding on time stepping size inside the adapter bcs = [DirichletBC(V, u_D, bottom_boundary)] # Define variational problem u = TrialFunction(V) v = TestFunction(V) F = u * v / dt * dx + alpha * dot(grad(u), grad(v)) * dx - u_n * v / dt * dx # apply Dirichlet boundary condition on coupling interface bcs.append(precice.create_coupling_dirichlet_boundary_condition(V)) a, L = lhs(F), rhs(F) # Time-stepping u_np1 = Function(V) F_known_u = u_np1 * v / dt * dx + alpha * dot(grad(u_np1), grad(v)) * dx - u_n * v / dt * dx t = 0
alpha=alpha, beta=beta, gamma=gamma, t=0) u_D_function = interpolate(u_D, V) # Define flux in x direction on coupling interface (grad(u_D) in normal direction) f_N = Expression('2 * gamma*t*x[0] + 2 * (1-gamma)*x[0] ', degree=1, gamma=gamma, t=0) f_N_function = interpolate(f_N, V) coupling_boundary = CouplingBoundary() remaining_boundary = OuterBoundary() bcs = [DirichletBC(V, u_D, remaining_boundary)] # Define initial value u_n = interpolate(u_D, V) u_n.rename("Temperature", "") precice = Adapter(adapter_config_filename, interpolation_strategy=interpolation_strategy) if problem is ProblemType.DIRICHLET: precice_dt = precice.initialize(coupling_subdomain=coupling_boundary, 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,
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