Beispiel #1
0
def distribute_points(density, particles_per_cell=1, distribution='uniform'):
    """
Distribute points according to the distribution specified in density.
    :param density: binary tensor
    :param particles_per_cell: integer
    :param distribution: 'uniform' or 'center'
    :return: tensor of shape (batch_size, point_count, rank)
    """
    assert  distribution in ('center', 'uniform')
    index_array = []
    batch_size = math.staticshape(density)[0] if math.staticshape(density)[0] is not None else 1
    
    for batch in range(batch_size):
        indices = math.where(density[batch, ..., 0] > 0)
        indices = math.to_float(indices)

        temp = []
        for _ in range(particles_per_cell):
            if distribution == 'center':
                temp.append(indices + 0.5)
            elif distribution == 'uniform':
                temp.append(indices + math.random_uniform(math.shape(indices)))
        index_array.append(math.concat(temp, axis=0))
    try:
        index_array = math.stack(index_array)
        return index_array
    except ValueError:
        raise ValueError("all arrays in the batch must have the same number of active cells.")
Beispiel #2
0
 def f(value):
     if isinstance(value, field.StaggeredGrid):
         shape = math.staticshape(value.staggered_tensor())
         return (value._batch_size, ) + shape[1:]
     if isinstance(value, field.CenteredGrid):
         return value.staticshape.data
     else:
         return math.staticshape(value)
Beispiel #3
0
def gravity_tensor(gravity, rank):
    if isinstance(gravity, Gravity):
        gravity = gravity.gravity
    if math.is_scalar(gravity):
        gravity = gravity * GLOBAL_AXIS_ORDER.up_vector(rank)
    assert math.staticshape(gravity)[-1] == rank
    return math.to_float(
        math.expand_dims(gravity, 0,
                         rank + 2 - len(math.staticshape(gravity))))
Beispiel #4
0
def gravity_tensor(gravity, rank):
    if isinstance(gravity, Gravity):
        gravity = gravity.gravity
    if math.is_scalar(gravity):
        return math.to_float(
            math.expand_dims([gravity] + [0] * (rank - 1), 0, rank + 1))
    else:
        assert math.staticshape(gravity)[-1] == rank
        return math.to_float(
            math.expand_dims(gravity, 0,
                             rank + 2 - len(math.staticshape(gravity))))
Beispiel #5
0
def _expand_axes(data, points, collapse_dimensions=True):
    assert math.spatial_rank(data) >= 0
    data = math.expand_dims(data, 1, math.spatial_rank(points) - math.spatial_rank(data))
    if collapse_dimensions:
        return data
    else:
        points_axes = math.staticshape(points)[1:-1]
        data_axes = math.staticshape(data)[1:-1]
        for d_points, d_data in zip(points_axes, data_axes):
            assert d_points % d_data == 0
        tilings = [1] + [d_points // d_data for d_points, d_data in zip(math.staticshape(points)[1:-1], math.staticshape(data)[1:-1])] + [1]
        data = math.tile(data, tilings)
        return data
Beispiel #6
0
 def __add__(self, other):
     if other is 0:
         return self
     assert isinstance(other, Gravity), type(other)
     if self._batch_size is not None:
         assert self._batch_size == other._batch_size
     # Add gravity
     if math.is_scalar(self.gravity) and math.is_scalar(other.gravity):
         return Gravity(self.gravity + other.gravity)
     else:
         rank = math.staticshape(other.gravity)[-1] if math.is_scalar(self.gravity)\
             else math.staticshape(self.gravity)[-1]
         sum_tensor = gravity_tensor(self, rank) + gravity_tensor(
             other, rank)
         return Gravity(sum_tensor)
Beispiel #7
0
def _expand_axes(data, points, batch_size=1):
    assert math.spatial_rank(data) >= 0
    data = math.expand_dims(
        data, 1,
        math.spatial_rank(points) - math.spatial_rank(data))
    points_axes = math.staticshape(points)[1:-1]
    data_axes = math.staticshape(data)[1:-1]
    for d_points, d_data in zip(points_axes, data_axes):
        assert d_points % d_data == 0
    tilings = [batch_size or 1] + [
        d_points // d_data for d_points, d_data in zip(
            math.staticshape(points)[1:-1],
            math.staticshape(data)[1:-1])
    ] + [1]
    data = math.tile(data, tilings)
    return data
