def compute_cfl_number(mesh, velocity, time_step): assert(isinstance(mesh, dlfn.Mesh)) assert(isinstance(velocity, dlfn.Function)) assert(isinstance(time_step, float) and time_step > 0.0) Vh_DG = dlfn.FunctionSpace(mesh, "DG", 0) h_cell = dlfn.CellDiameter(mesh) cfl_cell_expression = dlfn.sqrt(dlfn.inner(velocity, velocity)) * time_step / h_cell local_cfl = dlfn.project(cfl_cell_expression, Vh_DG) return local_cfl.vector().max()
def _compute_cfl_number(self, velocity, time_step): assert hasattr(self, "_mesh") assert (isinstance(velocity, dlfn.Function)) assert (isinstance(time_step, float) and time_step > 0.0) Vh = dlfn.FunctionSpace(self._mesh, "DG", 0) h_cell = dlfn.CellDiameter(self._mesh) from dolfin import sqrt, inner cfl_cell_expression = sqrt(inner(velocity, velocity)) * time_step / ( self._parameters.velocity_degree * h_cell) local_cfl = dlfn.project(cfl_cell_expression, Vh) return local_cfl.vector().max()
def update_mesh_data(self, connectivity_changed=True): """ Some precomputed values must be calculated before the timestepping and updated every time the mesh changes """ if connectivity_changed: init_connectivity(self) precompute_cell_data(self) precompute_facet_data(self) # Work around missing consensus on what CellDiameter is for bendy cells mesh = self.data['mesh'] if mesh.ufl_coordinate_element().degree() > 1: h = dolfin.CellVolume(mesh)**(1 / mesh.topology().dim()) else: h = dolfin.CellDiameter(mesh) self.data['h'] = h
def __init__(self, mesh, Vh, prior, misfit, simulation_times, wind_velocity, gls_stab): self.mesh = mesh self.Vh = Vh self.prior = prior self.misfit = misfit # Assume constant timestepping self.simulation_times = simulation_times dt = simulation_times[1] - simulation_times[0] u = dl.TrialFunction(Vh[STATE]) v = dl.TestFunction(Vh[STATE]) kappa = dl.Constant(.001) dt_expr = dl.Constant(dt) r_trial = u + dt_expr * (-ufl.div(kappa * ufl.grad(u)) + ufl.inner(wind_velocity, ufl.grad(u))) r_test = v + dt_expr * (-ufl.div(kappa * ufl.grad(v)) + ufl.inner(wind_velocity, ufl.grad(v))) h = dl.CellDiameter(mesh) vnorm = ufl.sqrt(ufl.inner(wind_velocity, wind_velocity)) if gls_stab: tau = ufl.min_value((h * h) / (dl.Constant(2.) * kappa), h / vnorm) else: tau = dl.Constant(0.) self.M = dl.assemble(ufl.inner(u, v) * dl.dx) self.M_stab = dl.assemble(ufl.inner(u, v + tau * r_test) * dl.dx) self.Mt_stab = dl.assemble(ufl.inner(u + tau * r_trial, v) * dl.dx) Nvarf = (ufl.inner(kappa * ufl.grad(u), ufl.grad(v)) + ufl.inner(wind_velocity, ufl.grad(u)) * v) * dl.dx Ntvarf = (ufl.inner(kappa * ufl.grad(v), ufl.grad(u)) + ufl.inner(wind_velocity, ufl.grad(v)) * u) * dl.dx self.N = dl.assemble(Nvarf) self.Nt = dl.assemble(Ntvarf) stab = dl.assemble(tau * ufl.inner(r_trial, r_test) * dl.dx) self.L = self.M + dt * self.N + stab self.Lt = self.M + dt * self.Nt + stab self.solver = dl.PETScLUSolver(dl.as_backend_type(self.L)) self.solvert = dl.PETScLUSolver(dl.as_backend_type(self.Lt)) # Part of model public API self.gauss_newton_approx = False
def set_forms(self): """ Set up week forms """ # Assume constant timestepping dt = self.simulation_times[1] - self.simulation_times[0] self.wind_velocity = self.computeVelocityField() u = dl.TrialFunction(self.Vh[STATE]) v = dl.TestFunction(self.Vh[STATE]) kappa = dl.Constant(self.kappa) dt_expr = dl.Constant(dt) r_trial = u + dt_expr * (-ufl.div(kappa * ufl.grad(u)) + ufl.inner(self.wind_velocity, ufl.grad(u))) r_test = v + dt_expr * (-ufl.div(kappa * ufl.grad(v)) + ufl.inner(self.wind_velocity, ufl.grad(v))) h = dl.CellDiameter(self.mesh) vnorm = ufl.sqrt(ufl.inner(self.wind_velocity, self.wind_velocity)) if self.gls_stab: tau = ufl.min_value((h * h) / (dl.Constant(2.) * kappa), h / vnorm) else: tau = dl.Constant(0.) self.M = dl.assemble(ufl.inner(u, v) * ufl.dx) self.M_stab = dl.assemble(ufl.inner(u, v + tau * r_test) * ufl.dx) self.Mt_stab = dl.assemble(ufl.inner(u + tau * r_trial, v) * ufl.dx) Nvarf = (ufl.inner(kappa * ufl.grad(u), ufl.grad(v)) + ufl.inner(self.wind_velocity, ufl.grad(u)) * v) * ufl.dx Ntvarf = (ufl.inner(kappa * ufl.grad(v), ufl.grad(u)) + ufl.inner(self.wind_velocity, ufl.grad(v)) * u) * ufl.dx self.N = dl.assemble(Nvarf) self.Nt = dl.assemble(Ntvarf) stab = dl.assemble(tau * ufl.inner(r_trial, r_test) * ufl.dx) self.L = self.M + dt * self.N + stab self.Lt = self.M + dt * self.Nt + stab self.solver = PETScLUSolver(self.mpi_comm) self.solver.set_operator(dl.as_backend_type(self.L)) self.solvert = PETScLUSolver(self.mpi_comm) self.solvert.set_operator(dl.as_backend_type(self.Lt))
def apply_stabilization(self): df.dS = self.dS #Important <---- h_msh = df.CellDiameter(self.mesh) h_avg = (h_msh("+") + h_msh("-")) / 2.0 #Output local_stab = 0.0 if self.stab_type == 'cip': if self.moment_order == 3 or self.moment_order == 'ns': local_stab += self.DELTA_T * h_avg**self.ht * df.jump(df.grad(self.u[0]),self.nv)\ * df.jump(df.grad(self.v[0]),self.nv) * df.dS #cip for temp if self.moment_order == 6: local_stab += self.DELTA_P * h_avg**self.hp * df.jump(df.grad(self.u[0]),self.nv)\ * df.jump(df.grad(self.v[0]),self.nv) * df.dS #cip for pressure local_stab += self.DELTA_U * h_avg**self.hu * df.jump(df.grad(self.u[1]),self.nv)\ * df.jump(df.grad(self.v[1]),self.nv) * df.dS #cip for velocity_x local_stab += self.DELTA_U * h_avg**self.hu * df.jump(df.grad(self.u[2]),self.nv)\ * df.jump(df.grad(self.v[2]),self.nv) * df.dS #cip for velocity_y if self.moment_order == 13: local_stab += self.DELTA_T * h_avg**self.ht * df.jump(df.grad(self.u[3]),self.nv)\ * df.jump(df.grad(self.v[3]),self.nv) * df.dS #cip for temp local_stab += self.DELTA_P * h_avg**self.hp * df.jump(df.grad(self.u[0]),self.nv)\ * df.jump(df.grad(self.v[0]),self.nv) * df.dS #cip for pressure local_stab += self.DELTA_U * h_avg**self.hu * df.jump(df.grad(self.u[1]),self.nv)\ * df.jump(df.grad(self.v[1]),self.nv) * df.dS #cip for velocity_x local_stab += self.DELTA_U * h_avg**self.hu * df.jump(df.grad(self.u[2]),self.nv)\ * df.jump(df.grad(self.v[2]),self.nv) * df.dS #cip for velocity_y if self.moment_order == 'grad13': local_stab += self.DELTA_T * h_avg**self.ht * df.jump(df.grad(self.u[3]),self.nv)\ * df.jump(df.grad(self.v[6]),self.nv) * df.dS #cip for temp local_stab += self.DELTA_P * h_avg**self.hp * df.jump(df.grad(self.u[0]),self.nv)\ * df.jump(df.grad(self.v[0]),self.nv) * df.dS #cip for pressure local_stab += self.DELTA_U * h_avg**self.hu * df.jump(df.grad(self.u[1]),self.nv)\ * df.jump(df.grad(self.v[1]),self.nv) * df.dS #cip for velocity_x local_stab += self.DELTA_U * h_avg**self.hu * df.jump(df.grad(self.u[2]),self.nv)\ * df.jump(df.grad(self.v[2]),self.nv) * df.dS #cip for velocity_y elif self.stab_type == 'gls': local_stab = (0.0001* h_msh * df.inner(self.SA_x * df.Dx(self.u,0)\ + self.SA_y * df.Dx(self.u,1) + self.SP_Coeff * self.u, self.SA_x * df.Dx(self.v,0)\ + self.SA_y * df.Dx(self.v,1) + self.SP_Coeff * self.v ) * df.dx) return local_stab
def __init__(self, mesh, energy, state, bcs, z, rayleigh=None, nullspace=None, parameters=None): self.i = 0 self.parameters = self.default_parameters() if parameters is not None: self.parameters.update(parameters) # for debug purposes self.u = state[0] self.alpha = state[1] self._u = dolfin.Vector(self.u.vector()) self._alpha = dolfin.Vector(self.alpha.vector()) self.meshsize = (mesh.hmax() + mesh.hmax()) / 2. self.mesh = mesh self.Z = z.function_space() self.z = z # self.Z = dolfin.FunctionSpace(mesh, dolfin.MixedElement([state[0].ufl_element(), state[1].ufl_element()])) # self.z = dolfin.Function(self.Z) self.z_old = dolfin.Function(self.Z) zeta = dolfin.TestFunction(self.Z) v, beta = dolfin.split(zeta) cdm = dolfin.project( dolfin.CellDiameter(self.mesh)**2., dolfin.FunctionSpace(self.mesh, 'CG', 1)) self.cellarea = dolfin.Function(z.function_space()) self.cellarea.assign(cdm) self.ownership = self.Z.dofmap().ownership_range() self.assigner = dolfin.FunctionAssigner( self.Z, # receiving space [self.u.function_space(), self.alpha.function_space()]) # assigning space dim = self.u.function_space().ufl_element().value_size() self.u_zero = dolfin.project( dolfin.Constant(0.), self.u.function_space()) if dim == 1 else dolfin.project( dolfin.Constant([0.] * dim), self.u.function_space()) self.a_one = dolfin.project(dolfin.Constant(1.), self.alpha.function_space()) Zu = self.Z.extract_sub_space([0]) Za = self.Z.extract_sub_space([1]) self.Xa = Za.collapse().tabulate_dof_coordinates() self.Xu = Zu.collapse().tabulate_dof_coordinates() (_, self.mapa) = Za.collapse(collapsed_dofs=True) (_, self.mapu) = Zu.collapse(collapsed_dofs=True) self.computed = [] self.provided = [] self.stable = '' self.negev = -1 self.Ealpha = dolfin.derivative( energy, self.alpha, dolfin.TestFunction(self.alpha.ufl_function_space())) self.energy = energy (z_u, z_a) = dolfin.split(self.z) # import pdb; pdb.set_trace() # energy = ufl.replace(energy, {self.u: z_u, self.alpha: z_a}) energy = ufl.replace(energy, {state[0]: z_u, state[1]: z_a}) self.J = dolfin.derivative(energy, self.z, dolfin.TestFunction(self.Z)) self.H = dolfin.derivative(self.J, self.z, dolfin.TrialFunction(self.Z)) self.nullspace = nullspace if rayleigh: rP = rayleigh[0] rN = rayleigh[1] self.rP = dolfin.derivative( dolfin.derivative(rP, self.z, dolfin.TestFunction(self.Z)), self.z, dolfin.TrialFunction(self.Z)) self.rN = dolfin.derivative( dolfin.derivative(rN, self.z, dolfin.TestFunction(self.Z)), self.z, dolfin.TrialFunction(self.Z)) self.ownership_range = self.Z.dofmap().ownership_range() if len(bcs) > 0: self.bcs = bcs self.bc_dofs = self.get_bc_dofs(bcs) else: self.bcs = None self.bc_dofs = set() self.perturbation_v = dolfin.Function(self.Z.sub(0).collapse()) self.perturbation_beta = dolfin.Function(self.Z.sub(1).collapse()) dolfin.PETScOptions.set("ksp_type", "preonly") dolfin.PETScOptions.set("pc_type", "cholesky") dolfin.PETScOptions.set("pc_factor_mat_solver_type", "mumps") dolfin.PETScOptions.set("mat_mumps_icntl_24", 1) dolfin.PETScOptions.set("mat_mumps_icntl_13", 1) dolfin.PETScOptions.set("eps_monitor")
def initialize_variables(self): r""" Initialize the model variables to default values. The variables defined here are: Various things : * ``self.element`` -- the finite-element * ``self.top_dim`` -- the topological dimension * ``self.dofmap`` -- :class:`~dolfin.cpp.fem.DofMap` for converting between vertex to nodal indicies * ``self.h`` -- Cell diameter formed by calling :func:`~dolfin.functions.specialfunctions.CellDiameter` with ``self.mesh`` Grid velocity vector :math:`\mathbf{u}_i = [u\ v\ w]^{\intercal}`: * ``self.U_mag`` -- velocity vector magnitude * ``self.U3`` -- velocity vector * ``self.u`` -- :math:`x`-component of velocity vector * ``self.v`` -- :math:`y`-component of velocity vector * ``self.w`` -- :math:`z`-component of velocity vector Grid acceleration vector :math:`\mathbf{a}_i = [a_x\ a_y\ a_z]^{\intercal}`: * ``self.a_mag`` -- acceleration vector magnitude * ``self.a3`` -- acceleration vector * ``self.a_x`` -- :math:`x`-component of acceleration vector * ``self.a_y`` -- :math:`y`-component of acceleration vector * ``self.a_z`` -- :math:`z`-component of acceleration vector Grid internal force vector :math:`\mathbf{f}_i^{\mathrm{int}} = [f_x^{\mathrm{int}}\ f_y^{\mathrm{int}}\ f_z^{\mathrm{int}}]^{\intercal}`: * ``self.f_int_mag`` -- internal force vector magnitude * ``self.f_int`` -- internal force vector * ``self.f_int_x`` -- :math:`x`-component of internal force vector * ``self.f_int_y`` -- :math:`y`-component of internal force vector * ``self.f_int_z`` -- :math:`z`-component of internal force vector Grid mass :math:`m_i`: * ``self.m`` -- mass :math:`m_i` * ``self.m0`` -- inital mass :math:`m_i^0` """ s = "::: initializing grid variables :::" print_text(s, cls=self.this) # the finite-element used : self.element = self.Q.element() # topological dimension : self.top_dim = self.element.geometric_dimension() # map from verticies to nodes : self.dofmap = self.Q.dofmap() # for finding vertices sitting on boundary : self.d2v = dl.dof_to_vertex_map(self.Q) # list of arrays of vertices and bcs set by self.set_boundary_conditions() : self.bc_vrt = None self.bc_val = None # cell diameter : self.h = dl.project(dl.CellDiameter(self.mesh), self.Q) # cell volume : self.Ve = dl.project(dl.CellVolume(self.mesh), self.Q) # grid velocity : self.U_mag = dl.Function(self.Q, name='U_mag') self.U3 = dl.Function(self.Q3, name='U3') u, v, w = self.U3.split() u.rename('u', '') v.rename('v', '') w.rename('w', '') self.u = u self.v = v self.w = w # grid acceleration : self.a_mag = dl.Function(self.Q, name='a_mag') self.a3 = dl.Function(self.Q3, name='a3') a_x, a_y, a_z = self.a3.split() a_x.rename('a_x', '') a_y.rename('a_y', '') a_z.rename('a_z', '') self.a_x = a_x self.a_y = a_y self.a_z = a_z # grid internal force vector : self.f_int_mag = dl.Function(self.Q, name='f_int_mag') self.f_int = dl.Function(self.Q3, name='f_int') f_int_x, f_int_y, f_int_z = self.f_int.split() f_int_x.rename('f_int_x', '') f_int_y.rename('f_int_y', '') f_int_z.rename('f_int_z', '') self.f_int_x = f_int_x self.f_int_y = f_int_y self.f_int_z = f_int_z # grid mass : self.m = dl.Function(self.Q, name='m') self.m0 = dl.Function(self.Q, name='m0') # function assigners speed assigning up : self.assu = dl.FunctionAssigner(self.u.function_space(), self.Q) self.assv = dl.FunctionAssigner(self.v.function_space(), self.Q) self.assw = dl.FunctionAssigner(self.w.function_space(), self.Q) self.assa_x = dl.FunctionAssigner(self.a_x.function_space(), self.Q) self.assa_y = dl.FunctionAssigner(self.a_y.function_space(), self.Q) self.assa_z = dl.FunctionAssigner(self.a_z.function_space(), self.Q) self.assf_int_x = dl.FunctionAssigner(self.f_int_x.function_space(), self.Q) self.assf_int_y = dl.FunctionAssigner(self.f_int_y.function_space(), self.Q) self.assf_int_z = dl.FunctionAssigner(self.f_int_z.function_space(), self.Q) self.assm = dl.FunctionAssigner(self.m.function_space(), self.Q) # save the number of degrees of freedom : self.dofs = self.m.vector().size()
psih_avg = 0.5 * (psi_h("+") + psi_h("-")) psih_jump = psi_h("+") * nhat("+") + psi_h("-") * nhat("-") Qs_upwind = df.avg(flow_dir) * Qs_avg + 0.5 * Qs_jump # Sediment flux (Eq 8) if geom == "1sided": R_Qs = ((ddot - edot) * psi_Q * df.dx + df.dot(Qs_upwind, psiQ_jump) * df.dS + Qs * flow_dir * nhat * psi_Q * ds(1)) else: R_Qs = ((ddot - edot) * psi_Q * df.dx + df.dot(Qs_upwind, psiQ_jump) * df.dS + Qs * flow_dir * nhat * psi_Q * ds # (1) ) # Sediment transport (Eq 5) h = df.CellDiameter(mesh) dhsdt = (h_s("+") - h_s("-")) / (0.5 * (h("+") + h("-"))) R_hs = (psi_h * ((h_s - h_s0) / dt + rho_r / rho_s * Bdot - ddot + edot) * df.dx + df.avg(k_diff) * dhsdt * psih_jump * df.dS) # Bedrock evolution (Eq 2) R_B = psi_B * ((B - B0) / dt - Bdot) * df.dx # ?? R_hsx = psi_h_ * (h_s - h_s_) * df.dx # Effective thickness ? R_heff = psi_eff * (h_eff - softplus(h_0, Base - Bhat, alpha=10.0)) * df.dx # Weak form of sediment dynamics, solves for bedrock elevation, fluvial sed. flux, # sediment thickness, projected sediment thickness, and effective water layer thickness. R_sed = R_B + R_Qs + R_hs + R_hsx + R_heff J_sed = df.derivative(R_sed, T, dT)
########################################################## ######################## MESH ########################## ########################################################## # Define a unit interval mesh with equal cell size N_cells = 300 mesh = df.IntervalMesh(N_cells, 0, 1) # Shift vertices such that they are concentrated towards the # terminus following a polynomial curve. mesh_exp = 1.5 mesh.coordinates()[:] = (1 - mesh.coordinates()**mesh_exp)[::-1] # Mesh Functions h = df.CellDiameter(mesh) # Cell size x_spatial = df.SpatialCoordinate(mesh) # spatial coordinate nhat = df.FacetNormal(mesh) # facet normal vector ocean = df.MeshFunction('size_t', mesh, 0, 0) # boundary subdomain function # ocean=1 -> terminus # ocean=2 -> ice divide df.ds = df.ds(subdomain_data=ocean) for f in df.facets(mesh): if df.near(f.midpoint().x(), 1): ocean[f] = 1 if df.near(f.midpoint().x(), 0): ocean[f] = 2 ######################################################### ################# FUNCTION SPACES #####################
# Geometric variables d = Max(-Bhat, 1 / Z) # Water depth l = Max(0, Bhat + 1 / Z) # water surface height B = Max(Bhat, -rho / rho_w * H) # Ice base N = H - Max(rho_w / rho * (l - B), 0.8 * H) # Effective pressure dt = df.Constant(1e-8) # Non-dimensional groups U_ = (rho * g * Z) / (beta2 * L) gamma = b / 2 * (U_ / L)**(1. / n) / (rho * g * Z) tau_ = L / U_ # Signum and Heaviside function S = phi / df.sqrt(phi**2 + df.CellDiameter(mesh)**2) Heav = (S + 1) * 0.5 # Effective pressure regularization N0 = 1e-3 # SSA stress balance eta = (u.dx(0)**2 + v.dx(1)**2 + u.dx(0) * v.dx(1) + 0.25 * (u.dx(1) + v.dx(0))**2 + (L / U_)**2 * 1e-10)**((1 - n) / (2 * n)) R_u = (-gamma * lamda_x.dx(0) * 2 * eta * H * (2 * u.dx(0) + v.dx(1)) - gamma * lamda_x.dx(1) * eta * H * (u.dx(1) + v.dx(0)) - lamda_x * (N + N0) * u - lamda_x * H * B.dx(0) + 0.5 * H**2 * lamda_x.dx(0)) * df.dx - 0.5 * H**2 * lamda_x * nhat[0] * df.ds R_v = (-gamma * lamda_y.dx(0) * eta * H * (u.dx(1) + v.dx(0)) - gamma * lamda_y.dx(1) * 2 * eta * H *
def __init__(self, mesh, Vh, t_init, t_final, t_1, dt, wind_velocity, gls_stab, Prior): self.mesh = mesh self.Vh = Vh self.t_init = t_init self.t_final = t_final self.t_1 = t_1 self.dt = dt self.sim_times = np.arange(self.t_init, self.t_final+.5*self.dt, self.dt) u = dl.TrialFunction(Vh[STATE]) v = dl.TestFunction(Vh[STATE]) kappa = dl.Constant(.001) dt_expr = dl.Constant(self.dt) r_trial = u + dt_expr*( -dl.div(kappa*dl.nabla_grad(u))+ dl.inner(wind_velocity, dl.nabla_grad(u)) ) r_test = v + dt_expr*( -dl.div(kappa*dl.nabla_grad(v))+ dl.inner(wind_velocity, dl.nabla_grad(v)) ) h = dl.CellDiameter(mesh) vnorm = dl.sqrt(dl.inner(wind_velocity, wind_velocity)) if gls_stab: tau = ufl.min_value((h*h)/(dl.Constant(2.)*kappa), h/vnorm ) else: tau = dl.Constant(0.) self.M = dl.assemble( dl.inner(u,v)*dl.dx ) self.M_stab = dl.assemble( dl.inner(u, v+tau*r_test)*dl.dx ) self.Mt_stab = dl.assemble( dl.inner(u+tau*r_trial,v)*dl.dx ) Nvarf = (dl.inner(kappa *dl.nabla_grad(u), dl.nabla_grad(v)) + dl.inner(wind_velocity, dl.nabla_grad(u))*v )*dl.dx Ntvarf = (dl.inner(kappa *dl.nabla_grad(v), dl.nabla_grad(u)) + dl.inner(wind_velocity, dl.nabla_grad(v))*u )*dl.dx self.N = dl.assemble( Nvarf ) self.Nt = dl.assemble(Ntvarf) stab = dl.assemble( tau*dl.inner(r_trial, r_test)*dl.dx) self.L = self.M + dt*self.N + stab self.Lt = self.M + dt*self.Nt + stab boundaries = dl.MeshFunction("size_t", mesh,1) boundaries.set_all(0) class InsideBoundary(dl.SubDomain): def inside(self,x,on_boundary): x_in = x[0] > dl.DOLFIN_EPS and x[0] < 1 - dl.DOLFIN_EPS y_in = x[1] > dl.DOLFIN_EPS and x[1] < 1 - dl.DOLFIN_EPS return on_boundary and x_in and y_in Gamma_M = InsideBoundary() Gamma_M.mark(boundaries,1) ds_marked = dl.Measure("ds", subdomain_data=boundaries) self.Q = dl.assemble( self.dt*dl.inner(u, v) * ds_marked(1) ) self.Prior = Prior self.solver = dl.PETScLUSolver( dl.as_backend_type(self.L) ) self.solvert = dl.PETScLUSolver( dl.as_backend_type(self.Lt) ) self.ud = self.generate_vector(STATE) self.noise_variance = 0 # Part of model public API self.gauss_newton_approx = False
def boundary_components(self, trial, test): """ Generates momentum (stress, velocity) and thermal (heat flux. temperature) boundary components of the TVAcoustic weak form equation. I expect the usage should be something like: bcond = {"noslip" : True, "heat_flux" : 1.} """ pressure, velocity, temperature = trial test_pressure, test_velocity, test_temperature = test stress_boundary_component = dolf.Constant(0.0) * self.domain.ds() temperature_boundary_component = dolf.Constant(0.0) * self.domain.ds() for boundary in self.domain.boundary_elements: # Step 1. Parse boundary condition data provided by boundary elements. # We only accept one boundary condition for stress/velocity and temperature/heat flux. temperature_bc = self._pop_boundary_condition( boundary.bcond, self.allowed_temperature_bcs ) temperature_bc_type = self.allowed_temperature_bcs[temperature_bc] # Step 2. If the boundary condition is one of the Neumann or Robin, # we construct necessary boundary integrals in weak form. if temperature_bc_type == "Neumann" or temperature_bc_type == "Robin": if temperature_bc == "adiabatic": heat_flux = dolf.Constant(0.0) elif temperature_bc == "heat_flux": heat_flux = self._parse_dolf_expression( boundary.bcond[temperature_bc] ) elif temperature_bc == "thermal_accommodation": heat_flux = ( -self._parse_dolf_expression(boundary.bcond[temperature_bc]) * temperature ) else: raise TypeError( f"Invalid temperature boundary condition type for {temperature_bc_type} condition." ) temperature_boundary_component += ( -heat_flux * test_temperature * self.domain.ds((boundary.surface_index,)) ) # Same for stress / velocity boundary conditions stress_bc = self._pop_boundary_condition( boundary.bcond, self.allowed_stress_bcs ) stress_bc_type = self.allowed_stress_bcs[stress_bc] if stress_bc_type == "Neumann" or stress_bc_type == "Robin": if stress_bc == "free": stress = dolf.Constant((0.0,) * self.geometric_dimension) elif stress_bc == "force": stress = self._parse_dolf_expression(boundary.bcond[stress_bc]) elif stress_bc == "impedance": stress = ( self._parse_dolf_expression(boundary.bcond[stress_bc]) * velocity ) elif stress_bc == "inhom_impedance": # Impedance term stress = ( self._parse_dolf_expression(boundary.bcond[stress_bc][0]) * velocity ) # Force term normal_force = boundary.bcond[stress_bc][1] normal_force = 0.5 * ( normal_force.real + normal_force.imag ) - 0.5j * (normal_force.real - normal_force.imag) stress += self._parse_dolf_expression(normal_force) * self.domain.n elif stress_bc == "nozzle_impedance": capacitance = boundary.bcond[stress_bc]["capacitance"] inductance = boundary.bcond[stress_bc]["inductance"] resistance = boundary.bcond[stress_bc]["resistance"] frequency = boundary.bcond[stress_bc]["frequency"] z1 = -1.0 / (frequency * capacitance) - frequency * inductance z1 = self._parse_dolf_expression(z1) z2 = self._parse_dolf_expression(resistance) stress = z1 * dolf.inner( velocity, self.domain.n ) * self.domain.n + z2 * velocity.dx(0).dx(0) elif stress_bc == "inhom_nozzle_impedance": inductance = boundary.bcond[stress_bc]["inductance"] resistance = boundary.bcond[stress_bc]["resistance"] frequency = boundary.bcond[stress_bc]["frequency"] z1 = -1.0j * frequency * inductance z1 = self._parse_dolf_expression(z1) z2 = self._parse_dolf_expression(resistance) stress = z1 * velocity + z2 * velocity.dx(0).dx(0) # Force term normal_force = boundary.bcond[stress_bc]["force"] normal_force = 0.5 * ( normal_force.real + normal_force.imag ) - 0.5j * (normal_force.real - normal_force.imag) stress += self._parse_dolf_expression(normal_force) * self.domain.n elif stress_bc == "shape_impedance": try: shape = boundary.velocity_shape except AttributeError: raise AttributeError( "`shape_impedance` boundary condition requires expected velocity shape. " + "Provide it as a dolfin Expression." ) stress = ( self._parse_dolf_expression(boundary.bcond[stress_bc]) * velocity / shape ) elif stress_bc == "normal_impedance": stress = ( self._parse_dolf_expression(boundary.bcond[stress_bc]) * dolf.inner(velocity, self.domain.n) * self.domain.n ) elif stress_bc == "normal_force": normal_force = boundary.bcond[stress_bc] # normal_force = 0.5 * ( # normal_force.real + normal_force.imag # ) - 0.5j * (normal_force.real - normal_force.imag) stress = self._parse_dolf_expression(normal_force) * self.domain.n elif stress_bc == "slip" or stress_bc == "normal_velocity": normal_velocity = ( dolf.Constant(0.0) if stress_bc == "slip" else self._parse_dolf_expression(boundary.bcond[stress_bc]) ) stress = ( dolf.Constant(-1.0e2) / dolf.CellDiameter(self.domain.mesh) * (dolf.inner(velocity, self.domain.n) - normal_velocity) * self.domain.n ) else: raise TypeError( f"Invalid temperature boundary condition type for {stress_bc_type} condition." ) stress_boundary_component += -dolf.inner( stress, test_velocity ) * self.domain.ds((boundary.surface_index,)) return stress_boundary_component + temperature_boundary_component