def test_analytical(monitor, method, plot_mesh=False): """ Check that the moved mesh matches that obtained previously. """ fname = '_'.join([monitor.__name__, method]) fpath = os.path.dirname(__file__) op = Options(approach='monge_ampere', r_adapt_rtol=1.0e-03, nonlinear_method=method) mesh = UnitSquareMesh(20, 20) orig_vol = assemble(Constant(1.0)*dx(domain=mesh)) mm = MeshMover(mesh, monitor, op=op) mm.adapt() mesh.coordinates.assign(mm.x) vol = assemble(Constant(1.0)*dx(domain=mesh)) assert np.allclose(orig_vol, vol), "Volume is not conserved!" if plot_mesh: import matplotlib.pyplot as plt fig, axes = plt.subplots(figsize=(5, 5)) triplot(mesh, axes=axes, interior_kw={'linewidth': 0.1}, boundary_kw={'color': 'k'}) axes.axis(False) savefig(fname, os.path.join(fpath, 'outputs'), extensions=['png']) if not os.path.exists(os.path.join(fpath, 'data', fname + '.npy')): np.save(os.path.join(fpath, 'data', fname), mm.x.dat.data) if not plot_mesh: pytest.xfail("Needed to set up the test. Please try again.") loaded = np.load(os.path.join(fpath, 'data', fname + '.npy')) assert np.allclose(mm.x.dat.data, loaded), "Mesh does not match data"
def test_combine_monitor(plot_mesh=False): """ For monitor functions based on the sensors above, compare the meshes resulting from averaging and intersection (visually). In terms of testing, we just check for convergence and that volume is conserved. """ alpha = Constant(10.0) # Controls magnitude of arc beta = Constant(10.0) # Controls width of arc def monitor1(mesh=None, x=None): x, y = x or SpatialCoordinate(mesh) return 1.0 + alpha*exp(-beta*abs(0.5 - x**2 - y**2)) def monitor2(mesh=None, x=None): x, y = x or SpatialCoordinate(mesh) return 1.0 + alpha*exp(-beta*abs(0.5 - (1 - x)**2 - y**2)) # Set parameters kwargs = { 'approach': 'monge_ampere', 'r_adapt_rtol': 1.0e-08, 'nonlinear_method': 'relaxation', } op = Options(**kwargs) fpath = os.path.dirname(__file__) for mode in [1, 2, 'avg', 'int']: fname = {1: 'monitor1', 2: 'monitor2', 'avg': 'average', 'int': 'intersection'}[mode] monitor = monitor_combine(monitor1, monitor2, mode) # Create domain mesh = uniform_mesh(2, 100) orig_vol = assemble(Constant(1.0)*dx(domain=mesh)) # Move the mesh and check for volume conservation mm = MeshMover(mesh, monitor, op=op) mm.adapt() mesh.coordinates.assign(mm.x) vol = assemble(Constant(1.0)*dx(domain=mesh)) assert np.isclose(orig_vol, vol), "Volume is not conserved!" h = interpolate(CellSize(mesh), mm.P0).vector().gather() print("Combination method '{:s}':".format(fname)) print("Minimum cell size = {:.4e}".format(h.min())) print("Maximum cell size = {:.4e}".format(h.max())) print("Mean cell size = {:.4e}".format(np.mean(h))) # Plot mesh if plot_mesh: fig, axes = plt.subplots(figsize=(5, 5)) triplot(mesh, axes=axes, interior_kw={'linewidth': 0.1}, boundary_kw={'color': 'k'}) axes.axis(False) savefig(fname, os.path.join(fpath, 'outputs', op.approach), extensions=['png']) plt.close()
def set_monitor_functions(self, monitors, bc=None, bbc=None): """ Pass a monitor function to each mesh, thereby defining a `MeshMover` object which drives r-adaptation. This method is used under 'monge_ampere' and 'laplacian_smoothing' adaptation approaches. :arg monitors: a monitor function which takes one argument (the mesh) or a list thereof. :kwarg bc: boundary conditions to apply within the mesh movement algorithm. :kwarg bbc: boundary conditions to apply within EquationBC objects which appear in the mesh movement algorithm. """ from adapt_utils.adapt.r import MeshMover # Sanitise input assert self.approach in ('monge_ampere', 'laplacian_smoothing') assert monitors is not None if callable(monitors): monitors = [monitors for mesh in self.meshes] # Create `MeshMover` objects which drive r-adaptation kwargs = { 'method': self.approach, 'bc': bc, 'bbc': bbc, 'op': self.op, } self.op.print_debug("MESH MOVEMENT: Creating MeshMover objects...") for i in range(self.num_meshes): assert monitors[i] is not None args = (Mesh(self.meshes[i].coordinates.copy(deepcopy=True)), monitors[i]) self.mesh_movers[i] = MeshMover(*args, **kwargs)
def test_adjoint(monitor, nonlinear_method, ctrl, bnd, qoi): """ Test replay and gradient for control `ctrl`, boundary condition `bnd` and quantity of interest `qoi`. """ rtol = 1.0e-01 maxiter = 100 alpha = Constant(10.0) beta = Constant(200.0) gamma = Constant(0.15) if ctrl == 'alpha': init = 10.0 control = Control(alpha) elif ctrl == 'beta': init = 200.0 control = Control(beta) def ring(x=None, **kwargs): """ An analytically defined monitor function which concentrates mesh density in a narrow ring within the unit square domain. """ r = dot(x, x) return Constant(1.0) + alpha * pow(cosh(beta * (r - gamma)), -2) def bell(mesh=None, x=None): """ An analytically defined monitor function which concentrates mesh density in a bell region within the unit square domain. """ r = dot(x, x) return 1.0 + alpha * pow(cosh(0.5 * beta * r), -2) # Setup mesh with stop_annotating(): mesh = UnitSquareMesh(20, 20) coords = mesh.coordinates.copy(deepcopy=True) coords.interpolate(coords - as_vector([0.5, 0.5])) mesh = Mesh(coords) # Setup boundary conditions if bnd == 'dirichlet': P1_vec = VectorFunctionSpace(mesh, "CG", 1) bc = DirichletBC(P1_vec, 0, 'on_boundary') bbc = [] elif bnd == 'freeslip': bc, bbc = [], [] else: bc, bbc = None, None # Move mesh mm = MeshMover( mesh, ring if monitor == 'ring' else bell, bc=bc, bbc=bbc, nonlinear_method=nonlinear_method, ) mm.adapt(rtol=rtol, maxiter=maxiter) # Evaluate some functional J = assemble(qoi(mm.x)) stop_annotating() # Test replay given same input Jhat = ReducedFunctional(J, control) c = Constant(init) JJ = Jhat(c) assert np.isclose(J, JJ), f"{J} != {JJ}" # Test replay given different input c = Constant(1.1 * init) JJ = Jhat(c) JJJ = Jhat(c) assert np.isclose(JJ, JJJ), f"{JJ} != {JJJ}" # Taylor test c = Constant(init) dc = Constant(1.0) minconv = taylor_test(Jhat, c, dc) assert minconv > 1.90, "Taylor test failed"
fpath = os.path.join(fpath, initial_monitor_type) fpath = os.path.join(fpath, monitor_type) op = BoydOptions(approach='monge_ampere', n=n_coarse, fpath=fpath, order=kwargs['order']) op.update(kwargs) # --- Initialise mesh swp = AdaptiveProblem(op) # Refine around equator and/or soliton if initial_monitor is not None: mesh_mover = MeshMover(swp.meshes[0], initial_monitor, method='monge_ampere', op=op) mesh_mover.adapt() mesh = Mesh(mesh_mover.x) op.__init__(mesh=mesh, **kwargs) swp.__init__(op, meshes=[mesh]) # --- Monitor function definitions def elevation_norm_monitor(mesh, alpha=40.0, norm_type='H1', **kwargs): """ Monitor function derived from the elevation `norm_type` norm. :kwarg alpha: controls the amplitude of the monitor function. """
def test_sensors(sensor, monitor_type, method, plot_mesh=False): alpha = Constant(5.0) hessian_kwargs = dict(enforce_constraints=False, normalise=True, noscale=True) pytest.xfail("FIXME") # FIXME def shift_and_scale(mesh=None, x=None): """ Adapt to the scaled and shifted sensor magnitude. """ f = interpolate(abs(sensor(mesh, xy=x)), FunctionSpace(mesh, "CG", 1)) return 1.0 + alpha * abs(sensor(mesh, xy=x)) / f.vector().gather().max() def gradient(mesh=None, x=None): """ Adapt to a recovered gradient for the sensor. """ g = recover_gradient(sensor(mesh, xy=x), mesh=mesh, op=op) gmax = g.vector().gather().max() return 1.0 + alpha * dot(g, g) / dot(gmax, gmax) def frobenius(mesh=None, x=None): """ Adapt to the Frobenius norm of an L1-normalised Hessian metric for the sensor. """ P1 = FunctionSpace(mesh, "CG", 1) M = steady_metric(sensor(mesh, xy=x), mesh=mesh, op=op, **hessian_kwargs) M_F = local_frobenius_norm(M, space=P1) return 1.0 + alpha * M_F / interpolate(M_F, P1).vector().gather().max() def density(mesh=None, x=None): """ Adapt to the density of an L1-normalised Hessian metric for the sensor. """ M = steady_metric(sensor(mesh, xy=x), mesh=mesh, op=op, **hessian_kwargs) rho = get_density_and_quotients(M)[0] return 1.0 + alpha * rho / rho.vector().gather().max() rtol = 1.0e-03 if monitor_type == 'shift_and_scale': monitor = shift_and_scale elif monitor_type == 'gradient': monitor = gradient elif monitor_type == 'frobenius': monitor = frobenius elif monitor_type == 'density': monitor = density else: raise ValueError( "Monitor function type {:s} not recognised.".format(monitor_type)) # Set parameters kwargs = { 'approach': 'monge_ampere', 'r_adapt_rtol': rtol, 'nonlinear_method': method, 'normalisation': 'complexity', 'norm_order': None, } op = Options(**kwargs) fname = '_'.join([sensor.__name__, monitor_type, method]) fpath = os.path.dirname(__file__) # Create domain n = 100 mesh = SquareMesh(n, n, 2) x, y = SpatialCoordinate(mesh) mesh.coordinates.interpolate(as_vector([x - 1, y - 1])) orig_vol = assemble(Constant(1.0) * dx(domain=mesh)) # Move the mesh and check for volume conservation mm = MeshMover(mesh, monitor, op=op) mm.adapt() mesh.coordinates.assign(mm.x) vol = assemble(Constant(1.0) * dx(domain=mesh)) assert np.isclose(orig_vol, vol), "Volume is not conserved!" # Plot mesh if plot_mesh: import matplotlib.pyplot as plt fig, axes = plt.subplots(figsize=(5, 5)) triplot(mesh, axes=axes, interior_kw={'linewidth': 0.1}, boundary_kw={'color': 'k'}) axes.axis(False) savefig(fname, os.path.join(fpath, 'outputs', op.approach), extensions=['png']) plt.close() return # Save mesh coordinates to file if not os.path.exists(os.path.join(fpath, 'data', fname + '.npy')): np.save(os.path.join(fpath, 'data', fname), mm.x.dat.data) if not plot_mesh: pytest.xfail("Needed to set up the test. Please try again.") loaded = np.load(os.path.join(fpath, 'data', fname + '.npy')) assert np.allclose(mm.x.dat.data, loaded), "Mesh does not match data"