def bdrag_to_alpha(self, B2): """Convert basal drag to alpha""" sl = self.params.ice_dynamics.sliding_law if sl == 'linear': alpha = sqrt(B2) elif sl == 'budd': bed = self.bed H = self.H g = self.params.constants.g rhoi = self.params.constants.rhoi rhow = self.params.constants.rhow u_obs = self.u_obs_M v_obs = self.v_obs_M vel_rp = self.params.constants.vel_rp # Flotation Criterion H_flt = -rhow / rhoi * bed fl_ex = conditional(H <= H_flt, 1.0, 0.0) N = (1 - fl_ex) * (H * rhoi * g + ufl.Min(bed, 0.0) * rhow * g) U_mag = sqrt(u_obs**2 + v_obs**2 + vel_rp**2) alpha = (1 - fl_ex) * sqrt( B2 * ufl.Max(N, 0.01)**(-1.0 / 3.0) * U_mag**(2.0 / 3.0)) return alpha
def sliding_law(self, alpha, U): constants = self.params.constants bed = self.bed H = self.H rhoi = constants.rhoi rhow = constants.rhow g = constants.g sl = self.params.ice_dynamics.sliding_law vel_rp = constants.vel_rp fl_ex = self.float_conditional(H) C = alpha * alpha u, v = split(U) if sl == 'linear': B2 = C elif sl == 'weertman': N = (1 - fl_ex) * (H * rhoi * g + ufl.Min(bed, 0.0) * rhow * g) U_mag = sqrt(U[0]**2 + U[1]**2 + vel_rp**2) # Need to catch N <= 0.0 here, as it's raised to # 1/3 (forward) and -2/3 (adjoint) N_term = ufl.conditional(N > 0.0, N**(1.0 / 3.0), 0) B2 = (1 - fl_ex) * (C * N_term * U_mag**(-2.0 / 3.0)) return B2
def damage(eps_eqv, mesh, E, f_t, G_f): h_cb = ufl.CellVolume(mesh) ** (1 / 3) eps0 = f_t / E eps_f = G_f / 1.0e+6 / (f_t * h_cb) + eps0 / 2 dmg = ufl.Min(1.0 - eps0 / eps_eqv * ufl.exp(- (eps_eqv - eps0) / (eps_f - eps0)), 1.0 - dmg_eps) return ufl.conditional(eps_eqv <= eps0, 0.0, 0.0 if args.damage_off else dmg)
def gen_alpha(self, a_bgd=500.0, a_lb=1e2, a_ub=1e4): """Generate initial guess for alpha (slip coeff)""" bed = self.bed H = self.H g = self.params.constants.g rhoi = self.params.constants.rhoi rhow = self.params.constants.rhow u_obs = self.u_obs_M v_obs = self.v_obs_M vel_rp = self.params.constants.vel_rp U = ufl.Max((u_obs**2 + v_obs**2)**(1 / 2.0), 50.0) # Flotation Criterion H_s = -rhow / rhoi * bed fl_ex = conditional(H <= H_s, 1.0, 0.0) # Thickness Criterion m_d = conditional(H > 0, 1.0, 0.0) # Calculate surface gradient R_f = ((1.0 - fl_ex) * bed + (fl_ex) * (-rhoi / rhow) * H) s_ = ufl.Max(H + R_f, 0) s = project(s_, self.Q) grads = (s.dx(0)**2.0 + s.dx(1)**2.0)**(1.0 / 2.0) # Calculate alpha, apply background, apply bound B2_ = ((1.0 - fl_ex) * rhoi * g * H * grads / U + (fl_ex) * a_bgd) * m_d + (1.0 - m_d) * a_bgd B2_tmp1 = ufl.Max(B2_, a_lb) B2_tmp2 = ufl.Min(B2_tmp1, a_ub) sl = self.params.ice_dynamics.sliding_law if sl == 'linear': alpha = sqrt(B2_tmp2) elif sl == 'weertman': N = (1 - fl_ex) * (H * rhoi * g + ufl.Min(bed, 0.0) * rhow * g) U_mag = sqrt(u_obs**2 + v_obs**2 + vel_rp**2) alpha = (1 - fl_ex) * sqrt( B2_tmp2 * ufl.Max(N, 0.01)**(-1.0 / 3.0) * U_mag**(2.0 / 3.0)) self.alpha = project(alpha, self.Qp) self.alpha.rename('alpha', 'a Function')
def f1(phi): """Factor representing humidity effects, i.e. carbonation reaction stops for very low humidities. Note ---- Saetta 1993, page 764, formula (5) """ mid = 2.5 * (phi - 0.5) return ufl.Max(ufl.Min(mid, 1.0), 0.0)
def initControl(self): # Initialize control spaces self.controlSpace = [] self.controlSpace.append(FunctionSpace(self.mesh, "DG", 0)) self.controlSpace.append(FunctionSpace(self.mesh, "DG", 0)) # self.controlSpace.append(FunctionSpace(self.mesh, "CG", 1)) # self.controlSpace.append(FunctionSpace(self.mesh, "CG", 1)) self.gamma = [] # Initialize controls Dxu = Dx(self.u, 0) spx = Dxu + self.beta smx = Dxu - self.beta Dyu = Dx(self.u, 1) spy = Dyu + self.beta smy = Dyu - self.beta if self.alpha < 1e-15: self.gamma.append( conditional(spx < 0, self.cmax, conditional(smx > 0, self.cmin, 0))) self.gamma.append( conditional(spy < 0, self.cmax, conditional(smy > 0, self.cmin, 0))) else: self.gamma.append( conditional( spx < 0, ufl.Min(-1.0 * spx / self.alpha, self.cmax), conditional(smx > 0, ufl.Max(-1.0 * smx / self.alpha, self.cmin), 0))) self.gamma.append( conditional( spy < 0, ufl.Min(-1.0 * spy / self.alpha, self.cmax), conditional(smy > 0, ufl.Max(-1.0 * smy / self.alpha, self.cmin), 0)))
def initControl(self): self.controlSpace = [ FunctionSpace(self.mesh, "DG", 1), FunctionSpace(self.mesh, "DG", 1) ] self.gamma = [] # Dxu = project(Dx(self.u,0),FunctionSpace(self.mesh, "DG", 0)) Dxu = Dx(self.u, 0) spx = Dxu + self.beta smx = Dxu - self.beta # Dyu = project(Dx(self.u,1),FunctionSpace(self.mesh, "DG", 0)) Dyu = Dx(self.u, 1) spy = Dyu + self.beta smy = Dyu - self.beta if self.alpha < 1e-15: self.gamma.append( conditional(spx < 0, self.cmax, conditional(smx > 0, self.cmin, 0))) self.gamma.append( conditional(spy < 0, self.cmax, conditional(smy > 0, self.cmin, 0))) else: self.gamma.append( conditional( spx < 0, ufl.Min(-1.0 * spx / self.alpha, self.cmax), conditional(smx > 0, ufl.Max(-1.0 * smx / self.alpha, self.cmin), 0))) self.gamma.append( conditional( spy < 0, ufl.Min(-1.0 * spy / self.alpha, self.cmax), conditional(smy > 0, ufl.Max(-1.0 * smy / self.alpha, self.cmin), 0)))
def eigenstate_legacy(A): """Eigenvalues and eigenprojectors of the 3x3 (real-valued) tensor A. Provides the spectral decomposition A = sum_{a=0}^{2} λ_a * E_a with eigenvalues λ_a and their associated eigenprojectors E_a = n_a^R x n_a^L ordered by magnitude. The eigenprojectors of eigenvalues with multiplicity n are returned as 1/n-fold projector. Note: Tensor A must not have complex eigenvalues! """ if ufl.shape(A) != (3, 3): raise RuntimeError( f"Tensor A of shape {ufl.shape(A)} != (3, 3) is not supported!") # eps = 1.0e-10 # A = ufl.variable(A) # # --- determine eigenvalues λ0, λ1, λ2 # # additively decompose: A = tr(A) / 3 * I + dev(A) = q * I + B q = ufl.tr(A) / 3 B = A - q * ufl.Identity(3) # observe: det(λI - A) = 0 with shift λ = q + ω --> det(ωI - B) = 0 = ω**3 - j * ω - b j = ufl.tr( B * B ) / 2 # == -I2(B) for trace-free B, j < 0 indicates A has complex eigenvalues b = ufl.tr(B * B * B) / 3 # == I3(B) for trace-free B # solve: 0 = ω**3 - j * ω - b by substitution ω = p * cos(phi) # 0 = p**3 * cos**3(phi) - j * p * cos(phi) - b | * 4 / p**3 # 0 = 4 * cos**3(phi) - 3 * cos(phi) - 4 * b / p**3 | --> p := sqrt(j * 4 / 3) # 0 = cos(3 * phi) - 4 * b / p**3 # 0 = cos(3 * phi) - r with -1 <= r <= +1 # phi_k = [acos(r) + (k + 1) * 2 * pi] / 3 for k = 0, 1, 2 p = 2 / ufl.sqrt(3) * ufl.sqrt(j + eps**2) # eps: MMM r = 4 * b / p**3 r = ufl.Max(ufl.Min(r, +1 - eps), -1 + eps) # eps: LMM, MMH phi = ufl.acos(r) / 3 # sorted eigenvalues: λ0 <= λ1 <= λ2 λ0 = q + p * ufl.cos(phi + 2 / 3 * ufl.pi) # low λ1 = q + p * ufl.cos(phi + 4 / 3 * ufl.pi) # middle λ2 = q + p * ufl.cos(phi) # high # # --- determine eigenprojectors E0, E1, E2 # E0 = ufl.diff(λ0, A).T E1 = ufl.diff(λ1, A).T E2 = ufl.diff(λ2, A).T # return [λ0, λ1, λ2], [E0, E1, E2]
def updateCoefficients(self): x, y = SpatialCoordinate(self.mesh) sigma1 = 0.3 sigma2 = 0.3 rho = 0.5 r = 0.05 K = 40 self.u_T = ufl.Max(K - ufl.Min(x, y), 0) self.u_ = ExplicitSolution_WorstOfTwoAssetsPut( self.t, K, r, self.T[1], sigma1, sigma2, rho, cell=self.mesh.ufl_cell(), domain=self.mesh) # Init coefficient matrix self.a = 0.5 * \ as_matrix([[sigma1**2 * x**2, rho*sigma1*sigma2*x*y], [rho*sigma1*sigma2*x*y, sigma2**2 * y**2]]) self.b = as_vector([r * x, r * y]) self.c = Constant(-r) # Init right-hand side self.f = Constant(0.0) # Set boundary conditions to exact solution if self.exact_bc: self.g = ExplicitSolution_WorstOfTwoAssetsPut( self.t, K, r, self.T[1], sigma1, sigma2, rho, cell=self.mesh.ufl_cell(), domain=self.mesh) else: self.g = [(Constant(0.0), 'near(max(x[0],x[1]),200)'), (Constant(K * exp(-r * (self.T[1] - self.t))), 'near(min(x[0],x[1]),0)')]
def distance_function_segments_ufl(P, control_points, impact_radii): if len(control_points) == 1: return distance_function_point_ufl(P, control_points[0]), impact_radii[0] else: rho1 = impact_radii[0] rho2 = impact_radii[1] df, xi = distance_function_line_segement_ufl(P, control_points[0], control_points[1]) for i in range(len(control_points) - 1): tmp_df, tmp_xi = distance_function_line_segement_ufl( P, control_points[i], control_points[i + 1]) xi = fe.conditional(fe.lt(tmp_df, df), tmp_xi, xi) rho1 = fe.conditional(fe.lt(tmp_df, df), impact_radii[i], rho1) rho2 = fe.conditional(fe.lt(tmp_df, df), impact_radii[i + 1], rho2) df = ufl.Min(tmp_df, df) return df, (1 - xi) * rho1 + xi * rho2
def stableNeumannBC(traction, rho, u, v, n, g=None, ds=ds, gamma=Constant(1.0)): """ This function returns the boundary contribution of a stable Neumann BC corresponding to a boundary ``traction`` when the velocity ``u`` (with corresponding test function ``v``) is flowing out of the domain, as determined by comparison with the outward-pointing normal, ``n``. The optional velocity ``g`` can be used to offset the boundary velocity, as when this term is used to obtain a(n inflow- stabilized) consistent traction for weak enforcement of Dirichlet BCs. The paramter ``gamma`` can optionally be used to scale the inflow term. The BC is integrated using the optionally-specified boundary measure ``ds``, which defaults to the entire boundary. NOTE: The sign convention here assumes that the return value is ADDED to the residual given by ``interiorResidual``. NOTE: The boundary traction enforced differs from ``traction`` if ``gamma`` is nonzero. A pure traction BC is not generally stable, which is why the default ``gamma`` is one. See https://www.oden.utexas.edu/media/reports/2004/0431.pdf for theory in the advection--diffusion model problem, and https://doi.org/10.1007/s00466-011-0599-0 for discussion in the context of Navier--Stokes. """ if (g == None): u_minus_g = u else: u_minus_g = u - g return -(inner(traction, v) + gamma * rho * ufl.Min(inner(u, n), Constant(0.0)) * inner(u_minus_g, v)) * ds
def eig(A): """Eigenvalues of 3x3 tensor""" eps = 1.0e-12 q = ufl.tr(A) / 3.0 p1 = 0.5 * (A[0, 1]**2 + A[1, 0]**2 + A[0, 2]**2 + A[2, 0]**2 + A[1, 2]**2 + A[2, 1]**2) p2 = (A[0, 0] - q)**2 + (A[1, 1] - q)**2 + (A[2, 2] - q)**2 + 2 * p1 p = ufl.sqrt(p2 / 6) B = (A - q * ufl.Identity(3)) r = ufl.det(B) / (2 * p**3) r = ufl.Max(ufl.Min(r, 1.0 - eps), -1.0 + eps) phi = ufl.acos(r) / 3.0 eig0 = ufl.conditional(p2 < eps, q, q + 2 * p * ufl.cos(phi)) eig2 = ufl.conditional(p2 < eps, q, q + 2 * p * ufl.cos(phi + (2 * numpy.pi / 3))) eig1 = ufl.conditional(p2 < eps, q, 3 * q - eig0 - eig2) # since trace(A) = eig1 + eig2 + eig3 return eig0, eig1, eig2
def norm2(u, eps=0.0): return sqrt(inner(u, u) + eps**2) c_s = sqrt((K + 4.0 * mu / 3.0) / rho) dxi_dxiHat = 0.5 * ufl.Jacobian(spline.mesh) dx_dxi = spline.parametricGrad(spline.F + u_alpha) dx_dxiHat = dx_dxi * dxi_dxiHat dxiHat_dx = inv(dx_dxiHat) G = dxiHat_dx.T * dxiHat_dx h_K2 = tr(inv(G)) h = sqrt(h_K2) nu_DC_res = h*C_DC*norm2(resLHS_strong(uddot_alpha,u_alpha)-f)\ /norm2(gradx(u_alpha,udot_alpha),eps=Constant(DOLFIN_EPS)) nu_DC_max = 0.5 * rho / J(u_alpha) * C_max * c_s * h nu_DC = ufl.Min(nu_DC_res, nu_DC_max) def D_op(u, udot): return sym(gradx(u, udot)) res_DC = nu_DC*inner(D_op(u_alpha,udot_alpha),D_op(u_alpha,w))\ *J(u_alpha)*spline.dx # Residual of the variational formulation: res = res_weak if (not GALERKIN): res += res_DC # Linearization and definition of a nonlinear problem: tangent = derivative(res, u) problem = ExtractedNonlinearProblem(spline, res, tangent, u)
def gen_alpha(self, a_bgd=500.0, a_lb=1e2, a_ub=1e4): """Generate initial guess for alpha (slip coeff)""" method = self.params.inversion.initial_guess_alpha_method.lower() if method == "wearing": # Martin Wearing's: alpha = 358.4 - 26.9*log(17.9*speed_obs); u_obs = self.u_obs_M v_obs = self.v_obs_M vel_rp = self.params.constants.vel_rp U_mag = sqrt(u_obs**2 + v_obs**2 + vel_rp**2) # U_mag = ufl.Max((u_obs**2 + v_obs**2)**(1/2.0), 50.0) B2 = 358.4 - 26.9 * ln(17.9 * U_mag) alpha = self.bdrag_to_alpha(B2) self.alpha.assign(project(alpha, self.Qp)) elif method == "sia": bed = self.bed H = self.H g = self.params.constants.g rhoi = self.params.constants.rhoi rhow = self.params.constants.rhow u_obs = self.u_obs_M v_obs = self.v_obs_M vel_rp = self.params.constants.vel_rp U = ufl.Max((u_obs**2 + v_obs**2)**(1 / 2.0), 50.0) # Flotation Criterion H_flt = -rhow / rhoi * bed fl_ex = conditional(H <= H_flt, 1.0, 0.0) # Thickness Criterion m_d = conditional(H > 0, 1.0, 0.0) # Calculate surface gradient R_f = ((1.0 - fl_ex) * bed + (fl_ex) * (-rhoi / rhow) * H) s_ = ufl.Max(H + R_f, 0) s = project(s_, self.Q) grads = (s.dx(0)**2.0 + s.dx(1)**2.0)**(1.0 / 2.0) # Calculate alpha, apply background, apply bound B2_ = ((1.0 - fl_ex) * rhoi * g * H * grads / U + (fl_ex) * a_bgd) * m_d + (1.0 - m_d) * a_bgd B2_tmp1 = ufl.Max(B2_, a_lb) B2_tmp2 = ufl.Min(B2_tmp1, a_ub) alpha = self.bdrag_to_alpha(B2_tmp2) self.alpha.assign(project(alpha, self.Qp)) elif method == "constant": init_guess = self.params.inversion.initial_guess_alpha self.alpha.vector()[:] = init_guess self.alpha.vector().apply('insert') else: raise NotImplementedError(f"Don't have code for method {method}") function_update_state(self.alpha) write_diag = self.params.io.write_diagnostics if write_diag: diag_dir = self.params.io.diagnostics_dir phase_suffix = self.params.inversion.phase_suffix phase_name = self.params.inversion.phase_name inout.write_variable(self.alpha, self.params, name="alpha_init_guess", outdir=diag_dir, phase_name=phase_name, phase_suffix=phase_suffix)
def visc(u): # second invariant of strain eps_dot = sqrt(0.5 * inner(sym(grad(u)), sym(grad(u)))) nu_out = glen_fact * eps_dot**((1.0 - nglen) / nglen) # return nu_out return ufl.Min(nu_out, 2e15) # would introduce a viscosity limit
def psi_minus_linear_elasticity_model_B(epsilon, lamda, mu): dim = 2 bulk_mod = lamda + 2. * mu / dim tr_epsilon_minus = ufl.Min(fe.tr(epsilon), 0) return bulk_mod / 2. * tr_epsilon_minus**2