def action(self, **kwargs): r"""Return the action functional that gives the hybrid model as its Euler-Lagrange equations""" u, h = itemgetter("velocity", "thickness")(kwargs) mesh = u.ufl_domain() ice_front_ids = tuple(kwargs.pop("ice_front_ids", ())) side_wall_ids = tuple(kwargs.pop("side_wall_ids", ())) metadata = {"quadrature_degree": self.quadrature_degree(**kwargs)} dx = firedrake.dx(metadata=metadata) ds_b = firedrake.ds_b(domain=mesh, metadata=metadata) ds_v = firedrake.ds_v(domain=mesh) viscosity = self.viscosity(**kwargs) * dx gravity = self.gravity(**kwargs) * dx friction = self.friction(**kwargs) * ds_b side_friction = self.side_friction(**kwargs) * ds_v(side_wall_ids) if get_mesh_axes(mesh) == "xyz": penalty = self.penalty(**kwargs) * ds_v(side_wall_ids) else: penalty = 0.0 xdegree_u, zdegree_u = u.ufl_element().degree() degree_h = h.ufl_element().degree()[0] degree = (xdegree_u + degree_h, 2 * zdegree_u + 1) ds_t = firedrake.ds_v(ice_front_ids, metadata={"quadrature_degree": degree}) terminus = self.terminus(**kwargs) * ds_t return viscosity + friction + side_friction - gravity - terminus + penalty
def calculate_Pi0(state, theta0, rho0): # exner function Vr = rho0.function_space() Pi = Function(Vr).interpolate(thermodynamics.pi(state.parameters, rho0, theta0)) Pi0 = assemble(Pi*dx)/assemble(Constant(1)*dx(domain=state.mesh)) return Pi0
def runSolvers(solverBeta, solverTheta, modelResults, uObs, mesh, nSteps=30, rtol=5.e-3, solveViscosity=True, solveBeta=True): """ Run joint solvers """ JLastBeta, JLastTheta = np.inf, np.inf # area = firedrake.assemble(firedrake.Constant(1) * firedrake.dx(mesh)) # # step solvers convergeTheta, convergeBeta = False, False for i in range(0, nSteps): # # run solver step for beta Print(f'\n\033[1mIteration {i}\033[0m') if solverBeta is not None: convergeBeta, JLastBeta, ve = solverStep(solverBeta, solverTheta, area, JLastBeta, uObs, 'beta', 'theta', rtol) # # run solver step for theta if solverTheta is not None: convergeTheta, JLastTheta, ve = solverStep(solverTheta, solverBeta, area, JLastTheta, uObs, 'theta', 'beta', rtol) modelResults[f'Verror_{i:03}'] = ve # Last error for step if convergeTheta and convergeBeta: # Done?? break # Done, print message Print(f'Done at {datetime.now().strftime("%H:%M:%S")}')
def AdvectionDiffusionGLS( V: fd.FunctionSpace, theta: fd.Function, phi: fd.Function, PeInv: float = 1e-4, phi_t: fd.Function = None, ): PeInv_ct = fd.Constant(PeInv) rho = fd.TestFunction(V) F = (inner(theta, grad(phi)) * rho + PeInv_ct * inner(grad(phi), grad(rho))) * dx if phi_t: F += phi_t * rho * dx h = fd.CellDiameter(V.ufl_domain()) R_U = dot(theta, grad(phi)) - PeInv_ct * div(grad(phi)) if phi_t: R_U += phi_t beta_gls = 0.9 tau_gls = beta_gls * ((4.0 * dot(theta, theta) / h**2) + 9.0 * (4.0 * PeInv_ct / h**2)**2)**(-0.5) theta_U = dot(theta, grad(rho)) - PeInv_ct * div(grad(rho)) F += tau_gls * inner(R_U, theta_U) * dx() return F
def action(self, **kwargs): r"""Return the action functional that gives the shallow ice diagnostic model as the Euler-Lagrange equations Parameters ---------- velocity : firedrake.Function thickness : firedrake.Function surface : firedrake.Function fluidity : firedrake.Function or firedrake.Constant Returns ------- E : firedrake.Form the shallow ice action functional Other parameters ---------------- **kwargs All other keyword arguments will be passed on to the 'mass', 'gravity' and 'penalty' functionals """ metadata = {"quadrature_degree": self.quadrature_degree(**kwargs)} dx = firedrake.dx(metadata=metadata) mass = self.mass(**kwargs) * dx gravity = self.gravity(**kwargs) * dx penalty = self.penalty(**kwargs) * dx return mass + gravity + penalty
def action(self, **kwargs): r"""Return the action functional that gives the ice stream diagnostic model as the Euler-Lagrange equations""" u = kwargs["velocity"] mesh = u.ufl_domain() ice_front_ids = tuple(kwargs.pop("ice_front_ids", ())) side_wall_ids = tuple(kwargs.pop("side_wall_ids", ())) metadata = {"quadrature_degree": self.quadrature_degree(**kwargs)} dx = firedrake.dx(metadata=metadata) ds = firedrake.ds(domain=mesh, metadata=metadata) viscosity = self.viscosity(**kwargs) * dx friction = self.friction(**kwargs) * dx gravity = self.gravity(**kwargs) * dx side_friction = self.side_friction(**kwargs) * ds(side_wall_ids) if get_mesh_axes(mesh) == "xy": penalty = self.penalty(**kwargs) * ds(side_wall_ids) else: penalty = 0.0 terminus = self.terminus(**kwargs) * ds(ice_front_ids) return viscosity + friction + side_friction - gravity - terminus + penalty
def variational_form_residual(sim, solution): Gr = sim.grashof_number Pr = sim.prandtl_number ihat, jhat = sapphire.simulation.unit_vectors(sim.mesh) sim.gravity_direction = fe.Constant(-jhat) ghat = sim.gravity_direction p, u, T = fe.split(solution) psi_p, psi_u, psi_T = fe.TestFunctions(solution.function_space()) mass = psi_p * div(u) momentum = dot(psi_u, grad(u)*u + Gr*T*ghat) \ - div(psi_u)*p + 2.*inner(sym(grad(psi_u)), sym(grad(u))) energy = psi_T * dot(u, grad(T)) + dot(grad(psi_T), 1. / Pr * grad(T)) dx = fe.dx(degree=sim.quadrature_degree) return (mass + momentum + energy) * dx
def energy(self): Re = self.reynolds_number Pr = self.prandtl_number Ste = self.stefan_number _, u, T = fe.split(self.solution) phil = self.liquid_volume_fraction(temperature = T) rho_sl = self.density_solid_to_liquid_ratio c_sl = self.heat_capacity_solid_to_liquid_ratio C = phase_dependent_material_property(rho_sl*c_sl)(phil) k_sl = self.thermal_conductivity_solid_to_liquid_ratio k = phase_dependent_material_property(k_sl)(phil) CT_t, phil_t = self.extra_time_discrete_terms() _, _, psi_T = fe.TestFunctions(self.solution_space) dx = fe.dx(degree = self.quadrature_degree) return (psi_T*(CT_t + 1./Ste*phil_t + dot(u, grad(C*T))) \ + 1./(Re*Pr)*dot(grad(psi_T), k*grad(T)))*dx
def computeSummaryData(SD, h, s, u, a, melt, SMB, grounded, floating, year, Q, mesh, deltaT, beginTime): ''' Compute summary results and sort in summaryData Save all as float for yaml output ''' deltaVF, deltaVG = \ volumeChange(grounded, floating, h, u, a, mesh) SD['deltaVF'][-1] += deltaVF * deltaT * iceToWater SD['deltaVG'][-1] += deltaVG * deltaT * iceToWater SD['DVF'][-1] += deltaVF * deltaT * iceToWater SD['DVG'][-1] += deltaVG * deltaT * iceToWater print(f"++{year:0.3f} {SD['deltaVF'][-1]/1e9:0.3f} " f"{SD['deltaVG'][-1]/1e9:0.3f} {SD['DVF'][-1]/1e9:0.3f} " f"{SD['DVG'][-1]/1e9:0.3f}") # # If not with half a time step of year return # Need to update this for different update interval (~=1) dTint = abs(year - round(year, 0)) # difference from int year # if before first year or not with half time step of int year return if year < (1. - deltaT * 0.5) or not (dTint < deltaT * 0.5): return False print(f'year {year} runtime {datetime.now() - beginTime}') # gArea = firedrake.assemble(grounded * firedrake.dx(mesh)) SD['year'].append(float(year)) SD['gArea'].append(gArea) SD['fArea'].append(SD['area'] - gArea) # append a new zero value to start incrementing SD['deltaVF'].append(0) SD['deltaVG'].append(0) # append current value to start incrementing SD['DVF'].append(SD['DVF'][-1]) SD['DVG'].append(SD['DVG'][-1]) # meltTot = firedrake.assemble( icepack.interpolate(floating * melt, Q) * firedrake.dx(mesh)) SD['meltTot'].append(float(meltTot)) SMBfloating = firedrake.assemble( icepack.interpolate(floating * SMB, Q) * firedrake.dx(mesh)) SD['SMBfloating'].append(float(SMBfloating)) SMBgrounded = firedrake.assemble( icepack.interpolate(grounded * SMB, Q) * firedrake.dx(mesh)) SD['SMBgrounded'].append(float(SMBgrounded)) # print(f'{year}: Initial melt {SD["meltTot"][0] / 1e9:.2f} current melt ' f' {meltTot / 1e9:.2f}') return True
def initSummary(grounded0, floating0, h0, u0, meltModel, meltParams, SMB, Q, mesh, forwardParams, restart=False): ''' Compute initial areas and summary data if restart, try reload restart data. If not go with clean slate, which should be a legacy case (from when intermediates steps were not saved) ''' if forwardParams['restart']: summaryData = reinitSummary(forwardParams) if summaryData is not None: return summaryData # No summary.tmp from a prior run so reset restart forwardParams['restart'] = False # start from scratch area = firedrake.assemble(firedrake.Constant(1) * firedrake.dx(mesh)) gArea0 = firedrake.assemble(grounded0 * firedrake.dx(mesh)) fArea0 = area - gArea0 melt = meltModel(h0, floating0, meltParams, Q, u0) meltTot = firedrake.assemble( icepack.interpolate(floating0 * melt, Q) * firedrake.dx(mesh)) SMBfloating = firedrake.assemble( icepack.interpolate(floating0 * SMB, Q) * firedrake.dx(mesh)) SMBgrounded = firedrake.assemble( icepack.interpolate(grounded0 * SMB, Q) * firedrake.dx(mesh)) summaryData = { 'year': [0], 'DVG': [0, 0], 'DVF': [0, 0], 'deltaVF': [0, 0], 'deltaVG': [0, 0], 'gArea': [float(gArea0)], 'fArea': [float(fArea0)], 'meltTot': [float(meltTot)], 'area': float(area), 'SMBgrounded': [float(SMBgrounded)], 'SMBfloating': [float(SMBfloating)], 'dTsum': 1. } # dTsum fixed - code modes needed to change return summaryData
def value_form(self): # volume integral self.detDT.interpolate(fd.det(fd.grad(self.Q.T))) if min(self.detDT.vector()) > 0.05: integrand = (self.sigma - self.f)**2 else: integrand = np.nan * (self.sigma - self.f)**2 return integrand * fd.dx(metadata={"quadrature_degree": 1})
def weak_form_residual(self): return super().weak_form_residual() \ - mms_source( sim = self, strong_residual = strong_residual, manufactured_solution = manufactured_solution)\ *fe.dx(degree = self.quadrature_degree)
def poisson_gll(mesh, degree): V = FunctionSpace( mesh, FiniteElement('Q', mesh.ufl_cell(), degree, variant='spectral')) u = TrialFunction(V) v = TestFunction(V) dim = mesh.topological_dimension() finat_rule = gauss_lobatto_legendre_cube_rule(dim, degree) return dot(grad(u), grad(v)) * dx(rule=finat_rule)
def initial_values(sim): print("Solving steady heat driven cavity to obtain initial values") Ra = 2.518084e6 Pr = 6.99 sim.grashof_number = sim.grashof_number.assign(Ra / Pr) sim.prandtl_number = sim.prandtl_number.assign(Pr) dim = sim.mesh.geometric_dimension() T_c = sim.cold_wall_temperature.__float__() w = fe.interpolate( fe.Expression((0., ) + (0., ) * dim + (T_c, ), element=sim.element), sim.function_space) F = heat_driven_cavity_variational_form_residual( sim=sim, solution=w) * fe.dx(degree=sim.quadrature_degree) T_h = sim.hot_wall_temperature.__float__() problem = fe.NonlinearVariationalProblem( F=F, u=w, bcs=dirichlet_boundary_conditions(sim), J=fe.derivative(F, w)) solver = fe.NonlinearVariationalSolver(problem=problem, solver_parameters={ "snes_type": "newtonls", "snes_monitor": True, "ksp_type": "preonly", "pc_type": "lu", "mat_type": "aij", "pc_factor_mat_solver_type": "mumps" }) def solve(): solver.solve() return w w, _ = \ sapphire.continuation.solve_with_bounded_regularization_sequence( solve = solve, solution = w, backup_solution = fe.Function(w), regularization_parameter = sim.grashof_number, initial_regularization_sequence = ( 0., sim.grashof_number.__float__())) return w
def mass(self): u = self.solution_fields["u"] psi_p = self.test_functions["p"] dx = fe.dx(degree = self.quadrature_degree) return psi_p*div(u)*dx
def mass(self): u, _ = fe.split(self.solution) _, psi_p = fe.TestFunctions(self.solution_space) dx = fe.dx(degree = self.quadrature_degree) return psi_p*div(u)*dx
def momentum(self): u_t = self.time_discrete_terms() psi_u, _ = fe.TestFunctions(self.solution_space) dx = fe.dx(degree=self.quadrature_degree) return super().momentum() + dot(psi_u, u_t) * dx
def energy(self): _, T_t = self.time_discrete_terms() _, _, psi_T = fe.TestFunctions(self.solution_space) dx = fe.dx(degree=self.quadrature_degree) return super().energy() + psi_T * T_t * dx
def energy(self): T_t = self.time_discrete_terms["T"] psi_T = self.test_functions["T"] dx = fe.dx(degree=self.quadrature_degree) return super().energy() + psi_T * T_t * dx
def momentum(self): u_t = self.time_discrete_terms["u"] psi_u = self.test_functions["u"] dx = fe.dx(degree=self.quadrature_degree) return super().momentum() + dot(psi_u, u_t) * dx
def scale(self, **kwargs): r"""Return the positive, convex part of the action functional The positive part of the action functional is used as a dimensional scale to determine when to terminate an optimization algorithm. """ metadata = {"quadrature_degree": self.quadrature_degree(**kwargs)} dx = firedrake.dx(metadata=metadata) return (self.viscosity(**kwargs) + self.friction(**kwargs)) * dx
def calculate_exner0(state, theta0, rho0): # exner function Vr = rho0.function_space() exner = Function(Vr).interpolate( thermodynamics.exner_pressure(state.parameters, rho0, theta0)) exner0 = assemble(exner * dx) / assemble( Constant(1) * dx(domain=state.mesh)) return exner0
def solve_something(mesh): V = fd.FunctionSpace(mesh, "CG", 1) u = fd.Function(V) v = fd.TestFunction(V) x, y = fd.SpatialCoordinate(mesh) # f = fd.sin(x) * fd.sin(y) + x**2 + y**2 # uex = x**4 * y**4 uex = fd.sin(x) * fd.sin(y) #*(x*y)**3 # def source(xs, ys): # return 1/((x-xs)**2+(y-ys)**2 + 0.1) # uex = source(0, 0) uex = uex - fd.assemble(uex * fd.dx) / fd.assemble(1 * fd.dx(domain=mesh)) # f = fd.conditional(fd.ge(abs(x)-abs(y), 0), 1, 0) from firedrake import inner, grad, dx, ds, div, sym eps = fd.Constant(0.0) f = uex - div(grad(uex)) + eps * div(grad(div(grad(uex)))) n = fd.FacetNormal(mesh) g = inner(grad(uex), n) g1 = inner(grad(div(grad(uex))), n) g2 = div(grad(uex)) # F = 0.1 * inner(u, v) * dx + inner(grad(u), grad(v)) * dx + inner(grad(grad(u)), grad(grad(v))) * dx - f * v * dx - g * v * ds F = inner(u, v) * dx + inner(grad(u), grad(v)) * dx - f * v * dx - g * v * ds F += eps * inner(div(grad(u)), div(grad(v))) * dx F += eps * g1 * v * ds F -= eps * g2 * inner(grad(v), n) * ds # f = -div(grad(uex)) # F = inner(grad(u), grad(v)) * dx - f * v * dx # bc = fd.DirichletBC(V, uex, "on_boundary") bc = None fd.solve(F == 0, u, bcs=bc, solver_parameters={ "ksp_type": "cg", "ksp_atol": 1e-13, "ksp_rtol": 1e-13, "ksp_dtol": 1e-13, "ksp_stol": 1e-13, "pc_type": "jacobi", "pc_factor_mat_solver_type": "mumps", "snes_type": "ksponly", "ksp_converged_reason": None }) print("||u-uex|| =", fd.norm(u - uex)) print("||grad(u-uex)|| =", fd.norm(grad(u - uex))) print("||grad(grad(u-uex))|| =", fd.norm(grad(grad(u - uex)))) err = fd.Function( fd.TensorFunctionSpace(mesh, "DG", V.ufl_element().degree() - 2)).interpolate( grad(grad(u - uex))) # err = fd.Function(fd.FunctionSpace(mesh, "DG", V.ufl_element().degree())).interpolate(u-uex) fd.File(outdir + "sln.pvd").write(u) fd.File(outdir + "err.pvd").write(err)
def value_form(self): """Evaluate misfit functional.""" nu = self.pde_solver.viscosity if self.pde_solver.failed_to_solve: # return NaNs if state solve fails return np.nan * fd.dx(self.pde_solver.mesh_m) else: z = self.pde_solver.solution u, p = fd.split(z) return nu * fd.inner(fd.grad(u), fd.grad(u)) * fd.dx
def estimate_timestep(mesh, V, c, estimate_max_eigenvalue=True): """Estimate the maximum stable timestep based on the spectral radius using optionally the Gershgorin Circle Theorem to estimate the maximum generalized eigenvalue. Otherwise computes the maximum generalized eigenvalue exactly ONLY WORKS WITH KMV ELEMENTS """ u, v = fire.TrialFunction(V), fire.TestFunction(V) quad_rule = finat.quadrature.make_quadrature(V.finat_element.cell, V.ufl_element().degree(), "KMV") dxlump = fire.dx(rule=quad_rule) A = fire.assemble(u * v * dxlump) ai, aj, av = A.petscmat.getValuesCSR() av_inv = [] for value in av: if value == 0: av_inv.append(0.0) else: av_inv.append(1 / value) Asp = scipy.sparse.csr_matrix((av, aj, ai)) Asp_inv = scipy.sparse.csr_matrix((av_inv, aj, ai)) K = fire.assemble(c * c * dot(grad(u), grad(v)) * dxlump) ai, aj, av = K.petscmat.getValuesCSR() Ksp = scipy.sparse.csr_matrix((av, aj, ai)) # operator Lsp = Asp_inv.multiply(Ksp) if estimate_max_eigenvalue: # absolute maximum of diagonals max_eigval = np.amax(np.abs(Lsp.diagonal())) else: print( "Computing exact eigenvalues is extremely computationally demanding!", flush=True, ) max_eigval = scipy.sparse.linalg.eigs(Ksp, M=Asp, k=1, which="LM", return_eigenvectors=False)[0] # print(max_eigval) if np.sqrt(max_eigval) > 0.0: max_dt = np.float(2 / np.sqrt(max_eigval)) else: max_dt = 100000000 #print( # f"Maximum stable timestep should be about: {np.float(2 / np.sqrt(max_eigval))} seconds", # flush=True, #) return max_dt
def momentum(self): _, u, T = fe.split(self.solution) d = self.solid_velocity_relaxation(temperature = T) _, psi_u, _ = fe.TestFunctions(self.solution_space) dx = fe.dx(degree = self.quadrature_degree) return super().momentum() + dot(psi_u, d*u)*dx
def variational_form_residual(sim, solution): u = solution v = fe.TestFunction(solution.function_space()) dot, grad = fe.dot, fe.grad dx = fe.dx(degree=sim.quadrature_degree) return -dot(grad(v), grad(u)) * dx
def weak_form_residual(self): u = self.solution v = fe.TestFunction(self.solution_space) dot, grad = fe.dot, fe.grad dx = fe.dx(degree = self.quadrature_degree) return -dot(grad(v), grad(u))*dx
def scale(self, **kwargs): r"""Return the positive, convex part of the action functional The positive part of the action functional is used as a dimensional scale to determine when to terminate an optimization algorithm. """ u = kwargs["velocity"] mesh = u.ufl_domain() metadata = {"quadrature_degree": self.quadrature_degree(**kwargs)} dx = firedrake.dx(metadata=metadata) ds_b = firedrake.ds_b(domain=mesh, metadata=metadata) return self.viscosity(**kwargs) * dx + self.friction(**kwargs) * ds_b
def momentum(self): u, p = fe.split(self.solution) Re = self.reynolds_number psi_u, _ = fe.TestFunctions(self.solution_space) dx = fe.dx(degree = self.quadrature_degree) return (dot(psi_u, grad(u)*u) - div(psi_u)*p + \ 2./Re*inner(sym(grad(psi_u)), sym(grad(u))))*dx