Beispiel #8
0
 def sample(value, domain, batch_size=None, name=None):
     assert isinstance(domain, Domain)
     if isinstance(value, Field):
         assert_same_rank(
             value.rank, domain.rank,
             'rank of value (%s) does not match domain (%s)' %
             (value.rank, domain.rank))
         if isinstance(value,
                       CenteredGrid) and value.box == domain.box and np.all(
                           value.resolution == domain.resolution):
             data = value.data
         else:
             point_field = CenteredGrid.getpoints(domain.box,
                                                  domain.resolution)
             point_field._batch_size = batch_size
             data = value.at(point_field).data
     else:  # value is constant
         if callable(value):
             x = CenteredGrid.getpoints(
                 domain.box, domain.resolution).copied_with(
                     extrapolation=Material.extrapolation_mode(
                         domain.boundaries),
                     name=name)
             value = value(x)
             return value
         components = math.staticshape(
             value)[-1] if math.ndims(value) > 0 else 1
         data = math.add(
             math.zeros((batch_size, ) + tuple(domain.resolution) +
                        (components, )), value)
     return CenteredGrid(data,
                         box=domain.box,
                         extrapolation=Material.extrapolation_mode(
                             domain.boundaries),
                         name=name)
Beispiel #9
0
 def __dataop__(self, other, linear_if_scalar, data_operator):
     if isinstance(other, StaggeredGrid):
         assert self.compatible(
             other), 'Fields are not compatible: %s and %s' % (self, other)
         data = [
             data_operator(c1, c2) for c1, c2 in zip(self.data, other.data)
         ]
         flags = propagate_flags_operation(self.flags + other.flags, False,
                                           self.rank, self.component_count)
     elif math.ndims(other) > 0 and math.staticshape(other)[-1] > 1:
         other_components = math.unstack(math.as_tensor(other),
                                         axis=-1,
                                         keepdims=True)
         data = [
             data_operator(c1, c2)
             for c1, c2 in zip(self.data, other_components)
         ]
         flags = propagate_flags_operation(self.flags, False, self.rank,
                                           self.component_count)
     else:
         data = [data_operator(c1, other) for c1 in self.data]
         flags = propagate_flags_operation(self.flags, linear_if_scalar,
                                           self.rank, self.component_count)
     return self.copied_with(data=np.array(data, dtype=np.object),
                             flags=flags)
Beispiel #10
0
    def solve(self, field, domain, guess, enable_backprop):
        assert isinstance(domain, FluidDomain)
        active_mask = domain.active_tensor(extend=1)
        fluid_mask = domain.accessible_tensor(extend=1)
        dimensions = math.staticshape(field)[1:-1]
        N = int(np.prod(dimensions))
        periodic = Material.periodic(domain.domain.boundaries)

        if math.choose_backend([field, active_mask,
                                fluid_mask]).matches_name('SciPy'):
            A = sparse_pressure_matrix(dimensions, active_mask, fluid_mask,
                                       periodic)
        else:
            sidx, sorting = sparse_indices(dimensions, periodic)
            sval_data = sparse_values(dimensions, active_mask, fluid_mask,
                                      sorting, periodic)
            backend = math.choose_backend(field)
            sval_data = backend.cast(sval_data, field.dtype)
            A = backend.sparse_tensor(indices=sidx,
                                      values=sval_data,
                                      shape=[N, N])

        div_vec = math.reshape(field, [-1, int(np.prod(field.shape[1:]))])
        if guess is not None:
            guess = math.reshape(guess, [-1, int(np.prod(field.shape[1:]))])

        def apply_A(pressure):
            return math.matmul(A, pressure)

        result_vec, iterations = conjugate_gradient(div_vec, apply_A, guess,
                                                    self.accuracy,
                                                    self.max_iterations,
                                                    enable_backprop)
        return math.reshape(result_vec, math.shape(field)), iterations
