def test_lhs_rhs_simple(): """Test taking lhs/rhs of DOLFIN specific forms (constants without cell). """ mesh = RectangleMesh.create(MPI.comm_world, [Point(0, 0), Point(2, 1)], [3, 5], CellType.Type.triangle) V = FunctionSpace(mesh, "CG", 1) f = 2.0 g = 3.0 v = TestFunction(V) u = TrialFunction(V) F = inner(g * grad(f * v), grad(u)) * dx + f * v * dx a, L = system(F) Fl = lhs(F) Fr = rhs(F) assert (Fr) a0 = inner(grad(v), grad(u)) * dx n = assemble(a).norm("frobenius") # noqa nl = assemble(Fl).norm("frobenius") # noqa n0 = 6.0 * assemble(a0).norm("frobenius") # noqa assert round(n - n0, 7) == 0 assert round(n - nl, 7) == 0
def variational_forms(self, dt: df.Constant) -> Tuple[Any, Any]: """Create the variational forms corresponding to the given discretization of the given system of equations. *Arguments* kn (:py:class:`ufl.Expr` or float) The time step *Returns* (lhs, rhs) (:py:class:`tuple` of :py:class:`ufl.Form`) """ # Extract theta parameter and conductivities theta = self._parameters.theta Mi = self._intracellular_conductivity Me = self._extracellular_conductivity # Define variational formulation if self._parameters.linear_solver_type == "direct": v, u, multiplier = df.TrialFunctions(self._VUR) v_test, u_test, multiplier_test = df.TestFunctions(self._VUR) else: v, u = df.TrialFunctions(self._VUR) v_test, u_test = df.TestFunctions(self._VUR) Dt_v = (v - self._v_prev) / dt Dt_v *= self._chi_cm # Chi is surface to volume aration. Cm is capacitance v_mid = theta * v + (1.0 - theta) * self._v_prev # Set-up measure and rhs from stimulus dOmega = df.Measure("dx", domain=self._mesh, subdomain_data=self._cell_function) dGamma = df.Measure("ds", domain=self._mesh, subdomain_data=self._interface_function) # Loop over all domains G = Dt_v * v_test * dOmega() for key in self._cell_tags - self._restrict_tags: G += df.inner(Mi[key] * df.grad(v_mid), df.grad(v_test)) * dOmega(key) G += df.inner(Mi[key] * df.grad(v_mid), df.grad(u_test)) * dOmega(key) for key in self._cell_tags: G += df.inner(Mi[key] * df.grad(u), df.grad(v_test)) * dOmega(key) G += df.inner((Mi[key] + Me[key]) * df.grad(u), df.grad(u_test)) * dOmega(key) # If Lagrangian multiplier if self._parameters.linear_solver_type == "direct": G += (multiplier_test * u + multiplier * u_test) * dOmega(key) for key in set(self._interface_tags): # Default to 0 if not defined for tag G += self._neumann_bc.get(key, df.Constant(0)) * u_test * dGamma(key) a, L = df.system(G) return a, L
def variational_forms(self, k_n: df.Constant): """Create the variational forms corresponding to the given discretization of the given system of equations. *Arguments* k_n (:py:class:`ufl.Expr` or float) The time step *Returns* (lhs, rhs, prec) (:py:class:`tuple` of :py:class:`ufl.Form`) """ # Extract theta parameter and conductivities theta = self.parameters["theta"] M_i = self._M_i # Define variational formulation v = df.TrialFunction(self.V) w = df.TestFunction(self.V) chi = self.parameters["Chi"] capacitance = self.parameters["Cm"] lam = self.parameters["lambda"] lam_frac = df.Constant(lam / (1 + lam)) # Set-up variational problem Dt_v_k_n = (v - self.v_) / k_n Dt_v_k_n *= chi * capacitance v_mid = theta * v + (1.0 - theta) * self.v_ dz = df.Measure("dx", domain=self._mesh, subdomain_data=self._cell_domains) cell_tags = map(int, set( self._cell_domains.array())) # np.int64 does not work # Currently not used db = df.Measure("ds", domain=self._mesh, subdomain_data=self._facet_domains) facet_tags = map(int, set(self._facet_domains.array())) prec = 0 for key in cell_tags: G = Dt_v_k_n * w * dz(key) G += lam_frac * df.inner(M_i[key] * df.grad(v_mid), df.grad(w)) * dz(key) if self._I_s is None: G -= chi * df.Constant(0) * w * dz(key) else: G -= chi * self._I_s * w * dz(key) # Define preconditioner based on educated(?) guess by Marie prec += (v * w + k_n / 2.0 * df.inner(M_i[key] * df.grad(v), df.grad(w))) * dz(key) a, L = df.system(G) return a, L, prec
def compare_split_matrices(eq, mat, vec, Wv, Wu, eps=1e-14): # Assemble coupled system M, v = dolfin.system(eq) M = assemble(M).array() v = assemble(v).get_local() Ni, Nj = mat.shape def compute_diff(coupled, split): diff = abs(coupled - split).flatten().max() return diff # Rebuild coupled system from split parts M2 = numpy.zeros_like(M) v2 = numpy.zeros_like(v) for i in range(Ni): dm_Wi = Wv.sub(i).dofmap() if vec[i] is not None: data = assemble(vec[i]).get_local() dm_Vi = vec[i].arguments()[0].function_space().dofmap() for cell in dolfin.cells(Wv.mesh()): dofs_Wi = dm_Wi.cell_dofs(cell.index()) dofs_Vi = dm_Vi.cell_dofs(cell.index()) v2[dofs_Wi] = data[dofs_Vi] dofs_i = dm_Wi.dofs() diff = compute_diff(v[dofs_i], v2[dofs_i]) print( 'Vector part %d has error %10.3e' % (i, diff), '<---' if diff > eps else '' ) for j in range(Nj): dm_Wj = Wv.sub(j).dofmap() if mat[i, j] is not None: data = assemble(mat[i, j]).array() dm_Vi = mat[i, j].arguments()[0].function_space().dofmap() dm_Vj = mat[i, j].arguments()[1].function_space().dofmap() for cell in dolfin.cells(Wv.mesh()): dofs_Wi = dm_Wi.cell_dofs(cell.index()) dofs_Wj = dm_Wj.cell_dofs(cell.index()) dofs_Vi = dm_Vi.cell_dofs(cell.index()) dofs_Vj = dm_Vj.cell_dofs(cell.index()) W_idx = numpy.ix_(dofs_Wi, dofs_Wj) V_idx = numpy.ix_(dofs_Vi, dofs_Vj) M2[W_idx] = data[V_idx] dofs_j = dm_Wj.dofs() idx = numpy.ix_(dofs_i, dofs_j) diff = compute_diff(M[idx], M2[idx]) print( 'Matrix part (%d, %d) has error %10.3e' % (i, j, diff), '<---' if diff > eps else '', ) # Check that the original and rebuilt systems are identical assert compute_diff(M, M2) < eps assert compute_diff(v, v2) < eps
def define_coupled_equation(self): """ Setup the coupled Navier-Stokes equation This implementation assembles the full LHS and RHS each time they are needed """ Vcoupled = self.simulation.data['Vcoupled'] # Unpack the coupled trial and test functions uc = dolfin.TrialFunction(Vcoupled) vc = dolfin.TestFunction(Vcoupled) ulist = [] vlist = [] ndim = self.simulation.ndim for d in range(ndim): ulist.append(uc[d]) vlist.append(vc[d]) u = dolfin.as_vector(ulist) v = dolfin.as_vector(vlist) p = uc[ndim] q = vc[ndim] lm_trial = lm_test = None if self.use_lagrange_multiplicator: lm_trial = uc[ndim + 1] lm_test = vc[ndim + 1] assert self.flux_type == UPWIND eq = define_dg_equations( u, v, p, q, lm_trial, lm_test, self.simulation, include_hydrostatic_pressure=self.include_hydrostatic_pressure, incompressibility_flux_type=self.incompressibility_flux_type, use_grad_q_form=self.use_grad_q_form, use_grad_p_form=self.use_grad_p_form, use_stress_divergence_form=self.use_stress_divergence_form, velocity_continuity_factor_D12=self.velocity_continuity_factor_D12, pressure_continuity_factor=self.pressure_continuity_factor, ) a, L = dolfin.system(eq) self.form_lhs = a self.form_rhs = L self.tensor_lhs = None self.tensor_rhs = None
def _variational_forms(self) -> Tuple[Any, Any]: # Localise variables for convenicence dt = self._timestep theta = self._parameters.theta Mi = self._conductivity lbda = self._lambda dOmega = self._dOmega dGamma = self._dGamma v = self._v_trial v_test = self._v_test # Set-up variational problem dvdt = (v - self._v_prev) / dt dvdt *= self._chi_cm v_mid = theta * v + (1.0 - theta) * self._v_prev # Cell contributions Form = dvdt * v_test * dOmega() for cell_tag in filter(lambda x: x is not None, self._cell_tags): factor = lbda[cell_tag] / (1 + lbda[cell_tag]) Form += factor * df.inner(Mi[cell_tag] * df.grad(v_mid), df.grad(v_test)) * dOmega(cell_tag) # Boundary contributions for interface_tag in filter(lambda x: x is not None, self._interface_tags): neumann_bc = self._neumann_boundary_condition.get( interface_tag, df.Constant(0)) neumann_bc = neumann_bc * v_test * dGamma(interface_tag) Form += neumann_bc # Interface conditions # csf_tag = self._cell_tags.CSF # gm_tag = self._cell_tags.GM # csf_gm_interface_tag = self._interface_tags.CSF_GM # interface_contribution = df.inner(Mi[csf_tag]*df.grad(v), Mi[gm_tag]/(1 + lbda[gm_tag])*df.grad(v)) # interface_contribution *= dGamma(csf_gm_interface_tag) # Form += interface_contribution # rhs # TODO: This is not necessary # Form += df.Constant(0)*v_test*dOmega a, L = df.system(Form) return a, L
def step(self, solution_fields): """ Solve on the given time interval (t0, t1). *Arguments* interval (:py:class:`tuple`) The time interval (t0, t1) for the step *Invariants* Assuming that v\_ is in the correct state for t0, gives self.vur in correct state at t1. """ # Define variational problem a, L = system(self._form) problem = LinearVariationalProblem(a, L, solution_fields) # Set-up solver solver = LinearVariationalSolver(problem) solver.parameters.update(self.parameters) solver.solve()
def define_momentum_equation(self): """ Setup the momentum equation weak form """ sim = self.simulation Vuvw = sim.data['uvw_star'].function_space() tests = dolfin.TestFunctions(Vuvw) trials = dolfin.TrialFunctions(Vuvw) # Split into components v = dolfin.as_vector(tests[:]) u = dolfin.as_vector(trials[:]) # The pressure is explicit p* and q is zero (on a domain, to avoid warnings) p = sim.data['p'] class MyZero(Zero): def ufl_domains(self): return p.ufl_domains() q = MyZero() lm_trial = lm_test = None # Define the momentum equation weak form eq = define_dg_equations( u, v, p, q, lm_trial, lm_test, self.simulation, include_hydrostatic_pressure=self.include_hydrostatic_pressure, incompressibility_flux_type='central', # Only used with q use_grad_q_form=False, # Only used with q use_grad_p_form=self.use_grad_p_form, use_stress_divergence_form=self.use_stress_divergence_form, ) self.form_lhs, self.form_rhs = dolfin.system(eq)
def setup_S(w_S, u, p, v, q, p0, q0, dx, ds, normal, dirichlet_bcs, neumann_bcs, boundary_to_mark, u_1, phi_, rho_, rho_1, g_, M_, mu_, rho_e_, c_, V_, c_1, V_1, dbeta, solutes, per_tau, drho, sigma_bar, eps, dveps, grav, fric, u_comoving, enable_PF, enable_EC, use_iterative_solvers, use_pressure_stabilization, p_lagrange, q_rhs): """ Set up Stokes subproblem """ F = (per_tau * rho_1 * df.dot(u - u_1, v) * dx + mu_ * df.inner(df.grad(u), df.grad(v)) * dx - p * df.div(v) * dx + q * df.div(u) * dx - rho_ * df.dot(grav, v) * dx) print("Linear system size", w_S.function_space().dim()) a, L = df.system(F) problem = df.LinearVariationalProblem(a, L, w_S, dirichlet_bcs) solver = df.LinearVariationalSolver(problem) solver.parameters["linear_solver"] = "mumps" if use_iterative_solvers: solver.parameters["linear_solver"] = "gmres" solver.parameters["preconditioner"] = "amg" return solver
def variational_forms(self, kn: df.Constant) -> tp.Tuple[tp.Any, tp.Any]: """Create the variational forms corresponding to the given discretization of the given system of equations. *Arguments* kn (:py:class:`ufl.Expr` or float) The time step *Returns* (lhs, rhs) (:py:class:`tuple` of :py:class:`ufl.Form`) """ # Extract theta parameter and conductivities theta = self._parameters["theta"] Mi = self._M_i Me = self._M_e # Define variational formulation if self._parameters["linear_solver_type"] == "direct": v, u, l = df.TrialFunctions(self.VUR) w, q, lamda = df.TestFunctions(self.VUR) else: v, u = df.TrialFunctions(self.VUR) w, q = df.TestFunctions(self.VUR) # Get physical parameters chi = self._parameters["Chi"] capacitance = self._parameters["Cm"] Dt_v = (v - self.v_) / kn Dt_v *= chi * capacitance v_mid = theta * v + (1.0 - theta) * self.v_ # Set-up measure and rhs from stimulus dz = df.Measure("dx", domain=self._mesh, subdomain_data=self._cell_domains) db = df.Measure("ds", domain=self._mesh, subdomain_data=self._facet_domains) # Get domain tags cell_tags = map(int, set( self._cell_domains.array())) # np.int64 does not work facet_tags = map(int, set(self._facet_domains.array())) # Loop over all domains G = Dt_v * w * dz() for key in cell_tags: G += df.inner(Mi[key] * df.grad(v_mid), df.grad(w)) * dz(key) G += df.inner(Mi[key] * df.grad(u), df.grad(w)) * dz(key) G += df.inner(Mi[key] * df.grad(v_mid), df.grad(q)) * dz(key) G += df.inner( (Mi[key] + Me[key]) * df.grad(u), df.grad(q)) * dz(key) if self._I_s is None: G -= chi * df.Constant(0) * w * dz(key) else: _is = self._I_s.get(key, df.Constant(0)) G -= chi * _is * w * dz(key) # If Lagrangian multiplier if self._parameters["linear_solver_type"] == "direct": G += (lamda * u + l * q) * dz(key) if self._I_a: G -= chi * self._I_a[key] * q * dz(key) for key in facet_tags: if self._ect_current is not None: # Default to 0 if not defined for tag I do not I should apply `chi` here. G += self._ect_current.get(key, df.Constant(0)) * q * db(key) a, L = df.system(G) return a, L
def step(self, t0: float, t1: float) -> None: """Solve on the given time interval (t0, t1). Arguments: interval (:py:class:`tuple`) The time interval (t0, t1) for the step *Invariants* Assuming that v\_ is in the correct state for t0, gives self.vur in correct state at t1. """ timer = df.Timer("PDE step") # Extract theta and conductivities theta = self._parameters["theta"] Mi = self._M_i Me = self._M_e # Extract interval and thus time-step kn = df.Constant(t1 - t0) # Define variational formulation if self._parameters["linear_solver_type"] == "direct": v, u, l = df.TrialFunctions(self.VUR) w, q, lamda = df.TestFunctions(self.VUR) else: v, u = df.TrialFunctions(self.VUR) w, q = df.TestFunctions(self.VUR) # Get physical parameters chi = self._parameters["Chi"] capacitance = self._parameters["Cm"] Dt_v = (v - self.v_) / kn Dt_v *= chi * capacitance v_mid = theta * v + (1.0 - theta) * self.v_ # Set time t = t0 + theta * (t1 - t0) self.time.assign(t) # Define spatial integration domains: dz = df.Measure("dx", domain=self._mesh, subdomain_data=self._cell_domains) db = df.Measure("ds", domain=self._mesh, subdomain_data=self._facet_domains) # Get domain labels cell_tags = map(int, set( self._cell_domains.array())) # np.int64 does not workv facet_tags = map(int, set(self._facet_domains.array())) # Loop overe all domain labels G = Dt_v * w * dz() for key in cell_tags: G += df.inner(Mi[key] * df.grad(v_mid), df.grad(w)) * dz(key) G += df.inner(Mi[key] * df.grad(u), df.grad(w)) * dz(key) G += df.inner(Mi[key] * df.grad(v_mid), df.grad(q)) * dz(key) G += df.inner( (Mi[key] + Me[key]) * df.grad(u), df.grad(q)) * dz(key) if self._I_s is None: G -= chi * df.Constant(0) * w * dz(key) else: # _is = self._I_s.get(key, df.Constant(0)) # G -= chi*_is*w*dz(key) G -= chi * self._I_s[key] * w * dz(key) # If Lagrangian multiplier if self._parameters["linear_solver_type"] == "direct": G += (lamda * u + l * q) * dz(key) # Add applied current as source in elliptic equation if applicable if self._I_a: G -= chi * self._I_a[key] * q * dz(key) if self._ect_current is not None: for key in facet_tags: # Detfalt to 0 if not defined for that facet tag # TODO: Should I include `chi` here? I do not think so G += self._ect_current.get(key, df.Constant(0)) * q * db(key) # Define variational problem a, L = df.system(G) pde = df.LinearVariationalProblem(a, L, self.vur, bcs=self._bcs) # Set-up solver solver = df.LinearVariationalSolver(pde) solver.solve()
def on_simulation_start(self): """ This runs when the simulation starts. It does not run in __init__ since the N-S solver needs the density and viscosity we define, and we need the velocity that is defined by the solver """ if self.use_analytical_solution: return sim = self.simulation dirichlet_bcs = sim.data['dirichlet_bcs'].get('rho', []) if self.use_rk_method: V = self.simulation.data['Vrho'] if not V.ufl_element().family() == 'Discontinuous Lagrange': ocellaris_error( 'VariableDensity timestepping error', 'Can only use explicit SSP Runge-Kutta method with DG space for rho', ) Vu = sim.data['Vu'] u_conv, self.funcs_to_extrapolate = [], [] for d in range(sim.ndim): ux = Function(Vu) up = sim.data['up_conv%d' % d] upp = sim.data['upp_conv%d' % d] self.funcs_to_extrapolate.append((ux, up, upp)) u_conv.append(ux) u_conv = dolfin.as_vector(u_conv) from dolfin import dot, div, jump, dS mesh = self.simulation.data['mesh'] re = self.rho_explicit = dolfin.Function(V) c, d = dolfin.TrialFunction(V), dolfin.TestFunction(V) n = dolfin.FacetNormal(mesh) w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2 dx = dolfin.dx(domain=mesh) eq = c * d * dx # Convection integrated by parts two times to bring back the original # div form (this means we must subtract and add all fluxes) eq += div(re * u_conv) * d * dx # Replace downwind flux with upwind flux on downwind internal facets eq -= jump(w_nD * d) * jump(re) * dS # Replace downwind flux with upwind BC flux on downwind external facets for dbc in dirichlet_bcs: # Subtract the "normal" downwind flux eq -= w_nD * re * d * dbc.ds() # Add the boundary value upwind flux eq += w_nD * dbc.func() * d * dbc.ds() a, L = dolfin.system(eq) self.rk = RungeKuttaDGTimestepping( self.simulation, a, L, self.rho, self.rho_explicit, 'rho', order=None, explicit_funcs=self.funcs_to_extrapolate, bcs=dirichlet_bcs, ) else: # Use backward Euler (BDF1) for timestep 1 self.time_coeffs = Constant([1, -1, 0]) if dolfin.norm(self.rho_pp.vector()) > 0: # Use BDF2 from the start self.time_coeffs.assign(Constant([3 / 2, -2, 1 / 2])) self.simulation.log.info( 'Using second order timestepping from the start in VariableDensity' ) # Define equation for advection of the density # ∂ρ/∂t + ∇⋅(ρ u) = 0 beta = None u_conv = sim.data['u_conv'] forcing_zones = sim.data['forcing_zones'].get('rho', []) self.eq = AdvectionEquation( sim, sim.data['Vrho'], self.rho_p, self.rho_pp, u_conv, beta, self.time_coeffs, dirichlet_bcs, forcing_zones, ) self.solver = linear_solver_from_input(sim, 'solver/rho', SOLVER, PRECONDITIONER, None, KRYLOV_PARAMETERS) self.slope_limiter = SlopeLimiter(sim, 'rho', self.rho) self.slope_limiter.set_phi_old(self.rho_p) # Make sure the initial value is included in XDMF results from timestep 0 self.rho.assign(self.rho_p)
def define_coupled_equation(self): """ Setup the coupled Navier-Stokes equation This implementation assembles the full LHS and RHS each time they are needed """ sim = self.simulation mpm = sim.multi_phase_model mesh = sim.data['mesh'] Vcoupled = sim.data['Vcoupled'] u_conv = sim.data['u_conv'] # Unpack the coupled trial and test functions uc = dolfin.TrialFunction(Vcoupled) vc = dolfin.TestFunction(Vcoupled) ulist = [] vlist = [] ndim = self.simulation.ndim for d in range(ndim): ulist.append(uc[d]) vlist.append(vc[d]) u = dolfin.as_vector(ulist) v = dolfin.as_vector(vlist) p = uc[ndim] q = vc[ndim] c1, c2, c3 = sim.data['time_coeffs'] dt = sim.data['dt'] g = sim.data['g'] n = dolfin.FacetNormal(mesh) # Fluid properties rho = mpm.get_density(0) mu = mpm.get_laminar_dynamic_viscosity(0) # Hydrostatic pressure correction if self.include_hydrostatic_pressure: p += sim.data['p_hydrostatic'] # Start building the coupled equations eq = 0 # ALE mesh velocities if sim.mesh_morpher.active: u_mesh = sim.data['u_mesh'] # Either modify the convective velocity or just include the mesh # velocity on cell integral form. Only activate one of these lines # u_conv -= u_mesh eq -= dot(div(rho * dolfin.outer(u, u_mesh)), v) * dx # Divergence of u should balance expansion/contraction of the cell K # ∇⋅u = -∂x/∂t (See below for definition of the ∇⋅u term) cvol_new = dolfin.CellVolume(mesh) cvol_old = sim.data['cvolp'] eq += (cvol_new - cvol_old) / dt * q * dx # Lagrange multiplicator to remove the pressure null space # ∫ p dx = 0 if self.use_lagrange_multiplicator: lm_trial = uc[ndim + 1] lm_test = vc[ndim + 1] eq = (p * lm_test + q * lm_trial) * dx # Momentum equations for d in range(sim.ndim): up = sim.data['up%d' % d] upp = sim.data['upp%d' % d] # Divergence free criterion # ∇⋅u = 0 eq += u[d].dx(d) * q * dx # Time derivative # ∂u/∂t eq += rho * (c1 * u[d] + c2 * up + c3 * upp) / dt * v[d] * dx # Convection # ∇⋅(ρ u ⊗ u_conv) eq += rho * dot(u_conv, grad(u[d])) * v[d] * dx # Diffusion # -∇⋅μ(∇u) eq += mu * dot(grad(u[d]), grad(v[d])) * dx # -∇⋅μ(∇u)^T if self.use_stress_divergence_form: eq += mu * dot(u.dx(d), grad(v[d])) * dx # Pressure # ∇p eq -= v[d].dx(d) * p * dx # Body force (gravity) # ρ g eq -= rho * g[d] * v[d] * dx # Other sources for f in sim.data['momentum_sources']: eq -= f[d] * v[d] * dx # Neumann boundary conditions neumann_bcs_pressure = sim.data['neumann_bcs'].get('p', []) for nbc in neumann_bcs_pressure: eq += p * v[d] * n[d] * nbc.ds() # Outlet boundary for obc in sim.data['outlet_bcs']: # Diffusion mu_dudn = p * n[d] eq -= mu_dudn * v[d] * obc.ds() # Pressure p_ = mu * dot(dot(grad(u), n), n) eq += p_ * v[d] * n[d] * obc.ds() a, L = dolfin.system(eq) self.form_lhs = a self.form_rhs = L self.tensor_lhs = None self.tensor_rhs = None
def define_coupled_equation(self): """ Setup the coupled Navier-Stokes equation This implementation assembles the full LHS and RHS each time they are needed """ sim = self.simulation mpm = sim.multi_phase_model mesh = sim.data['mesh'] Vcoupled = sim.data['Vcoupled'] u_conv = sim.data['u_conv'] # Unpack the coupled trial and test functions uc = dolfin.TrialFunction(Vcoupled) vc = dolfin.TestFunction(Vcoupled) ulist = [] vlist = [] ndim = self.simulation.ndim sigma, tau = [], [] for d in range(ndim): ulist.append(uc[d]) vlist.append(vc[d]) indices = list(range(1 + ndim * (d + 1), 1 + ndim * (d + 2))) sigma.append(dolfin.as_vector([uc[i] for i in indices])) tau.append(dolfin.as_vector([vc[i] for i in indices])) u = dolfin.as_vector(ulist) v = dolfin.as_vector(vlist) p = uc[ndim] q = vc[ndim] c1, c2, c3 = sim.data['time_coeffs'] dt = sim.data['dt'] g = sim.data['g'] n = dolfin.FacetNormal(mesh) # Fluid properties rho = mpm.get_density(0) mu = mpm.get_laminar_dynamic_viscosity(0) # Upwind and downwind velocities w_nU = (dot(u_conv, n) + abs(dot(u_conv, n))) / 2.0 w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2.0 # LDG penalties C11 = Constant(1.0 + sim.ndim) switch = dolfin.conditional(dolfin.gt(w_nU('+'), 0.0), n('+'), n('-')) C12 = 0.5 * switch # Start building the coupled equations eq = 0 # Momentum equations for d in range(sim.ndim): up = sim.data['up%d' % d] upp = sim.data['upp%d' % d] # Divergence free criterion # ∇⋅u = 0 u_hat_p = avg(u[d]) if self.use_grad_q_form: eq -= u[d] * q.dx(d) * dx eq += u_hat_p * jump(q) * n[d]('+') * dS else: eq += q * u[d].dx(d) * dx eq -= avg(q) * jump(u[d]) * n[d]('+') * dS # Time derivative # ∂(ρu)/∂t eq += rho * (c1 * u[d] + c2 * up + c3 * upp) / dt * v[d] * dx # Convection: # -w⋅∇(ρu) flux_nU = u[d] * w_nU flux = jump(flux_nU) eq -= u[d] * dot(grad(rho * v[d]), u_conv) * dx eq += flux * jump(rho * v[d]) * dS # Diffusion: # -∇⋅∇u u_hat_dS = avg(u[d]) - dot(C12, jump(u[d], n)) sigma_hat_dS = avg( sigma[d]) - C11 * jump(u[d], n) + C12 * jump(sigma[d], n) eq += dot(sigma[d], tau[d]) * dx eq += u[d] * div(mu * tau[d]) * dx eq -= u_hat_dS * jump(mu * tau[d], n) * dS eq += dot(sigma[d], grad(v[d])) * dx eq -= dot(sigma_hat_dS, jump(v[d], n)) * dS # Pressure # ∇p if self.use_grad_p_form: eq += v[d] * p.dx(d) * dx eq -= avg(v[d]) * jump(p) * n[d]('+') * dS else: eq -= p * v[d].dx(d) * dx eq += avg(p) * jump(v[d]) * n[d]('+') * dS # Body force (gravity) # ρ g eq -= rho * g[d] * v[d] * dx # Other sources for f in sim.data['momentum_sources']: eq -= f[d] * v[d] * dx # Dirichlet boundary dirichlet_bcs = sim.data['dirichlet_bcs'].get('u%d' % d, []) for dbc in dirichlet_bcs: u_bc = dbc.func() # Divergence free criterion if self.use_grad_q_form: eq += q * u_bc * n[d] * dbc.ds() else: eq -= q * u[d] * n[d] * dbc.ds() eq += q * u_bc * n[d] * dbc.ds() # Convection eq += rho * u[d] * w_nU * v[d] * dbc.ds() eq += rho * u_bc * w_nD * v[d] * dbc.ds() # Diffusion u_hat_ds = u_bc sigma_hat_ds = sigma[d] - C11 * (u[d] - u_bc) * n eq -= u_hat_ds * mu * dot(tau[d], n) * dbc.ds() eq -= dot(sigma_hat_ds, n) * v[d] * dbc.ds() # Pressure if not self.use_grad_p_form: eq += p * v[d] * n[d] * dbc.ds() # Neumann boundary neumann_bcs = sim.data['neumann_bcs'].get('u%d' % d, []) for nbc in neumann_bcs: # Divergence free criterion if self.use_grad_q_form: eq += q * u[d] * n[d] * nbc.ds() else: eq -= q * u[d] * n[d] * nbc.ds() # Convection eq += rho * u[d] * w_nU * v[d] * nbc.ds() # Diffusion u_hat_ds = u[d] sigma_hat_ds = nbc.func() / mu * n eq -= u_hat_ds * mu * dot(tau[d], n) * nbc.ds() eq -= dot(sigma_hat_ds, n) * v[d] * nbc.ds() # Pressure if not self.use_grad_p_form: eq += p * v[d] * n[d] * nbc.ds() a, L = dolfin.system(eq) self.form_lhs = a self.form_rhs = L self.tensor_lhs = None self.tensor_rhs = None
def step(self, t0: float, t1: float) -> None: r"""Solve on the given time interval (t0, t1). *Arguments* interval (:py:class:`tuple`) The time interval (t0, t1) for the step *Invariants* Assuming that v\_ is in the correct state for t0, gives self.v in correct state at t1. """ # Extract interval and thus time-step k_n = df.Constant(t1 - t0) # Extract theta parameter and conductivities theta = self.parameters["theta"] M_i = self._M_i # Set time t = t0 + theta * (t1 - t0) self.time.assign(t) # Get physical parameters chi = self.parameters["Chi"] capacitance = self.parameters["Cm"] lam = self.parameters["lambda"] lam_frac = df.Constant(lam / (1 + lam)) # Define variational formulation v = df.TrialFunction(self.V) w = df.TestFunction(self.V) Dt_v_k_n = (v - self.v_) / k_n Dt_v_k_n *= chi * capacitance v_mid = theta * v + (1.0 - theta) * self.v_ dz = df.Measure("dx", domain=self._mesh, subdomain_data=self._cell_domains) db = df.Measure("ds", domain=self._mesh, subdomain_data=self._facet_domains) # dz, rhs = rhs_with_markerwise_field(self._I_s, self._mesh, w) cell_tags = map(int, set( self._cell_domains.array())) # np.int64 does not work facet_tags = map(int, set(self._facet_domains.array())) for key in cell_tags: G = Dt_v_k_n * w * dz(key) G += lam_frac * df.inner(M_i[key] * df.grad(v_mid), df.grad(w)) * dz(key) if self._I_s is None: G -= chi * df.Constant(0) * w * dz(key) else: G -= chi * self._I_s * w * dz(key) # Define variational problem a, L = df.system(G) pde = df.LinearVariationalProblem(a, L, self.v) # Set-up solver solver_type = self.parameters["linear_solver_type"] solver = df.LinearVariationalSolver(pde) solver.solve()
def define_coupled_equation(self): """ Setup the coupled Navier-Stokes equation This implementation assembles the full LHS and RHS each time they are needed """ sim = self.simulation mpm = sim.multi_phase_model mesh = sim.data['mesh'] Vcoupled = sim.data['Vcoupled'] u_conv = sim.data['u_conv'] # Unpack the coupled trial and test functions uc = dolfin.TrialFunction(Vcoupled) vc = dolfin.TestFunction(Vcoupled) ulist = [] vlist = [] sigmas, taus = [], [] for d in range(sim.ndim): ulist.append(uc[d]) vlist.append(vc[d]) indices = list( range(1 + sim.ndim * (d + 1), 1 + sim.ndim * (d + 2))) sigmas.append([uc[i] for i in indices]) taus.append([vc[i] for i in indices]) u = dolfin.as_vector(ulist) v = dolfin.as_vector(vlist) p = uc[sim.ndim] q = vc[sim.ndim] sigma = dolfin.as_tensor(sigmas) tau = dolfin.as_tensor(taus) c1, c2, c3 = sim.data['time_coeffs'] dt = sim.data['dt'] g = sim.data['g'] n = dolfin.FacetNormal(mesh) h = dolfin.FacetArea(mesh) # Fluid properties rho = mpm.get_density(0) mu = mpm.get_laminar_dynamic_viscosity(0) # Upwind and downwind velocities w_nU = (dot(u_conv, n) + abs(dot(u_conv, n))) / 2.0 w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2.0 u_uw_s = dolfin.conditional(dolfin.gt(dot(u_conv, n), 0.0), 1.0, 0.0)('+') u_uw = u_uw_s * u('+') + (1 - u_uw_s) * u('-') # LDG penalties # kappa_0 = Constant(4.0) # kappa = mu*kappa_0/h C11 = avg(mu / h) D11 = avg(h / mu) C12 = 0.2 * n('+') D12 = 0.2 * n('+') def ojump(v, n): return outer(v, n)('+') + outer(v, n)('-') # Interior facet fluxes # u_hat_dS = avg(u) # sigma_hat_dS = avg(sigma) - avg(kappa)*ojump(u, n) # p_hat_dS = avg(p) u_hat_s_dS = avg(u) + dot(ojump(u, n), C12) u_hat_p_dS = avg(u) + D11 * jump(p, n) + D12 * jump(u, n) sigma_hat_dS = avg(sigma) - C11 * ojump(u, n) - outer( jump(sigma, n), C12) p_hat_dS = avg(p) - dot(D12, jump(p, n)) # Time derivative up = sim.data['up'] upp = sim.data['upp'] eq = rho * dot(c1 * u + c2 * up + c3 * upp, v) / dt * dx # LDG equation 1 eq += inner(sigma, tau) * dx eq += dot(u, div(mu * tau)) * dx eq -= dot(u_hat_s_dS, jump(mu * tau, n)) * dS # LDG equation 2 eq += (inner(sigma, grad(v)) - p * div(v)) * dx eq -= (inner(sigma_hat_dS, ojump(v, n)) - p_hat_dS * jump(v, n)) * dS eq -= dot(u, div(outer(v, rho * u_conv))) * dx eq += rho('+') * dot(u_conv('+'), n('+')) * dot(u_uw, v('+')) * dS eq += rho('-') * dot(u_conv('-'), n('-')) * dot(u_uw, v('-')) * dS momentum_sources = sim.data['momentum_sources'] + [rho * g] eq -= dot(sum(momentum_sources), v) * dx # LDG equation 3 eq -= dot(u, grad(q)) * dx eq += dot(u_hat_p_dS, jump(q, n)) * dS # Dirichlet boundary dirichlet_bcs = get_collected_velocity_bcs(sim, 'dirichlet_bcs') for ds, u_bc in dirichlet_bcs.items(): # sigma_hat_ds = sigma - kappa*outer(u, n) sigma_hat_ds = sigma - C11 * outer(u - u_bc, n) u_hat_ds = u_bc p_hat_ds = p # LDG equation 1 eq -= dot(u_hat_ds, dot(mu * tau, n)) * ds # LDG equation 2 eq -= (inner(sigma_hat_ds, outer(v, n)) - p_hat_ds * dot(v, n)) * ds eq += rho * w_nU * dot(u, v) * ds eq += rho * w_nD * dot(u_bc, v) * ds # LDG equation 3 eq += dot(u_hat_ds, q * n) * ds # Neumann boundary neumann_bcs = get_collected_velocity_bcs(sim, 'neumann_bcs') assert not neumann_bcs for ds, du_bc in neumann_bcs.items(): # Divergence free criterion if self.use_grad_q_form: eq += q * dot(u, n) * ds else: eq -= q * dot(u, n) * ds # Convection eq += rho * w_nU * dot(u, v) * ds # Diffusion u_hat_ds = u sigma_hat_ds = outer(du_bc, n) / mu eq -= dot(u_hat_ds, dot(mu * tau, n)) * ds eq -= inner(sigma_hat_ds, outer(v, n)) * ds # Pressure if not self.use_grad_p_form: eq += p * dot(v, n) * ds a, L = dolfin.system(eq) self.form_lhs = a self.form_rhs = L self.tensor_lhs = None self.tensor_rhs = None
def define_advection_equation(self): """ Setup the advection equation for the colour function This implementation assembles the full LHS and RHS each time they are needed """ sim = self.simulation mesh = sim.data['mesh'] n = dolfin.FacetNormal(mesh) dS, dx = dolfin.dS(mesh), dolfin.dx(mesh) # Trial and test functions Vc = self.Vc c = dolfin.TrialFunction(Vc) d = dolfin.TestFunction(Vc) c1, c2, c3 = self.time_coeffs dt = self.dt u_conv = self.u_conv if not self.colour_is_discontinuous: # Continous Galerkin implementation of the advection equation # FIXME: add stabilization eq = (c1 * c + c2 * self.cp + c3 * self.cpp) / dt * d * dx + div( c * u_conv) * d * dx elif self.velocity_is_trace: # Upstream and downstream normal velocities w_nU = (dot(u_conv, n) + abs(dot(u_conv, n))) / 2 w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2 if self.beta is not None: # Define the blended flux # The blending factor beta is not DG, so beta('+') == beta('-') b = self.beta('+') flux = (1 - b) * jump(c * w_nU) + b * jump(c * w_nD) else: flux = jump(c * w_nU) # Discontinuous Galerkin implementation of the advection equation eq = (c1 * c + c2 * self.cp + c3 * self.cpp) / dt * d * dx + flux * jump(d) * dS # On each facet either w_nD or w_nU will be 0, the other is multiplied # with the appropriate flux, either the value c going out of the domain # or the Dirichlet value coming into the domain for dbc in self.dirichlet_bcs: eq += w_nD * dbc.func() * d * dbc.ds() eq += w_nU * c * d * dbc.ds() elif self.beta is not None: # Upstream and downstream normal velocities w_nU = (dot(u_conv, n) + abs(dot(u_conv, n))) / 2 w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2 if self.beta is not None: # Define the blended flux # The blending factor beta is not DG, so beta('+') == beta('-') b = self.beta('+') flux = (1 - b) * jump(c * w_nU) + b * jump(c * w_nD) else: flux = jump(c * w_nU) # Discontinuous Galerkin implementation of the advection equation eq = ((c1 * c + c2 * self.cp + c3 * self.cpp) / dt * d * dx - dot(c * u_conv, grad(d)) * dx + flux * jump(d) * dS) # Enforce Dirichlet BCs weakly for dbc in self.dirichlet_bcs: eq += w_nD * dbc.func() * d * dbc.ds() eq += w_nU * c * d * dbc.ds() else: # Downstream normal velocities w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2 # Discontinuous Galerkin implementation of the advection equation eq = (c1 * c + c2 * self.cp + c3 * self.cpp) / dt * d * dx # Convection integrated by parts two times to bring back the original # div form (this means we must subtract and add all fluxes) eq += div(c * u_conv) * d * dx # Replace downwind flux with upwind flux on downwind internal facets eq -= jump(w_nD * d) * jump(c) * dS # Replace downwind flux with upwind BC flux on downwind external facets for dbc in self.dirichlet_bcs: # Subtract the "normal" downwind flux eq -= w_nD * c * d * dbc.ds() # Add the boundary value upwind flux eq += w_nD * dbc.func() * d * dbc.ds() # Penalty forcing zones for fz in self.forcing_zones: eq += fz.penalty * fz.beta * (c - fz.target) * d * dx a, L = dolfin.system(eq) self.form_lhs = dolfin.Form(a) self.form_rhs = dolfin.Form(L) self.tensor_lhs = None self.tensor_rhs = None