def run_advection_diffusion(tmpdir): # Mesh, state and equation L = 10 mesh = PeriodicIntervalMesh(20, L) dt = 0.02 tmax = 1.0 diffusion_params = DiffusionParameters(kappa=0.75, mu=5) output = OutputParameters(dirname=str(tmpdir), dumpfreq=25) state = State(mesh, dt=dt, output=output) V = state.spaces("DG", "DG", 1) Vu = VectorFunctionSpace(mesh, "CG", 1) equation = AdvectionDiffusionEquation( state, V, "f", Vu=Vu, diffusion_parameters=diffusion_params) problem = [(equation, ((SSPRK3(state), transport), (BackwardEuler(state), diffusion)))] # Initial conditions x = SpatialCoordinate(mesh) xc_init = 0.25 * L xc_end = 0.75 * L umax = 0.5 * L / tmax # Get minimum distance on periodic interval to xc x_init = conditional( sqrt((x[0] - xc_init)**2) < 0.5 * L, x[0] - xc_init, L + x[0] - xc_init) x_end = conditional( sqrt((x[0] - xc_end)**2) < 0.5 * L, x[0] - xc_end, L + x[0] - xc_end) f_init = 5.0 f_end = f_init / 2.0 f_width_init = L / 10.0 f_width_end = f_width_init * 2.0 f_init_expr = f_init * exp(-(x_init / f_width_init)**2) f_end_expr = f_end * exp(-(x_end / f_width_end)**2) state.fields('f').interpolate(f_init_expr) state.fields('u').interpolate(as_vector([Constant(umax)])) f_end = state.fields('f_end', V).interpolate(f_end_expr) # Time stepper timestepper = PrescribedTransport(state, problem) timestepper.run(0, tmax=tmax) error = norm(state.fields('f') - f_end) / norm(f_end) return error
def n_min(self, n_min, apply_to_preconditioner=False): """Ensure n \geq n_min everywhere. If apply_to_preconditioner is True,then this is only done to the preconditioner.""" if apply_to_preconditioner: self.set_n_pre( fd.conditional(fd.lt(fd.real(self._n_pre), n_min), n_min, self._n_pre)) else: self.set_n( fd.conditional(fd.lt(fd.real(self._n), n_min), n_min, self._n))
def __init__(self, state, accretion=True, accumulation=True): super().__init__(state) # obtain our fields self.water_c = state.fields('water_c') self.rain = state.fields('rain') # declare function space Vt = self.water_c.function_space() # define some parameters as attributes dt = state.timestepping.dt k_1 = Constant(0.001) # accretion rate in 1/s k_2 = Constant(2.2) # accumulation rate in 1/s a = Constant(0.001) # min cloud conc in kg/kg b = Constant(0.875) # power for rain in accumulation # make default rates to be zero accr_rate = Constant(0.0) accu_rate = Constant(0.0) if accretion: accr_rate = k_1 * (self.water_c - a) if accumulation: accu_rate = k_2 * self.water_c * self.rain**b # make coalescence rate function, that needs to be the same for all updates in one time step coalesce_rate = Function(Vt) # adjust coalesce rate using min_value so negative cloud concentration doesn't occur self.lim_coalesce_rate = Interpolator( conditional( self.rain < 0.0, # if rain is negative do only accretion conditional(accr_rate < 0.0, 0.0, min_value(accr_rate, self.water_c / dt)), # don't turn rain back into cloud conditional( accr_rate + accu_rate < 0.0, 0.0, # if accretion rate is negative do only accumulation conditional( accr_rate < 0.0, min_value(accu_rate, self.water_c / dt), min_value(accr_rate + accu_rate, self.water_c / dt)))), coalesce_rate) # tell the prognostic fields what to update to self.water_c_new = Interpolator(self.water_c - dt * coalesce_rate, Vt) self.rain_new = Interpolator(self.rain + dt * coalesce_rate, Vt)
def setup_constants(self): x, y = fd.SpatialCoordinate(self.mesh) self.constants = { 'deltat': fd.Constant(self.prm['dt']), 'Kd': fd.Constant(0.01), 'k1': fd.Constant(0.005), 'k2': fd.Constant(0.00005), 'lamd1': fd.Constant(0.000005), 'lamd2': fd.Constant(0.0), 'rho_s': fd.Constant(1.), 'L': fd.Constant(1.), 'phi': fd.Constant(0.3), 'n': fd.FacetNormal(self.mesh), 'f': fd.Constant((0.0, 0.0)), 'nu': fd.Constant(0.001), 'frac': fd.Constant(1.), 'source1': fd.conditional( pow(x - 1, 2) + pow(y - 1.5, 2) < 0.25 * 0.25, 10.0, 0) }
def terminus(u, h, s): r"""Return the terminal stress part of the ice stream action functional The power exerted due to stress at the ice calving terminus :math:`\Gamma` is .. math:: E(u) = \frac{1}{2}\int_\Gamma\left(\rho_Igh^2 - \rho_Wgd^2\right) u\cdot \nu\, ds where :math:`d` is the water depth at the terminus. We assume that sea level is at :math:`z = 0` for purposes of calculating the water depth. Parameters ---------- u : firedrake.Function ice velocity h : firedrake.Function ice thickness s : firedrake.Function ice surface elevation ice_front_ids : list of int numeric IDs of the parts of the boundary corresponding to the calving front """ from firedrake import conditional, lt d = conditional(lt(s - h, 0), s - h, 0) τ_I = ρ_I * g * h**2 / 2 τ_W = ρ_W * g * d**2 / 2 ν = firedrake.FacetNormal(u.ufl_domain()) return (τ_I - τ_W) * inner(u, ν)
def residual(self, test, trial, trial_lagged, fields, bcs): u_adv = trial_lagged phi = test n = self.n u = trial F = -dot(u, div(outer(phi, u_adv)))*self.dx for id, bc in bcs.items(): if 'u' in bc: u_in = bc['u'] elif 'un' in bc: u_in = bc['un'] * n # this implies u_t=0 on the inflow else: u_in = zero(self.dim) F += conditional(dot(u_adv, n) < 0, dot(phi, u_in)*dot(u_adv, n), dot(phi, u)*dot(u_adv, n)) * self.ds(id) if not (is_continuous(self.trial_space) and normal_is_continuous(u_adv)): # s=0: u.n(-)<0 => flow goes from '+' to '-' => '+' is upwind # s=1: u.n(-)>0 => flow goes from '-' to '+' => '-' is upwind s = 0.5*(sign(dot(avg(u), n('-'))) + 1.0) u_up = u('-')*s + u('+')*(1-s) F += dot(u_up, (dot(u_adv('+'), n('+'))*phi('+') + dot(u_adv('-'), n('-'))*phi('-'))) * self.dS return -F
def build(self): """ Build the conditionally valued firedrake function. Raises: AttributeError: If required properties are not defined. AttributeError: If operator not in allows list. Returns: Function: firedrake function set to 1 where the condition is met and 0 where it is not. """ for k in self.properties: if self._props[k] is None: raise AttributeError('"{}" has not been defined.'.format(k)) op = self._props['operator'] lhs = self._props['lhs'] rhs = self._props['rhs'] if op not in self._dispatch_table: raise AttributeError('Unknown condition: {}'.format(op)) val = conditional(self._dispatch_table[op](lhs, rhs), 1, 0) return Function(self.V).interpolate(val)
def rate_factor(T): r"""Compute the rate factor in Glen's flow law for a given temperature The strain rate :math:`\dot\varepsilon` of ice resulting from a stress :math:`\tau` is .. math:: \dot\varepsilon = A(T)\tau^3 where :math:`A(T)` is the temperature-dependent rate factor: .. math:: A(T) = A_0\exp(-Q/RT) where :math:`R` is the ideal gas constant, :math:`Q` has units of energy per mole, and :math:`A_0` is a prefactor with units of pressure :math:`\text{MPa}^{-3}\times\text{yr}^{-1}`. Parameters ---------- T : float, np.ndarray, or UFL expression The ice temperature Returns ------- A : same type as T The ice fluidity """ import ufl if isinstance(T, ufl.core.expr.Expr): cold = firedrake.lt(T, transition_temperature) A0 = firedrake.conditional(cold, A0_cold, A0_warm) Q = firedrake.conditional(cold, Q_cold, Q_warm) A = A0 * firedrake.exp(-Q / (R * T)) if isinstance(T, firedrake.Constant): return firedrake.Constant(A) return A cold = T < transition_temperature warm = ~cold if isinstance(T, np.ndarray) else (not cold) A0 = A0_cold * cold + A0_warm * warm Q = Q_cold * cold + Q_warm * warm return A0 * np.exp(-Q / (R * T))
def new_max(a, b): """Helper function for f. a and b can be dimensions of a UFL SpatialCoordinate. """ return fd.conditional(fd.eq(a, b), a, heaviside(b - a) * b + heaviside(a - b) * a)
def _psi(self, a, dt, u, c): '''The top-surface boundary value for c. Where ice is present this is built using the surface mass balance value.''' # FIXME only set up for b=0 # FIXME criteria for significant ice is lame: "h > 1.1 Href" # FIXME assumes h^{n-1} = lambda (where there is significant ice) x = fd.SpatialCoordinate(self.mesh) h = x[self.k] + c dhdt = a - self._tangentu(u, x[self.k]) + u[self.k] return fd.conditional(h > 1.1 * self.Href, dt * dhdt, 0.0 - x[self.k])
def __init__(self, dq1_space): v = TestFunction(dq1_space) # nodes on squeezed facets are not included in dS_v or ds_v # and all other nodes are (for DQ1), so this step gives nonzero for these other nodes self.marker = assemble(avg(v) * dS_v + v * ds_v) # flip this: 1 for squeezed nodes, and 0 for all others self.marker.assign(conditional(self.marker > 1e-12, 0, 1)) self.P0 = FunctionSpace(dq1_space.mesh(), "DG", 0) self.u0 = Function(self.P0, name='averaged squeezed values') self.u1 = Function(dq1_space, name='aux. squeezed values')
def surface_force(self): A = 1. d = 0.9 * self.Lz l = 0.1 * self.Lz T = fd.Function(self.V) T.interpolate( fd.conditional( self.X[2] > d, fd.as_vector( [A * fd.sin((self.X[2] - d) / l * fd.pi / 2.)**2, 0., 0.]), fd.as_vector([0., 0., 0.]))) # surface force / area return T
def test_read_and_write_segy(): vp_name = "velocity_models/test" segy_file = vp_name + ".segy" hdf5_file = vp_name + ".hdf5" mesh = fire.UnitSquareMesh(10, 10) mesh.coordinates.dat.data[:, 0] *= -1 V = fire.FunctionSpace(mesh, 'CG', 3) x, y = fire.SpatialCoordinate(mesh) r = 0.2 xc = -0.5 yc = 0.5 vp = fire.Function(V) c = fire.conditional((x - xc)**2 + (y - yc)**2 < r**2, 3.0, 1.5) vp.interpolate(c) xi, yi, zi = spyro.io.write_function_to_grid(vp, V, 10.0 / 1000.0) spyro.io.create_segy(zi, segy_file) write_velocity_model(segy_file, vp_name) model = {} model["opts"] = { "method": "CG", # either CG or KMV "quadrature": "CG", # Equi or KMV "degree": 3, # p order "dimension": 2, # dimension } model["mesh"] = { "Lz": 1.0, # depth in km - always positive "Lx": 1.0, # width in km - always positive "Ly": 0.0, # thickness in km - always positive "meshfile": None, "initmodel": None, "truemodel": hdf5_file, } model["BCs"] = { "status": False, } vp_read = spyro.io.interpolate(model, mesh, V, guess=False) fire.File("velocity_models/test.pvd").write(vp_read) value_at_center = vp_read.at(xc, yc) test1 = math.isclose(value_at_center, 3.0) value_outside_circle = vp_read.at(xc + r + 0.1, yc) test2 = math.isclose(value_outside_circle, 1.5) assert all([test1, test2])
def setup_constants(self): x, y = fd.SpatialCoordinate(self.mesh) self.constants = { 'deltat': fd.Constant(self.prm['dt']), 'n': fd.FacetNormal(self.mesh), 'f': fd.Constant((0.0, 0.0)), 'nu': fd.Constant(0.001), 'eps': fd.Constant(0.01), 'K': fd.Constant(10.0), 'f_1': fd.conditional( pow(x - 0.1, 2) + pow(y - 0.1, 2) < 0.05 * 0.05, 0.1, 0), 'f_2': fd.conditional( pow(x - 0.1, 2) + pow(y - 0.3, 2) < 0.05 * 0.05, 0.1, 0), 'f_3': fd.Constant(0.0) }
def sources(self, **kwargs): keys = ("damage", "velocity", "strain_rate", "membrane_stress") D, u, ε, M = itemgetter(*keys)(kwargs) # Increase/decrease damage depending on stress and strain rates ε_1 = eigenvalues(ε)[0] σ_e = sqrt(inner(M, M) - det(M)) ε_h = firedrake.Constant(self.healing_strain_rate) σ_d = firedrake.Constant(self.damage_stress) γ_h = firedrake.Constant(self.healing_rate) γ_d = firedrake.Constant(self.damage_rate) healing = γ_h * min_value(ε_1 - ε_h, 0) fracture = γ_d * conditional(σ_e - σ_d > 0, ε_1, 0.0) * (1 - D) return healing + fracture
def setup_fallout(dirname): # declare grid shape, with length L and height H L = 10. H = 10. nlayers = 10 ncolumns = 10 # make mesh m = PeriodicIntervalMesh(ncolumns, L) mesh = ExtrudedMesh(m, layers=nlayers, layer_height=(H / nlayers)) x = SpatialCoordinate(mesh) dt = 0.1 output = OutputParameters(dirname=dirname + "/fallout", dumpfreq=10, dumplist=['rain_mixing_ratio']) parameters = CompressibleParameters() diagnostic_fields = [Precipitation()] state = State(mesh, dt=dt, output=output, parameters=parameters, diagnostic_fields=diagnostic_fields) Vrho = state.spaces("DG1_equispaced") problem = [(AdvectionEquation(state, Vrho, "rho", ufamily="CG", udegree=1), ForwardEuler(state))] state.fields("rho").assign(1.) physics_list = [Fallout(state)] rain0 = state.fields("rain_mixing_ratio") # set up rain xc = L / 2 zc = H / 2 rc = H / 4 r = sqrt((x[0] - xc)**2 + (x[1] - zc)**2) rain_expr = conditional(r > rc, 0., 1e-3 * (cos(pi * r / (rc * 2)))**2) rain0.interpolate(rain_expr) # build time stepper stepper = PrescribedTransport(state, problem, physics_list=physics_list) return stepper, 10.0
def sources(self, **kwargs): keys = ('damage', 'velocity', 'strain_rate', 'membrane_stress') keys_alt = ('D', 'u', 'ε', 'M') D, u, ε, M = get_kwargs_alt(kwargs, keys, keys_alt) # Increase/decrease damage depending on stress and strain rates ε_1 = eigenvalues(ε)[0] σ_e = sqrt(inner(M, M) - det(M)) ε_h = firedrake.Constant(self.healing_strain_rate) σ_d = firedrake.Constant(self.damage_stress) γ_h = firedrake.Constant(self.healing_rate) γ_d = firedrake.Constant(self.damage_rate) healing = γ_h * min_value(ε_1 - ε_h, 0) fracture = γ_d * conditional(σ_e - σ_d > 0, ε_1, 0.) * (1 - D) return healing + fracture
def writesolutiongeometry(filename, refmesh, mzfine, upc): # get fields on reference mesh u, p, c = upc.split() # compute h on base mesh as updated surface elevation # h(x,y) = lambda(x,y) + c(x,y,lambda(x,y)) lambase = surfaceelevation(refmesh) # bounded below by Href; space Q1base cbase, Q1base = _surfacevalue(refmesh, c) hbase0 = fd.Function(Q1base).interpolate(lambase + cbase) hbase = fd.Function(Q1base) hbase.interpolate(fd.conditional(hbase0 > 0.0, hbase0, 0.0)) # duplicate fine mesh and extend h onto it mesh = extrudedmesh(refmesh._base_mesh, mzfine, refine=-1, temporary_height=1.0) h = extend(mesh, hbase) # change mesh coordinate to linear times h Vcoord = mesh.coordinates.function_space() if mesh._base_mesh.cell_dimension() == 1: x, z = fd.SpatialCoordinate(mesh) Xnew = fd.Function(Vcoord).interpolate(fd.as_vector([x, h * z])) elif mesh._base_mesh.cell_dimension() == 2: x, y, z = fd.SpatialCoordinate(mesh) Xnew = fd.Function(Vcoord).interpolate(fd.as_vector([x, y, h * z])) else: raise ValueError('applies only to 2D and 3D extruded meshes') mesh.coordinates.assign(Xnew) # interpolate velocity u and pressure p from refmesh onto mesh and write Vu, Vp, _ = vectorspaces(mesh) unew = fd.Function(Vu) pnew = fd.Function(Vp) unew.rename('velocity') pnew.rename('pressure') # note f.at() searches for element to evaluate (thanks L Mitchell) print(Xnew.dat.data_ro) # FIXME shows inadmissible z: too high unew.dat.data[:] = u.at(Xnew.dat.data_ro) pnew.dat.data[:] = p.at(Xnew.dat.data_ro) fd.File(filename).write(unew, pnew) return 0
def test_inflow_boundary(scheme): start = 16 finish = 3 * start incr = 4 num_points = np.array(list(range(start, finish + incr, incr))) errors = np.zeros_like(num_points, dtype=np.float64) for k, nx in enumerate(num_points): mesh = firedrake.UnitSquareMesh(nx, nx, diagonal='crossed') degree = 1 Q = firedrake.FunctionSpace(mesh, 'DG', 1) x = firedrake.SpatialCoordinate(mesh) U = Constant(1.) u = as_vector((U, 0.)) q_in = Constant(1.) s = Constant(0.) final_time = 0.5 min_diameter = mesh.cell_sizes.dat.data_ro[:].min() max_speed = 1. multiplier = multipliers[scheme] / 2 timestep = multiplier * min_diameter / max_speed / (2 * degree + 1) num_steps = int(final_time / timestep) dt = final_time / num_steps q_0 = firedrake.project(q_in - x[0], Q) equation = plumes.models.advection.make_equation(u, s, q_in) integrator = scheme(equation, q_0) for step in range(num_steps): integrator.step(dt) q = integrator.state T = Constant(final_time) expr = q_in - firedrake.conditional(x[0] > U * T, x[0] - U * T, 0) errors[k] = assemble(abs(q - expr) * dx) / assemble(abs(expr) * dx) slope, intercept = np.polyfit(np.log2(1 / num_points), np.log2(errors), 1) print(f'log(error) ~= {slope:5.3f} * log(dx) {intercept:+5.3f}') assert slope > degree - 0.1
def __init__(self, salinity, temperature, pressure_perturbation, z, C, r=7.5e-4): super().__init__(salinity, temperature, pressure_perturbation, z) epsilon = 0.0625 # aspect ratio of frazil ice disks = 1/16 Nusselt = 1.0 # ratio of convective /conductive heat transfer. gammaT_frazil = Nusselt * self.kappa_T / (epsilon * r) gammaS_frazil = Nusselt * self.kappa_S / (epsilon * r) Aa = self.a Bb = -self.T + self.b + self.c * self.P_full Bb -= gammaS_frazil * self.Lf / (gammaT_frazil * self.c_p_m) Cc = self.S * gammaS_frazil * self.Lf / (gammaT_frazil * self.c_p_m) S1 = (-Bb + pow(Bb**2 - 4.0 * Aa * Cc, 0.5)) / (2.0 * Aa) S2 = (-Bb - pow(Bb**2 - 4.0 * Aa * Cc, 0.5)) / (2.0 * Aa) print(type(S1)) print(S1) if isinstance(S1, (float, sympy.core.numbers.Float)): # Print statements for testing print("S1 = ", S1) print("S2 = ", S2) if S1 > 0: self.Sc = S1 print("Choose S1") else: self.Sc = S2 print("Choose S2") elif isinstance(S1, sympy.core.add.Add): self.Sc = S2 else: self.Sc = conditional(S1 > 0.0, S1, S2) self.Tc = self.a * self.Sc + self.b + self.c * self.P_full self.wc = ((1 - C) * gammaS_frazil * (self.S - self.Sc) * 2 * C / r) / self.Sc
def sources(self, **kwargs): keys = ('damage', 'velocity', 'fluidity') keys_alt = ('D', 'u', 'A') D, u, A = get_kwargs_alt(kwargs, keys, keys_alt) # Increase/decrease damage depending on stress and strain rates ε = sym(grad(u)) ε_1 = eigenvalues(ε)[0] σ = M(ε, A) σ_e = sqrt(inner(σ, σ) - det(σ)) ε_h = firedrake.Constant(self.healing_strain_rate) σ_d = firedrake.Constant(self.damage_stress) γ_h = firedrake.Constant(self.healing_rate) γ_d = firedrake.Constant(self.damage_rate) healing = γ_h * min_value(ε_1 - ε_h, 0) fracture = γ_d * conditional(σ_e - σ_d > 0, ε_1, 0.) * (1 - D) return healing + fracture
def residual(self, trial, trial_lagged, fields, bcs): u = fields['velocity'] phi = self.test n = self.n q = trial F = q * div(phi * u) * self.dx for id, bc in bcs.items(): if 'q' in bc: F -= conditional( dot(u, n) < 0, phi * dot(u, n) * bc['q'], phi * dot(u, n) * q) * self.ds(id) if not (is_continuous(trial) and is_continuous(u)): # this is the same trick as in the DG_advection firedrake demo un = 0.5 * (dot(u, n) + abs(dot(u, n))) F -= (phi('+') - phi('-')) * (un('+') * q('+') - un('-') * q('-')) * self.dS return F
ext_mesh = ExtrudedMesh(m, layers=nlayers, layer_height=H/nlayers) Vc = VectorFunctionSpace(ext_mesh, "DG", 2) coord = SpatialCoordinate(ext_mesh) x = Function(Vc).interpolate(as_vector([coord[0], coord[1]])) a = 1000. xc = L/2. x, z = SpatialCoordinate(ext_mesh) hm = 1. zs = hm*a**2/((x-xc)**2 + a**2) smooth_z = True dirname = 'nh_mountain' if smooth_z: dirname += '_smootherz' zh = 5000. xexpr = as_vector([x, conditional(z < zh, z + cos(0.5*pi*z/zh)**6*zs, z)]) else: xexpr = as_vector([x, z + ((H-z)/H)*zs]) new_coords = Function(Vc).interpolate(xexpr) mesh = Mesh(new_coords) # sponge function W_DG = FunctionSpace(mesh, "DG", 2) x, z = SpatialCoordinate(mesh) zc = H-10000. mubar = 0.15/dt mu_top = conditional(z <= zc, 0.0, mubar*sin((pi/2.)*(z-zc)/(H-zc))**2) mu = Function(W_DG).interpolate(mu_top) fieldlist = ['u', 'rho', 'theta'] timestepping = TimesteppingParameters(dt=dt)
def d2(s): return conditional(le(s, fd.Constant(1.0)), s - fd.Constant(1.0), d1(s))
theta_b = Function(Vt).interpolate(Tsurf) rho_b = Function(Vr) # Calculate hydrostatic Pi compressible_hydrostatic_balance(state, theta_b, rho_b, solve_for_rho=True) x = SpatialCoordinate(mesh) a = 5.0e3 deltaTheta = 1.0e-2 xc = 0.5 * L xr = 4000. zc = 3000. zr = 2000. r = sqrt(((x[0] - xc) / xr)**2 + ((x[1] - zc) / zr)**2) theta_pert = conditional(r > 1., 0., -7.5 * (1. + cos(pi * r))) theta0.interpolate(theta_b + theta_pert) water0.interpolate(theta_pert) rho0.assign(rho_b) state.initialise([('u', u0), ('rho', rho0), ('theta', theta0), ('water', water0)]) state.set_reference_profiles([('rho', rho_b), ('theta', theta_b)]) # Set up advection schemes ueqn = EulerPoincare(state, Vu) rhoeqn = AdvectionEquation(state, Vr, equation_form="continuity") supg = True if supg: thetaeqn = SUPGAdvection(state, Vt, equation_form="advective") watereqn = SUPGAdvection(state, Vt, equation_form="advective")
theta_b = Function(Vt).interpolate(Constant(Tsurf)) # Calculate hydrostatic fields compressible_hydrostatic_balance(state, theta_b, rho0, solve_for_rho=True) # make mean fields rho_b = Function(Vr).assign(rho0) # define perturbation xc = L / 2 zc = 2000. rc = 2000. Tdash = 2.0 r = sqrt((x - xc)**2 + (z - zc)**2) theta_pert = Function(Vt).interpolate( conditional(r > rc, 0.0, Tdash * (cos(pi * r / (2.0 * rc)))**2)) # define initial theta theta0.assign(theta_b * (theta_pert / 300.0 + 1.0)) # find perturbed rho gamma = TestFunction(Vr) rho_trial = TrialFunction(Vr) lhs = gamma * rho_trial * dx rhs = gamma * (rho_b * theta_b / theta0) * dx rho_problem = LinearVariationalProblem(lhs, rhs, rho0) rho_solver = LinearVariationalSolver(rho_problem) rho_solver.solve() state.set_reference_profiles([('rho', rho_b), ('theta', theta_b)])
def __init__(self, salinity, temperature, pressure_perturbation, z, GammaTfunc=None, velocity=None, ice_heat_flux=True, HJ99Gamma=False, f=None): super().__init__(salinity, temperature, pressure_perturbation, z) if velocity is None: # Use constant turbulent thermal and salinity exchange values given in Holland and Jenkins 1999. gammaT = self.gammaT gammaS = self.gammaS else: u = velocity if HJ99Gamma: # Holland and Jenkins 1999 turbulent heat/salt exchange velocity as a function # of friction velocity. This should be the same as in MITgcm. # Holland, D.M. and Jenkins, A., 1999. Modeling thermodynamic ice–ocean interactions at # the base of an ice shelf. Journal of Physical Oceanography, 29(8), pp.1787-1800. # Calculate friction velocity if isinstance(u, float): print("Input velocity:", u) u_bounded = max(u, 1e-3) print("Bounded velocity:", u_bounded) else: u_bounded = conditional(u > 1e-3, u, 1e-3) u_star = pow(self.C_d * pow(u_bounded, 2), 0.5) # Calculate turbulent component of thermal and salinity exchange velocity (Eq 15) # N.b MITgcm sets etastar = 1 so that the first part of Gamma_turb term below is constant. # eta_star = (1 + (zeta_N * u_star) / (f * L0 * Rc))^-1/2 (Eq 18) # In H&J99 eta_star is set to 1 when the Obukhov length is negative (i.e the buoyancy flux # is destabilising. This occurs during freezing. (Melting is stabilising) # So it does seem a bit odd because then eta_star is tuned to freezing conditions # need to work this out...? Gamma_Turb = 1.0 / (2.0 * self.zeta_N * self.eta_star) - 1.0 / self.k if f is not None: # Add extra term if using coriolis term # Calculate viscous sublayer thickness (Eq 17) h_nu = 5.0 * self.nu / u_star Gamma_Turb += ln( u_star * self.zeta_N * pow(self.eta_star, 2) / (abs(f) * h_nu)) / self.k # Calculate molecular components of thermal exchange velocity (Eq 16) GammaT_Mole = 12.5 * pow(self.Pr, 2.0 / 3.0) - 6.0 # Calculate molecular component of salinity exchange velocity (Eq 16) GammaS_Mole = 12.5 * pow(self.Sc, 2.0 / 3.0) - 6.0 # Calculate thermal and salinity exchange velocity. (Eq 14) # Do we need to catch -ve gamma? could have -ve Gamma_Turb? gammaT = u_star / (Gamma_Turb + GammaT_Mole) gammaS = u_star / (Gamma_Turb + GammaS_Mole) # print exchange velocities if testing when input velocity is a float. if isinstance(gammaT, float) or isinstance(gammaS, float): print("gammaT = ", gammaT) print("gammaS = ", gammaS) else: # ISOMIP+ based on Jenkins et al 2010. Measurement of basal rates beneath Ronne Ice Shelf u_tidal = 0.01 u_star = pow(self.C_d * (pow(u, 2) + pow(u_tidal, 2)), 0.5) if GammaTfunc is None: gammaT = self.GammaT * u_star gammaS = self.GammaS * u_star else: gammaT = GammaTfunc * u_star gammaS = (GammaTfunc / 35.0) * u_star # print exchange velocities if testing when input velocity is a float. if isinstance(gammaT, float) or isinstance(gammaS, float): print("gammaT = ", gammaT) print("gammaS = ", gammaS) b_plus_cPb = (self.b + self.c * self.P_full ) # save calculating this each time... # Calculate coefficients in quadratic equation for salinity at ice-ocean boundary. # Aa.Sb^2 + Bb.Sb + Cc = 0 Aa = self.c_p_m * gammaT * self.a Bb = -gammaS * self.Lf Bb -= self.c_p_m * gammaT * self.T Bb += self.c_p_m * gammaT * b_plus_cPb Cc = gammaS * self.S * self.Lf if ice_heat_flux: Aa -= gammaS * self.c_p_i * self.a Bb += gammaS * self.S * self.c_p_i * self.a Bb -= gammaS * self.c_p_i * b_plus_cPb Bb += gammaS * self.c_p_i * self.T_ice Cc += gammaS * self.S * self.c_p_i * b_plus_cPb Cc -= gammaS * self.S * self.c_p_i * self.T_ice S1 = (-Bb + pow(Bb**2 - 4.0 * Aa * Cc, 0.5)) / (2.0 * Aa) S2 = (-Bb - pow(Bb**2 - 4.0 * Aa * Cc, 0.5)) / (2.0 * Aa) print(type(S1)) print(S1) if isinstance(S1, (float, sympy.core.numbers.Float)): # Print statements for testing print("S1 = ", S1) print("S2 = ", S2) if S1 > 0: self.Sb = S1 print("Choose S1") else: self.Sb = S2 print("Choose S2") elif isinstance(S1, sympy.core.add.Add): self.Sb = S2 else: self.Sb = conditional(S1 > 0.0, S1, S2) self.Tb = self.a * self.Sb + self.b + self.c * self.P_full self.wb = gammaS * (self.S - self.Sb) / self.Sb if ice_heat_flux: self.Q_ice = -self.rho0 * (self.T_ice - self.Tb) * self.c_p_i * self.wb else: if isinstance(S1, float): self.Q_ice = 0.0 else: self.Q_ice = Constant(0.0) self.Q_mixed = -self.rho0 * self.c_p_m * gammaT * (self.Tb - self.T) self.Q_latent = self.Q_ice - self.Q_mixed self.QS_mixed = -self.rho0 * gammaS * (self.Sb - self.S) self.T_flux_bc = -(self.wb + gammaT) * (self.Tb - self.T) self.S_flux_bc = -(self.wb + gammaS) * (self.Sb - self.S)
def domainify(vector, x): dim = len(x) return conditional( x[0] > 0.0, vector, as_vector(tuple(0.0 for _ in range(dim))) )
def setup_condens(dirname): # declare grid shape, with length L and height H L = 1000. H = 1000. nlayers = int(H / 100.) ncolumns = int(L / 100.) # make mesh m = PeriodicIntervalMesh(ncolumns, L) mesh = ExtrudedMesh(m, layers=nlayers, layer_height=(H / nlayers)) x = SpatialCoordinate(mesh) fieldlist = ['u', 'rho', 'theta'] timestepping = TimesteppingParameters(dt=1.0, maxk=4, maxi=1) output = OutputParameters(dirname=dirname + "/condens", dumpfreq=1, dumplist=['u'], perturbation_fields=['theta', 'rho']) parameters = CompressibleParameters() state = State(mesh, vertical_degree=1, horizontal_degree=1, family="CG", timestepping=timestepping, output=output, parameters=parameters, fieldlist=fieldlist, diagnostic_fields=[Sum('water_v', 'water_c')]) # declare initial fields u0 = state.fields("u") rho0 = state.fields("rho") theta0 = state.fields("theta") # spaces Vpsi = FunctionSpace(mesh, "CG", 2) Vt = theta0.function_space() Vr = rho0.function_space() # make a gradperp gradperp = lambda u: as_vector([-u.dx(1), u.dx(0)]) # declare tracer field and a background field water_v0 = state.fields("water_v", Vt) water_c0 = state.fields("water_c", Vt) # Isentropic background state Tsurf = Constant(300.) theta_b = Function(Vt).interpolate(Tsurf) rho_b = Function(Vr) # Calculate initial rho compressible_hydrostatic_balance(state, theta_b, rho_b, solve_for_rho=True) # set up water_v xc = 500. zc = 350. rc = 250. r = sqrt((x[0] - xc)**2 + (x[1] - zc)**2) w_expr = conditional(r > rc, 0., 0.25 * (1. + cos((pi / rc) * r))) # set up velocity field u_max = 10.0 psi_expr = ((-u_max * L / pi) * sin(2 * pi * x[0] / L) * sin(pi * x[1] / L)) psi0 = Function(Vpsi).interpolate(psi_expr) u0.project(gradperp(psi0)) theta0.interpolate(theta_b) rho0.interpolate(rho_b) water_v0.interpolate(w_expr) state.initialise([('u', u0), ('rho', rho0), ('theta', theta0), ('water_v', water_v0), ('water_c', water_c0)]) state.set_reference_profiles([('rho', rho_b), ('theta', theta_b)]) # set up advection schemes rhoeqn = AdvectionEquation(state, Vr, equation_form="continuity") thetaeqn = SUPGAdvection(state, Vt, supg_params={"dg_direction": "horizontal"}, equation_form="advective") # build advection dictionary advected_fields = [] advected_fields.append(("u", NoAdvection(state, u0, None))) advected_fields.append(("rho", SSPRK3(state, rho0, rhoeqn))) advected_fields.append(("theta", SSPRK3(state, theta0, thetaeqn))) advected_fields.append(("water_v", SSPRK3(state, water_v0, thetaeqn))) advected_fields.append(("water_c", SSPRK3(state, water_c0, thetaeqn))) physics_list = [Condensation(state)] # build time stepper stepper = AdvectionDiffusion(state, advected_fields, physics_list=physics_list) return stepper, 5.0
Kz = fd.Function(V) phi = fd.Constat(0.2) coords2ijk = np.vectorize(coords2ijk, excluded=['data_array', 'Delta']) Kx.dat.data[...] = coords2ijk(coords[:, 0], coords[:, 1], coords[:, 2], Delta=Delta, data_array=kx_array) Ky.dat.data[...] = coords2ijk(coords[:, 0], coords[:, 1], coords[:, 2], Delta=Delta, data_array=ky_array) Kz.dat.data[...] = coords2ijk(coords[:, 0], coords[:, 1], coords[:, 2], Delta=Delta, data_array=kz_array) print("END: Read in reservoir fields") # Permeability field harmonic interpolation to facets Kx_facet = fd.conditional(fd.gt(fd.avg(Kx), 0.0), Kx('+')*Kx('-') / fd.avg(Kx), 0.0) Ky_facet = fd.conditional(fd.gt(fd.avg(Ky), 0.0), Ky('+')*Ky('-') / fd.avg(Ky), 0.0) Kz_facet = fd.conditional(fd.gt(fd.avg(Kz), 0.0), Kz('+')*Kz('-') / fd.avg(Kz), 0.0) # We can now define the bilinear and linear forms for the left and right dx = fd.dx KdivU = fd.as_vector((Kx_facet*u.dx(0), Ky_facet*u.dx(1), Kz_facet*u.dx(2))) a = (fd.dot(KdivU, fd.grad(v))) * dx m = u * v * phi * dx # Defining the eigenvalue problem petsc_a = fd.assemble(a).M.handle petsc_m = fd.assemble(m).M.handle num_eigenvalues = 3
def pml(mesh, scatterer_bdy_id, outer_bdy_id, wave_number, options_prefix=None, solver_parameters=None, inner_region=None, fspace=None, tfspace=None, true_sol_grad=None, pml_type=None, delta=None, quad_const=None, speed=None, pml_min=None, pml_max=None): """ For unlisted arg descriptions, see run_method :arg inner_region: boundary id of non-pml region :arg pml_type: Type of pml function, either 'quadratic' or 'bdy_integral' :arg delta: For :arg:`pml_type` of 'bdy_integral', added to denominator to prevent 1 / 0 at edge of boundary :arg quad_const: For :arg:`pml_type` of 'quadratic', a scaling constant :arg speed: Speed of sound :arg pml_min: A list, *pml_min[i]* is where to begin pml layer in direction *i* :arg pml_max: A list, *pml_max[i]* is where to end pml layer in direction *i* """ # Handle defauls if pml_type is None: pml_type = 'bdy_integral' if delta is None: delta = 1e-3 if quad_const is None: quad_const = 1.0 if speed is None: speed = 340.0 pml_types = ['bdy_integral', 'quadratic'] if pml_type not in pml_types: raise ValueError("PML type of %s is not one of %s" % (pml_type, pml_types)) xx = SpatialCoordinate(mesh) # {{{ create sigma functions for PML sigma = None if pml_type == 'bdy_integral': sigma = [ Constant(speed) / (Constant(delta + extent) - abs(coord)) for extent, coord in zip(pml_max, xx) ] elif pml_type == 'quadratic': sigma = [ Constant(quad_const) * (abs(coord) - Constant(min_))**2 for min_, coord in zip(pml_min, xx) ] r""" Here \kappa is the wave number and c is the speed ..math:: \kappa = \frac{ \omega } { c } """ omega = wave_number * speed # {{{ Set up PML functions gamma = [ Constant(1.0) + conditional( abs(real(coord)) >= real(min_), Constant(1j / omega) * sigma_i, Constant(0.0)) for min_, coord, sigma_i in zip(pml_min, xx, sigma) ] kappa = [None] * len(gamma) gamma_prod = 1.0 for i in range(len(gamma)): gamma_prod *= gamma[i] tensor_i = [Constant(0.0) for _ in range(len(gamma))] tensor_i[i] = 1.0 r""" *i*th entry is .. math:: \frac{\prod_{j\neq i} \gamma_j}{ \gamma_i } """ for j in range(len(gamma)): if j != i: tensor_i[i] *= gamma[j] else: tensor_i[i] /= gamma[j] kappa[i] = tensor_i kappa = as_tensor(kappa) # }}} p = TrialFunction(fspace) q = TestFunction(fspace) k = wave_number # Just easier to look at a = (inner(dot(grad(p), kappa), grad(q)) - Constant(k**2) * gamma_prod * inner(p, q)) * dx n = FacetNormal(mesh) L = inner(dot(true_sol_grad, n), q) * ds(scatterer_bdy_id) bc = DirichletBC(fspace, Constant(0), outer_bdy_id) solution = Function(fspace) #solve(a == L, solution, bcs=[bc], options_prefix=options_prefix) # Create a solver and return the KSP object with the solution so that can get # PETSc information # Create problem problem = vs.LinearVariationalProblem(a, L, solution, [bc], None) # Create solver and call solve solver = vs.LinearVariationalSolver(problem, solver_parameters=solver_parameters, options_prefix=options_prefix) solver.solve() return solver.snes, solution