Beispiel #11
0
    def solve(self, field, domain, guess):
        assert isinstance(domain, FluidDomain)
        active_mask = domain.active_tensor(extend=1)
        fluid_mask = domain.accessible_tensor(extend=1)
        dimensions = math.staticshape(field)[1:-1]
        N = int(np.prod(dimensions))
        periodic = Material.periodic(domain.domain.boundaries)

        if math.choose_backend([field, active_mask, fluid_mask]).matches_name('SciPy'):
            A = sparse_pressure_matrix(dimensions, active_mask, fluid_mask, periodic)
        else:
            sidx, sorting = sparse_indices(dimensions, periodic)
            sval_data = sparse_values(dimensions, active_mask, fluid_mask, sorting, periodic)
            backend = math.choose_backend(field)
            sval_data = backend.cast(sval_data, field.dtype)
            A = backend.sparse_tensor(indices=sidx, values=sval_data, shape=[N, N])

        if self.autodiff:
            return sparse_cg(field, A, self.max_iterations, guess, self.accuracy, back_prop=True)
        else:
            def pressure_gradient(op, grad):
                return sparse_cg(grad, A, max_gradient_iterations, None, self.gradient_accuracy)[0]

            pressure, iteration = math.with_custom_gradient(sparse_cg,
                                                            [field, A, self.max_iterations, guess, self.accuracy],
                                                            pressure_gradient, input_index=0, output_index=0,
                                                            name_base='scg_pressure_solve')

            max_gradient_iterations = iteration if self.max_gradient_iterations == 'mirror' else self.max_gradient_iterations
            return pressure, iteration
 def solve(self, divergence, domain, guess, enable_backprop):
     """
     :param guess: not used in this implementation, Kernel takes the last pressure value for initial_guess
     """
     active_mask, accessible_mask = domain.active_tensor(
         extend=1), domain.accessible_tensor(extend=1)
     # Setup
     dimensions = math.staticshape(divergence)[1:-1]
     dimensions = dimensions[::
                             -1]  # the custom op needs it in the x,y,z order
     dim_array = np.array(dimensions)
     dim_product = np.prod(dimensions)
     mask_dimensions = dim_array + 2
     laplace_matrix = tf.zeros(dim_product * (len(dimensions) * 2 + 1),
                               dtype=tf.int8)
     # Helper variables for CG, make sure new memory is allocated for each variable.
     one_vector = tf.ones(dim_product, dtype=tf.float32)
     p = tf.zeros_like(divergence, dtype=tf.float32) + 1
     z = tf.zeros_like(divergence, dtype=tf.float32) + 2
     r = tf.zeros_like(divergence, dtype=tf.float32) + 3
     pressure = tf.zeros_like(divergence, dtype=tf.float32) + 4
     # Solve
     pressure, iteration = pressure_op.pressure_solve(
         dimensions, mask_dimensions, active_mask, accessible_mask,
         laplace_matrix, divergence, p, r, z, pressure, one_vector,
         dim_product, self.accuracy, self.max_iterations)
     return pressure, iteration
Beispiel #13
0
 def tensorshape(tensor):
     if tensor is None: return None
     default_batched_shape = staticshape(tensor)
     if len(default_batched_shape) >= 2:
         return (self._batch_size,) + default_batched_shape[1:]
     else:
         return default_batched_shape
Beispiel #14
0
 def component_count(self):
     if self._rank is None:
         return None
     try:
         example_value = self.sample_at([[0] * self._rank])
         return math.staticshape(example_value)[-1]
     except NotImplementedError:
         return None
Beispiel #15
0
def _convert_constant_to_data(value):
    if isinstance(value, numbers.Number):
        value = math.to_float(math.expand_dims(value))
    if isinstance(value, (list, tuple)):
        value = math.to_float(numpy.array(value))
    if len(math.staticshape(value)) < 2:
        value = math.expand_dims(value)
    return value
Beispiel #16
0
 def __init__(self,
              center,
              size,
              wave_vector,
              name='wave_packet',
              data=1.0,
              **kwargs):
     rank = math.staticshape(center)[-1]
     AnalyticField.__init__(self, **struct.kwargs(locals()))
Beispiel #17
0
 def resolution(self):
     if self.content_type in (struct.VALID, struct.INVALID):
         return math.as_tensor(math.staticshape(self.data)[1:-1])
     elif self.content_type in (struct.shape, struct.staticshape):
         return self.data[1:-1]
     else:
         raise AssertionError(
             'Cannot compute resolution of invalid CenteredGrid (content type = %s)'
             % self.content_type)
Beispiel #18
0
 def __init__(self,
              center=(0, 0),
              size=0,
              mode='RANDOM',
              factor=1.0,
              name='Seed',
              data=1.0,
              **kwargs):
     rank = math.staticshape(center)[-1]
     AnalyticField.__init__(self, **struct.kwargs(locals()))
