def SchemeUniform(u, SB, f, bc): # Compute the finite differences along the superbase directions d2u = bc.Diff2(u, SB) d2u[..., bc.not_interior] = 0. # Placeholder value to silent NaN warnings # Generate the parameters for the low dimensional optimization problem Q = 0.5 * np.array([[0, 1, 1], [1, 0, 1], [1, 1, 0]]) l = -d2u m = lp.dot_VV(SB, SB) # Evaluate the numerical scheme m = bc.as_field(m) from agd.FiniteDifferences import as_field Q = as_field(Q, m.shape[1:]) dim = 2 alpha = dim * f**(1 / dim) mask = (alpha == 0) Q = Q * np.where(mask, 1., alpha**2) residue = ConstrainedMaximize(Q, l, m).max(axis=0) residue[mask] = np.max(l / m, axis=0).max(axis=0)[mask] # Boundary conditions return ad.where(bc.interior, residue, u - bc.grid_values)
def SchemeCentered(u,cst,mult,omega,diff,bc,ret_hmax=False): """Discretization of a linear non-divergence form second order PDE cst + mult u + <omega,grad u>- tr(diff hess(u)) = 0 Second order accurate, centered yet monotone finite differences are used for <omega,grad u> - bc : boundary conditions. - ret_hmax : return the largest grid scale for which monotony holds """ # Decompose the tensor field coefs2,offsets = Selling.Decomposition(diff) # Decompose the vector field scals = lp.dot_VA(lp.solve_AV(diff,omega), offsets.astype(float)) coefs1 = coefs2*scals if ret_hmax: return 2./norm(scals,ord=np.inf) # Compute the first and second order finite differences du = bc.DiffCentered(u,offsets) d2u = bc.Diff2(u,offsets) # In interior : cst + mult u + <omega,grad u>- tr(diff hess(u)) = 0 coefs1,coefs2 = (bc.as_field(e) for e in (coefs1,coefs2)) residue = cst + mult*u +lp.dot_VV(coefs1,du) - lp.dot_VV(coefs2,d2u) # On boundary : u-bc = 0 return ad.where(bc.interior,residue,u-bc.grid_values)
def SchemeSampling_Opt(u, diffs, beta, bc): # Evaluate the operator using the envelope theorem result, _ = ad.apply(SchemeSampling_OptInner, u, bc.as_field(diffs), bc, envelope=True) # Boundary conditions return ad.where(bc.interior, beta - result, u - bc.grid_values)
def SchemeSampling(u, diffs, beta, bc): # Tensor decomposition coefs, offsets = Selling.Decomposition(diffs) # Numerical scheme coefs = bc.as_field(coefs) residue = beta - (coefs * bc.Diff2(u, offsets)).sum(0).min(0) # Boundary conditions return ad.where(bc.interior, residue, u - bc.grid_values)
def SchemeMALBR_Opt(u, SB, f, bc): # Evaluate using the envelope theorem result, _ = ad.apply(SchemeMALBR_OptInner, u, bc.as_field(SB), bc, envelope=True) # Boundary conditions return ad.where(bc.interior, f - result, u - bc.grid_values)
def SchemeMALBR(u, SB, f, bc): # Compute the finite differences along the superbase directions d2u = bc.Diff2(u, SB) d2u[..., bc. not_interior] = 0. # Replace NaNs with arbitrary values to silence warnings # Numerical scheme residue = f - MALBR_H(d2u).min(axis=0) # Boundary conditions return ad.where(bc.interior, residue, u - bc.grid_values)
def SchemeUniform_Opt(u, SB, f, bc): # Evaluate the maximum over the superbases using the envelope theorem residue, _ = ad.apply(SchemeUniform_OptInner, u, bc.as_field(SB), f, bc, envelope=True) return ad.where(bc.interior, residue, u - bc.grid_values)
def SchemeNonMonotone(u, f, bc): # Compute the hessian matrix of u uxx = bc.Diff2(u, (1, 0)) uyy = bc.Diff2(u, (0, 1)) uxy = 0.25 * (bc.Diff2(u, (1, 1)) - bc.Diff2(u, (1, -1))) # Numerical scheme det = uxx * uyy - uxy**2 residue = f - det # Boundary conditions return ad.where(bc.interior, residue, u - bc.grid_values)
def MALBR_H(d2u): a, b, c = ad.sort(np.maximum(0., d2u), axis=0) # General formula, handling infinite values separately A, B, C = (ad.where(e == np.inf, 0., e) for e in (a, b, c)) result = 0.5 * (A * B + B * C + C * A) - 0.25 * (A**2 + B**2 + C**2) pos_inf = np.logical_or.reduce(d2u == np.inf) result[pos_inf] = np.inf pos_ineq = a + b < c result[pos_ineq] = (A * B)[pos_ineq] return result
def SchemeLaxFriedrichs(u, A, F, bc): """ Discretization of - Tr(A(x) hess u(x)) + F(grad u(x)) - 1 = 0, with Dirichlet boundary conditions. The scheme is second order, and degenerate elliptic under suitable assumptions. """ # Compute the tensor decomposition coefs, offsets = Selling.Decomposition(A) A, coefs, offsets = (bc.as_field(e) for e in (A, coefs, offsets)) # Obtain the first and second order finite differences grad = Gradient(u, A, bc, decomp=(coefs, offsets)) d2u = bc.Diff2(u, offsets) # Numerical scheme in interior residue = -lp.dot_VV(coefs, d2u) + F(grad) - 1. # Placeholders outside domain return ad.where(bc.interior, residue, u - bc.grid_values)
def SchemeNonMonotone(u, alpha, beta, bc, sqrt_relax=1e-6): # Compute the hessian matrix of u uxx = bc.Diff2(u, (1, 0)) uyy = bc.Diff2(u, (0, 1)) uxy = 0.25 * (bc.Diff2(u, (1, 1)) - bc.Diff2(u, (1, -1))) # Compute the eigenvalues # The relaxation is here to tame the non-differentiability of the square root. htr = (uxx + uyy) / 2. sdelta = np.sqrt(np.maximum(((uxx - uyy) / 2.)**2 + uxy**2, sqrt_relax)) lambda_max = htr + sdelta lambda_min = htr - sdelta # Numerical scheme residue = beta - alpha * lambda_max - lambda_min # Boundary conditions return ad.where(bc.interior, residue, u - bc.grid_values)
def SchemeUpwind(u,cst,mult,omega,diff,bc): """Discretization of a linear non-divergence form second order PDE cst + mult u + <omega,grad u>- tr(diff hess(u)) = 0 First order accurate, upwind finite differences are used for <omega,grad u> - bc : boundary conditions. """ # Decompose the tensor field coefs2,offsets2 = Selling.Decomposition(diff) omega,coefs2 = (bc.as_field(e) for e in (omega,coefs2)) # Decompose the vector field coefs1 = -np.abs(omega) basis = bc.as_field(np.eye(len(omega))) offsets1 = -np.sign(omega)*basis # Compute the first and second order finite differences du = bc.DiffUpwind(u,offsets1.astype(int)) d2u = bc.Diff2(u,offsets2) # In interior : cst + mult u + <omega,grad u>- tr(diff hess(u)) = 0 residue = cst + mult*u +lp.dot_VV(coefs1,du) - lp.dot_VV(coefs2,d2u) # On boundary : u-bc = 0 return ad.where(bc.interior,residue,u-bc.grid_values)
def SchemeConsistent_Opt(u, alpha, beta, bc): value, _ = ad.apply(MinimizeTrace_Opt, u, alpha, bc, envelope=True) residue = beta - value return ad.where(bc.interior, residue, u - bc.grid_values)
def SchemeConsistent(u, alpha, beta, bc): value, _ = MinimizeTrace(u, alpha, bc) residue = beta - value return ad.where(bc.interior, residue, u - bc.grid_values)