Source code for stride.problem.geometry

import numpy as np
from collections import OrderedDict

from .base import GriddedSaved, ProblemBase
from .. import plotting
from ..utils import geometries


__all__ = ['Geometry']


[docs] class TransducerLocation(GriddedSaved): """ This determines the spatial location of a specific transducer device within the geometry. The location is determined by a numerical ID (>= 0), a transducer and the coordinates of the location within the space grid. In some cases, the orientation of the transducer might also be needed. Parameters ---------- id : int Numerical ID of the location (>=0). name : str Optional name for the transducer location. transducer : Transducer Transducer device to which this location refers. coordinates : ndarray Coordinates of the transducer in the space grid. orientation : ndarray, optional Orientation of the transducer with respect to its location. """ def __init__(self, id, transducer=None, coordinates=None, orientation=None, name=None, **kwargs): name = name or 'transducer_instance_%05d' % id super().__init__(name=name, **kwargs) if id < 0: raise ValueError('The transducer location needs a positive ID') self.id = id self.transducer = transducer if coordinates is not None: coordinates = transducer.coordinates + coordinates self.coordinates = coordinates self.orientation = orientation
[docs] def check_bounds(self): pass
[docs] def sub_problem(self, shot, sub_problem): """ Create a subset object for a certain shot. A SubProblem contains everything that is needed to fully determine how to run a particular shot. This method takes care of creating a TransducerLocation object that links to that new SubProblem. Parameters ---------- shot : Shot Shot for which the SubProblem is being generated. sub_problem : SubProblem Container for the sub-problem being generated. Returns ------- TransducerLocation Newly created TransducerLocation instance. """ return self
def __get_desc__(self, **kwargs): description = { 'id': self.id, 'transducer_id': self.transducer.id, 'coordinates': self.coordinates, } if self.orientation is not None: description['orientation'] = self.orientation return description def __set_desc__(self, description, transducers=None): self.id = description.id self.transducer = transducers.get(description.transducer_id) self.coordinates = description.coordinates.load() if 'orientation' in description: self.orientation = description.orientation.load()
[docs] class Geometry(ProblemBase): """ The Geometry represents a series of transducer locations that exist within the confines of the grid. Transducer locations are identified through a numerical ID, which is >= 0. Transducer locations can be added at a certain location through ``Geometry.add(id, transducer, coordinates, [orientation])`` and can be accessed through ``Geometry.get(location_id)``. The Geometry also provides utilities for loading and dumping these transducers and for plotting them. Parameters ---------- name : str Alternative name to give to the medium. problem : Problem Problem to which the Geometry belongs. transducers : Transducers Transducers object to which the Geometry refers. grid : Grid or any of Space or Time Grid on which the Geometry is defined """ def __init__(self, name='geometry', problem=None, **kwargs): super().__init__(name=name, problem=problem, **kwargs) if problem is not None: transducers = problem.transducers else: transducers = kwargs.pop('transducers', None) self._locations = OrderedDict() self._transducers = transducers
[docs] def add(self, id, transducer, coordinates, orientation=None): """ Add a new transducer location to the Geometry. Parameters ---------- id : int ID of the instantiation of the transducer in the geometry. transducer : Transducer Transducer to be added to the Geometry. coordinates : array Coordinates of the transducer in the grid. orientation : array, optional Orientation vector of the transducer. Returns ------- """ if id in self._locations.keys(): raise ValueError('Transducer location with ID "%d" already exists in the Geometry' % id) instance = TransducerLocation(id, transducer, coordinates, orientation, grid=self.grid) self._locations[id] = instance
[docs] def add_location(self, item): """ Add an existing location to the Geometry. Parameters ---------- item : TransducerLocation Transducer location instance to be added to the Geometry. Returns ------- """ if item.id in self._locations.keys(): raise ValueError('Transducer location with ID "%d" already exists in the Geometry' % item.id) self._locations[item.id] = item
[docs] def get(self, id): """ Get a transducer location from the Geometry with a known id. Parameters ---------- id : int Identifier of the transducer. Returns ------- TransducerLocation Found TransducerLocation. """ if isinstance(id, (np.int32, np.int64)): id = int(id) if not isinstance(id, int) or id < 0: raise ValueError('Transducer IDs have to be positive integer numbers') return self._locations[id]
[docs] def get_slice(self, start=None, end=None, step=None): """ Get a slice of the indices of the locations using ``slice(start, stop, step)``. Parameters ---------- start : int, optional Start of the slice, defaults to the first id. end : int, optional End of the slice, defaults to the last id. step : int, optional Steps in between transducers, defaults to 1. Returns ------- list Found transducer locations in the slice. """ section = OrderedDict() if start is None: _range = range(end) elif step is None: _range = range(start, end) else: _range = range(start, end, step) for index in _range: section[list(self._locations.keys())[index]] = list(self._locations.values())[index] return section
[docs] def default(self, geometry_type, *args, **kwargs): """ Fill the container with the default configuration. In this case, that means using one of the default geometry functions in ``stride.utils.geometries`` and using the same transducer for all of them. Parameters ---------- geometry_type : str Type of geometry to use. Returns ------- """ if geometry_type == 'elliptical': default_radius = ((self.space.limit[0] - 15.e-3) / 2, (self.space.limit[1] - 13.e-3) / 2) default_centre = (self.space.limit[0] / 2, self.space.limit[1] / 2) kwargs['radius'] = kwargs.get('radius', default_radius) kwargs['centre'] = kwargs.get('centre', default_centre) elif geometry_type == 'ellipsoidal': default_radius = ((self.space.limit[0] - 15.e-3) / 2, (self.space.limit[1] - 15.e-3) / 2, (self.space.limit[2] - 15.e-3) / 2) default_centre = (self.space.limit[0] / 2, self.space.limit[1] / 2, self.space.limit[2] / 2) if len(args) < 2: kwargs['radius'] = kwargs.get('radius', default_radius) if len(args) < 3: kwargs['centre'] = kwargs.get('centre', default_centre) kwargs['threshold'] = kwargs.get('threshold', 0.3) geometry_fun = getattr(geometries, geometry_type) coordinates = geometry_fun(*args, **kwargs) for index in range(coordinates.shape[0]): _coordinates = coordinates[index, :] if len(_coordinates) != self.space.dim: _coordinates = np.pad(_coordinates, ((0, 1),)) _coordinates[-1] = self.space.limit[2] / 2 self.add(index, self._transducers.get(0), _coordinates)
@property def transducers(self): return self._transducers @property def num_locations(self): """ Get number of locations in the Geometry. """ return len(self._locations.keys()) @property def locations(self): """ Get all locations in the Geometry as a list. """ return list(self._locations.values()) @property def location_ids(self): """ Get all location IDs in the Geometry as a list. """ return list(self._locations.keys()) @property def coordinates(self): """ Get the coordinates of all locations packed in an array format. Coordinates are defined as a 2 or 3-dimensional array with shape (n_transducers, n_dimensions). """ coordinates = np.zeros((self.num_locations, self.space.dim), dtype=np.float32) index = 0 for location in self._locations.values(): coordinates[index, :] = location.coordinates index += 1 return coordinates
[docs] def plot(self, **kwargs): """ Plot the locations of the transducers as scattered points. Parameters ---------- kwargs Arguments for plotting. Returns ------- axes Axes on which the plotting is done. """ title = kwargs.pop('title', self.name) plot = kwargs.pop('plot', True) coordinates = self.coordinates if self.space.dim > 2: coordinates = coordinates / np.array(self.space.spacing) axis = plotting.plot_points(coordinates, title=title, **kwargs) if plot is True: plotting.show(axis) return axis
[docs] def sub_problem(self, shot, sub_problem): """ Create a subset object for a certain shot. A SubProblem contains everything that is needed to fully determine how to run a particular shot. This method takes care of selecting the portions of the Geometry that are needed for a given shot. Parameters ---------- shot : Shot Shot for which the SubProblem is being generated. sub_problem : SubProblem Container for the sub-problem being generated. Returns ------- Geometry Newly created Geometry instance. """ sub_geometry = Geometry(name=self.name, transducers=sub_problem.transducers, problem=sub_problem, grid=self.grid) source_ids = shot.source_ids receiver_ids = shot.receiver_ids location_ids = list(set(source_ids) | set(receiver_ids)) for location_id in location_ids: location = self.get(location_id) location = location.sub_problem(shot, sub_problem) sub_geometry.add_location(location) return sub_geometry
def __get_desc__(self, **kwargs): description = { 'num_locations': self.num_locations, 'locations': [], } for location_id, location in self._locations.items(): description['locations'].append(location.__get_desc__()) return description def __set_desc__(self, description): for location_desc in description.locations: if location_desc.id not in self.location_ids: instance = TransducerLocation(location_desc.id) self.add_location(instance) instance = self.get(location_desc.id) instance.__set_desc__(location_desc, self._transducers)