Beispiel #19
0
 def sequence_get(self, sequence, name):
     if isinstance(sequence, dict):
         return sequence[name]
     if isinstance(sequence, (tuple, list)):
         assert len(sequence) == self.rank
         return sequence[self.names.index(name)]
     if math.is_tensor(sequence):
         assert math.staticshape(sequence) == (self.rank,)
         return sequence[self.names.index(name)]
     else:  # just a constant
         return sequence
Beispiel #20
0
 def step(self, u, dt=1.0, **dependent_states):
     assert isinstance(u, CenteredGrid)
     grad = u.gradient()
     laplace = u.laplace()
     laplace2 = laplace.laplace()
     du_dt = -laplace - laplace2 - 0.5 * grad**2
     result = u + dt * du_dt
     result -= math.mean(result.data,
                         axis=tuple(
                             range(1, len(math.staticshape(result.data)))),
                         keepdims=True)
     return result.copied_with(age=u.age + dt, name=u.name)
Beispiel #21
0
 def global_to_child(self, location):
     """ Inverse transform """
     delta = location - self.center
     if math.staticshape(location)[-1] == 2:
         angle = -math.batch_align(self.angle, 0, location)
         sin = math.sin(angle)
         cos = math.cos(angle)
         y, x = math.unstack(delta, axis=-1)
         if GLOBAL_AXIS_ORDER.is_x_first:
             x, y = y, x
         rot_x = cos * x - sin * y
         rot_y = sin * x + cos * y
         rotated = math.stack([rot_y, rot_x], axis=-1)
     elif math.staticshape(location)[-1] == 3:
         angle = math.batch_align(self.angle, 1, location)
         raise NotImplementedError(
             'not yet implemented')  # ToDo apply angle
     else:
         raise NotImplementedError('Rotation only supported in 2D and 3D')
     final = rotated + self.center
     return final
Beispiel #22
0
 def _component_grid(self, grid, axis):
     resolution = list(grid.resolution if isinstance(grid, CenteredGrid) else math.staticshape(grid)[1:-1])
     resolution[axis] -= 1
     box = staggered_component_box(resolution, axis, self.box)
     if isinstance(grid, CenteredGrid):
         assert grid.component_count == 1
         assert grid.rank == self.rank
         assert grid.box == box
         if grid.extrapolation != self.extrapolation:
             grid = grid.copied_with(extrapolation=self.extrapolation)
     else:
         grid = CenteredGrid(data=grid, box=box, extrapolation=self.extrapolation, name=_subname(self.name, axis),
                             batch_size=self._batch_size, flags=propagate_flags_children(self.flags, box.rank, 1))
     return grid
Beispiel #23
0
 def sample_at(self, points):
     points_rank = math.spatial_rank(points)
     src_rank = math.spatial_rank(self.location)
     # --- Expand shapes to format (batch_size, points_dims..., src_dims..., channels) ---
     points = math.expand_dims(points, axis=-2, number=src_rank)
     src_points = math.expand_dims(self.location,
                                   axis=-2,
                                   number=points_rank)
     src_strength = math.expand_dims(self.strength, axis=-1)
     src_strength = math.batch_align(src_strength, 0, self.location)
     src_strength = math.expand_dims(src_strength,
                                     axis=-1,
                                     number=points_rank)
     src_axes = tuple(range(-2, -2 - src_rank, -1))
     # --- Compute distances and falloff ---
     distances = points - src_points
     if self.falloff is not None:
         raise NotImplementedError()
         # distances_squared = math.sum(distances ** 2, axis=-1, keepdims=True)
         # unit_distances = distances / math.sqrt(distances_squared)
         # strength = src_strength * math.exp(-distances_squared)
     else:
         strength = src_strength
     # --- Compute velocities ---
     if math.staticshape(points)[-1] == 2:  # Curl in 2D
         dist_1, dist_2 = math.unstack(distances, axis=-1)
         if GLOBAL_AXIS_ORDER.is_x_first:
             velocity = strength * math.stack([-dist_2, dist_1], axis=-1)
         else:
             velocity = strength * math.stack([dist_2, -dist_1], axis=-1)
     elif math.staticshape(points)[-1] == 3:  # Curl in 3D
         raise NotImplementedError('not yet implemented')
     else:
         raise AssertionError(
             'Vector product not available in > 3 dimensions')
     velocity = math.sum(velocity, axis=src_axes)
     return velocity
