from mosaic import h5
from .domain import Space, Time, SlowTime, Grid
__all__ = ['Gridded', 'Saved', 'GriddedSaved', 'ProblemBase']
[docs]
class Gridded:
"""
Objects of this type are defined over a spatio-temporal grid.
This grid can be provided either as a Grid object or as any of Space, Time or SlowTime
objects that define a grid.
Parameters
----------
grid : Grid, optional
Existing grid, if not provided one will be created.
space : Space, optional
time : Time, optional
slow_time : SlowTime, optional
"""
def __init__(self, **kwargs):
grid = kwargs.pop('grid', None)
space = kwargs.pop('space', None)
time = kwargs.pop('time', None)
slow_time = kwargs.pop('slow_time', None)
if grid is None:
grid = Grid(space, time, slow_time)
else:
grid = Grid(grid.space, grid.time, grid.slow_time)
self._grid = grid
@property
def grid(self):
"""
Access the grid.
"""
return self._grid
@property
def space(self):
"""
Access the space grid.
"""
return self._grid.space
@property
def time(self):
"""
Access the time grid.
"""
return self._grid.time
@property
def slow_time(self):
"""
Access the slow time grid.
"""
return self._grid.slow_time
[docs]
def resample(self, grid=None, space=None, time=None, slow_time=None):
raise NotImplementedError('Resampling has not been implemented yet.')
[docs]
class Saved:
"""
Saved objects include helper functions to interact with the file system.
Classes that inherit from Saved need to define ``__get_desc__`` and ``__set_desc__``
to define how the object is described to be saved and how a loaded description is
digested by the class respectively.
``__get_desc__`` expects a dict-like object with all the attributes that need to
be stored to disk and is called when dumping the object.
``__set_desc__`` will take a dict-like object as a parameter, which it can then be
used to set the state of the object, and is called when loading it.
Parameters
----------
name : str
Name of the saved object.
"""
def __init__(self, **kwargs):
self.name = kwargs.pop('name', self.__class__.__name__.lower())
[docs]
def dump(self, *args, **kwargs):
"""
Dump the object according to the ``__get_desc__`` description.
See :class:`~mosaic.file_manipulation.h5.HDF5` for more information on the parameters of this method.
Returns
-------
"""
description = self.__get_desc__(**kwargs)
kwargs['parameter'] = kwargs.get('parameter', self.name)
with h5.HDF5(*args, **kwargs, mode='w') as file:
file.dump(description)
[docs]
def append(self, *args, **kwargs):
"""
Append the object to a file according to the ``__get_desc__`` description.
See :class:`~mosaic.file_manipulation.h5.HDF5` for more information on the parameters of this method.
Returns
-------
"""
kwargs['parameter'] = kwargs.get('parameter', self.name)
if not h5.file_exists(*args, **kwargs):
self.dump(*args, **kwargs)
return
description = self.__get_desc__(**kwargs)
with h5.HDF5(*args, **kwargs, mode='a') as file:
file.append(description)
[docs]
def load(self, *args, **kwargs):
"""
Load the object using ``__set_desc__`` to digest the description.
See :class:`~mosaic.file_manipulation.h5.HDF5` for more information on the parameters of this method.
Returns
-------
"""
kwargs['parameter'] = self.name
with h5.HDF5(*args, **kwargs, mode='r') as file:
description = file.load(filter=kwargs.pop('filter', None), only=kwargs.pop('only', None))
self.__set_desc__(description)
[docs]
def rm(self, *args, **kwargs):
"""
Remove file.
See :class:`~mosaic.file_manipulation.h5.rm` for more information on the parameters of this method.
Returns
-------
"""
kwargs['parameter'] = self.name
h5.rm(*args, **kwargs, mode='r')
def __get_desc__(self, **kwargs):
return {}
def __set_desc__(self, description):
pass
[docs]
class GriddedSaved(Saved, Gridded):
"""
Objects of this type are include utils to dump and load the instance, taking into
account that it is defined over a grid.
"""
def __init__(self, **kwargs):
Saved.__init__(self, **kwargs)
Gridded.__init__(self, **kwargs)
[docs]
def dump(self, *args, **kwargs):
"""
Dump the object according to the ``__get_desc__`` description.
It will ensure that the grid of the instance is also dumped to disk.
See :class:`~mosaic.file_manipulation.h5.HDF5` for more information on the parameters of this method.
Returns
-------
"""
grid_description = self.grid_description()
description = self.__get_desc__(**kwargs)
grid_description.update(description)
kwargs['parameter'] = kwargs.get('parameter', self.name)
with h5.HDF5(*args, **kwargs, mode='w') as file:
file.dump(grid_description)
[docs]
def append(self, *args, **kwargs):
"""
Append the object to a file according to the ``__get_desc__`` description.
It will ensure that the grid of the instance is also dumped to disk.
See :class:`~mosaic.file_manipulation.h5.HDF5` for more information on the parameters of this method.
Returns
-------
"""
kwargs['parameter'] = kwargs.get('parameter', self.name)
if not h5.file_exists(*args, **kwargs):
self.dump(*args, **kwargs)
return
grid_description = self.grid_description()
description = self.__get_desc__(**kwargs)
grid_description.update(description)
with h5.HDF5(*args, **kwargs, mode='a') as file:
file.append(grid_description)
[docs]
def load(self, *args, **kwargs):
"""
Load the object using ``__set_desc__`` to digest the description.
It will use the grid loaded from file to determine the grid of the instance.
See :class:`~mosaic.file_manipulation.h5.HDF5` for more information on the parameters of this method.
Returns
-------
"""
kwargs['parameter'] = self.name
with h5.HDF5(*args, **kwargs, mode='r') as file:
description = file.load(filter=kwargs.pop('filter', None), only=kwargs.pop('only', None))
# TODO If there's already a grid and they don't match, resample instead
if 'space' in description and self._grid.space is None:
space = Space(shape=description.space.shape,
spacing=description.space.spacing,
extra=description.space.extra,
absorbing=description.space.absorbing)
self._grid.space = space
if 'time' in description and self._grid.time is None:
time = Time(start=description.time.start,
stop=description.time.stop,
step=description.time.step,
num=description.time.num)
self._grid.time = time
if 'slow_time' in description and self._grid.slow_time is None:
slow_time = SlowTime(frame_step=description.slow_time.frame_step,
frame_rate=description.slow_time.frame_rate,
num_frame=description.slow_time.num_frame,
acq_step=description.slow_time.acq_step,
acq_rate=description.slow_time.acq_rate,
num_acq=description.slow_time.num_acq)
self._grid.slow_time = slow_time
self.__set_desc__(description)
[docs]
def grid_description(self):
"""
Get a description of the grid of the object.
Returns
-------
dict
Description of the grid.
"""
grid_description = dict()
if self.space is not None:
space = self.space
grid_description['space'] = {
'shape': space.shape,
'spacing': space.spacing,
'extra': space.extra,
'absorbing': space.absorbing,
}
if self.time is not None:
time = self.time
grid_description['time'] = {
'start': time.start,
'stop': time.stop,
'step': time.step,
'num': time.num,
}
if self.slow_time is not None:
slow_time = self.slow_time
grid_description['slow_time'] = {
'start': slow_time.start,
'stop': slow_time.stop,
'frame_step': slow_time.frame_step,
'frame_rate': slow_time.frame_rate,
'num_frame': slow_time.num_frame,
'acq_step': slow_time.acq_step,
'acq_rate': slow_time.acq_rate,
'num_acq': slow_time.num_acq,
}
return grid_description
[docs]
def resample(self, grid=None, space=None, time=None, slow_time=None):
super().resample(grid=grid, space=space, time=time, slow_time=slow_time)
[docs]
class ProblemBase(GriddedSaved):
"""
Base class for the different components of the problem that need to have access to it and
that also create sub-problems.
Parameters
----------
name : str
Name of the object.
problem : Problem
Problem to which the object belongs.
grid : Grid or any of Space or Time
Grid on which the object is defined
"""
def __init__(self, **kwargs):
problem = kwargs.pop('problem', None)
if problem is not None:
kwargs['space'] = kwargs.get('space', problem.space)
kwargs['time'] = kwargs.get('time', problem.time)
kwargs['slow_time'] = kwargs.get('slow_time', problem.slow_time)
super().__init__(**kwargs)
self._problem = problem
@property
def problem(self):
"""
Access problem object.
"""
return self._problem
[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 object that are needed
for a given shot.
By default, this has no effect.
Parameters
----------
shot : Shot
Shot for which the SubProblem is being generated.
sub_problem : SubProblem
Container for the sub-problem being generated.
Returns
-------
ProblemBase
ProblemBase instance.
"""
return self