def _indices(cls, **kwargs): """Return the default dimension indices for a given data shape :param dimensions: Optional, list of :class:`Dimension` objects that defines data layout. :param shape: Optional, shape of the spatial data to automatically infer dimension symbols. :return: Dimension indices used for each axis. """ dimensions = kwargs.get('dimensions', None) if dimensions is None: # Infer dimensions from default and data shape if 'shape' not in kwargs: error("Creating symbolic data objects requries either" "a 'shape' or 'dimensions' argument") raise ValueError("Unknown symbol dimensions or shape") _indices = (x, y, z) shape = kwargs.get('shape') if len(shape) <= 3: dimensions = _indices[:len(shape)] else: dimensions = [ Dimension("x%d" % i) for i in range(1, len(shape) + 1) ] return dimensions
def _indices(cls, **kwargs): """Return the default dimension indices for a given data shape :param grid: :class:`Grid` object from which to infer the data shape and :class:`Dimension` indices. :return: Dimension indices used for each axis. """ save = kwargs.get('save', None) grid = kwargs.get('grid', None) time_dim = kwargs.get('time_dim', None) if grid is None: error('TimeFunction objects require a grid parameter.') raise ValueError('No grid provided for TimeFunction.') if time_dim is None: time_dim = grid.time_dim if save else grid.stepping_dim elif not isinstance(time_dim, TimeDimension): raise ValueError("time_dim must be a TimeDimension, not %s" % type(time_dim)) assert (isinstance(time_dim, Dimension) and time_dim.is_Time) _indices = Function._indices(**kwargs) return tuple([time_dim] + list(_indices))
def malloc_aligned(shape, alignment=None, dtype=np.float32): """ Allocate memory using the C function malloc_aligned :param shape: Shape of the array to allocate :param alignment: number of bytes to align to. Defaults to page size if not set. :param dtype: Numpy datatype to allocate. Default to np.float32 :returns (pointer, data_pointer) the first element of the tuple is the reference that can be used to access the data as a ctypes object. The second element is the low-level reference that is needed only for the call to free. """ libc = ctypes.CDLL(find_library('c')) data_pointer = ctypes.cast(ctypes.c_void_p(), ctypes.POINTER(ctypes.c_float)) arraysize = int(reduce(mul, shape)) ctype = convert_dtype_to_ctype(dtype) if alignment is None: alignment = libc.getpagesize() ret = libc.posix_memalign(ctypes.byref(data_pointer), alignment, ctypes.c_ulong(arraysize * ctypes.sizeof(ctype))) if not ret == 0: error("Unable to allocate memory for shape %s", str(shape)) return None data_pointer = ctypes.cast( data_pointer, np.ctypeslib.ndpointer(dtype=dtype, shape=shape)) pointer = np.ctypeslib.as_array(data_pointer, shape=shape) return (pointer, data_pointer)
def __new__(cls, name, grid, ntime=None, npoint=None, data=None, coordinates=None, **kwargs): p_dim = kwargs.get('dimension', Dimension(name='p_%s' % name)) npoint = npoint or coordinates.shape[0] if data is None: if ntime is None: error('Either data or ntime are required to' 'initialise source/receiver objects') else: ntime = ntime or data.shape[0] # Create the underlying SparseTimeFunction object obj = SparseTimeFunction.__new__(cls, name=name, grid=grid, dimensions=[grid.time_dim, p_dim], npoint=npoint, nt=ntime, coordinates=coordinates, **kwargs) # If provided, copy initial data into the allocated buffer if data is not None: obj.data[:] = data return obj
def __init__(self, *args, **kwargs): if not self._cached(): self.name = kwargs.get('name') self.grid = kwargs.get('grid', None) if self.grid is None: self.shape_domain = kwargs.get('shape', None) self.dtype = kwargs.get('dtype', np.float32) if self.shape_domain is None: error("Creating a Function requires either 'shape'" "or a 'grid' argument") raise ValueError("Unknown symbol dimensions or shape") else: self.shape_domain = self.grid.shape_domain self.dtype = kwargs.get('dtype', self.grid.dtype) self.indices = self._indices(**kwargs) self.staggered = kwargs.get('staggered', tuple(0 for _ in self.indices)) if len(self.staggered) != len(self.indices): error("Staggering argument needs %s entries for indices %s" % (len(self.indices), self.indices)) raise ValueError("Insufficient staggered entries") self.space_order = kwargs.get('space_order', 1) self.initializer = kwargs.get('initializer', None) if self.initializer is not None: assert (callable(self.initializer)) self._first_touch = kwargs.get('first_touch', configuration['first_touch']) self._data_object = None # Dynamically add derivative short-cuts self._initialize_derivatives()
def _indices(cls, **kwargs): """Return the default dimension indices for a given data shape :param grid: :class:`Grid` that defines the spatial domain. :param dimensions: Optional, list of :class:`Dimension` objects that defines data layout. :return: Dimension indices used for each axis. ..note:: Only one of :param grid: or :param dimensions: is required. """ grid = kwargs.get('grid', None) dimensions = kwargs.get('dimensions', None) if grid is None: if dimensions is None: error("Creating a Function object requries either " "a 'grid' or the 'dimensions' argument.") raise ValueError("Unknown symbol dimensions or shape") else: if dimensions is not None: warning( "Creating Function with 'grid' and 'dimensions' " "argument; ignoring the 'dimensions' and using 'grid'.") dimensions = grid.dimensions return dimensions
def coefficients(self): """Symbolic expression for the coefficients for sparse point interpolation according to: https://en.wikipedia.org/wiki/Bilinear_interpolation. :returns: List of coefficients, eg. [b_11, b_12, b_21, b_22] """ # Grid indices corresponding to the corners of the cell x1, y1, z1, x2, y2, z2 = sympy.symbols('x1, y1, z1, x2, y2, z2') # Coordinate values of the sparse point px, py, pz = self.point_symbols if self.grid.dim == 2: A = sympy.Matrix([[1, x1, y1, x1*y1], [1, x1, y2, x1*y2], [1, x2, y1, x2*y1], [1, x2, y2, x2*y2]]) p = sympy.Matrix([[1], [px], [py], [px*py]]) # Map to reference cell x, y = self.grid.dimensions reference_cell = {x1: 0, y1: 0, x2: x.spacing, y2: y.spacing} elif self.grid.dim == 3: A = sympy.Matrix([[1, x1, y1, z1, x1*y1, x1*z1, y1*z1, x1*y1*z1], [1, x1, y2, z1, x1*y2, x1*z1, y2*z1, x1*y2*z1], [1, x2, y1, z1, x2*y1, x2*z1, y2*z1, x2*y1*z1], [1, x1, y1, z2, x1*y1, x1*z2, y1*z2, x1*y1*z2], [1, x2, y2, z1, x2*y2, x2*z1, y2*z1, x2*y2*z1], [1, x1, y2, z2, x1*y2, x1*z2, y2*z2, x1*y2*z2], [1, x2, y1, z2, x2*y1, x2*z2, y1*z2, x2*y1*z2], [1, x2, y2, z2, x2*y2, x2*z2, y2*z2, x2*y2*z2]]) p = sympy.Matrix([[1], [px], [py], [pz], [px*py], [px*pz], [py*pz], [px*py*pz]]) # Map to reference cell x, y, z = self.grid.dimensions reference_cell = {x1: 0, y1: 0, z1: 0, x2: x.spacing, y2: y.spacing, z2: z.spacing} else: error('Point interpolation only supported for 2D and 3D') raise NotImplementedError('Interpolation coefficients not ' 'implemented for %d dimensions.' % self.grid.dim) A = A.subs(reference_cell) return A.inv().T.dot(p)
def sniff_compiler_version(cc): """ Detect the compiler version. Adapted from: :: https://github.com/OP2/PyOP2/ """ try: res = run([cc, "--version"], stdout=PIPE, stderr=DEVNULL) ver = res.stdout.decode("utf-8") if not ver: return version.LooseVersion("unknown") except UnicodeDecodeError: return version.LooseVersion("unknown") except FileNotFoundError: error("The `%s` compiler isn't available on this system" % cc) sys.exit(1) if ver.startswith("gcc"): compiler = "gcc" elif ver.startswith("clang"): compiler = "clang" elif ver.startswith("Apple LLVM"): compiler = "clang" elif ver.startswith("icc"): compiler = "icc" elif ver.startswith("pgcc"): compiler = "pgcc" else: compiler = "unknown" ver = version.LooseVersion("unknown") if compiler in ["gcc", "icc"]: try: # gcc-7 series only spits out patch level on dumpfullversion. res = run([cc, "-dumpfullversion"], stdout=PIPE, stderr=DEVNULL) ver = res.stdout.decode("utf-8") ver = '.'.join(ver.strip().split('.')[:3]) if not ver: res = run([cc, "-dumpversion"], stdout=PIPE, stderr=DEVNULL) ver = res.stdout.decode("utf-8") ver = '.'.join(ver.strip().split('.')[:3]) if not ver: return version.LooseVersion("unknown") ver = version.StrictVersion(ver) except UnicodeDecodeError: pass # Pure integer versions (e.g., ggc5, rather than gcc5.0) need special handling try: ver = version.StrictVersion(float(ver)) except TypeError: pass return ver
def _write_block_report(self, times): """Writes auto tuning report for block sizes :param times: sorted list - times with block sizes :raises IOError: if fails to write report """ try: full_report_text = ["%s %f\n" % (str(block), timee) for block, timee in times] # Cache blocking dimensions cb_dims = str(self.blocked_dims).replace(" ", '') shape = str(self.op.shape).replace(" ", '') # Writes all auto tuning information into full report with open(path.join(self.report_dir, "%s_time_o_%s_spc_bo_%s_shp_%s_b_%s_report.txt" % (self.op.getName(), self.op.time_order, self.op.spc_border, shape, cb_dims)), 'w') as f: f.writelines(full_report_text) # string that describes the model model_desc_str = self.model_desc_template % (self.op.getName(), self.op.time_order, self.op.spc_border, shape, cb_dims) str_to_write = "%s %s\n" % (model_desc_str, str(times[0][0]).replace(" ", '')) # initialises report file if it does not exist if not path.isfile(self.final_report_path): with open(self.final_report_path, 'w') as final_report: final_report.write("f name, time o, space bo ," "shape, block dims, best block size\n") final_report.write(str_to_write) # writes the string return else: # reads all the contents, checks whether entry already exist and updates. # Otherwise appends to the end of the file with open(self.final_report_path, 'r') as final_report: lines = final_report.readlines() entry_found = False for i in range(1, len(lines)): if model_desc_str in lines[i]: lines[i] = str_to_write entry_found = True break if not entry_found: # if entry not found append string to the end of file lines.append(str_to_write) # writes all the contents with open(self.final_report_path, 'w') as final_report: final_report.writelines(lines) except IOError as e: error("Failed to write auto tuning report because %s" % e.message)
def GradientOperator(model, source, receiver, space_order=4, save=True, kernel='OT2', **kwargs): """ Constructor method for the gradient operator in an acoustic media :param model: :class:`Model` object containing the physical parameters :param source: :class:`PointData` object containing the source geometry :param receiver: :class:`PointData` object containing the acquisition geometry :param time_order: Time discretization order :param space_order: Space discretization order """ m, damp = model.m, model.damp # Gradient symbol and wavefield symbols grad = Function(name='grad', grid=model.grid) u = TimeFunction(name='u', grid=model.grid, save=source.nt if save else None, time_order=2, space_order=space_order) v = TimeFunction(name='v', grid=model.grid, save=None, time_order=2, space_order=space_order) rec = Receiver(name='rec', grid=model.grid, time_range=receiver.time_range, npoint=receiver.npoint) s = model.grid.stepping_dim.spacing eqn = iso_stencil(v, m, s, damp, kernel, forward=False) if kernel == 'OT2': gradient_update = Inc(grad, grad - u.dt2 * v) elif kernel == 'OT4': gradient_update = Inc( grad, grad - (u.dt2 + s**2 / 12.0 * u.laplace2(m**(-2))) * v) else: error("Unrecognized kernel, has to be OT2 or OT4") # Add expression for receiver injection receivers = rec.inject(field=v.backward, expr=rec * s**2 / m, offset=model.nbpml) # Substitute spacing terms to reduce flops return Operator(eqn + receivers + [gradient_update], subs=model.spacing_map, name='Gradient', **kwargs)
def point_increments(self): """Index increments in each dimension for each point symbol""" if self.grid.dim == 2: return ((0, 0), (0, 1), (1, 0), (1, 1)) elif self.grid.dim == 3: return ((0, 0, 0), (0, 1, 0), (1, 0, 0), (0, 0, 1), (1, 1, 0), (0, 1, 1), (1, 0, 1), (1, 1, 1)) else: error('Point interpolation only supported for 2D and 3D') raise NotImplementedError('Point increments not defined ' 'for %d dimensions.' % self.grid.dim)
def adjoint(self, matvec): if matvec == direct: return self else: if self == centered: return centered elif self == right: return left elif self == left: return right else: error("Unsupported side value")
def sniff_compiler_version(cc): """ Detect the compiler version. Adapted from: :: https://github.com/OP2/PyOP2/ """ try: ver = check_output([cc, "--version"]).decode("utf-8") except (CalledProcessError, UnicodeDecodeError): return version.LooseVersion("unknown") except FileNotFoundError: error("The `%s` compiler isn't available on this system" % cc) sys.exit(1) if ver.startswith("gcc"): compiler = "gcc" elif ver.startswith("clang"): compiler = "clang" elif ver.startswith("Apple LLVM"): compiler = "clang" elif ver.startswith("icc"): compiler = "icc" else: compiler = "unknown" ver = version.LooseVersion("unknown") if compiler in ["gcc", "icc"]: try: # gcc-7 series only spits out patch level on dumpfullversion. ver = check_output([cc, "-dumpfullversion"], stderr=DEVNULL).decode("utf-8") ver = '.'.join(ver.strip().split('.')[:3]) ver = version.StrictVersion(ver) except CalledProcessError: try: ver = check_output([cc, "-dumpversion"], stderr=DEVNULL).decode("utf-8") ver = '.'.join(ver.strip().split('.')[:3]) ver = version.StrictVersion(ver) except (CalledProcessError, UnicodeDecodeError): pass except UnicodeDecodeError: pass # Pure integer versions (e.g., ggc5, rather than gcc5.0) need special handling try: ver = version.StrictVersion(float(ver)) except TypeError: pass return ver
def __indices_setup__(cls, **kwargs): grid = kwargs.get('grid') dimensions = kwargs.get('dimensions') if grid is None: if dimensions is None: error("Creating a Function object requries either " "a 'grid' or the 'dimensions' argument.") raise ValueError("Unknown symbol dimensions or shape") else: if dimensions is not None: warning( "Creating Function with 'grid' and 'dimensions' " "argument; ignoring the 'dimensions' and using 'grid'.") dimensions = grid.dimensions return dimensions
def __indices_setup__(cls, **kwargs): save = kwargs.get('save') grid = kwargs.get('grid') time_dim = kwargs.get('time_dim') if grid is None: error('TimeFunction objects require a grid parameter.') raise ValueError('No grid provided for TimeFunction.') if time_dim is None: time_dim = grid.time_dim if save else grid.stepping_dim elif not (isinstance(time_dim, Dimension) and time_dim.is_Time): raise ValueError("'time_dim' must be a time dimension") return (time_dim, ) + Function.__indices_setup__(**kwargs)
def apply(self, *args, **kwargs): """Apply defined stencil kernel to a set of data objects""" if len(args) <= 0: args = self.signature args = [a.data if isinstance(a, SymbolicData) else a for a in args] # Check shape of argument data for arg, v in zip(args, self.signature): if not isinstance(arg, np.ndarray): raise TypeError('No array data found for argument %s' % v.name) if arg.shape != v.shape: error('Expected argument %s with shape %s, but got shape %s' % (v.name, v.shape, arg)) raise ValueError('Argument with wrong shape') # Invoke kernel function with args self.cfunction(*args)
def _indices(cls, **kwargs): """Return the default dimension indices for a given data shape :param grid: :class:`Grid` object from which to infer the data shape and :class:`Dimension` indices. :return: Dimension indices used for each axis. """ save = kwargs.get('save', None) grid = kwargs.get('grid', None) if grid is None: error('TimeFunction objects require a grid parameter.') raise ValueError('No grid provided for TimeFunction.') tidx = grid.time_dim if save else grid.stepping_dim _indices = Function._indices(**kwargs) return tuple([tidx] + list(_indices))
def _dle_arguments(self, dim_sizes): # Add user-provided block sizes, if any dle_arguments = OrderedDict() autotune = True for i in self.dle_arguments: dim_size = dim_sizes.get(i.original_dim.name, i.original_dim.size) if dim_size is None: error('Unable to derive size of dimension %s from defaults. ' 'Please provide an explicit value.' % i.original_dim.name) raise InvalidArgument('Unknown dimension size') if i.value: try: dle_arguments[i.argument.name] = i.value(dim_size) except TypeError: dle_arguments[i.argument.name] = i.value autotune = False else: dle_arguments[i.argument.name] = dim_size return dle_arguments, autotune
def laplacian(field, time_order, m, s): """ Spacial discretization for the isotropic acoustic wave equation. For a 4th order in time formulation, the 4th order time derivative is replaced by a double laplacian: H = (laplacian + s**2/12 laplacian(1/m*laplacian)) :param field: Symbolic TimeFunction object, solution to be computed :param time_order: time order :param m: square slowness :param s: symbol for the time-step :return: H """ if time_order == 2: biharmonic = 0 elif time_order == 4: biharmonic = field.laplace2(1 / m) else: error("Unsupported time order %d, order has to be 2 or 4" % time_order) return field.laplace + s**2 / 12 * biharmonic
def __init__(self, *args, **kwargs): if not self._cached(): self.nt = kwargs.get('nt', 0) self.npoint = kwargs.get('npoint') kwargs['shape'] = (self.nt, self.npoint) super(SparseFunction, self).__init__(self, *args, **kwargs) if self.grid is None: error('SparseFunction objects require a grid parameter.') raise ValueError('No grid provided for SparseFunction.') # Allocate and copy coordinate data d = Dimension('d') self.coordinates = Function(name='%s_coords' % self.name, dimensions=[self.indices[-1], d], shape=(self.npoint, self.grid.dim)) self._children.append(self.coordinates) coordinates = kwargs.get('coordinates', None) if coordinates is not None: self.coordinates.data[:] = coordinates[:]
def __new__(cls, *args, **kwargs): options = kwargs.get('options', {}) key = cls obj = cls._cache_get(key) if obj is not None: newobj = sympy.Function.__new__(cls, *args, **options) newobj.__init_cached__(key) return newobj p_dim = kwargs.get('dimension', Dimension('p_%s' % kwargs.get("name"))) npoint = kwargs.get("npoint") coords = kwargs.get("coordinates") if npoint is None: if coords is None: raise TypeError("Need either `npoint` or `coordinates`") else: npoint = coords.shape[0] grid = kwargs.get("grid") ntime = kwargs.get("ntime") if kwargs.get("data") is None: if ntime is None: error('Either data or ntime are required to' 'initialise source/receiver objects') else: ntime = kwargs.get("ntime") or kwargs.get("data").shape[0] # Create the underlying SparseTimeFunction object kwargs["nt"] = ntime kwargs['npoint'] = npoint obj = SparseTimeFunction.__new__(cls, dimensions=[grid.time_dim, p_dim], **kwargs) # If provided, copy initial data into the allocated buffer if kwargs.get("data") is not None: obj.data[:] = kwargs.get("data") return obj
def __init__(self, *args, **kwargs): if not self._cached(): super(TimeFunction, self).__init__(*args, **kwargs) self.time_dim = kwargs.get('time_dim', None) self.time_order = kwargs.get('time_order', 1) self.save = kwargs.get('save', False) if not self.save: if self.time_dim is not None: warning( 'Explicit time dimension size (time_dim) found for ' 'TimeFunction symbol %s, despite \nusing a stepping time ' 'dimension (save=False). This value will be ignored!' % self.name) self.time_dim = self.time_order + 1 self.indices[0].modulo = self.time_dim else: if self.time_dim is None: error('Time dimension (time_dim) is required' 'to save intermediate data with save=True') raise ValueError("Unknown time dimensions")
def unique(self, key): """ Returns a unique value for a given key, if such a value exists, and raises a ``ValueError`` if it does not. :param key: Key for which to retrieve a unique value """ candidates = self.getall(key) def compare_to_first(v): first = candidates[0] if isinstance(first, np.ndarray) or isinstance(v, np.ndarray): return (first == v).all() else: return first == v if len(candidates) == 1: return candidates[0] elif all(map(compare_to_first, candidates)): return candidates[0] else: error("Unable to find unique value for key %s, candidates: %s" % (key, candidates)) raise ValueError('Inconsistent values for key reduction')
def locate_intel_advisor(): """ Detect if Intel Advisor is installed on the machine and return its location if it is. """ path = None try: # Check if the directory to Intel Advisor is specified path = Path(os.environ['DEVITO_ADVISOR_DIR']) except KeyError: # Otherwise, 'sniff' the location of Advisor's directory error_msg = 'Intel Advisor cannot be found on your system, consider if you'\ ' have sourced its environment variables correctly. Information can'\ ' be found at https://software.intel.com/content/www/us/en/develop/'\ 'documentation/advisor-user-guide/top/launch-the-intel-advisor/'\ 'intel-advisor-cli/setting-and-using-intel-advisor-environment'\ '-variables.html' try: res = run(["advixe-cl", "--version"], stdout=PIPE, stderr=DEVNULL) ver = res.stdout.decode("utf-8") if not ver: error(error_msg) return None except (UnicodeDecodeError, FileNotFoundError): error(error_msg) return None env_path = os.environ["PATH"] env_path_dirs = env_path.split(":") for env_path_dir in env_path_dirs: # intel/advisor is the advisor directory for Intel Parallel Studio, # intel/oneapi/advisor is the directory for Intel oneAPI if "intel/advisor" in env_path_dir or "intel/oneapi/advisor" in env_path_dir: path = Path(env_path_dir) if path.name.startswith('bin'): path = path.parent if not path: error(error_msg) return None if path.joinpath('bin64').joinpath('advixe-cl').is_file(): return path else: warning("Requested `advisor` profiler, but couldn't locate executable" "in advisor directory") return None
def staggered_diff(f, dim, order, stagger=centered, theta=0, phi=0): """ Utility function to generate staggered derivatives :param f: function objects, eg. `f(x, y)` or `g(t, x, y, z)`. :param dims: symbol defining the dimension wrt. which to differentiate, eg. `x`, `y`, `z` or `t`. :param order: Order of the coefficient discretization and thus the width of the resulting stencil expression. :param stagger: Shift for the FD, `left`, `right` or `centered` :param theta: Dip (or polar) angle for rotated FD :param phi: Azimuth angle for rotated FD """ ndim = len(f.space_dimensions) off = dict([(d, 0) for d in f.space_dimensions]) if stagger == left: off[dim] = -.5 elif stagger == right: off[dim] = .5 else: off[dim] = 0 if theta == 0 and phi == 0: diff = dim.spacing idx = list( set([(dim + int(i + .5 + off[dim]) * diff) for i in range(-int(order / 2), int(order / 2))])) if int(order / 2) == 1: idx = [dim - diff, dim] deriv = f.diff(dim).as_finite_difference(idx, x0=dim + off[dim] * dim.spacing) return deriv.evalf(_PRECISION) else: if ndim < 2 or ndim > 3: error("Only 2D and 3D supports rotated finite difference," " %d dimensions as input" % ndim) raise ValueError("Non supported number of dimensions") x = f.space_dimensions[0] z = f.space_dimensions[-1] idxx = list( set([(x + int(i + .5 + off[x]) * x.spacing) for i in range(-int(order / 2), int(order / 2))])) dx = f.diff(x).as_finite_difference(idxx, x0=x + off[x] * x.spacing) dx = dx.evalf(_PRECISION) idxz = list( set([(z + int(i + .5 + off[z]) * z.spacing) for i in range(-int(order / 2), int(order / 2))])) dz = f.diff(z).as_finite_difference(idxz, x0=z + off[z] * z.spacing) dz = dx.evalf(_PRECISION) dy = 0 is_y = False if ndim == 3: y = f.space_dimensions[1] idxy = list( set([(y + int(i + .5 + off[y]) * y.spacing) for i in range(-int(order / 2), int(order / 2))])) dy = f.diff(y).as_finite_difference(idxy, x0=y + off[y] * y.spacing) dy = dy.evalf(_PRECISION) is_y = (dim == y) if dim == x: return cos(theta) * cos(phi) * dx + sin(phi) * cos(theta) * dy -\ sin(theta) * dz elif dim == z: return sin(theta) * cos(phi) * dx + sin(phi) * sin(theta) * dy +\ cos(theta) * dz elif is_y: return -sin(phi) * dx + cos(phi) * dy else: return 0
def fwi_gradient_shot(vp_in, i, solver_params): error("Initialising solver") tn = solver_params['tn'] nbl = solver_params['nbl'] space_order = solver_params['space_order'] dtype = solver_params['dtype'] origin = solver_params['origin'] spacing = solver_params['spacing'] shots_container = solver_params['shots_container'] true_d, source_location, old_dt = load_shot(i, container=shots_container) model = Model(vp=vp_in, nbl=nbl, space_order=space_order, dtype=dtype, shape=vp_in.shape, origin=origin, spacing=spacing, bcs="damp") geometry = create_geometry(model, tn, source_location) error("tn: %d, nt: %d, dt: %f" % (geometry.tn, geometry.nt, geometry.dt)) error("Reinterpolate shot from %d samples to %d samples" % (true_d.shape[0], geometry.nt)) true_d = reinterpolate(true_d, geometry.nt, old_dt) solver = AcousticWaveSolver(model, geometry, kernel='OT2', nbl=nbl, space_order=space_order, dtype=dtype) grad = Function(name="grad", grid=model.grid) residual = Receiver(name='rec', grid=model.grid, time_range=geometry.time_axis, coordinates=geometry.rec_positions) u0 = TimeFunction(name='u', grid=model.grid, time_order=2, space_order=solver.space_order, save=geometry.nt) error("Forward prop") smooth_d, _, _ = solver.forward(save=True, u=u0) error("Misfit") residual.data[:] = smooth_d.data[:] - true_d[:] objective = .5 * np.linalg.norm(residual.data.ravel())**2 error("Gradient") solver.gradient(rec=residual, u=u0, grad=grad) grad = clip_boundary_and_numpy(grad.data, model.nbl) return objective, -grad
def demo_model(preset, **kwargs): """ Utility function to create preset :class:`Model` objects for demonstration and testing purposes. The particular presets are :: * 'layer2D': Simple two-layer model with velocities 1.5 km/s and 2.5 km/s in the top and bottom layer respectively. * 'marmousi2D': Loads the 2D Marmousi data set from the given filepath. Requires the ``opesci/data`` repository to be available on your machine. """ if preset.lower() in ['constant-isotropic']: # A constant single-layer model in a 2D or 3D domain # with velocity 1.5km/s. shape = kwargs.pop('shape', (101, 101)) spacing = kwargs.pop('spacing', tuple([10. for _ in shape])) origin = kwargs.pop('origin', tuple([0. for _ in shape])) nbpml = kwargs.pop('nbpml', 10) vp = kwargs.pop('vp', 1.5) return Model(vp=vp, origin=origin, shape=shape, spacing=spacing, nbpml=nbpml, **kwargs) elif preset.lower() in ['constant-tti']: # A constant single-layer model in a 2D or 3D domain # with velocity 1.5km/s. shape = kwargs.pop('shape', (101, 101)) spacing = kwargs.pop('spacing', tuple([10. for _ in shape])) origin = kwargs.pop('origin', tuple([0. for _ in shape])) nbpml = kwargs.pop('nbpml', 10) v = np.empty(shape, dtype=np.float32) v[:] = 1.5 epsilon = .3 * np.ones(shape) delta = .2 * np.ones(shape) theta = .7 * np.ones(shape) phi = None if len(shape) > 2: phi = .35 * np.ones(shape) return Model(vp=v, origin=origin, shape=shape, spacing=spacing, nbpml=nbpml, epsilon=epsilon, delta=delta, theta=theta, phi=phi, **kwargs) elif preset.lower() in [ 'layers-isotropic', 'twolayer-isotropic', '2layer-isotropic' ]: # A two-layer model in a 2D or 3D domain with two different # velocities split across the height dimension: # By default, the top part of the domain has 1.5 km/s, # and the bottom part of the domain has 2.5 km/s. shape = kwargs.pop('shape', (101, 101)) spacing = kwargs.pop('spacing', tuple([10. for _ in shape])) origin = kwargs.pop('origin', tuple([0. for _ in shape])) nbpml = kwargs.pop('nbpml', 10) ratio = kwargs.pop('ratio', 2) vp_top = kwargs.pop('vp_top', 1.5) vp_bottom = kwargs.pop('vp_bottom', 2.5) # Define a velocity profile in km/s v = np.empty(shape, dtype=np.float32) v[:] = vp_top # Top velocity (background) v[..., int(shape[-1] / ratio):] = vp_bottom # Bottom velocity return Model(vp=v, origin=origin, shape=shape, spacing=spacing, nbpml=nbpml, **kwargs) elif preset.lower() in ['layers-tti', 'twolayer-tti', '2layer-tti']: # A two-layer model in a 2D or 3D domain with two different # velocities split across the height dimension: # By default, the top part of the domain has 1.5 km/s, # and the bottom part of the domain has 2.5 km/s.\ shape = kwargs.pop('shape', (101, 101)) spacing = kwargs.pop('spacing', tuple([10. for _ in shape])) origin = kwargs.pop('origin', tuple([0. for _ in shape])) nbpml = kwargs.pop('nbpml', 10) ratio = kwargs.pop('ratio', 2) vp_top = kwargs.pop('vp_top', 1.5) vp_bottom = kwargs.pop('vp_bottom', 2.5) # Define a velocity profile in km/s v = np.empty(shape, dtype=np.float32) v[:] = vp_top # Top velocity (background) v[..., int(shape[-1] / ratio):] = vp_bottom # Bottom velocity epsilon = .3 * (v - 1.5) delta = .2 * (v - 1.5) theta = .5 * (v - 1.5) phi = None if len(shape) > 2: phi = .1 * (v - 1.5) return Model(vp=v, origin=origin, shape=shape, spacing=spacing, nbpml=nbpml, epsilon=epsilon, delta=delta, theta=theta, phi=phi, **kwargs) elif preset.lower() in ['circle-isotropic']: # A simple circle in a 2D domain with a background velocity. # By default, the circle velocity is 2.5 km/s, # and the background veloity is 3.0 km/s. shape = kwargs.pop('shape', (101, 101)) spacing = kwargs.pop('spacing', tuple([10. for _ in shape])) origin = kwargs.pop('origin', tuple([0. for _ in shape])) nbpml = kwargs.pop('nbpml', 10) vp = kwargs.pop('vp', 3.0) vp_background = kwargs.pop('vp_background', 2.5) r = kwargs.pop('r', 15) # Only a 2D preset is available currently assert (len(shape) == 2) v = np.empty(shape, dtype=np.float32) v[:] = vp_background a, b = shape[0] / 2, shape[1] / 2 y, x = np.ogrid[-a:shape[0] - a, -b:shape[1] - b] v[x * x + y * y <= r * r] = vp return Model(vp=v, origin=origin, shape=shape, spacing=spacing, nbpml=nbpml) elif preset.lower() in ['marmousi-isotropic', 'marmousi2d-isotropic']: shape = (1601, 401) spacing = (7.5, 7.5) origin = (0., 0.) # Read 2D Marmousi model from opesc/data repo data_path = kwargs.get('data_path', None) if data_path is None: error("Path to opesci/data not found! Please specify with " "'data_path=<path/to/opesci/data>'") raise ValueError("Path to model data unspecified") path = os.path.join(data_path, 'Simple2D/vp_marmousi_bi') v = np.fromfile(path, dtype='float32', sep="") v = v.reshape(shape) # Cut the model to make it slightly cheaper v = v[301:-300, :] return Model(vp=v, origin=origin, shape=v.shape, spacing=spacing, nbpml=20) elif preset.lower() in ['marmousi-tti2d', 'marmousi2d-tti']: shape_full = (201, 201, 70) shape = (201, 70) spacing = (10., 10.) origin = (0., 0.) nbpml = kwargs.pop('nbpml', 20) # Read 2D Marmousi model from opesc/data repo data_path = kwargs.pop('data_path', None) if data_path is None: error("Path to opesci/data not found! Please specify with " "'data_path=<path/to/opesci/data>'") raise ValueError("Path to model data unspecified") path = os.path.join(data_path, 'marmousi3D/vp_marmousi_bi') # velocity vp = 1e-3 * np.fromfile(os.path.join(data_path, 'marmousi3D/MarmousiVP.raw'), dtype='float32', sep="") vp = vp.reshape(shape_full) vp = vp[101, :, :] # Epsilon, in % in file, resale between 0 and 1 epsilon = np.fromfile(os.path.join(data_path, 'marmousi3D/MarmousiEps.raw'), dtype='float32', sep="") * 1e-2 epsilon = epsilon.reshape(shape_full) epsilon = epsilon[101, :, :] # Delta, in % in file, resale between 0 and 1 delta = np.fromfile(os.path.join(data_path, 'marmousi3D/MarmousiDelta.raw'), dtype='float32', sep="") * 1e-2 delta = delta.reshape(shape_full) delta = delta[101, :, :] # Theta, in degrees in file, resale in radian theta = np.fromfile(os.path.join(data_path, 'marmousi3D/MarmousiTilt.raw'), dtype='float32', sep="") theta = np.float32(np.pi / 180 * theta.reshape(shape_full)) theta = theta[101, :, :] return Model(vp=vp, origin=origin, shape=shape, spacing=spacing, nbpml=nbpml, epsilon=epsilon, delta=delta, theta=theta, **kwargs) elif preset.lower() in ['marmousi-tti3d', 'marmousi3d-tti']: shape = (201, 201, 70) spacing = (10., 10., 10.) origin = (0., 0., 0.) nbpml = kwargs.pop('nbpml', 20) # Read 2D Marmousi model from opesc/data repo data_path = kwargs.pop('data_path', None) if data_path is None: error("Path to opesci/data not found! Please specify with " "'data_path=<path/to/opesci/data>'") raise ValueError("Path to model data unspecified") path = os.path.join(data_path, 'marmousi3D/vp_marmousi_bi') # Velcoity vp = 1e-3 * np.fromfile(os.path.join(data_path, 'marmousi3D/MarmousiVP.raw'), dtype='float32', sep="") vp = vp.reshape(shape) # Epsilon, in % in file, resale between 0 and 1 epsilon = np.fromfile(os.path.join(data_path, 'marmousi3D/MarmousiEps.raw'), dtype='float32', sep="") * 1e-2 epsilon = epsilon.reshape(shape) # Delta, in % in file, resale between 0 and 1 delta = np.fromfile(os.path.join(data_path, 'marmousi3D/MarmousiDelta.raw'), dtype='float32', sep="") * 1e-2 delta = delta.reshape(shape) # Theta, in degrees in file, resale in radian theta = np.fromfile(os.path.join(data_path, 'marmousi3D/MarmousiTilt.raw'), dtype='float32', sep="") theta = np.float32(np.pi / 180 * theta.reshape(shape)) # Phi, in degrees in file, resale in radian phi = np.fromfile(os.path.join(data_path, 'marmousi3D/Azimuth.raw'), dtype='float32', sep="") phi = np.float32(np.pi / 180 * phi.reshape(shape)) return Model(vp=vp, origin=origin, shape=shape, spacing=spacing, nbpml=nbpml, epsilon=epsilon, delta=delta, theta=theta, phi=phi, **kwargs) else: error('Unknown model preset name %s' % preset)
def arguments(self, *args, **kwargs): """ Return the arguments necessary to apply the Operator. """ if len(args) == 0: args = self.parameters # Will perform auto-tuning if the user requested it and loop blocking was used maybe_autotune = kwargs.get('autotune', False) # Initialise argument map and a map of dimension names to values arguments = OrderedDict([(arg.name, arg) for arg in self.parameters]) dim_sizes = dict([(arg.name, arg.size) for arg in self.parameters if isinstance(arg, Dimension)]) o_vals = {} for name, arg in kwargs.items(): # Override explicitly provided dim sizes from **kwargs if name in dim_sizes: dim_sizes[name] = arg # Override explicitly provided SymbolicData if name in arguments and isinstance(arguments[name], SymbolicData): # Override the original symbol o_vals[name] = arg original = arguments[name] if original.is_CompositeData: for orig, child in zip(original.children, arg.children): o_vals[orig.name] = child # Replace the overridden values with the provided ones for argname in o_vals.keys(): arguments[argname] = o_vals[argname] # Traverse positional args and infer loop sizes for open dimensions f_args = [(name, f) for name, f in arguments.items() if isinstance(f, SymbolicData)] for fname, f in f_args: arguments[fname] = self._arg_data(f) shape = self._arg_shape(f) # Ensure data dimensions match symbol dimensions for i, dim in enumerate(f.indices): # We don't need to check sizes for buffered dimensions # against data shapes, all we need is the size of the parent. if dim.is_Buffered: continue # Check data sizes for dimensions with a fixed size if dim.size is not None: if not shape[i] <= dim.size: error( 'Size of data argument for %s is greater than the size ' 'of dimension %s: %d' % (fname, dim.name, dim.size)) raise InvalidArgument('Wrong data shape encountered') else: continue if dim_sizes[dim.name] is None: # We haven't determined the size of this dimension yet, # try to infer it from the data shape. dim_sizes[dim.name] = shape[i] else: # We know the dimension size, check if data shape agrees if not dim_sizes[dim.name] <= shape[i]: error('Size of dimension %s was determined to be %d, ' 'but data for symbol %s has shape %d.' % (dim.name, dim_sizes[dim.name], fname, shape[i])) raise InvalidArgument('Wrong data shape encountered') # Make sure we have defined all buffered dimensions and their parents, # even if they are not explicitly given or used. d_args = [d for d in arguments.values() if isinstance(d, Dimension)] for d in d_args: if d.is_Buffered: if dim_sizes[d.parent.name] is None: dim_sizes[d.parent.name] = dim_sizes[d.name] if dim_sizes[d.name] is None: dim_sizes[d.name] = dim_sizes[d.parent.name] # Add user-provided block sizes, if any dle_arguments = OrderedDict() for i in self._dle_state.arguments: dim_size = dim_sizes.get(i.original_dim.name, i.original_dim.size) if dim_size is None: error('Unable to derive size of dimension %s from defaults. ' 'Please provide an explicit value.' % i.original_dim.name) raise InvalidArgument('Unknown dimension size') if i.value: try: dle_arguments[i.argument.name] = i.value(dim_size) except TypeError: dle_arguments[i.argument.name] = i.value # User-provided block size available, do not autotune maybe_autotune = False else: dle_arguments[i.argument.name] = dim_size dim_sizes.update(dle_arguments) # Insert loop size arguments from dimension values d_args = [d for d in arguments.values() if isinstance(d, Dimension)] for d in d_args: arguments[d.name] = dim_sizes[d.name] # Might have been asked to auto-tune the block size if maybe_autotune: arguments = self._autotune(arguments) # Add profiler structs arguments.update(self._extra_arguments()) # Sanity check argument derivation for name, arg in arguments.items(): if isinstance(arg, SymbolicData) or isinstance(arg, Dimension): raise ValueError('Runtime argument %s not defined' % arg) return arguments, dim_sizes
def _arg_data(self, argument): # Ensure we're dealing or deriving numpy arrays data = argument.data if not isinstance(data, np.ndarray): error('No array data found for argument %s' % argument.name) return data