Beispiel #24
0
 def from_scalar(scalar_field, axis_forces, name=None):
     assert isinstance(scalar_field, CenteredGrid)
     assert scalar_field.component_count == 1, 'channel must be scalar but has %d components' % scalar_field.component_count
     tensors = []
     for axis in range(scalar_field.rank):
         force = axis_forces[axis] if isinstance(axis_forces, (list, tuple)) else axis_forces[...,axis]
         if isinstance(force, Number) and force == 0:
             dims = list(math.staticshape(scalar_field.data))
             dims[axis+1] += 1
             tensors.append(math.zeros(dims, math.dtype(scalar_field.data)))
         else:
             upper = scalar_field.axis_padded(axis, 0, 1).data
             lower = scalar_field.axis_padded(axis, 1, 0).data
             tensors.append(math.mul((upper + lower) / 2, force))
     return StaggeredGrid(tensors, scalar_field.box, name=name, batch_size=scalar_field._batch_size)
Beispiel #25
0
 def sample_at(self, points, collapse_dimensions=True):
     if not isinstance(self.extrapolation, six.string_types):
         return self._padded_resample(points)
     local_points = self.box.global_to_local(points)
     local_points = local_points * math.to_float(self.resolution) - 0.5
     if self.extrapolation == 'periodic':
         data = math.pad(self.data,
                         [[0, 0]] + [[0, 1]] * self.rank + [[0, 0]],
                         mode='wrap')
         local_points = local_points % math.to_float(
             math.staticshape(self.data)[1:-1])
         resampled = math.resample(data,
                                   local_points,
                                   interpolation=self.interpolation)
     else:
         boundary = 'replicate' if self.extrapolation == 'boundary' else 'zero'
         resampled = math.resample(self.data,
                                   local_points,
                                   boundary=boundary,
                                   interpolation=self.interpolation)
     return resampled
def cuda_solve_forward(divergence, active_mask, fluid_mask, accuracy,
                       max_iterations):
    # Setup
    dimensions = math.staticshape(divergence)[1:-1]
    dimensions = dimensions[::-1]  # the custom op needs it in the x,y,z order
    dim_array = np.array(dimensions)
    dim_product = np.prod(dimensions)
    mask_dimensions = dim_array + 2
    laplace_matrix = tf.zeros(dim_product * (len(dimensions) * 2 + 1),
                              dtype=tf.int8)
    # Helper variables for CG, make sure new memory is allocated for each variable.
    one_vector = tf.ones(dim_product, dtype=tf.float32)
    p = tf.zeros_like(divergence, dtype=tf.float32) + 1
    z = tf.zeros_like(divergence, dtype=tf.float32) + 2
    r = tf.zeros_like(divergence, dtype=tf.float32) + 3
    pressure = tf.zeros_like(divergence, dtype=tf.float32) + 4
    # Solve
    pressure, iteration = pressure_op.pressure_solve(
        dimensions, mask_dimensions, active_mask, fluid_mask, laplace_matrix,
        divergence, p, r, z, pressure, one_vector, dim_product, accuracy,
        max_iterations)
    return pressure, iteration
Beispiel #27
0
 def sample(value, domain, batch_size=None):
     assert isinstance(domain, Domain)
     if isinstance(value, Field):
         assert_same_rank(
             value.rank, domain.rank,
             'rank of value (%s) does not match domain (%s)' %
             (value.rank, domain.rank))
         if isinstance(value,
                       CenteredGrid) and value.box == domain.box and np.all(
                           value.resolution == domain.resolution):
             data = value.data
         else:
             data = value.sample_at(
                 CenteredGrid.getpoints(domain.box, domain.resolution).data)
     else:  # value is constant
         components = math.staticshape(
             value)[-1] if math.ndims(value) > 0 else 1
         data = math.zeros((batch_size, ) + tuple(domain.resolution) +
                           (components, )) + value
     return CenteredGrid(data,
                         box=domain.box,
                         extrapolation=Material.extrapolation_mode(
                             domain.boundaries))
Beispiel #28
0
 def resolution(self):
     return math.as_tensor(math.staticshape(self.data)[1:-1])
