def __init__(self, mesh, conditions, timestepping, params, output, solver_params): super().__init__(mesh, conditions, timestepping, params, output, solver_params) self.u0 = Function(self.V) self.u1 = Function(self.V) self.sigma0 = Function(self.S) self.sigma1 = Function(self.S) theta = conditions.theta uh = (1-theta) * self.u0 + theta * self.u1 a = Function(self.U) h = Function(self.U) p = TestFunction(self.V) q = TestFunction(self.S) self.initial_condition((self.u0, conditions.ic['u']), (a, conditions.ic['a']), (h, conditions.ic['h'])) ep_dot = self.strain(grad(uh)) zeta = self.zeta(h, a, self.delta(uh)) eta = zeta * params.e ** (-2) rheology = 2 * eta * ep_dot + (zeta - eta) * tr(ep_dot) * Identity(2) - 0.5 * self.Ice_Strength(h, a) * Identity(2) self.initial_condition((self.sigma0, rheology),(self.sigma1, self.sigma0)) def sigma_next(timestep, zeta, ep_dot, sigma, P): A = 1 + 0.25 * (timestep * params.e ** 2) / params.T B = timestep * 0.125 * (1 - params.e ** 2) / params.T rhs = (1 - (timestep * params.e ** 2) / (4 * params.T)) * sigma - timestep / params.T * ( 0.125 * (1 - params.e ** 2) * tr(sigma) * Identity(2) - 0.25 * P * Identity(2) + zeta * ep_dot) C = (rhs[0, 0] - rhs[1, 1]) / A D = (rhs[0, 0] + rhs[1, 1]) / (A + 2 * B) sigma00 = 0.5 * (C + D) sigma11 = 0.5 * (D - C) sigma01 = rhs[0, 1] sigma = as_matrix([[sigma00, sigma01], [sigma01, sigma11]]) return sigma s = sigma_next(self.timestep, zeta, ep_dot, self.sigma0, self.Ice_Strength(h, a)) sh = (1-theta) * s + theta * self.sigma0 eqn = self.momentum_equation(h, self.u1, self.u0, p, sh, params.rho, uh, conditions.ocean_curr, params.rho_a, params.C_a, params.rho_w, params.C_w, conditions.geo_wind, params.cor, self.timestep, ind=self.ind) tensor_eqn = inner(self.sigma1-s, q) * dx if conditions.stabilised['state']: alpha = conditions.stabilised['alpha'] eqn += stabilisation_term(alpha=alpha, zeta=avg(zeta), mesh=mesh, v=uh, test=p) bcs = DirichletBC(self.V, conditions.bc['u'], "on_boundary") uprob = NonlinearVariationalProblem(eqn, self.u1, bcs) self.usolver = NonlinearVariationalSolver(uprob, solver_parameters=solver_params.bt_params) sprob = NonlinearVariationalProblem(tensor_eqn, self.sigma1) self.ssolver = NonlinearVariationalSolver(sprob, solver_parameters=solver_params.bt_params)
def __init__(self, mesh, conditions, timestepping, params, output, solver_params): super().__init__(mesh, conditions, timestepping, params, output, solver_params) self.u0 = Function(self.V) self.u1 = Function(self.V) self.h = Function(self.U) self.a = Function(self.U) self.p = TestFunction(self.V) theta = conditions.theta self.uh = (1-theta) * self.u0 + theta * self.u1 ep_dot = self.strain(grad(self.uh)) self.initial_condition((self.u0, conditions.ic['u']),(self.u1, self.u0), (self.a, conditions.ic['a']),(self.h, conditions.ic['h'])) zeta = self.zeta(self.h, self.a, self.delta(self.uh)) eta = zeta * params.e ** -2 sigma = 2 * eta * ep_dot + (zeta - eta) * tr(ep_dot) * Identity(2) - 0.5 * self.Ice_Strength(self.h,self.a) * Identity(2) self.eqn = self.momentum_equation(self.h, self.u1, self.u0, self.p, sigma, params.rho, self.uh, conditions.ocean_curr, params.rho_a, params.C_a, params.rho_w, params.C_w, conditions.geo_wind, params.cor, self.timestep) if conditions.stabilised['state']: alpha = conditions.stabilised['alpha'] self.eqn += self.stabilisation_term(alpha=alpha, zeta=avg(zeta), mesh=mesh, v=self.uh, test=self.p) self.bcs = DirichletBC(self.V, conditions.bc['u'], "on_boundary")
def M(ε, A): r"""Calculate the membrane stress for a given strain rate and fluidity""" I = Identity(2) tr_ε = trace(ε) ε_e = sqrt((inner(ε, ε) + tr_ε**2) / 2) μ = 0.5 * A**(-1 / n) * ε_e**(1 / n - 1) return 2 * μ * (ε + tr_ε * I)
def stresses(ε_x, ε_z, A): r"""Calculate the membrane and vertical shear stresses for the given horizontal and shear strain rates and fluidity""" I = Identity(2) tr = trace(ε_x) ε_e = sqrt((inner(ε_x, ε_x) + inner(ε_z, ε_z) + tr**2) / 2) μ = 0.5 * A**(-1 / n) * ε_e**(1 / n - 1) return 2 * μ * (ε_x + tr * I), 2 * μ * ε_z
def sigma_next(timestep, zeta, ep_dot, sigma, P): A = 1 + 0.25 * (timestep * params.e ** 2) / params.T B = timestep * 0.125 * (1 - params.e ** 2) / params.T rhs = (1 - (timestep * params.e ** 2) / (4 * params.T)) * sigma - timestep / params.T * ( 0.125 * (1 - params.e ** 2) * tr(sigma) * Identity(2) - 0.25 * P * Identity(2) + zeta * ep_dot) C = (rhs[0, 0] - rhs[1, 1]) / A D = (rhs[0, 0] + rhs[1, 1]) / (A + 2 * B) sigma00 = 0.5 * (C + D) sigma11 = 0.5 * (D - C) sigma01 = rhs[0, 1] sigma = as_matrix([[sigma00, sigma01], [sigma01, sigma11]]) return sigma
def __init__(self, mesh, conditions, timestepping, params, output, solver_params): super().__init__(mesh, conditions, timestepping, params, output, solver_params) self.w0 = Function(self.W3) self.w1 = Function(self.W3) u0, s0, h0, a0 = self.w0.split() p, q, r, m = TestFunctions(self.W3) self.initial_condition((u0, conditions.ic['u']), (s0, conditions.ic['s']), (a0, conditions.ic['a']), (h0, conditions.ic['h'])) self.w1.assign(self.w0) u1, s1, h1, a1 = split(self.w1) u0, s0, h0, a0 = split(self.w0) theta = conditions.theta uh = (1-theta) * u0 + theta * u1 sh = (1-theta) * s0 + theta * s1 hh = (1-theta) * h0 + theta * h1 ah = (1-theta) * a0 + theta * a1 ep_dot = self.strain(grad(uh)) zeta = self.zeta(hh, ah, self.delta(uh)) rheology = params.e ** 2 * sh + Identity(2) * 0.5 * ((1 - params.e ** 2) * tr(sh) + self.Ice_Strength(hh, ah)) eqn = self.momentum_equation(hh, u1, u0, p, sh, params.rho, uh, conditions.ocean_curr, params.rho_a, params.C_a, params.rho_w, params.C_w, conditions.geo_wind, params.cor, self.timestep, ind=self.ind) eqn += self.transport_equation(uh, hh, ah, h1, h0, a1, a0, r, m, self.n, self.timestep) eqn += inner(self.ind * (s1 - s0) + 0.5 * self.timestep * rheology / params.T, q) * dx eqn -= inner(q * zeta * self.timestep / params.T, ep_dot) * dx if conditions.stabilised['state']: alpha = conditions.stabilised['alpha'] eqn += self.stabilisation_term(alpha=alpha, zeta=avg(zeta), mesh=mesh, v=uh, test=p) bcs = DirichletBC(self.W3.sub(0), conditions.bc['u'], "on_boundary") uprob = NonlinearVariationalProblem(eqn, self.w1, bcs) self.usolver = NonlinearVariationalSolver(uprob, solver_parameters=solver_params.bt_params) self.u1, self.s0, self.h1, self.a1 = self.w1.split()
def __init__(self, mesh, conditions, timestepping, params, output, solver_params): super().__init__(mesh, conditions, timestepping, params, output, solver_params) self.w0 = Function(self.W2) self.w1 = Function(self.W2) u0, h0, a0 = self.w0.split() p, q, r = TestFunctions(self.W2) self.initial_condition((u0, conditions.ic['u']), (h0, conditions.ic['h']), (a0, conditions.ic['a'])) self.w1.assign(self.w0) u1, h1, a1 = split(self.w1) u0, h0, a0 = split(self.w0) theta = conditions.theta uh = (1-theta) * u0 + theta * u1 ah = (1-theta) * a0 + theta * a1 hh = (1-theta) * h0 + theta * h1 ep_dot = self.strain(grad(uh)) zeta = self.zeta(hh, ah, self.delta(uh)) eta = zeta * params.e ** (-2) sigma = 2 * eta * ep_dot + (zeta - eta) * tr(ep_dot) * Identity(2) - self.Ice_Strength(hh, ah) * 0.5 * Identity( 2) eqn = self.momentum_equation(hh, u1, u0, p, sigma, params.rho, uh, conditions.ocean_curr, params.rho_a, params.C_a, params.rho_w, params.C_w, conditions.geo_wind, params.cor, self.timestep) eqn += self.transport_equation(uh, hh, ah, h1, h0, a1, a0, q, r, self.n, self.timestep) if conditions.stabilised['state']: alpha = conditions.stabilised['alpha'] eqn += self.stabilisation_term(alpha=alpha, zeta=avg(zeta), mesh=mesh, v=uh, test=p) bcs = DirichletBC(self.W2.sub(0), conditions.bc['u'], "on_boundary") uprob = NonlinearVariationalProblem(eqn, self.w1, bcs) self.usolver = NonlinearVariationalSolver(uprob, solver_parameters=solver_params.bt_params) self.u1, self.h1, self.a1 = self.w1.split()
def hyperelasticity(mesh, degree): V = VectorFunctionSpace(mesh, 'Q', degree) v = TestFunction(V) du = TrialFunction(V) # Incremental displacement u = Function(V) # Displacement from previous iteration B = Function(V) # Body force per unit mass # Kinematics I = Identity(mesh.topological_dimension()) F = I + grad(u) # Deformation gradient C = F.T * F # Right Cauchy-Green tensor E = (C - I) / 2 # Euler-Lagrange strain tensor E = variable(E) # Material constants mu = Constant(1.0) # Lame's constants lmbda = Constant(0.001) # Strain energy function (material model) psi = lmbda / 2 * (tr(E)**2) + mu * tr(E * E) S = diff(psi, E) # Second Piola-Kirchhoff stress tensor PK = F * S # First Piola-Kirchoff stress tensor # Variational problem return derivative((inner(PK, grad(v)) - inner(B, v)) * dx, u, du)
def __init__(self, mesh, conditions, timestepping, params, output, solver_params): super().__init__(mesh, conditions, timestepping, params, output, solver_params) self.w0 = Function(self.W1) self.w1 = Function(self.W1) self.a = Function(self.U) self.h = Function(self.U) self.u0, self.s0 = self.w0.split() self.p, self.q = TestFunctions(self.W1) self.initial_condition((self.u0, conditions.ic['u']), (self.s0, conditions.ic['s']), (self.a, conditions.ic['a']), (self.h, conditions.ic['h'])) self.w1.assign(self.w0) u1, s1 = split(self.w1) u0, s0 = split(self.w0) theta = conditions.theta uh = (1-theta) * u0 + theta * u1 sh = (1-theta) * s0 + theta * s1 self.ep_dot = self.strain(grad(uh)) zeta = self.zeta(self.h, self.a, self.delta(uh)) self.rheology = params.e ** 2 * sh + Identity(2) * 0.5 * ((1 - params.e ** 2) * tr(sh) + self.Ice_Strength(self.h, self.a)) self.eqn = self.momentum_equation(self.h, u1, u0, self.p, sh, params.rho, uh, conditions.ocean_curr, params.rho_a, params.C_a, params.rho_w, params.C_w, conditions.geo_wind, params.cor, self.timestep, ind=self.ind) self.eqn += inner(self.ind * (s1 - s0) + 0.5 * self.timestep * self.rheology / params.T, self.q) * dx self.eqn -= inner(self.q * zeta * self.timestep / params.T, self.ep_dot) * dx if conditions.stabilised['state']: alpha = conditions.stabilised['alpha'] self.eqn += self.stabilisation_term(alpha=alpha, zeta=avg(zeta), mesh=mesh, v=uh, test=self.p) self.bcs = DirichletBC(self.W1.sub(0), conditions.bc['u'], "on_boundary")
def M(ε, B): I = Identity(2) tr_ε = trace(ε) ε_e = sqrt((inner(ε, ε) + tr_ε**2) / 2) μ = 0.5 * B * ε_e**(1 / n - 1) return 2 * μ * (ε + tr_ε * I)
def sigma(v): return 2.0 * mu * epsilon(v) + lmbda * tr(epsilon(v)) * Identity(2)
def residual(self, test, trial, trial_lagged, fields, bcs): if 'background_viscosity' in fields: assert('grid_resolution' in fields) mu_background = fields['background_viscosity'] grid_dx = fields['grid_resolution'][0] grid_dz = fields['grid_resolution'][1] mu_h = 0.5*abs(trial[0]) * grid_dx + mu_background mu_v = 0.5*abs(trial[1]) * grid_dz + mu_background print("use redx viscosity") diff_tensor = as_tensor([[mu_h, 0], [0, mu_v]]) else: mu = fields['viscosity'] if len(mu.ufl_shape) == 2: diff_tensor = mu else: diff_tensor = mu * Identity(self.dim) phi = test n = self.n u = trial u_lagged = trial_lagged grad_test = nabla_grad(phi) stress = dot(diff_tensor, nabla_grad(u)) if self.symmetric_stress: stress += dot(diff_tensor, grad(u)) F = 0 F += inner(grad_test, stress)*self.dx # Interior Penalty method # # see https://www.researchgate.net/publication/260085826 for details # on the choice of sigma degree = self.trial_space.ufl_element().degree() if not isinstance(degree, int): degree = max(degree[0], degree[1]) # safety factor: 1.0 is theoretical minimum alpha = fields.get('interior_penalty', 2.0) if degree == 0: # probably only works for orthog. quads and hexes sigma = 1.0 else: nf = self.mesh.ufl_cell().num_facets() family = self.trial_space.ufl_element().family() if family in ['DQ', 'TensorProductElement', 'EnrichedElement']: degree_gradient = degree else: degree_gradient = degree - 1 sigma = alpha * cell_edge_integral_ratio(self.mesh, degree_gradient) * nf # we use (3.23) + (3.20) from https://www.researchgate.net/publication/260085826 # instead of maximum over two adjacent cells + and -, we just sum (which is 2*avg()) # and the for internal facets we have an extra 0.5: # WEIRDNESS: avg(1/CellVolume(mesh)) crashes TSFC - whereas it works in scalar diffusion! - instead just writing out explicitly sigma *= FacetArea(self.mesh)*(1/CellVolume(self.mesh)('-') + 1/CellVolume(self.mesh)('+'))/2 if not is_continuous(self.trial_space): u_tensor_jump = tensor_jump(n, u) if self.symmetric_stress: u_tensor_jump += transpose(u_tensor_jump) F += sigma*inner(tensor_jump(n, phi), dot(avg(diff_tensor), u_tensor_jump))*self.dS F += -inner(avg(dot(diff_tensor, nabla_grad(phi))), u_tensor_jump)*self.dS F += -inner(tensor_jump(n, phi), avg(stress))*self.dS for id, bc in bcs.items(): if 'u' in bc or 'un' in bc: if 'u' in bc: u_tensor_jump = outer(n, u-bc['u']) else: u_tensor_jump = outer(n, n)*(dot(n, u)-bc['un']) if self.symmetric_stress: u_tensor_jump += transpose(u_tensor_jump) # this corresponds to the same 3 terms as the dS integrals for DG above: F += 2*sigma*inner(outer(n, phi), dot(diff_tensor, u_tensor_jump))*self.ds(id) F += -inner(dot(diff_tensor, nabla_grad(phi)), u_tensor_jump)*self.ds(id) if 'u' in bc: F += -inner(outer(n, phi), stress) * self.ds(id) elif 'un' in bc: # we only keep, the normal part of stress, the tangential # part is assumed to be zero stress (i.e. free slip), or prescribed via 'stress' F += -dot(n, phi)*dot(n, dot(stress, n)) * self.ds(id) if 'stress' in bc: # a momentum flux, a.k.a. "force" # here we need only the third term, because we assume jump_u=0 (u_ext=u) # the provided stress = n.(mu.stress_tensor) F += dot(-phi, bc['stress']) * self.ds(id) if 'drag' in bc: # (bottom) drag of the form tau = -C_D u |u| C_D = bc['drag'] if 'coriolis_frequency' in fields and self.dim == 2: assert 'u_velocity' in fields u_vel_component = fields['u_velocity'] unorm = pow(dot(u_lagged, u_lagged) + pow(u_vel_component, 2) + 1e-6, 0.5) else: unorm = pow(dot(u_lagged, u_lagged) + 1e-6, 0.5) F += dot(-phi, -C_D*unorm*u) * self.ds(id) # NOTE 1: unspecified boundaries are equivalent to free stress (i.e. free in all directions) # NOTE 2: 'un' can be combined with 'stress' provided the stress force is tangential (e.g. no-normal flow with wind) if 'u' in bc and 'stress' in bc: raise ValueError("Cannot apply both 'u' and 'stress' bc on same boundary") if 'u' in bc and 'drag' in bc: raise ValueError("Cannot apply both 'u' and 'drag' bc on same boundary") if 'u' in bc and 'un' in bc: raise ValueError("Cannot apply both 'u' and 'un' bc on same boundary") return -F