def corner_submesh(mesh): """create a submesh extending from the origin to the midpoint""" # # Because SubMesh only works sequentially, build the corner # submesh in the rank 0 process, then distribute it. # submesh = None # needs to be initialized in all ranks # if MPI.COMM_WORLD.rank == 0: if True: logPERIODIC('constructing submesh') cf = MeshFunction("size_t", mesh, mesh.geometric_dimension()) logPERIODIC('CornerDomain') corner = CornerDomain(mesh) logPERIODIC('cf.set_all(0)') cf.set_all(0) logPERIODIC('corner.mark(cf, 1)') corner.mark(cf, 1) logPERIODIC('calling SubMesh') logPERIODIC('mesh.mpi_comm().size', mesh.mpi_comm().size) submesh = SubMesh(mesh, cf, 1) logPERIODIC('submesh', submesh) logPERIODIC('submesh.mpi_comm().size', submesh.mpi_comm().size) logPERIODIC('distributing submesh') dsubmesh = distribute_mesh(submesh) return(dsubmesh)
def local_refine(mesh, center, r): xc, yc = center cell_markers = MeshFunction("bool", mesh, mesh.topology().dim()) for c in cells(mesh): mp = c.midpoint() cell_markers[c] = sqrt((mp[0] - xc) * (mp[0] - xc) + (mp[1] - yc) * (mp[1] - yc)) < r mesh = refine(mesh, cell_markers) return mesh
def __init__(self, mesh, N=0): # Define mesh parameters self.mesh = mesh self.cell_size = CellDiameter(mesh) self.normal_vector = FacetNormal(mesh) # Define measures inner_boundary = Inner_boundary() sub_domains = MeshFunction("size_t", mesh, mesh.topology().dim() - 1) sub_domains.set_all(0) inner_boundary.mark(sub_domains, 1) self.dx = Measure("dx", domain=mesh) self.ds = Measure("ds", domain=mesh, subdomain_data=sub_domains) # Define function spaces finite_element = FiniteElement("Lagrange", mesh.ufl_cell(), 1) self.function_space = FunctionSpace(mesh, finite_element * finite_element) self.function_space_split = [ self.function_space.sub(0).collapse(), self.function_space.sub(1).collapse(), ]
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
raise Exception("Boundary markers are not implemented yet") # return dot(coupling_bc_expression, v) * dolfin.dss(boundary_marker) a, L = lhs(F), rhs(F) # Time-stepping u_np1 = Function(V) u_np1.rename("Temperature", "") t = 0 # reference solution at t=0 u_ref = interpolate(u_D, V) u_ref.rename("reference", " ") # mark mesh w.r.t ranks mesh_rank = MeshFunction("size_t", mesh, mesh.topology().dim()) if problem is ProblemType.NEUMANN: mesh_rank.set_all(MPI.rank(MPI.comm_world) + 4) else: mesh_rank.set_all(MPI.rank(MPI.comm_world) + 0) mesh_rank.rename("myRank", "") # Generating output files temperature_out = File("out/%s.pvd" % precice.get_participant_name()) ref_out = File("out/ref%s.pvd" % precice.get_participant_name()) error_out = File("out/error%s.pvd" % precice.get_participant_name()) ranks = File("out/ranks%s.pvd" % precice.get_participant_name()) # output solution and reference solution at t=0, n=0 n = 0 print('output u^%d and u_ref^%d' % (n, n))
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)
class HyperelasticBeam: """Class implementing the solution to a hyperelastic problem on a deformable beam. """ 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) 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 __call__(self, z, x, reshape=True, retall=False): """Solves the equation for the provided parameter `z` and returns the evaluation for each one of the nodes in `x` and each evaluation time instant (passed at creation). Parameters ---------- z : float or numpy.ndarray Value of the parameter (Young's modulus); if `z` is a vector, it has to have only one cell (the rest will not be considered). x : numpy.ndarray Matrix containing the coordinates of the points at which the solution has to be evaluated (one column per each point). reshape : bool, default True If `reshape` is set to True, then the results of the evaluations at different time points will be concatenated into a single vector, otherwise they are returned as a matrix whose columns correspond to values at different evaluation timesteps. retall : bool, default False If set to True, then also the solution at the last computed time step is returned. Returns ------- numpy.ndarray or (numpy.ndarray, object) The vector or matrix of evaluation and additionally the FEniCS solution object (if `retall` was set to true). """ evals, u = self._solve(z, x) evals = evals.flatten() if reshape else evals return (evals, u) if retall else evals def plot(self, z): """Solves the problem and then plots the beam with a colormap corresponding to the displacement. Parameters ---------- z : float or numpy.ndarray Value of the parameter (Young's modulus); if `z` is a vector, it has to have only one cell (the rest will not be considered). """ u = self._solve(z) vplot(u)
E[0] = 1. # macroscopic value E = Constant(E) if dim == 2: mesh = UnitSquareMesh(*N) # generation of mesh inclusion = Inclusion_2d() point0 = "near(x[0], 0) && near(x[1], 0)" elif dim == 3: mesh = UnitCubeMesh(*N) # generation of mesh 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)
def compute_potential_flow(self): class Hole(SubDomain): def inside(s, x, on_boundary): return on_boundary and self.boundary_fnc(x) #class ActiveArea(SubDomain): # def inside(self, x, on_boundary): # return between(x[0], (0., 0.1)) and between(x[1], (0, 0.2)) # Initialize sub-domain instances hole = Hole() # self.activate = ActiveArea() # Initialize mesh function for interior domains self.domains = MeshFunction("size_t", self.mesh, 2) self.domains.set_all(0) # self.activate.mark(self.domains, 1) # Initialize mesh function for boundary domains self.boundaries = MeshFunction("size_t", self.mesh, 1) self.boundaries.set_all(0) hole.mark(self.boundaries, 1) # Define new measures associated with the interior domains and # exterior boundaries self.dx = Measure('dx', domain=self.mesh, subdomain_data=self.domains) self.ds = Measure('ds', domain=self.mesh, subdomain_data=self.boundaries) dx, ds = self.dx, self.ds # Define function space and basis functions V = FunctionSpace(self.mesh, "P", 1) self.V_vel = V phi = TrialFunction(V) # potential v = TestFunction(V) # Define Dirichlet boundary conditions at top and bottom boundaries bcs = [fe.DirichletBC(V, 0.0, self.boundaries, 1)] # Define input data a0 = fe.Constant(1.0) a1 = fe.Constant(0.01) f_water = fe.Constant(self.water_in_flux) # Define the reaction equation # Define variational form F_pot = inner(a0 * grad(phi), grad(v)) * dx - f_water * v * dx # Separate left and right hand sides of equation a, L = fe.lhs(F_pot), fe.rhs(F_pot) # Solve problem phi = fe.Function(V) fe.solve(a == L, phi, bcs) # Evaluate integral of normal gradient over top boundary n = fe.FacetNormal(self.mesh) result_flux = fe.assemble(dot(grad(phi), n) * ds(1)) # test conservation of water expected_flux = -self.water_in_flux * fe.assemble(1 * dx) print("relative error of conservation of water %f" % ((result_flux - expected_flux) / expected_flux)) self.phi = phi self.flow = -grad(phi) # Save result output = fe.File("/tmp/potential.pvd") output << self.phi
class DuctState: def __init__(self): self.mesh = None self.vid = None # parameters self.water_in_flux = 0.1 self.boundary_fnc = lambda x: near(x[0], 1) and 0.3 < x[1] < 0.6 def load_mesh(self, fn): self.mesh = Mesh(fn) def load_video_mesh(self, nx = 30, ny = 30): self.mesh = UnitSquareMesh(nx, ny) def load_video(self, fn): self.video_filename = fn def compute_potential_flow(self): class Hole(SubDomain): def inside(s, x, on_boundary): return on_boundary and self.boundary_fnc(x) #class ActiveArea(SubDomain): # def inside(self, x, on_boundary): # return between(x[0], (0., 0.1)) and between(x[1], (0, 0.2)) # Initialize sub-domain instances hole = Hole() # self.activate = ActiveArea() # Initialize mesh function for interior domains self.domains = MeshFunction("size_t", self.mesh, 2) self.domains.set_all(0) # self.activate.mark(self.domains, 1) # Initialize mesh function for boundary domains self.boundaries = MeshFunction("size_t", self.mesh, 1) self.boundaries.set_all(0) hole.mark(self.boundaries, 1) # Define new measures associated with the interior domains and # exterior boundaries self.dx = Measure('dx', domain=self.mesh, subdomain_data=self.domains) self.ds = Measure('ds', domain=self.mesh, subdomain_data=self.boundaries) dx, ds = self.dx, self.ds # Define function space and basis functions V = FunctionSpace(self.mesh, "P", 1) self.V_vel = V phi = TrialFunction(V) # potential v = TestFunction(V) # Define Dirichlet boundary conditions at top and bottom boundaries bcs = [fe.DirichletBC(V, 0.0, self.boundaries, 1)] # Define input data a0 = fe.Constant(1.0) a1 = fe.Constant(0.01) f_water = fe.Constant(self.water_in_flux) # Define the reaction equation # Define variational form F_pot = inner(a0 * grad(phi), grad(v)) * dx - f_water * v * dx # Separate left and right hand sides of equation a, L = fe.lhs(F_pot), fe.rhs(F_pot) # Solve problem phi = fe.Function(V) fe.solve(a == L, phi, bcs) # Evaluate integral of normal gradient over top boundary n = fe.FacetNormal(self.mesh) result_flux = fe.assemble(dot(grad(phi), n) * ds(1)) # test conservation of water expected_flux = -self.water_in_flux * fe.assemble(1 * dx) print("relative error of conservation of water %f" % ((result_flux - expected_flux) / expected_flux)) self.phi = phi self.flow = -grad(phi) # Save result output = fe.File("/tmp/potential.pvd") output << self.phi def F_diff_conv(self, u, v, n, flow, D, s, source): dx, ds = self.dx, self.ds return D * inner(grad(u), grad(v)) * dx \ + s * inner(flow, grad(u)) * v * dx \ - source * v * dx 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 compute_conv_diff_reac(self, initial_condition=None): names = {'Cl', 'Na', 'K'} dt = 0.1 t = 0. t_end = 1. P1 = FiniteElement('P', fe.triangle, 3) element = MixedElement([P1, P1, P1]) V = FunctionSpace(self.mesh, element) self.V_conc = V u_init = Function(V) (u_cl, u_na, u_k) = TrialFunction(V) (v_cl, v_na, v_k) = TestFunction(V) if initial_condition is None: initial_condition = Expression(("exp(-((x[0]-0.1)*(x[0]-0.1)+x[1]*x[1])/0.01)", "exp(-((x[0]-0.12)*(x[0]-0.12)+x[1]*x[1])/0.01)", "0."), element=element) u_init = fe.interpolate(initial_condition, V) u_init_cl = u_init[0] u_init_na = u_init[1] u_init_k = u_init[2] assert (self.flow is not None) n = fe.FacetNormal(self.mesh) dx, ds = self.dx, self.ds flow = 10 * self.flow f_in = fe.Constant(0.00) D = fe.Constant(0.01) k1 = fe.Constant(0.1) k_1 = fe.Constant(0.001) F = ( (u_cl - u_init_cl) * v_cl * dx + dt * D * inner(grad(u_cl), grad(v_cl)) * dx + dt * inner(flow, grad(u_cl)) * v_cl * dx + (u_na - u_init_na) * v_na * dx + dt * D * inner(grad(u_na), grad(v_na)) * dx + dt * inner(flow, grad(u_na)) * v_na * dx + (u_k - u_init_k) * v_k * dx + dt * D * inner(grad(u_k), grad(v_k)) * dx + dt * inner(flow, grad(u_k)) * v_k * dx + f_in * v_cl * dx + f_in * v_na * dx + f_in * v_k * dx + dt * k1 * u_init_cl * u_init_na * v_cl * dx + dt * k1 * u_init_cl * u_init_na * v_na * dx - dt * k1 * u_init_cl * u_init_na * v_k * dx - dt * k_1 * u_init_k * v_cl * dx - dt * k_1 * u_init_k * v_na * dx + dt * k_1 * u_init_k * v_k * dx ) self.F = F a, L = fe.lhs(F), fe.rhs(F) a_mat = fe.assemble(a) L_vec = fe.assemble(L) output1 = fe.File('/tmp/cl_dyn.pvd') output2 = fe.File('/tmp/na_dyn.pvd') output3 = fe.File('/tmp/k_dyn.pvd') output4 = fe.File('/tmp/all_dyn.pvd') # solve self.sol = [] while t < t_end: t = t + dt print(t) u = Function(V) a_mat = fe.assemble(a) L_vec = fe.assemble(L) fe.solve(a_mat, u.vector(), L_vec) # NonlinearVariationalProblem(F,u) u_init.assign(u) u_cl, u_na, u_k = u.split() # u_init_cl.assign(u_cl) # u_init_na.assign(u_na) # u_init_k.assign(u_k) u_cl.rename("cl", "cl") u_na.rename("na", "na") u_k.rename("k", "k") output1 << u_cl, t output2 << u_na, t output3 << u_k, t self.sol.append((u_cl, u_na, u_k)) self.u_cl = u_cl self.u_na = u_na self.u_k = u_k def compute_conv_diff_reac_video(self, initial_condition=None, video_ref=None, video_size=None): names = {'Cl', 'K'} P1 = FiniteElement('P', fe.triangle, 1) element = fe.MixedElement([P1, P1]) V_single = FunctionSpace(self.mesh, P1) V = FunctionSpace(self.mesh, element) self.V_conc = V # load video video = VideoData(element=P1) video.load_video(self.video_filename) if( video_ref is not None and video_size is not None): video.set_reference_frame(video_ref, video_size) print(video) dt = 0.05 t = 0. t_end = 20. u_init = Function(V) (u_cl, u_k) = TrialFunction(V) (v_cl, v_k) = TestFunction(V) #u_na = video if initial_condition is None: #initial_condition = Expression(("f*exp(-0.5*((x[0]-a)*(x[0]-a)+(x[1]-b)*(x[1]-b))/var)/(sqrt(2*pi)*var)", # "0."), a = 80, b= 55, var=10, f=10, pi=fe.pi, element=element) initial_condition = Expression(("f", "0."), a=80, b=55, var=0.1, f=0.1, pi=fe.pi, element=element) u_init = fe.interpolate(initial_condition, V) u_init_cl = u_init[0] u_init_k = u_init[1] u_init_na : VideoData= video assert (self.flow is not None) n = fe.FacetNormal(self.mesh) dx, ds = self.dx, self.ds flow = 5. * self.flow f_in = fe.Constant(0.00) f_in_cl = fe.Constant(-0.05) D = fe.Constant(0.1) C_na = fe.Constant(0.1) k1 = fe.Constant(0.2) k_1 = fe.Constant(0.00001) # explicit F = ( (u_cl - u_init_cl) * v_cl * dx + dt * D * inner(grad(u_cl), grad(v_cl)) * dx + dt * inner(flow, grad(u_cl)) * v_cl * dx #+ (u_na - u_init_na) * v_na * dx #+ dt * D * inner(grad(u_na), grad(v_na)) * dx #+ dt * inner(flow, grad(u_na)) * v_na * dx + (u_k - u_init_k) * v_k * dx + dt * D * inner(grad(u_k), grad(v_k)) * dx + dt * inner(flow, grad(u_k)) * v_k * dx + f_in_cl * v_cl * dx #+ f_in * v_na * dx + f_in * v_k * dx + dt * k1 * u_init_cl * C_na * u_init_na * v_cl * dx #+ dt * k1 * u_init_cl * u_init_na * v_na * dx - dt * k1 * u_init_cl * C_na * u_init_na * v_k * dx - dt * k_1 * u_init_k * v_cl * dx #- dt * k_1 * u_init_k * v_na * dx + dt * k_1 * u_init_k * v_k * dx ) # implicit F = ( (u_cl - u_init_cl) * v_cl * dx + dt * D * inner(grad(u_cl), grad(v_cl)) * dx + dt * inner(flow, grad(u_cl)) * v_cl * dx # + (u_na - u_init_na) * v_na * dx # + dt * D * inner(grad(u_na), grad(v_na)) * dx # + dt * inner(flow, grad(u_na)) * v_na * dx + (u_k - u_init_k) * v_k * dx + dt * D * inner(grad(u_k), grad(v_k)) * dx + dt * inner(flow, grad(u_k)) * v_k * dx + f_in_cl * v_cl * dx # + f_in * v_na * dx + f_in * v_k * dx + dt * k1 * u_cl * C_na * u_init_na * v_cl * dx # + dt * k1 * u_init_cl * u_init_na * v_na * dx - dt * k1 * u_cl * C_na * u_init_na * v_k * dx - dt * k_1 * u_k * v_cl * dx # - dt * k_1 * u_init_k * v_na * dx + dt * k_1 * u_k * v_k * dx ) self.F = F a, L = fe.lhs(F), fe.rhs(F) a_mat = fe.assemble(a) L_vec = fe.assemble(L) output1 = fe.File('/tmp/cl_dyn.pvd') output2 = fe.File('/tmp/na_dyn.pvd') output3 = fe.File('/tmp/k_dyn.pvd') output4 = fe.File('/tmp/all_dyn.pvd') # solve self.sol = [] u_na = Function(V_single) u_na = fe.interpolate(u_init_na,V_single) na_inflow = 0 t_plot = 0.5 t_last_plot = 0 while t < t_end: t = t + dt t_last_plot += dt print(t) u = Function(V) u_init_na.set_time(5*t) a_mat = fe.assemble(a) L_vec = fe.assemble(L) fe.solve(a_mat, u.vector(), L_vec) # NonlinearVariationalProblem(F,u) u_init.assign(u) u_cl, u_k = u.split() # u_init_cl.assign(u_cl) # u_init_na.assign(u_na) # u_init_k.assign(u_k) u_na = fe.interpolate(u_init_na, V_single) u_cl.rename("cl", "cl") u_na.rename("na", "na") u_k.rename("k", "k") output1 << u_cl, t output2 << u_na, t output3 << u_k, t self.sol.append((u_cl, u_na, u_k)) print( fe.assemble(u_cl*self.dx)) if t_last_plot > t_plot: t_last_plot = 0 plt.figure(figsize=(8,16)) plt.subplot(211) fe.plot(u_cl) plt.subplot(212) fe.plot(u_k) plt.show() self.u_cl = u_cl #self.u_na = u_na self.u_k = u_k def check_conservation_law(self, sol): dx = self.dx ds = self.ds total = [] out = [] for s in sol: cl = s[0] na = s[1] k = s[2] # integrate interior concentration mass_cl = fe.assemble(cl * dx) mass_na = fe.assemble(na * dx) mass_k = fe.assemble(k * dx) # integrate outflux n = fe.FacetNormal(self.mesh) flow = self.flow outflux_cl = fe.assemble(cl * inner(flow, n) * ds) outflux_na = fe.assemble(na * inner(flow, n) * ds) outflux_k = fe.assemble(k * inner(flow, n) * ds) total.append(mass_cl + mass_na + 2 * mass_k) out.append(outflux_cl + outflux_na + 2 * outflux_k) self.total_mass = total self.outflux = out