Beispiel #29
0
class CenteredGrid(Field):
    def __init__(self,
                 data,
                 box=None,
                 extrapolation='boundary',
                 name=None,
                 **kwargs):
        """Create new CenteredGrid from array like data
        
        :param data: numerical values to be set as values of CenteredGrid (immutable)
        :type data: array-like
        :param box: numerical values describing the surrounding area of the CenteredGrid, defaults to None
        :type box: domain.box, optional
        :param extrapolation: set conditions for boundaries, defaults to 'boundary'
        :type extrapolation: str, optional
        :param name: give CenteredGrid a custom name (immutable), defaults to None
        :type name: string, optional
        """
        Field.__init__(self, **struct.kwargs(locals()))
        self._sample_points = None

    @staticmethod
    def sample(value, domain, batch_size=None):
        assert isinstance(domain, Domain)
        if isinstance(value, Field):
            assert_same_rank(
                value.rank, domain.rank,
                'rank of value (%s) does not match domain (%s)' %
                (value.rank, domain.rank))
            if isinstance(value,
                          CenteredGrid) and value.box == domain.box and np.all(
                              value.resolution == domain.resolution):
                data = value.data
            else:
                data = value.sample_at(
                    CenteredGrid.getpoints(domain.box, domain.resolution).data)
        else:  # value is constant
            components = math.staticshape(
                value)[-1] if math.ndims(value) > 0 else 1
            data = math.zeros((batch_size, ) + tuple(domain.resolution) +
                              (components, )) + value
        return CenteredGrid(data,
                            box=domain.box,
                            extrapolation=Material.extrapolation_mode(
                                domain.boundaries))

    @struct.variable()
    def data(self, data):
        if data is None:
            return None
        if isinstance(data, (tuple, list)):
            data = np.array(data)  # numbers or objects
        while math.ndims(data) < 2:
            data = math.expand_dims(data)
        return data

    data.override(
        struct.staticshape, lambda self, data:
        (self._batch_size, ) + math.staticshape(data)[1:])

    @property
    def resolution(self):
        return math.as_tensor(math.staticshape(self.data)[1:-1])

    @struct.constant(dependencies=Field.data)
    def box(self, box):
        return AABox.to_box(box, resolution_hint=self.resolution)

    @property
    def dx(self):
        return self.box.size / self.resolution

    @property
    def rank(self):
        return math.spatial_rank(self.data)

    @struct.constant(default='boundary')
    def extrapolation(self, extrapolation):
        if extrapolation is None:
            return 'boundary'
        assert extrapolation in ('periodic', 'constant',
                                 'boundary') or isinstance(
                                     extrapolation,
                                     (tuple, list)), extrapolation
        return collapse(extrapolation)

    @struct.constant(default='linear')
    def interpolation(self, interpolation):
        assert interpolation == 'linear'
        return interpolation

    def sample_at(self, points, collapse_dimensions=True):
        if not isinstance(self.extrapolation, six.string_types):
            return self._padded_resample(points)
        local_points = self.box.global_to_local(points)
        local_points = math.mul(local_points, math.to_float(
            self.resolution)) - 0.5
        resampled = math.resample(self.data,
                                  local_points,
                                  boundary=_pad_mode(self.extrapolation),
                                  interpolation=self.interpolation)
        return resampled

    def at(self,
           other_field,
           collapse_dimensions=True,
           force_optimization=False,
           return_self_if_compatible=False):
        if self.compatible(
                other_field
        ):  # and return_self_if_compatible: not applicable for fields with Points
            return self
        if isinstance(other_field, CenteredGrid) and np.allclose(
                self.dx, other_field.dx):
            paddings = _required_paddings_transposed(self.box, self.dx,
                                                     other_field.box)
            if math.sum(paddings) == 0:
                origin_in_local = self.box.global_to_local(
                    other_field.box.lower) * self.resolution
                data = _crop_for_interpolation(self.data, origin_in_local,
                                               other_field.resolution)
                dimensions = self.resolution != other_field.resolution
                dimensions = [
                    d for d in math.spatial_dimensions(data)
                    if dimensions[d - 1]
                ]
                data = math.interpolate_linear(data, origin_in_local % 1.0,
                                               dimensions)
                return CenteredGrid(data,
                                    other_field.box,
                                    name=self.name,
                                    batch_size=self._batch_size)
            elif math.sum(paddings) < 16:
                padded = self.padded(np.transpose(paddings).tolist())
                return padded.at(other_field, collapse_dimensions,
                                 force_optimization)
        return Field.at(self,
                        other_field,
                        force_optimization=force_optimization)

    @property
    def component_count(self):
        return self.data.shape[-1]

    def unstack(self):
        flags = propagate_flags_children(self.flags, self.rank, 1)
        return [
            CenteredGrid(math.expand_dims(component),
                         box=self.box,
                         name='%s[...,%d]' % (self.name, i),
                         flags=flags,
                         batch_size=self._batch_size)
            for i, component in enumerate(math.unstack(self.data, -1))
        ]

    @property
    def points(self):
        if SAMPLE_POINTS in self.flags:
            return self
        if self._sample_points is None:
            self._sample_points = CenteredGrid.getpoints(
                self.box, self.resolution)
        return self._sample_points

    def compatible(self, other_field):
        if not other_field.has_points:
            return True
        if isinstance(other_field, CenteredGrid):
            if self.box != other_field.box:
                return False
            if self.rank != other_field.rank:
                return False
            for r1, r2 in zip(self.resolution, other_field.resolution):
                if r1 != r2 and r2 != 1 and r1 != 1:
                    return False
            return True
        else:
            return False

    def __repr__(self):
        if self.is_valid:
            return 'Grid[%s(%d), size=%s]' % ('x'.join([
                str(r) for r in self.resolution
            ]), self.component_count, self.box.size)
        else:
            return struct.Struct.__repr__(self)

    def padded(self, widths):
        data = math.pad(self.data, [[0, 0]] + widths + [[0, 0]],
                        _pad_mode(self.extrapolation))
        w_lower, w_upper = np.transpose(widths)
        box = AABox(self.box.lower - w_lower * self.dx,
                    self.box.upper + w_upper * self.dx)
        return self.copied_with(data=data, box=box)

    def axis_padded(self, axis, lower, upper):
        widths = [[lower, upper] if ax == axis else [0, 0]
                  for ax in range(self.rank)]
        return self.padded(widths)

    @staticmethod
    def getpoints(box, resolution):
        idx_zyx = np.meshgrid(*[
            np.linspace(0.5 / dim, 1 - 0.5 / dim, dim) for dim in resolution
        ],
                              indexing="ij")
        local_coords = math.expand_dims(math.stack(idx_zyx, axis=-1),
                                        0).astype(np.float32)
        points = box.local_to_global(local_coords)
        return CenteredGrid(points,
                            box,
                            name='grid_centers(%s, %s)' % (box, resolution),
                            flags=[SAMPLE_POINTS])

    def laplace(self, physical_units=True, axes=None):
        if not physical_units:
            data = math.laplace(self.data,
                                padding=_pad_mode(self.extrapolation),
                                axes=axes)
        else:
            if not self.has_cubic_cells:
                raise NotImplementedError('Only cubic cells supported.')
            laplace = math.laplace(self.data,
                                   padding=_pad_mode(self.extrapolation),
                                   axes=axes)
            data = laplace / self.dx[0]**2
        extrapolation = map_for_axes(_gradient_extrapolation,
                                     self.extrapolation, axes, self.rank)
        return self.copied_with(data=data,
                                extrapolation=extrapolation,
                                flags=())

    def gradient(self, physical_units=True):
        if not physical_units or self.has_cubic_cells:
            data = math.gradient(self.data,
                                 dx=np.mean(self.dx),
                                 padding=_pad_mode(self.extrapolation))
            return self.copied_with(data=data,
                                    extrapolation=_gradient_extrapolation(
                                        self.extrapolation),
                                    flags=())
        else:
            raise NotImplementedError('Only cubic cells supported.')

    @property
    def has_cubic_cells(self):
        return np.allclose(self.dx, np.mean(self.dx))

    def normalized(self, total, epsilon=1e-5):
        if isinstance(total, CenteredGrid):
            total = total.data
        normalize_data = math.normalize_to(self.data, total, epsilon)
        return self.with_data(normalize_data)

    def _padded_resample(self, points):
        data = self.padded([[1, 1]] * self.rank).data
        local_points = self.box.global_to_local(points)
        local_points = local_points * math.to_float(
            self.resolution) + 0.5  # depends on amount of padding
        resampled = math.resample(data,
                                  local_points,
                                  boundary='replicate',
                                  interpolation=self.interpolation)
        return resampled
Beispiel #30
0
def staggered_grid(tensor, name='manta_staggered'):
    tensor = tensor[..., ::-1]  # manta: xyz, phiflow: zyx
    assert math.staticshape(tensor)[-1] == math.spatial_rank(tensor)
    return StaggeredGrid(tensor, name=name)