def test_codegen(self): input_grid = DenseData(name="input_grid", shape=(3, 2), dtype=np.float64) input_grid.data[:] = np.arange(6, dtype=np.float64).reshape((3, 2)) output_grid = DenseData(name="output_grid", shape=input_grid.shape, dtype=input_grid.dtype) eq = Eq(output_grid.indexed[t, x], input_grid.indexed[t, x] + 3) op = SimpleOperator(input_grid, output_grid, [eq]) op.apply() assert(output_grid.data[2][1] == 8)
def __init__(self, model, src, damp, data, time_order=2, spc_order=6, save=False, **kwargs): nrec, nt = data.shape dt = model.get_critical_dt() u = TimeData(name="u", shape=model.get_shape_comp(), time_dim=nt, time_order=time_order, space_order=spc_order, save=save, dtype=damp.dtype) m = DenseData(name="m", shape=model.get_shape_comp(), dtype=damp.dtype) m.data[:] = model.padm() u.pad_time = save rec = SourceLike(name="rec", npoint=nrec, nt=nt, dt=dt, h=model.get_spacing(), coordinates=data.receiver_coords, ndim=len(damp.shape), dtype=damp.dtype, nbpml=model.nbpml) # Derive stencil from symbolic equation eqn = m * u.dt2 - u.laplace + damp * u.dt stencil = solve(eqn, u.forward)[0] # Add substitutions for spacing (temporal and spatial) s, h = symbols('s h') subs = {s: dt, h: model.get_spacing()} super(ForwardOperator, self).__init__(nt, m.shape, stencils=Eq(u.forward, stencil), subs=subs, spc_border=spc_order / 2, time_order=time_order, forward=True, dtype=m.dtype, **kwargs) # Insert source and receiver terms post-hoc self.input_params += [src, src.coordinates, rec, rec.coordinates] self.output_params += [rec] self.propagator.time_loop_stencils_a = src.add(m, u) + rec.read(u) self.propagator.add_devito_param(src) self.propagator.add_devito_param(src.coordinates) self.propagator.add_devito_param(rec) self.propagator.add_devito_param(rec.coordinates)
def test_compare(self): input_grid = DenseData(name="input_grid", shape=(3, 2), dtype=np.float64) input_grid.data[:] = np.arange(6, dtype=np.float64).reshape((3, 2)) output_grid_py = DenseData(name="output_grid_py", shape=input_grid.shape, dtype=input_grid.dtype) output_grid_cg = DenseData(name="output_grid_cg", shape=input_grid.shape, dtype=input_grid.dtype) eq_py = Eq(output_grid_py.indexed[t, x], input_grid.indexed[t, x] + 3) eq_cg = Eq(output_grid_cg.indexed[t, x], input_grid.indexed[t, x] + 3) op_cg = SimpleOperator(input_grid, output_grid_cg, [eq_cg]) op_py = SimpleOperator(input_grid, output_grid_py, [eq_py]) op_cg.apply() op_py.apply(debug=True) assert(np.equal(output_grid_cg.data, output_grid_py.data).all())
def __init__(self, *args, **kwargs): if not self._cached(): self.nt = kwargs.get('nt') self.npoint = kwargs.get('npoint') self.ndim = kwargs.get('ndim') kwargs['shape'] = (self.nt, self.npoint) super(PointData, self).__init__(self, *args, **kwargs) # Allocate and copy coordinate data self.coordinates = DenseData(name='%s_coords' % self.name, dimensions=[self.indices[1], d], shape=(self.npoint, self.ndim)) self._children.append(self.coordinates) coordinates = kwargs.get('coordinates', None) if coordinates is not None: self.coordinates.data[:] = coordinates[:]
def cache_blocking_test(self, shape, time_order, spc_border, cache_blocking): symbols_combinations = [(t, ), (t, x), (t, x, z), (t, x, y, z)] indexes = symbols_combinations[len(shape) - 1] size = 1 for element in shape: size *= element input_grid = DenseData(name="input_grid", shape=shape, dtype=np.float64) input_grid.data[:] = np.arange(size, dtype=np.float64).reshape(shape) output_grid_noblock = DenseData(name="output_grid", shape=shape, dtype=np.float64) eq_noblock = Eq( output_grid_noblock.indexed[indexes], output_grid_noblock.indexed[indexes] + input_grid.indexed[indexes] + 3) op_noblock = SimpleOperator(input_grid, output_grid_noblock, [eq_noblock], time_order=time_order, spc_border=spc_border) op_noblock.apply() output_grid_block = DenseData(name="output_grid", shape=shape, dtype=np.float64) eq_block = Eq( output_grid_block.indexed[indexes], output_grid_block.indexed[indexes] + input_grid.indexed[indexes] + 3) op_block = SimpleOperator(input_grid, output_grid_block, [eq_block], cache_blocking=cache_blocking, time_order=time_order, spc_border=spc_border) op_block.apply() assert np.equal(output_grid_block.data, output_grid_noblock.data).all()
def __init__(self, origin, spacing, dimensions, vp, rho=None, epsilon=None, delta=None, theta=None, phi=None, nbpml=40): self.vp = vp self.origin = origin self.spacing = spacing self.nbpml = nbpml self.shape = dimensions self.dtype = np.float32 # Create square slowness of the wave as symbol `m` if isinstance(vp, np.ndarray): self.m = DenseData(name="m", shape=self.shape_domain, dtype=self.dtype) else: self.m = 1 / vp**2 # Create dampening field as symbol `damp` self.damp = DenseData(name="damp", shape=self.shape_domain, dtype=self.dtype) # Additional parameter fields for TTI operators if rho is not None: if isinstance(rho, np.ndarray): self.rho = DenseData(name="rho", shape=self.shape_domain, dtype=self.dtype) else: self.rho = rho else: self.rho = 1
def auto_tuning_test_general(self, block_dims, tune_range, expected_result): shape = (50, 50, 50, 50) input_grid = DenseData(name="input_grid", shape=shape, dtype=np.float64) input_grid.data[:] = np.arange(6250000, dtype=np.float64).reshape(shape) output_grid = DenseData(name="output_grid", shape=shape, dtype=np.float64) indexes = (t, x, y, z) eq = Eq(output_grid.indexed[indexes], output_grid.indexed[indexes] + input_grid.indexed[indexes] + 3) op = SimpleOperator(input_grid, output_grid, [eq], time_order=2, spc_border=2) auto_tuner = AutoTuner(op, block_dims, self.test_dir) auto_tuner.auto_tune_blocks(tune_range[0], tune_range[1]) assert auto_tuner.block_size == expected_result
def _new_operator1(shape, **kwargs): infield = DenseData(name='in', shape=shape, dtype=np.int32) infield.data[:] = np.arange(reduce(mul, shape), dtype=np.int32).reshape(shape) outfield = DenseData(name='out', shape=shape, dtype=np.int32) stencil = Eq(outfield.indexify(), outfield.indexify() + infield.indexify() * 3.0) # Run the operator Operator(stencil, **kwargs)(infield, outfield) return outfield
def gradient(self, rec, u, v=None, grad=None, m=None, **kwargs): """ Gradient modelling function for computing the adjoint of the Linearized Born modelling function, ie. the action of the Jacobian adjoint on an input data. :param recin: Receiver data as a numpy array :param u: Symbol for full wavefield `u` (created with save=True) :param v: (Optional) Symbol to store the computed wavefield :param grad: (Optional) Symbol to store the gradient field :returns: Gradient field and performance summary """ # Gradient symbol if grad is None: grad = DenseData(name='grad', shape=self.model.shape_domain, dtype=self.model.dtype) # Create the forward wavefield if v is None: v = TimeData(name='v', shape=self.model.shape_domain, save=False, time_dim=self.source.nt, time_order=self.time_order, space_order=self.space_order, dtype=self.model.dtype) # Pick m from model unless explicitly provided if m is None: m = m or self.model.m summary = self.op_grad.apply(rec=rec, grad=grad, v=v, u=u, m=m, **kwargs) return grad, summary
def __init__(self, u, m, rec, damp, time_order=4, spc_order=12): assert(m.shape == damp.shape) input_params = [u, m, rec, damp] v = TimeData("v", m.shape, rec.nt, time_order=time_order, save=False, dtype=m.dtype) grad = DenseData("grad", m.shape, dtype=m.dtype) output_params = [grad, v] dim = len(m.shape) total_dim = self.total_dim(dim) space_dim = self.space_dim(dim) lhs = v.indexed[total_dim] stencil, subs = self._init_taylor(dim, time_order, spc_order)[1] stencil = self.smart_sympy_replace(dim, time_order, stencil, Function('p'), v, fw=False) stencil_args = [m.indexed[space_dim], rec.dt, rec.h, damp.indexed[space_dim]] main_stencil = Eq(lhs, lhs + stencil) gradient_update = Eq(grad.indexed[space_dim], grad.indexed[space_dim] - (v.indexed[total_dim] - 2 * v.indexed[tuple((t + 1,) + space_dim)] + v.indexed[tuple((t + 2,) + space_dim)]) * u.indexed[total_dim]) reset_v = Eq(v.indexed[tuple((t + 2,) + space_dim)], 0) stencils = [main_stencil, gradient_update, reset_v] substitutions = [dict(zip(subs, stencil_args)), {}, {}] super(GradientOperator, self).__init__(rec.nt, m.shape, stencils=stencils, subs=substitutions, spc_border=spc_order/2, time_order=time_order, forward=False, dtype=m.dtype, input_params=input_params, output_params=output_params) # Insert source and receiver terms post-hoc self.propagator.time_loop_stencils_b = rec.add(m, v)
def __init__(self, model, damp, data, recin, time_order=2, spc_order=6, **kwargs): nrec, nt = data.shape dt = model.get_critical_dt() v = TimeData(name="v", shape=model.get_shape_comp(), time_dim=nt, time_order=time_order, space_order=spc_order, save=False, dtype=damp.dtype) m = DenseData(name="m", shape=model.get_shape_comp(), dtype=damp.dtype) m.data[:] = model.padm() v.pad_time = False srca = SourceLike( name="srca", npoint=1, nt=nt, dt=dt, h=model.get_spacing(), coordinates=np.array(data.source_coords, dtype=damp.dtype)[np.newaxis, :], ndim=len(damp.shape), dtype=damp.dtype, nbpml=model.nbpml) rec = SourceLike(name="rec", npoint=nrec, nt=nt, dt=dt, h=model.get_spacing(), coordinates=data.receiver_coords, ndim=len(damp.shape), dtype=damp.dtype, nbpml=model.nbpml) rec.data[:] = recin[:] # Derive stencil from symbolic equation eqn = m * v.dt2 - v.laplace - damp * v.dt stencil = solve(eqn, v.backward)[0] # Add substitutions for spacing (temporal and spatial) s, h = symbols('s h') subs = {s: model.get_critical_dt(), h: model.get_spacing()} super(AdjointOperator, self).__init__(nt, m.shape, stencils=Eq(v.backward, stencil), subs=subs, spc_border=spc_order / 2, time_order=time_order, forward=False, dtype=m.dtype, **kwargs) # Insert source and receiver terms post-hoc self.input_params += [srca, srca.coordinates, rec, rec.coordinates] self.propagator.time_loop_stencils_a = rec.add(m, v) + srca.read(v) self.output_params = [srca] self.propagator.add_devito_param(srca) self.propagator.add_devito_param(srca.coordinates) self.propagator.add_devito_param(rec) self.propagator.add_devito_param(rec.coordinates)
def __init__(self, model, src, damp, data, dmin, time_order=2, spc_order=6, **kwargs): nrec, nt = data.shape dt = model.get_critical_dt() u = TimeData(name="u", shape=model.get_shape_comp(), time_dim=nt, time_order=time_order, space_order=spc_order, save=False, dtype=damp.dtype) U = TimeData(name="U", shape=model.get_shape_comp(), time_dim=nt, time_order=time_order, space_order=spc_order, save=False, dtype=damp.dtype) m = DenseData(name="m", shape=model.get_shape_comp(), dtype=damp.dtype) m.data[:] = model.padm() dm = DenseData(name="dm", shape=model.get_shape_comp(), dtype=damp.dtype) dm.data[:] = model.pad(dmin) rec = SourceLike(name="rec", npoint=nrec, nt=nt, dt=dt, h=model.get_spacing(), coordinates=data.receiver_coords, ndim=len(damp.shape), dtype=damp.dtype, nbpml=model.nbpml) # Derive stencils from symbolic equation first_eqn = m * u.dt2 - u.laplace - damp * u.dt first_stencil = solve(first_eqn, u.forward)[0] second_eqn = m * U.dt2 - U.laplace - damp * U.dt second_stencil = solve(second_eqn, U.forward)[0] # Add substitutions for spacing (temporal and spatial) s, h = symbols('s h') subs = {s: src.dt, h: src.h} # Add Born-specific updates and resets src2 = -(src.dt**-2) * (-2 * u + u.forward + u.backward) * dm insert_second_source = Eq(U, U + (src.dt * src.dt) / m * src2) stencils = [ Eq(u.forward, first_stencil), Eq(U.forward, second_stencil), insert_second_source ] super(BornOperator, self).__init__(src.nt, m.shape, stencils=stencils, subs=[subs, subs, {}, {}], spc_border=spc_order / 2, time_order=time_order, forward=True, dtype=m.dtype, **kwargs) # Insert source and receiver terms post-hoc self.input_params += [ dm, src, src.coordinates, rec, rec.coordinates, U ] self.output_params = [rec] self.propagator.time_loop_stencils_b = src.add(m, u, t - 1) self.propagator.time_loop_stencils_a = rec.read(U) self.propagator.add_devito_param(dm) self.propagator.add_devito_param(src) self.propagator.add_devito_param(src.coordinates) self.propagator.add_devito_param(rec) self.propagator.add_devito_param(rec.coordinates) self.propagator.add_devito_param(U)
def __init__(self, model, damp, data, recin, u, time_order=2, spc_order=6, **kwargs): nrec, nt = data.shape dt = model.get_critical_dt() v = TimeData(name="v", shape=model.get_shape_comp(), time_dim=nt, time_order=time_order, space_order=spc_order, save=False, dtype=damp.dtype) m = DenseData(name="m", shape=model.get_shape_comp(), dtype=damp.dtype) m.data[:] = model.padm() v.pad_time = False rec = SourceLike(name="rec", npoint=nrec, nt=nt, dt=dt, h=model.get_spacing(), coordinates=data.receiver_coords, ndim=len(damp.shape), dtype=damp.dtype, nbpml=model.nbpml) rec.data[:] = recin grad = DenseData(name="grad", shape=m.shape, dtype=m.dtype) # Derive stencil from symbolic equation eqn = m * v.dt2 - v.laplace - damp * v.dt stencil = solve(eqn, v.backward)[0] # Add substitutions for spacing (temporal and spatial) s, h = symbols('s h') subs = {s: model.get_critical_dt(), h: model.get_spacing()} # Add Gradient-specific updates. The dt2 is currently hacky # as it has to match the cyclic indices gradient_update = Eq( grad, grad - s**-2 * (v + v.forward - 2 * v.forward.forward) * u.forward) stencils = [gradient_update, Eq(v.backward, stencil)] super(GradientOperator, self).__init__(rec.nt - 1, m.shape, stencils=stencils, subs=[subs, subs, {}], spc_border=spc_order / 2, time_order=time_order, forward=False, dtype=m.dtype, input_params=[m, v, damp, u], **kwargs) # Insert receiver term post-hoc self.input_params += [grad, rec, rec.coordinates] self.output_params = [grad] self.propagator.time_loop_stencils_b = rec.add(m, v, t + 1) self.propagator.add_devito_param(rec) self.propagator.add_devito_param(rec.coordinates)
def __new__(cls, *args, **kwargs): nt = kwargs.get('nt') npoint = kwargs.get('npoint') kwargs['shape'] = (nt, npoint) return DenseData.__new__(cls, *args, **kwargs)
def __init__(self, model, src, damp, data, time_order=2, spc_order=4, save=False, **kwargs): nrec, nt = data.shape dt = model.get_critical_dt() u = TimeData(name="u", shape=model.get_shape_comp(), time_dim=nt, time_order=time_order, space_order=spc_order, save=save, dtype=damp.dtype) v = TimeData(name="v", shape=model.get_shape_comp(), time_dim=nt, time_order=time_order, space_order=spc_order, save=save, dtype=damp.dtype) m = DenseData(name="m", shape=model.get_shape_comp(), dtype=damp.dtype) m.data[:] = model.padm() if model.epsilon is not None: epsilon = DenseData(name="epsilon", shape=model.get_shape_comp(), dtype=damp.dtype) epsilon.data[:] = model.pad(model.epsilon) else: epsilon = 1.0 if model.delta is not None: delta = DenseData(name="delta", shape=model.get_shape_comp(), dtype=damp.dtype) delta.data[:] = model.pad(model.delta) else: delta = 1.0 if model.theta is not None: theta = DenseData(name="theta", shape=model.get_shape_comp(), dtype=damp.dtype) theta.data[:] = model.pad(model.theta) else: theta = 0 if len(model.get_shape_comp()) == 3: if model.phi is not None: phi = DenseData(name="phi", shape=model.get_shape_comp(), dtype=damp.dtype) phi.data[:] = model.pad(model.phi) else: phi = 0 u.pad_time = save v.pad_time = save rec = SourceLike(name="rec", npoint=nrec, nt=nt, dt=dt, h=model.get_spacing(), coordinates=data.receiver_coords, ndim=len(damp.shape), dtype=damp.dtype, nbpml=model.nbpml) def Bhaskarasin(angle): if angle == 0: return 0 else: return (16.0 * angle * (3.1416 - abs(angle)) / (49.3483 - 4.0 * abs(angle) * (3.1416 - abs(angle)))) def Bhaskaracos(angle): if angle == 0: return 1.0 else: return Bhaskarasin(angle + 1.5708) Hp, Hzr = symbols('Hp Hzr') if len(m.shape) == 3: ang0 = Function('ang0')(x, y, z) ang1 = Function('ang1')(x, y, z) ang2 = Function('ang2')(x, y, z) ang3 = Function('ang3')(x, y, z) else: ang0 = Function('ang0')(x, y) ang1 = Function('ang1')(x, y) s, h = symbols('s h') ang0 = Bhaskaracos(theta) ang1 = Bhaskarasin(theta) spc_brd = spc_order / 2 # Derive stencil from symbolic equation if len(m.shape) == 3: ang2 = Bhaskaracos(phi) ang3 = Bhaskarasin(phi) Gy1p = (ang3 * u.dxl - ang2 * u.dyl) Gyy1 = (first_derivative(Gy1p, ang3, dim=x, side=right, order=spc_brd) - first_derivative(Gy1p, ang2, dim=y, side=right, order=spc_brd)) Gy2p = (ang3 * u.dxr - ang2 * u.dyr) Gyy2 = (first_derivative(Gy2p, ang3, dim=x, side=left, order=spc_brd) - first_derivative(Gy2p, ang2, dim=y, side=left, order=spc_brd)) Gx1p = (ang0 * ang2 * u.dxl + ang0 * ang3 * u.dyl - ang1 * u.dzl) Gz1r = (ang1 * ang2 * v.dxl + ang1 * ang3 * v.dyl + ang0 * v.dzl) Gxx1 = (first_derivative(Gx1p, ang0, ang2, dim=x, side=right, order=spc_brd) + first_derivative(Gx1p, ang0, ang3, dim=y, side=right, order=spc_brd) - first_derivative(Gx1p, ang1, dim=z, side=right, order=spc_brd)) Gzz1 = (first_derivative(Gz1r, ang1, ang2, dim=x, side=right, order=spc_brd) + first_derivative(Gz1r, ang1, ang3, dim=y, side=right, order=spc_brd) + first_derivative(Gz1r, ang0, dim=z, side=right, order=spc_brd)) Gx2p = (ang0 * ang2 * u.dxr + ang0 * ang3 * u.dyr - ang1 * u.dzr) Gz2r = (ang1 * ang2 * v.dxr + ang1 * ang3 * v.dyr + ang0 * v.dzr) Gxx2 = (first_derivative(Gx2p, ang0, ang2, dim=x, side=left, order=spc_brd) + first_derivative(Gx2p, ang0, ang3, dim=y, side=left, order=spc_brd) - first_derivative(Gx2p, ang1, dim=z, side=left, order=spc_brd)) Gzz2 = (first_derivative(Gz2r, ang1, ang2, dim=x, side=left, order=spc_brd) + first_derivative(Gz2r, ang1, ang3, dim=y, side=left, order=spc_brd) + first_derivative(Gz2r, ang0, dim=z, side=left, order=spc_brd)) parm = [m, damp, epsilon, delta, theta, phi, u, v] else: Gyy2 = 0 Gyy1 = 0 parm = [m, damp, epsilon, delta, theta, u, v] Gx1p = (ang0 * u.dxr - ang1 * u.dy) Gz1r = (ang1 * v.dxr + ang0 * v.dy) Gxx1 = (first_derivative(Gx1p * ang0, dim=x, side=left, order=spc_brd) - first_derivative(Gx1p * ang1, dim=y, side=centered, order=spc_brd)) Gzz1 = (first_derivative(Gz1r * ang1, dim=x, side=left, order=spc_brd) + first_derivative(Gz1r * ang0, dim=y, side=centered, order=spc_brd)) Gx2p = (ang0 * u.dx - ang1 * u.dyr) Gz2r = (ang1 * v.dx + ang0 * v.dyr) Gxx2 = (first_derivative(Gx2p * ang0, dim=x, side=centered, order=spc_brd) - first_derivative(Gx2p * ang1, dim=y, side=left, order=spc_brd)) Gzz2 = (first_derivative(Gz2r * ang1, dim=x, side=centered, order=spc_brd) + first_derivative(Gz2r * ang0, dim=y, side=left, order=spc_brd)) stencilp = 1.0 / (2.0 * m + s * damp) * \ (4.0 * m * u + (s * damp - 2.0 * m) * u.backward + 2.0 * s**2 * (epsilon * Hp + delta * Hzr)) stencilr = 1.0 / (2.0 * m + s * damp) * \ (4.0 * m * v + (s * damp - 2.0 * m) * v.backward + 2.0 * s**2 * (delta * Hp + Hzr)) Hp_val = -(.5 * Gxx1 + .5 * Gxx2 + .5 * Gyy1 + .5 * Gyy2) Hzr_val = -(.5 * Gzz1 + .5 * Gzz2) factorized = {Hp: Hp_val, Hzr: Hzr_val} # Add substitutions for spacing (temporal and spatial) subs = [{s: src.dt, h: src.h}, {s: src.dt, h: src.h}] first_stencil = Eq(u.forward, stencilp.xreplace(factorized)) second_stencil = Eq(v.forward, stencilr.xreplace(factorized)) stencils = [first_stencil, second_stencil] super(ForwardOperator, self).__init__(src.nt, m.shape, stencils=stencils, subs=subs, spc_border=spc_order/2 + 2, time_order=time_order, forward=True, dtype=m.dtype, input_params=parm, **kwargs) # Insert source and receiver terms post-hoc self.input_params += [src, src.coordinates, rec, rec.coordinates] self.output_params += [v, rec] self.propagator.time_loop_stencils_a = (src.add(m, u) + src.add(m, v) + rec.read2(u, v)) self.propagator.add_devito_param(src) self.propagator.add_devito_param(src.coordinates) self.propagator.add_devito_param(rec) self.propagator.add_devito_param(rec.coordinates)
class PointData(CompositeData): """ Data object for sparse point data that acts as a Function symbol :param name: Name of the resulting :class:`sympy.Function` symbol :param npoint: Number of points to sample :param nt: Size of the time dimension for point data :param ndim: Dimension of the coordinate data, eg. 2D or 3D :param coordinates: Optional coordinate data for the sparse points :param dtype: Data type of the buffered data """ is_PointData = True def __init__(self, *args, **kwargs): if not self._cached(): self.nt = kwargs.get('nt') self.npoint = kwargs.get('npoint') self.ndim = kwargs.get('ndim') kwargs['shape'] = (self.nt, self.npoint) super(PointData, self).__init__(self, *args, **kwargs) # Allocate and copy coordinate data self.coordinates = DenseData(name='%s_coords' % self.name, dimensions=[self.indices[1], d], shape=(self.npoint, self.ndim)) self._children.append(self.coordinates) coordinates = kwargs.get('coordinates', None) if coordinates is not None: self.coordinates.data[:] = coordinates[:] def __new__(cls, *args, **kwargs): nt = kwargs.get('nt') npoint = kwargs.get('npoint') kwargs['shape'] = (nt, npoint) return DenseData.__new__(cls, *args, **kwargs) @classmethod def _indices(cls, **kwargs): """Return the default dimension indices for a given data shape :param shape: Shape of the spatial data :return: indices used for axis. """ dimensions = kwargs.get('dimensions', None) return dimensions or [time, p] @property 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 = symbols('x1, y1, z1, x2, y2, z2') # Coordinate values of the sparse point px, py, pz = self.point_symbols if self.ndim == 2: A = Matrix([[1, x1, y1, x1 * y1], [1, x1, y2, x1 * y2], [1, x2, y1, x2 * y1], [1, x2, y2, x2 * y2]]) p = Matrix([[1], [px], [py], [px * py]]) elif self.ndim == 3: A = 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 = Matrix([[1], [px], [py], [pz], [px * py], [px * pz], [py * pz], [px * py * pz]]) else: error('Point interpolation only supported for 2D and 3D') raise NotImplementedError('Interpolation coefficients not ' 'implemented for %d dimensions.' % self.ndim) # Map to reference cell reference_cell = { x1: 0, y1: 0, z1: 0, x2: x.spacing, y2: y.spacing, z2: z.spacing } A = A.subs(reference_cell) return A.inv().T.dot(p) @property def point_symbols(self): """Symbol for coordinate value in each dimension of the point""" return symbols('px, py, pz') @property def point_increments(self): """Index increments in each dimension for each point symbol""" if self.ndim == 2: return ((0, 0), (0, 1), (1, 0), (1, 1)) elif self.ndim == 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.ndim) @property def coordinate_symbols(self): """Symbol representing the coordinate values in each dimension""" p_dim = self.indices[1] return tuple( [self.coordinates.indexify((p_dim, i)) for i in range(self.ndim)]) @property def coordinate_indices(self): """Symbol for each grid index according to the coordinates""" indices = (x, y, z) return tuple([ INT(Function('floor')(c / i.spacing)) for c, i in zip(self.coordinate_symbols, indices[:self.ndim]) ]) @property def coordinate_bases(self): """Symbol for the base coordinates of the reference grid point""" indices = (x, y, z) return tuple([ FLOAT(c - idx * i.spacing) for c, idx, i in zip(self.coordinate_symbols, self.coordinate_indices, indices[:self.ndim]) ]) def interpolate(self, expr, offset=0, **kwargs): """Creates a :class:`sympy.Eq` equation for the interpolation of an expression onto this sparse point collection. :param expr: The expression to interpolate. :param offset: Additional offset from the boundary for absorbing boundary conditions. :param u_t: (Optional) time index to use for indexing into field data in `expr`. :param p_t: (Optional) time index to use for indexing into the sparse point data. """ u_t = kwargs.get('u_t', None) p_t = kwargs.get('p_t', None) expr = indexify(expr) # Apply optional time symbol substitutions to expr if u_t is not None: expr = expr.subs(t, u_t).subs(time, u_t) variables = list(retrieve_indexed(expr)) # List of indirection indices for all adjacent grid points index_matrix = [ tuple(idx + ii + offset for ii, idx in zip(inc, self.coordinate_indices)) for inc in self.point_increments ] # Generate index substituions for all grid variables idx_subs = [] for i, idx in enumerate(index_matrix): v_subs = [(v, v.base[v.indices[:-self.ndim] + idx]) for v in variables] idx_subs += [OrderedDict(v_subs)] # Substitute coordinate base symbols into the coefficients subs = OrderedDict(zip(self.point_symbols, self.coordinate_bases)) rhs = sum([ expr.subs(vsub) * b.subs(subs) for b, vsub in zip(self.coefficients, idx_subs) ]) # Apply optional time symbol substitutions to lhs of assignment lhs = self if p_t is None else self.subs(self.indices[0], p_t) return [Eq(lhs, rhs)] def inject(self, field, expr, offset=0, **kwargs): """Symbol for injection of an expression onto a grid :param field: The grid field into which we inject. :param expr: The expression to inject. :param offset: Additional offset from the boundary for absorbing boundary conditions. :param u_t: (Optional) time index to use for indexing into `field`. :param p_t: (Optional) time index to use for indexing into `expr`. """ u_t = kwargs.get('u_t', None) p_t = kwargs.get('p_t', None) expr = indexify(expr) field = indexify(field) variables = list(retrieve_indexed(expr)) + [field] # Apply optional time symbol substitutions to field and expr if u_t is not None: field = field.subs(field.indices[0], u_t) if p_t is not None: expr = expr.subs(self.indices[0], p_t) # List of indirection indices for all adjacent grid points index_matrix = [ tuple(idx + ii + offset for ii, idx in zip(inc, self.coordinate_indices)) for inc in self.point_increments ] # Generate index substituions for all grid variables except # the sparse `PointData` types idx_subs = [] for i, idx in enumerate(index_matrix): v_subs = [(v, v.base[v.indices[:-self.ndim] + idx]) for v in variables if not v.base.function.is_PointData] idx_subs += [OrderedDict(v_subs)] # Substitute coordinate base symbols into the coefficients subs = OrderedDict(zip(self.point_symbols, self.coordinate_bases)) return [ Eq(field.subs(vsub), field.subs(vsub) + expr.subs(subs).subs(vsub) * b.subs(subs)) for b, vsub in zip(self.coefficients, idx_subs) ]
def densedata(name, shape, dimensions): return DenseData(name=name, shape=shape, dimensions=dimensions)