def test_linearity_adjoint_F(self, shape, dtype, so): """ Test the linearity of the adjoint modeling operator by verifying: a F^T(r) = F^T(a r) """ np.random.seed(0) solver = acoustic_sa_setup(shape=shape, dtype=dtype, space_order=so) src0 = solver.geometry.src rec, _, _ = solver.forward(src0) a = -1 + 2 * np.random.rand() src1, _, _ = solver.adjoint(rec) rec.data[:] = a * rec.data[:] src2, _, _ = solver.adjoint(rec) src1.data[:] *= a # Check adjoint source wavefeild linearity # Normalize by rms of rec2, to enable using abolute tolerance below rms2 = np.sqrt(np.mean(src2.data**2)) diff = (src1.data - src2.data) / rms2 info("linearity adjoint F %s (so=%d) rms 1,2,diff; " "%+16.10e %+16.10e %+16.10e" % (shape, so, np.sqrt(np.mean(src1.data**2)), np.sqrt(np.mean(src2.data**2)), np.sqrt(np.mean(diff**2)))) tol = 1.e-12 assert np.allclose(diff, 0.0, atol=tol)
def test_array_reduction(self, so, dim): """ Test generation of OpenMP reduction clauses involving Function's. """ grid = Grid(shape=(3, 3, 3)) d = grid.dimensions[dim] f = Function(name='f', shape=(3, ), dimensions=(d, ), grid=grid, space_order=so) u = TimeFunction(name='u', grid=grid) op = Operator(Inc(f, u + 1), opt=('openmp', { 'par-collapse-ncores': 1 })) iterations = FindNodes(Iteration).visit(op) assert "reduction(+:f[0:f_vec->size[0]])" in iterations[1].pragmas[ 0].value try: op(time_M=1) except: # Older gcc <6.1 don't support reductions on array info("Un-supported older gcc version for array reduction") assert True return assert np.allclose(f.data, 18)
def basic_gradient_test(wave, space_order, v0, v, rec, F0, gradient, dm): G = np.dot(mat2vec(gradient.data), dm.reshape(-1)) # FWI Gradient test H = [0.5, 0.25, .125, 0.0625, 0.0312, 0.015625, 0.0078125] error1 = np.zeros(7) error2 = np.zeros(7) for i in range(0, 7): # Add the perturbation to the model def initializer(data): data[:] = np.sqrt(v0**2 * v**2 / ((1 - H[i]) * v**2 + H[i] * v0**2)) vloc = Function(name='vloc', grid=wave.model.grid, space_order=space_order, initializer=initializer) # Data for the new model d = wave.forward(vp=vloc, dt=wave.model.critical_dt)[0] # First order error Phi(m0+dm) - Phi(m0) F_i = .5 * linalg.norm((d.data - rec.data).reshape(-1))**2 error1[i] = np.absolute(F_i - F0) # Second order term r Phi(m0+dm) - Phi(m0) - <J(m0)^T \delta d, dm> error2[i] = np.absolute(F_i - F0 - H[i] * G) # Test slope of the tests p1 = np.polyfit(np.log10(H), np.log10(error1), 1) p2 = np.polyfit(np.log10(H), np.log10(error2), 1) info('1st order error, Phi(m0+dm)-Phi(m0): %s' % (p1)) info(r'2nd order error, Phi(m0+dm)-Phi(m0) - <J(m0)^T \delta d, dm>: %s' % (p2)) assert np.isclose(p1[0], 1.0, rtol=0.1) assert np.isclose(p2[0], 2.0, rtol=0.1)
def run(shape=(50, 50, 50), spacing=(20.0, 20.0, 20.0), tn=250.0, autotune=False, time_order=2, space_order=4, nbl=10, kernel='centered', full_run=False, **kwargs): solver = tti_setup(shape=shape, spacing=spacing, tn=tn, space_order=space_order, nbl=nbl, kernel=kernel, **kwargs) info("Applying Forward") rec, u, v, summary = solver.forward(autotune=autotune) if not full_run: return summary.gflopss, summary.oi, summary.timings, [rec, u, v] info("Applying Adjoint") solver.adjoint(rec, autotune=autotune) return summary.gflopss, summary.oi, summary.timings, [rec, u, v]
def test_gradientJ(self, shape, kernel, space_order): """ This test ensures that the Jacobian computed with devito satisfies the Taylor expansion property: .. math:: F(m0 + h dm) = F(m0) + \O(h) \\ F(m0 + h dm) = F(m0) + J dm + \O(h^2) \\ with F the Forward modelling operator. :param dimensions: size of the domain in all dimensions in number of grid points :param time_order: order of the time discretization scheme :param space_order: order of the spacial discretization scheme :return: assertion that the Taylor properties are satisfied """ spacing = tuple(15. for _ in shape) wave = setup(shape=shape, spacing=spacing, dtype=np.float64, kernel=kernel, space_order=space_order, tn=1000., nbpml=10+space_order/2) m0 = Function(name='m0', grid=wave.model.grid, space_order=space_order) smooth(m0, wave.model.m) dm = np.float64(wave.model.m.data - m0.data) linrec = Receiver(name='rec', grid=wave.model.grid, time_range=wave.receiver.time_range, coordinates=wave.receiver.coordinates.data) # Compute receiver data and full wavefield for the smooth velocity rec, u0, _ = wave.forward(m=m0, save=False) # Gradient: J dm Jdm, _, _, _ = wave.born(dm, rec=linrec, m=m0) # FWI Gradient test H = [0.5, 0.25, .125, 0.0625, 0.0312, 0.015625, 0.0078125] error1 = np.zeros(7) error2 = np.zeros(7) for i in range(0, 7): # Add the perturbation to the model def initializer(data): data[:] = m0.data + H[i] * dm mloc = Function(name='mloc', grid=wave.model.m.grid, space_order=space_order, initializer=initializer) # Data for the new model d = wave.forward(m=mloc)[0] delta_d = (d.data - rec.data).reshape(-1) # First order error F(m0 + hdm) - F(m0) error1[i] = np.linalg.norm(delta_d, 1) # Second order term F(m0 + hdm) - F(m0) - J dm error2[i] = np.linalg.norm(delta_d - H[i] * Jdm.data.reshape(-1), 1) # Test slope of the tests p1 = np.polyfit(np.log10(H), np.log10(error1), 1) p2 = np.polyfit(np.log10(H), np.log10(error2), 1) info('1st order error, Phi(m0+dm)-Phi(m0) with slope: %s compared to 1' % (p1[0])) info('2nd order error, Phi(m0+dm)-Phi(m0) - <J(m0)^T \delta d, dm>with slope:' ' %s comapred to 2' % (p2[0])) assert np.isclose(p1[0], 1.0, rtol=0.1) assert np.isclose(p2[0], 2.0, rtol=0.1)
def test_gradientJ(self, shape, kernel, space_order): r""" This test ensures that the Jacobian computed with devito satisfies the Taylor expansion property: .. math:: F(m0 + h dm) = F(m0) + \O(h) \\ F(m0 + h dm) = F(m0) + J dm + \O(h^2) \\ with F the Forward modelling operator. """ spacing = tuple(15. for _ in shape) wave = setup(shape=shape, spacing=spacing, dtype=np.float64, kernel=kernel, space_order=space_order, tn=1000., nbpml=10+space_order/2) m0 = Function(name='m0', grid=wave.model.grid, space_order=space_order) smooth(m0, wave.model.m) dm = np.float64(wave.model.m.data - m0.data) linrec = Receiver(name='rec', grid=wave.model.grid, time_range=wave.geometry.time_axis, coordinates=wave.geometry.rec_positions) # Compute receiver data and full wavefield for the smooth velocity rec, u0, _ = wave.forward(m=m0, save=False) # Gradient: J dm Jdm, _, _, _ = wave.born(dm, rec=linrec, m=m0) # FWI Gradient test H = [0.5, 0.25, .125, 0.0625, 0.0312, 0.015625, 0.0078125] error1 = np.zeros(7) error2 = np.zeros(7) for i in range(0, 7): # Add the perturbation to the model def initializer(data): data[:] = m0.data + H[i] * dm mloc = Function(name='mloc', grid=wave.model.m.grid, space_order=space_order, initializer=initializer) # Data for the new model d = wave.forward(m=mloc)[0] delta_d = (d.data - rec.data).reshape(-1) # First order error F(m0 + hdm) - F(m0) error1[i] = np.linalg.norm(delta_d, 1) # Second order term F(m0 + hdm) - F(m0) - J dm error2[i] = np.linalg.norm(delta_d - H[i] * Jdm.data.reshape(-1), 1) # Test slope of the tests p1 = np.polyfit(np.log10(H), np.log10(error1), 1) p2 = np.polyfit(np.log10(H), np.log10(error2), 1) info('1st order error, Phi(m0+dm)-Phi(m0) with slope: %s compared to 1' % (p1[0])) info(r'2nd order error, Phi(m0+dm)-Phi(m0) - <J(m0)^T \delta d, dm>with slope:' ' %s comapred to 2' % (p2[0])) assert np.isclose(p1[0], 1.0, rtol=0.1) assert np.isclose(p2[0], 2.0, rtol=0.1)
def check_norms(fn_norms, reference): with open(fn_norms, 'r') as f: norms = eval(f.read()) assert isinstance(norms, dict) assert isinstance(reference, dict) assert set(norms) == set(reference) for k, v in norms.items(): norm = float(v) info("norm(%s) = %f (expected = %f, delta = %f)" % (k, norm, reference[k], abs(norm - reference[k])))
def test_gradientJ(self, dtype, space_order, kernel, shape, spacing, time_order, setup_func): """ This test ensures that the Jacobian computed with devito satisfies the Taylor expansion property: .. math:: F(m0 + h dm) = F(m0) + \O(h) \\ F(m0 + h dm) = F(m0) + J dm + \O(h^2) \\ with F the Forward modelling operator. """ wave = setup_func(shape=shape, spacing=spacing, dtype=dtype, kernel=kernel, space_order=space_order, time_order=time_order, tn=1000., nbl=10+space_order/2) v0 = Function(name='v0', grid=wave.model.grid, space_order=space_order) smooth(v0, wave.model.vp) v = wave.model.vp.data dm = dtype(wave.model.vp.data**(-2) - v0.data**(-2)) # Compute receiver data and full wavefield for the smooth velocity rec = wave.forward(vp=v0, save=False)[0] # Gradient: J dm Jdm = wave.jacobian(dm, vp=v0)[0] # FWI Gradient test H = [0.5, 0.25, .125, 0.0625, 0.0312, 0.015625, 0.0078125] error1 = np.zeros(7) error2 = np.zeros(7) for i in range(0, 7): # Add the perturbation to the model def initializer(data): data[:] = np.sqrt(v0.data**2 * v**2 / ((1 - H[i]) * v**2 + H[i] * v0.data**2)) vloc = Function(name='vloc', grid=wave.model.grid, space_order=space_order, initializer=initializer) # Data for the new model d = wave.forward(vp=vloc)[0] delta_d = (d.data - rec.data).reshape(-1) # First order error F(m0 + hdm) - F(m0) error1[i] = np.linalg.norm(delta_d, 1) # Second order term F(m0 + hdm) - F(m0) - J dm error2[i] = np.linalg.norm(delta_d - H[i] * Jdm.data.reshape(-1), 1) # Test slope of the tests p1 = np.polyfit(np.log10(H), np.log10(error1), 1) p2 = np.polyfit(np.log10(H), np.log10(error2), 1) info('1st order error, Phi(m0+dm)-Phi(m0) with slope: %s compared to 1' % (p1[0])) info(r'2nd order error, Phi(m0+dm)-Phi(m0) - <J(m0)^T \delta d, dm>with slope:' ' %s compared to 2' % (p2[0])) assert np.isclose(p1[0], 1.0, rtol=0.1) assert np.isclose(p2[0], 2.0, rtol=0.1)
def cli_run_jit_backdoor(problem, **kwargs): """`click` interface for the `run_jit_backdoor` mode in `benchmark.py`.""" # Preset shared parameters kwargs['space_order'] = [12] kwargs['time_order'] = [2] kwargs['nbl'] = 10 kwargs['spacing'] = (20.0, 20.0, 20.0) # Preset problem-specific parameters if problem == 'tti': kwargs['shape'] = (350, 350, 350) kwargs['tn'] = 50 # End time of the simulation in ms kwargs['dse'] = 'aggressive' # Reference norms for the output fields reference = {'rec': 66.417102, 'u': 30.707737, 'v': 30.707728} elif problem == 'acoustic': kwargs['shape'] = (492, 492, 492) kwargs['tn'] = 100 # End time of the simulation in ms kwargs['dse'] = 'advanced' # Reference norms for the output fields reference = {'rec': 184.526400, 'u': 151.545837} else: assert False # Dummy values as they will be unused kwargs['block_shape'] = [] kwargs['autotune'] = 'off' retval = run_jit_backdoor(problem, **kwargs) if retval is not None: for i in retval: if isinstance(i, DiscreteFunction): v = norm(i) info( "norm(%s) = %f (expected = %f, delta = %f)" % (i.name, v, reference[i.name], abs(v - reference[i.name]))) # Record DEVITO_ environment env = [(k, v) for k, v in os.environ.items() if k.startswith('DEVITO_')] content = "{%s}" % ", ".join("'%s': '%s'" % (k, v) for k, v in env) with open('env.py', 'w') as f: f.write(content)
def save_rec(rec, src_coords, path, nt_new): comm = rec.grid.distributor.comm rank = comm.Get_rank() if rec.data.size != 0: rec_save, coords = resample_shot(rec, nt_new) info("From rank %s, shot record of size %s, number of rec locations %s" % (rank, rec_save.shape, coords.shape)) info("From rank %s, writing %s in rec, maximum value is %s" % (rank, rec_save.shape, np.max(rec_save))) segy_write(rec_save, [src_coords[0]], [src_coords[-1]], coords[:, 0], coords[:, -1], 2.0, path + "_rank_%s.segy" % (rank), sourceY=[src_coords[1]], groupY=coords[:, 1])
def test_derivative_skew_symmetry(self, dtype, so): """ We ensure that the first derivatives constructed with calls like f.dx(x0=x+0.5*x.spacing) Are skew (anti) symmetric. See the notebook ssa_01_iso_implementation.ipynb for more details. """ np.random.seed(0) n = 101 d = 1.0 shape = (n, ) origin = (0., ) extent = (d * (n - 1), ) # Initialize Devito grid and Functions for input(f1,g1) and output(f2,g2) grid1d = Grid(shape=shape, extent=extent, origin=origin, dtype=dtype) x = grid1d.dimensions[0] f1 = Function(name='f1', grid=grid1d, space_order=8) f2 = Function(name='f2', grid=grid1d, space_order=8) g1 = Function(name='g1', grid=grid1d, space_order=8) g2 = Function(name='g2', grid=grid1d, space_order=8) # Fill f1 and g1 with random values in [-1,+1] f1.data[:] = -1 + 2 * np.random.rand(n, ) g1.data[:] = -1 + 2 * np.random.rand(n, ) # Equation defining: [f2 = forward 1/2 cell shift derivative applied to f1] equation_f2 = Eq(f2, f1.dx(x0=x + 0.5 * x.spacing)) # Equation defining: [g2 = backward 1/2 cell shift derivative applied to g1] equation_g2 = Eq(g2, g1.dx(x0=x - 0.5 * x.spacing)) # Define an Operator to implement these equations and execute op = Operator([equation_f2, equation_g2]) op() # Compute the dot products and the relative error f1g2 = np.dot(f1.data, g2.data) g1f2 = np.dot(g1.data, f2.data) diff = (f1g2 + g1f2) / (f1g2 - g1f2) info( "skew symmetry (so=%d) -- f1g2, g1f2, diff; %+16.10e %+16.10e %+16.10e" % (so, f1g2, g1f2, diff)) assert np.isclose(diff, 0., atol=1.e-12)
def test_adjoint_J(self, shape, dtype, so): """ Test the Jacobian of the forward modeling operator by verifying for 'random' dm, dr: dr . J(dm) = J^T(dr) . dm """ np.random.seed(0) solver = acoustic_ssa_setup(shape=shape, dtype=dtype, space_order=so) src0 = solver.geometry.src m0 = Function(name='m0', grid=solver.model.grid, space_order=so) dm1 = Function(name='dm1', grid=solver.model.grid, space_order=so) m0.data[:] = 1.5 # Model perturbation, box of random values centered on middle of model dm1.data[:] = 0 size = 5 ns = 2 * size + 1 if len(shape) == 2: nx2, nz2 = shape[0] // 2, shape[1] // 2 dm1.data[nx2-size:nx2+size, nz2-size:nz2+size] = \ -1 + 2 * np.random.rand(ns, ns) else: nx2, ny2, nz2 = shape[0] // 2, shape[1] // 2, shape[2] // 2 nx, ny, nz = shape dm1.data[nx2-size:nx2+size, ny2-size:ny2+size, nz2-size:nz2+size] = \ -1 + 2 * np.random.rand(ns, ns, ns) # Data perturbation rec1 = solver.geometry.rec nt, nr = rec1.data.shape rec1.data[:] = np.random.rand(nt, nr) # Linearized modeling rec2, u0, _, _ = solver.jacobian_forward(dm1, src0, vp=m0, save=True) dm2, _, _, _ = solver.jacobian_adjoint(rec1, u0, vp=m0) sum_m = np.dot(dm1.data.reshape(-1), dm2.data.reshape(-1)) sum_d = np.dot(rec1.data.reshape(-1), rec2.data.reshape(-1)) diff = (sum_m - sum_d) / (sum_m + sum_d) info( "adjoint J %s (so=%d) sum_m, sum_d, diff; %16.10e %+16.10e %+16.10e" % (shape, so, sum_m, sum_d, diff)) assert np.isclose(diff, 0., atol=1.e-11)
def test_linearity_forward_J(self, shape, dtype, so): """ Test the linearity of the forward Jacobian of the forward modeling operator by verifying a J(dm) = J(a dm) """ np.random.seed(0) solver = acoustic_sa_setup(shape=shape, dtype=dtype, space_order=so) src0 = solver.geometry.src m0 = Function(name='m0', grid=solver.model.grid, space_order=so) m1 = Function(name='m1', grid=solver.model.grid, space_order=so) m0.data[:] = 1.5 # Model perturbation, box of random values centered on middle of model m1.data[:] = 0 size = 5 ns = 2 * size + 1 if len(shape) == 2: nx2, nz2 = shape[0]//2, shape[1]//2 m1.data[nx2-size:nx2+size, nz2-size:nz2+size] = \ -1 + 2 * np.random.rand(ns, ns) else: nx2, ny2, nz2 = shape[0]//2, shape[1]//2, shape[2]//2 nx, ny, nz = shape m1.data[nx2-size:nx2+size, ny2-size:ny2+size, nz2-size:nz2+size] = \ -1 + 2 * np.random.rand(ns, ns, ns) a = np.random.rand() rec1, _, _, _ = solver.jacobian(m1, src0, vp=m0, save=True) rec1.data[:] = a * rec1.data[:] m1.data[:] = a * m1.data[:] rec2, _, _, _ = solver.jacobian(m1, src0, vp=m0) # Normalize by rms of rec2, to enable using abolute tolerance below rms2 = np.sqrt(np.mean(rec2.data**2)) diff = (rec1.data - rec2.data) / rms2 info("linearity forward J %s (so=%d) rms 1,2,diff; " "%+16.10e %+16.10e %+16.10e" % (shape, so, np.sqrt(np.mean(rec1.data**2)), np.sqrt(np.mean(rec2.data**2)), np.sqrt(np.mean(diff**2)))) tol = 1.e-12 assert np.allclose(diff, 0.0, atol=tol)
def test_array_reduction(self, so, dim): """ Test generation of OpenMP reduction clauses involving Function's. """ grid = Grid(shape=(3, 3, 3)) d = grid.dimensions[dim] f = Function(name='f', shape=(3, ), dimensions=(d, ), grid=grid, space_order=so) u = TimeFunction(name='u', grid=grid) op = Operator(Inc(f, u + 1), opt=('openmp', { 'par-collapse-ncores': 1 })) iterations = FindNodes(Iteration).visit(op) parallelized = iterations[dim + 1] assert parallelized.pragmas if parallelized is iterations[-1]: # With the `f[z] += u[t0][x + 1][y + 1][z + 1] + 1` expr, the innermost # `z` Iteration gets parallelized, nothing is collapsed, hence no # reduction is required assert "reduction" not in parallelized.pragmas[0].value elif Ompizer._support_array_reduction(configuration['compiler']): assert "reduction(+:f[0:f_vec->size[0]])" in parallelized.pragmas[ 0].value else: # E.g. old GCC's assert "atomic update" in str(iterations[-1]) try: op(time_M=1) except: # Older gcc <6.1 don't support reductions on array info("Un-supported older gcc version for array reduction") assert True return assert np.allclose(f.data, 18)
def test_adjoint_F(self, shape, dtype, so): """ Test the forward modeling operator by verifying for random s, r: r . F(s) = F^T(r) . s """ solver = acoustic_sa_setup(shape=shape, dtype=dtype, space_order=so) src1 = solver.geometry.src rec1 = solver.geometry.rec rec2, _, _ = solver.forward(src1) # flip sign of receiver data for adjoint to make it interesting rec1.data[:] = rec2.data[:] src2, _, _ = solver.adjoint(rec1) sum_s = np.dot(src1.data.reshape(-1), src2.data.reshape(-1)) sum_r = np.dot(rec1.data.reshape(-1), rec2.data.reshape(-1)) diff = (sum_s - sum_r) / (sum_s + sum_r) info("adjoint F %s (so=%d) sum_s, sum_r, diff; %+16.10e %+16.10e %+16.10e" % (shape, so, sum_s, sum_r, diff)) assert np.isclose(diff, 0., atol=1.e-12)
def run_jit_backdoor(problem, **kwargs): """ A single run using the DEVITO_JIT_BACKDOOR to test kernel customization. """ configuration['develop-mode'] = False setup = model_type[problem]['setup'] time_order = kwargs.pop('time_order')[0] space_order = kwargs.pop('space_order')[0] autotune = kwargs.pop('autotune') info("Preparing simulation...") solver = setup(space_order=space_order, time_order=time_order, **kwargs) # Generate code (but do not JIT yet) op = solver.op_fwd() # Get the filename in the JIT cache cfile = "%s.c" % str(op._compiler.get_jit_dir().joinpath(op._soname)) if not os.path.exists(cfile): # First time we run this problem, let's generate and jit-compile code op.cfunction info("You may now edit the generated code in `%s`. " "Then save the file, and re-run this benchmark." % cfile) return info("Running wave propagation Operator...") @switchconfig(jit_backdoor=True) def _run_jit_backdoor(): return solver.forward(autotune=autotune) return _run_jit_backdoor()
def test_linearity_forward_F(self, shape, dtype, so): """ Test the linearity of the forward modeling operator by verifying: a F(s) = F(a s) """ solver = acoustic_sa_setup(shape=shape, dtype=dtype, space_order=so) src = solver.geometry.src a = -1 + 2 * np.random.rand() rec1, _, _ = solver.forward(src) src.data[:] *= a rec2, _, _ = solver.forward(src) rec1.data[:] *= a # Check receiver wavefeild linearity # Normalize by rms of rec2, to enable using abolute tolerance below rms2 = np.sqrt(np.mean(rec2.data**2)) diff = (rec1.data - rec2.data) / rms2 info("linearity forward F %s (so=%d) rms 1,2,diff; " "%+16.10e %+16.10e %+16.10e" % (shape, so, np.sqrt(np.mean(rec1.data**2)), np.sqrt(np.mean(rec2.data**2)), np.sqrt(np.mean(diff**2)))) tol = 1.e-12 assert np.allclose(diff, 0.0, atol=tol)
def run(shape=(50, 50, 50), spacing=(20.0, 20.0, 20.0), tn=250.0, autotune=False, space_order=4, nbl=10, preset='layers-tti', kernel='centered', full_run=False, checkpointing=False, **kwargs): solver = tti_setup(shape=shape, spacing=spacing, tn=tn, space_order=space_order, nbl=nbl, kernel=kernel, preset=preset, **kwargs) info("Applying Forward") # Whether or not we save the whole time history. We only need the full wavefield # with 'save=True' if we compute the gradient without checkpointing, if we use # checkpointing, PyRevolve will take care of the time history save = full_run and not checkpointing # Define receiver geometry (spread across `x, y`` just below surface) rec, u, v, summary = solver.forward(save=save, autotune=autotune) if preset == 'constant': # With a new m as Constant v0 = Constant(name="v", value=2.0, dtype=np.float32) solver.forward(save=save, vp=v0) # With a new vp as a scalar value solver.forward(save=save, vp=2.0) if not full_run: return summary.gflopss, summary.oi, summary.timings, [rec, u, v] # Smooth velocity initial_vp = Function(name='v0', grid=solver.model.grid, space_order=space_order) smooth(initial_vp, solver.model.vp) dm = np.float32(initial_vp.data**(-2) - solver.model.vp.data**(-2)) info("Applying Adjoint") solver.adjoint(rec, autotune=autotune) info("Applying Born") solver.jacobian(dm, autotune=autotune) info("Applying Gradient") solver.jacobian_adjoint(rec, u, v, autotune=autotune, checkpointing=checkpointing) return summary.gflopss, summary.oi, summary.timings, [rec, u, v]
def chain_contractions(A, B, C, D, E, F, optimize): """``AB + AC = D, DE = F``.""" op = Operator([Eq(D, A*B + A*C), Eq(F, D*E)], dle=optimize) op.apply() info('Executed `AB + AC = D, DE = F`')
def mat_mat_sum(A, B, C, D, optimize): """``AB + AC = D``.""" op = Operator(Eq(D, A*B + A*C), dle=optimize) op.apply() info('Executed `AB + AC = D`')
def mat_mat(A, B, C, optimize): """``AB = C``.""" op = Operator(Eq(C, A*B), dle=optimize) op.apply() info('Executed `AB = C`')
def transpose_mat_vec(A, x, b, optimize): """``A -> A^T, A^Tx = b``.""" i, j = A.indices op = Operator([Eq(b, A.indexed[j, i]*x)], dle=optimize) op.apply() info('Executed `A^Tx = b`')
def mat_vec(A, x, b, optimize): """``Ax = b``.""" op = Operator(Eq(b, A*x), dle=optimize) op.apply() info('Executed `Ax = b`')
def test_gradientFWI(self, shape, kernel, space_order, checkpointing): """ This test ensures that the FWI gradient computed with devito satisfies the Taylor expansion property: .. math:: \Phi(m0 + h dm) = \Phi(m0) + \O(h) \\ \Phi(m0 + h dm) = \Phi(m0) + h \nabla \Phi(m0) + \O(h^2) \\ \Phi(m0) = .5* || F(m0 + h dm) - D ||_2^2 where .. math:: \nabla \Phi(m0) = <J^T \delta d, dm> \\ \delta d = F(m0+ h dm) - D \\ with F the Forward modelling operator. :param dimensions: size of the domain in all dimensions in number of grid points :param time_order: order of the time discretization scheme :param space_order: order of the spacial discretization scheme :return: assertion that the Taylor properties are satisfied """ spacing = tuple(10. for _ in shape) wave = setup(shape=shape, spacing=spacing, dtype=np.float64, kernel=kernel, space_order=space_order, nbpml=40) m0 = Function(name='m0', grid=wave.model.m.grid, space_order=space_order) m0.data[:] = smooth10(wave.model.m.data, wave.model.m.shape_domain) dm = np.float32(wave.model.m.data - m0.data) # Compute receiver data for the true velocity rec, u, _ = wave.forward() # Compute receiver data and full wavefield for the smooth velocity rec0, u0, _ = wave.forward(m=m0, save=True) # Objective function value F0 = .5 * linalg.norm(rec0.data - rec.data)**2 # Gradient: <J^T \delta d, dm> residual = Receiver(name='rec', grid=wave.model.grid, data=rec0.data - rec.data, time_range=rec.time_range, coordinates=rec0.coordinates.data) gradient, _ = wave.gradient(residual, u0, m=m0, checkpointing=checkpointing) G = np.dot(gradient.data.reshape(-1), dm.reshape(-1)) # FWI Gradient test H = [0.5, 0.25, .125, 0.0625, 0.0312, 0.015625, 0.0078125] error1 = np.zeros(7) error2 = np.zeros(7) for i in range(0, 7): # Add the perturbation to the model def initializer(data): data[:] = m0.data + H[i] * dm mloc = Function(name='mloc', grid=wave.model.m.grid, space_order=space_order, initializer=initializer) # Data for the new model d = wave.forward(m=mloc)[0] # First order error Phi(m0+dm) - Phi(m0) error1[i] = np.absolute(.5 * linalg.norm(d.data - rec.data)**2 - F0) # Second order term r Phi(m0+dm) - Phi(m0) - <J(m0)^T \delta d, dm> error2[i] = np.absolute(.5 * linalg.norm(d.data - rec.data)**2 - F0 - H[i] * G) # Test slope of the tests p1 = np.polyfit(np.log10(H), np.log10(error1), 1) p2 = np.polyfit(np.log10(H), np.log10(error2), 1) info('1st order error, Phi(m0+dm)-Phi(m0): %s' % (p1)) info('2nd order error, Phi(m0+dm)-Phi(m0) - <J(m0)^T \delta d, dm>: %s' % (p2)) assert np.isclose(p1[0], 1.0, rtol=0.1) assert np.isclose(p2[0], 2.0, rtol=0.1)
def test_gradientFWI(self, shape, kernel, space_order, checkpointing): r""" This test ensures that the FWI gradient computed with devito satisfies the Taylor expansion property: .. math:: \Phi(m0 + h dm) = \Phi(m0) + \O(h) \\ \Phi(m0 + h dm) = \Phi(m0) + h \nabla \Phi(m0) + \O(h^2) \\ \Phi(m0) = .5* || F(m0 + h dm) - D ||_2^2 where .. math:: \nabla \Phi(m0) = <J^T \delta d, dm> \\ \delta d = F(m0+ h dm) - D \\ with F the Forward modelling operator. """ spacing = tuple(10. for _ in shape) wave = setup(shape=shape, spacing=spacing, dtype=np.float64, kernel=kernel, space_order=space_order, nbpml=40) m0 = Function(name='m0', grid=wave.model.grid, space_order=space_order) smooth(m0, wave.model.m) dm = np.float32(wave.model.m.data[:] - m0.data[:]) # Compute receiver data for the true velocity rec, u, _ = wave.forward() # Compute receiver data and full wavefield for the smooth velocity rec0, u0, _ = wave.forward(m=m0, save=True) # Objective function value F0 = .5*linalg.norm(rec0.data - rec.data)**2 # Gradient: <J^T \delta d, dm> residual = Receiver(name='rec', grid=wave.model.grid, data=rec0.data - rec.data, time_range=wave.geometry.time_axis, coordinates=wave.geometry.rec_positions) gradient, _ = wave.gradient(residual, u0, m=m0, checkpointing=checkpointing) G = np.dot(gradient.data.reshape(-1), dm.reshape(-1)) # FWI Gradient test H = [0.5, 0.25, .125, 0.0625, 0.0312, 0.015625, 0.0078125] error1 = np.zeros(7) error2 = np.zeros(7) for i in range(0, 7): # Add the perturbation to the model def initializer(data): data[:] = m0.data + H[i] * dm mloc = Function(name='mloc', grid=wave.model.m.grid, space_order=space_order, initializer=initializer) # Data for the new model d = wave.forward(m=mloc)[0] # First order error Phi(m0+dm) - Phi(m0) F_i = .5*linalg.norm((d.data - rec.data).reshape(-1))**2 error1[i] = np.absolute(F_i - F0) # Second order term r Phi(m0+dm) - Phi(m0) - <J(m0)^T \delta d, dm> error2[i] = np.absolute(F_i - F0 - H[i] * G) # Test slope of the tests p1 = np.polyfit(np.log10(H), np.log10(error1), 1) p2 = np.polyfit(np.log10(H), np.log10(error2), 1) info('1st order error, Phi(m0+dm)-Phi(m0): %s' % (p1)) info(r'2nd order error, Phi(m0+dm)-Phi(m0) - <J(m0)^T \delta d, dm>: %s' % (p2)) assert np.isclose(p1[0], 1.0, rtol=0.1) assert np.isclose(p2[0], 2.0, rtol=0.1)
def run(problem, **kwargs): """ A single run with a specific set of performance parameters. """ setup = model_type[problem]['setup'] options = {} time_order = kwargs.pop('time_order')[0] space_order = kwargs.pop('space_order')[0] autotune = kwargs.pop('autotune') options['autotune'] = autotune block_shapes = as_tuple(kwargs.pop('block_shape')) operator = kwargs.pop('operator', 'forward') warmup = kwargs.pop('warmup') # Should a specific block-shape be used? Useful if one wants to skip # the autotuning pass as a good block-shape is already known # Note: the following piece of code is horribly *hacky*, but it works for now for i, block_shape in enumerate(block_shapes): for n, level in enumerate(block_shape): for d, s in zip(['x', 'y', 'z'], level): options['%s%d_blk%d_size' % (d, i, n)] = s solver = setup(space_order=space_order, time_order=time_order, **kwargs) if warmup: info("Performing warm-up run ...") set_log_level('ERROR', comm=MPI.COMM_WORLD) run_op(solver, operator, **options) set_log_level('DEBUG', comm=MPI.COMM_WORLD) info("DONE!") retval = run_op(solver, operator, **options) try: rank = MPI.COMM_WORLD.rank except AttributeError: # MPI not available rank = 0 dumpfile = kwargs.pop('dump_summary') if dumpfile: if configuration['profiling'] != 'advanced': raise RuntimeError( "Must set DEVITO_PROFILING=advanced (or, alternatively, " "DEVITO_LOGGING=PERF) with --dump-summary") if rank == 0: with open(dumpfile, 'w') as f: summary = retval[-1] assert isinstance(summary, PerformanceSummary) f.write(str(summary.globals_all)) dumpfile = kwargs.pop('dump_norms') if dumpfile: norms = [ "'%s': %f" % (i.name, norm(i)) for i in retval[:-1] if isinstance(i, DiscreteFunction) ] if rank == 0: with open(dumpfile, 'w') as f: f.write("{%s}" % ', '.join(norms)) return retval
def test_linearization_F(self, shape, dtype, so): """ Test the linearization of the forward modeling operator by verifying for sequence of h decreasing that the error in the linearization E is of second order. E = 0.5 || F(m + h dm) - F(m) - h J(dm) ||^2 This is done by fitting a 1st order polynomial to the norms """ np.random.seed(0) solver = acoustic_sa_setup(shape=shape, dtype=dtype, space_order=so) src = solver.geometry.src # Create Functions for models and perturbation m0 = Function(name='m0', grid=solver.model.grid, space_order=so) mm = Function(name='mm', grid=solver.model.grid, space_order=so) dm = Function(name='dm', grid=solver.model.grid, space_order=so) # Background model m0.data[:] = 1.5 # Model perturbation, box of random values centered on middle of model dm.data[:] = 0 size = 5 ns = 2 * size + 1 if len(shape) == 2: nx2, nz2 = shape[0]//2, shape[1]//2 dm.data[nx2-size:nx2+size, nz2-size:nz2+size] = \ -1 + 2 * np.random.rand(ns, ns) else: nx2, ny2, nz2 = shape[0]//2, shape[1]//2, shape[2]//2 nx, ny, nz = shape dm.data[nx2-size:nx2+size, ny2-size:ny2+size, nz2-size:nz2+size] = \ -1 + 2 * np.random.rand(ns, ns, ns) # Compute F(m + dm) rec0, u0, summary0 = solver.forward(src, vp=m0) # Compute J(dm) rec1, u1, du, summary1 = solver.jacobian(dm, src=src, vp=m0) # Linearization test via polyfit (see devito/tests/test_gradient.py) # Solve F(m + h dm) for sequence of decreasing h dh = np.sqrt(2.0) h = 0.1 nstep = 7 scale = np.empty(nstep) norm1 = np.empty(nstep) norm2 = np.empty(nstep) for kstep in range(nstep): h = h / dh mm.data[:] = m0.data + h * dm.data rec2, _, _ = solver.forward(src, vp=mm) scale[kstep] = h norm1[kstep] = 0.5 * np.linalg.norm(rec2.data - rec0.data)**2 norm2[kstep] = 0.5 * np.linalg.norm(rec2.data - rec0.data - h * rec1.data)**2 # Fit 1st order polynomials to the error sequences # Assert the 1st order error has slope dh^2 # Assert the 2nd order error has slope dh^4 p1 = np.polyfit(np.log10(scale), np.log10(norm1), 1) p2 = np.polyfit(np.log10(scale), np.log10(norm2), 1) info("linearization F %s (so=%d) 1st (%.1f) = %.4f, 2nd (%.1f) = %.4f" % (shape, so, dh**2, p1[0], dh**4, p2[0])) # we only really care the 2nd order err is valid, not so much the 1st order error assert np.isclose(p1[0], dh**2, rtol=0.25) assert np.isclose(p2[0], dh**4, rtol=0.10)
def test_gradientFWI(self): r""" This test ensures that the FWI gradient computed with devito satisfies the Taylor expansion property: .. math:: \Phi(m0 + h dm) = \Phi(m0) + \O(h) \\ \Phi(m0 + h dm) = \Phi(m0) + h \nabla \Phi(m0) + \O(h^2) \\ \Phi(m0) = .5* || F(m0 + h dm) - D ||_2^2 where .. math:: \nabla \Phi(m0) = <J^T \delta d, dm> \\ \delta d = F(m0+ h dm) - D \\ with F the Forward modelling operator. """ initial_model_filename = "overthrust_3D_initial_model_2D.h5" true_model_filename = "overthrust_3D_true_model_2D.h5" tn = 4000 dtype = np.float32 so = 6 nbl = 40 shots_container = "shots-iso" shot_id = 10 ########## model0 = overthrust_model_iso(initial_model_filename, datakey="m0", dtype=dtype, space_order=so, nbl=nbl) model_t = overthrust_model_iso(true_model_filename, datakey="m", dtype=dtype, space_order=so, nbl=nbl) _, geometry, _ = initial_setup(initial_model_filename, tn, dtype, so, nbl, datakey="m0") # rec, source_location, old_dt = load_shot(shot_id, # container=shots_container) source_location = geometry.src_positions solver_params = { 'h5_file': initial_model_filename, 'tn': tn, 'space_order': so, 'dtype': dtype, 'datakey': 'm0', 'nbl': nbl, 'origin': model0.origin, 'spacing': model0.spacing, 'shots_container': shots_container, 'src_coordinates': source_location } solver = overthrust_solver_iso(**solver_params) true_solver_params = solver_params.copy() true_solver_params['h5_file'] = true_model_filename true_solver_params['datakey'] = "m" solver_true = overthrust_solver_iso(**true_solver_params) rec, _, _ = solver_true.forward() v0 = mat2vec(clip_boundary_and_numpy(model0.vp.data, model0.nbl)) v_t = mat2vec(clip_boundary_and_numpy(model_t.vp.data, model_t.nbl)) dm = np.float64(v_t**(-2) - v0**(-2)) print("dm", np.linalg.norm(dm), dm.shape) F0, gradient = fwi_gradient_shot(vec2mat(v0, model0.shape), shot_id, solver_params, source_location) G = np.dot(gradient.reshape(-1), dm.reshape(-1)) # FWI Gradient test H = [0.5, 0.25, .125, 0.0625, 0.0312, 0.015625, 0.0078125] error1 = np.zeros(7) error2 = np.zeros(7) for i in range(0, 7): # Add the perturbation to the model vloc = np.sqrt(v0**2 * v_t**2 / ((1 - H[i]) * v_t**2 + H[i] * v0**2)) m = Model(vp=vloc, nbl=nbl, space_order=so, dtype=dtype, shape=model0.shape, origin=model0.origin, spacing=model0.spacing, bcs="damp") # Data for the new model d = solver.forward(vp=m.vp)[0] # First order error Phi(m0+dm) - Phi(m0) F_i = .5 * linalg.norm((d.data - rec.data).reshape(-1))**2 error1[i] = np.absolute(F_i - F0) # Second order term r Phi(m0+dm) - Phi(m0) - <J(m0)^T \delta d, dm> error2[i] = np.absolute(F_i - F0 - H[i] * G) print(i, F0, F_i, H[i] * G) # Test slope of the tests p1 = np.polyfit(np.log10(H), np.log10(error1), 1) p2 = np.polyfit(np.log10(H), np.log10(error2), 1) info('1st order error, Phi(m0+dm)-Phi(m0): %s' % (p1)) info( r'2nd order error, Phi(m0+dm)-Phi(m0) - <J(m0)^T \delta d, dm>: %s' % (p2)) print("Error 1:") print(error1) print("***") print("Error 2:") print(error2) assert np.isclose(p1[0], 1.0, rtol=0.1) assert np.isclose(p2[0], 2.0, rtol=0.1)
def mat_vec(A, x, b, optimize): """``Ax = b``.""" op = Operator(Inc(b, A*x), dle=optimize) op.apply() info('Executed `Ax = b`')
def transpose_mat_vec(A, x, b, optimize): """``A -> A^T, A^Tx = b``.""" i, j = A.indices op = Operator([Inc(b, A[j, i]*x)], dle=optimize) op.apply() info('Executed `A^Tx = b`')
def mat_mat(A, B, C, optimize): """``AB = C``.""" op = Operator(Inc(C, A*B), dle=optimize) op.apply() info('Executed `AB = C`')
def mat_mat_sum(A, B, C, D, optimize): """``AB + AC = D``.""" op = Operator(Inc(D, A*B + A*C), dle=optimize) op.apply() info('Executed `AB + AC = D`')
def chain_contractions(A, B, C, D, E, F, optimize): """``AB + AC = D, DE = F``.""" op = Operator([Inc(D, A*B + A*C), Inc(F, D*E)], dle=optimize) op.apply() info('Executed `AB + AC = D, DE = F`')
def test_gradientFWI(self, dtype, space_order, kernel, shape, ckp, setup_func, time_order): """ This test ensures that the FWI gradient computed with devito satisfies the Taylor expansion property: .. math:: \Phi(m0 + h dm) = \Phi(m0) + \O(h) \\ \Phi(m0 + h dm) = \Phi(m0) + h \nabla \Phi(m0) + \O(h^2) \\ \Phi(m0) = .5* || F(m0 + h dm) - D ||_2^2 where .. math:: \nabla \Phi(m0) = <J^T \delta d, dm> \\ \delta d = F(m0+ h dm) - D \\ with F the Forward modelling operator. """ spacing = tuple(10. for _ in shape) wave = setup_func(shape=shape, spacing=spacing, dtype=dtype, kernel=kernel, tn=400.0, space_order=space_order, nbl=40, time_order=time_order) vel0 = Function(name='vel0', grid=wave.model.grid, space_order=space_order) smooth(vel0, wave.model.vp) v = wave.model.vp.data dm = dtype(wave.model.vp.data**(-2) - vel0.data**(-2)) # Compute receiver data for the true velocity rec = wave.forward()[0] # Compute receiver data and full wavefield for the smooth velocity if setup_func is tti_setup: rec0, u0, v0, _ = wave.forward(vp=vel0, save=True) else: rec0, u0 = wave.forward(vp=vel0, save=True)[0:2] # Objective function value F0 = .5 * linalg.norm(rec0.data - rec.data)**2 # Gradient: <J^T \delta d, dm> residual = Receiver(name='rec', grid=wave.model.grid, data=rec0.data - rec.data, time_range=wave.geometry.time_axis, coordinates=wave.geometry.rec_positions) if setup_func is tti_setup: gradient, _ = wave.jacobian_adjoint(residual, u0, v0, vp=vel0, checkpointing=ckp) else: gradient, _ = wave.jacobian_adjoint(residual, u0, vp=vel0, checkpointing=ckp) G = np.dot(gradient.data.reshape(-1), dm.reshape(-1)) # FWI Gradient test H = [0.5, 0.25, .125, 0.0625, 0.0312, 0.015625, 0.0078125] error1 = np.zeros(7) error2 = np.zeros(7) for i in range(0, 7): # Add the perturbation to the model def initializer(data): data[:] = np.sqrt(vel0.data**2 * v**2 / ((1 - H[i]) * v**2 + H[i] * vel0.data**2)) vloc = Function(name='vloc', grid=wave.model.grid, space_order=space_order, initializer=initializer) # Data for the new model d = wave.forward(vp=vloc)[0] # First order error Phi(m0+dm) - Phi(m0) F_i = .5 * linalg.norm((d.data - rec.data).reshape(-1))**2 error1[i] = np.absolute(F_i - F0) # Second order term r Phi(m0+dm) - Phi(m0) - <J(m0)^T \delta d, dm> error2[i] = np.absolute(F_i - F0 - H[i] * G) # Test slope of the tests p1 = np.polyfit(np.log10(H), np.log10(error1), 1) p2 = np.polyfit(np.log10(H), np.log10(error2), 1) info('1st order error, Phi(m0+dm)-Phi(m0): %s' % (p1)) info( r'2nd order error, Phi(m0+dm)-Phi(m0) - <J(m0)^T \delta d, dm>: %s' % (p2)) assert np.isclose(p1[0], 1.0, rtol=0.1) assert np.isclose(p2[0], 2.0, rtol=0.1)
def test_analytic_comparison_2d(self, dtype, so): """ Wnsure that the farfield response from the propagator matches analytic reponse in a wholespace. """ # Setup time / frequency nt = 1001 dt = 0.1 tmin = 0.0 tmax = dt * (nt - 1) fpeak = 0.090 t0w = 1.0 / fpeak omega = 2.0 * np.pi * fpeak # Model space_order = 8 npad = 50 dx = 0.5 shape = (801, 801) dtype = np.float64 qmin = 0.1 qmax = 100000 v0 = 1.5 init_damp = lambda fu, nbl: setup_w_over_q(fu, omega, qmin, qmax, nbl, sigma=0) o = tuple([0]*len(shape)) spacing = tuple([dx]*len(shape)) model = Model(origin=o, shape=shape, vp=v0, b=1.0, spacing=spacing, nbl=npad, space_order=space_order, bcs=init_damp) # Source and reciver coordinates src_coords = np.empty((1, 2), dtype=dtype) rec_coords = np.empty((1, 2), dtype=dtype) src_coords[0, :] = np.array(model.domain_size) * .5 rec_coords[0, :] = np.array(model.domain_size) * .5 + 60 geometry = AcquisitionGeometry(model, rec_coords, src_coords, t0=0.0, tn=tmax, src_type='Ricker', f0=fpeak) # Solver setup solver = SaIsoAcousticWaveSolver(model, geometry, space_order=space_order) # Numerical solution recNum, uNum, _ = solver.forward() # Analytic response def analytic_response(): """ Computes analytic solution of 2D acoustic wave-equation with Ricker wavelet peak frequency fpeak, temporal padding 20x per the accuracy notebook: examples/seismic/acoustic/accuracy.ipynb u(r,t) = 1/(2 pi) sum[ -i pi H_0^2(k,r) q(w) e^{i w t} dw where: r = sqrt{(x_s - x_r)^2 + (z_s - z_r)^2} w = 2 pi f q(w) = Fourier transform of Ricker source wavelet H_0^2(k,r) Hankel function of the second kind k = w/v (wavenumber) """ sx, sz = src_coords[0, :] rx, rz = rec_coords[0, :] ntpad = 20 * (nt - 1) + 1 tmaxpad = dt * (ntpad - 1) time_axis_pad = TimeAxis(start=tmin, stop=tmaxpad, step=dt) srcpad = RickerSource(name='srcpad', grid=model.grid, f0=fpeak, npoint=1, time_range=time_axis_pad, t0w=t0w) nf = int(ntpad / 2 + 1) df = 1.0 / tmaxpad faxis = df * np.arange(nf) # Take the Fourier transform of the source time-function R = np.fft.fft(srcpad.wavelet[:]) R = R[0:nf] nf = len(R) # Compute the Hankel function and multiply by the source spectrum U_a = np.zeros((nf), dtype=complex) for a in range(1, nf - 1): w = 2 * np.pi * faxis[a] r = np.sqrt((rx - sx)**2 + (rz - sz)**2) U_a[a] = -1j * np.pi * hankel2(0.0, w * r / v0) * R[a] # Do inverse fft on 0:dt:T and you have analytical solution U_t = 1.0/(2.0 * np.pi) * np.real(np.fft.ifft(U_a[:], ntpad)) # Note that the analytic solution is scaled by dx^2 to convert to pressure return (np.real(U_t) * (dx**2)), srcpad uAnaPad, srcpad = analytic_response() uAna = uAnaPad[0:nt] # Compute RMS and difference diff = (recNum.data - uAna) nrms = np.max(np.abs(recNum.data)) arms = np.max(np.abs(uAna)) drms = np.max(np.abs(diff)) info("Maximum absolute numerical,analytic,diff; %+12.6e %+12.6e %+12.6e" % (nrms, arms, drms)) # This isnt a very strict tolerance ... tol = 0.1 assert np.allclose(diff, 0.0, atol=tol)