brimfile.file

  1import numpy as np
  2import warnings
  3
  4from .data import Data
  5
  6from .utils import concatenate_paths
  7from .constants import brim_obj_names
  8from . import units
  9from . import subtypes
 10
 11from .file_abstraction import FileAbstraction, StoreType, sync
 12from .validation import validate_json, ValidationError, ValidationLevel
 13from .validation.json_descriptor import generate_json_descriptor
 14
 15# don't import _AbstractFile if running in pyodide (it is defined in js)
 16import sys
 17if "pyodide" not in sys.modules:
 18    from .file_abstraction import _AbstractFile
 19
 20__docformat__ = "google"
 21
 22class File:
 23    """
 24    Represents a brim file with Brillouin data, extending h5py.File.
 25    """
 26
 27    if "pyodide" in sys.modules:
 28        def __init__(self, file):
 29            self._file = file
 30            if not self.is_valid():
 31                raise ValueError("The brim file is not valid!")
 32    else:
 33        def __init__(self, filename: str, mode: str = 'r',
 34                     store_type: StoreType = StoreType.AUTO, * ,
 35                     validate: bool = False) -> None:
 36            """
 37            Initialize the File object.
 38
 39            Args:
 40                filename (str): Path to the brim file.
 41                mode: {'r', 'r+', 'a', 'w', 'w-'} the mode for opening the file (default is 'r' for read-only).
 42                            See the definition of `mode` in `brimfile.file_abstraction._zarrFile.__init__()` for more details.
 43                            'r' means read only (must exist); 'r+' means read/write (must exist);
 44                            'a' means read/write (create if doesn't exist); 'w' means create (overwrite if exists); 'w-' means create (fail if exists).
 45                store_type (StoreType): Type of the store to use, as defined in `brimfile.file_abstraction.StoreType`. Default is 'AUTO'.
 46                validate (bool): Whether to validate the file upon initialization. Default is False.
 47            """
 48            self._file = _AbstractFile(
 49                filename, mode=mode, store_type=store_type)
 50            if not self.is_valid():
 51                raise ValueError("The brim file is not valid!")
 52            if validate:
 53                validation_errors: list[ValidationError] = self.validate()
 54                for err in validation_errors:
 55                    if err.level == ValidationLevel.WARNING or err.level == ValidationLevel.ERROR:
 56                        warnings.warn(f"Validation warning at {err.path}: {err.message}")
 57                    elif err.level == ValidationLevel.CRITICAL:
 58                        raise ValueError(f"Validation error at {err.path}: {err.message}")
 59
 60    def validate(self) -> list[ValidationError]:
 61        """
 62        Validate the brim file and return a list of validation errors.
 63
 64        Returns:
 65            list[ValidationError]: A list of validation errors found in the brim file.
 66            If the list is empty, the file is valid.
 67        """
 68        json_descriptor = generate_json_descriptor(self._file)
 69        validation_errors: list[ValidationError] = validate_json(json_descriptor)
 70        return validation_errors
 71    
 72    def __del__(self):
 73        try:
 74            if hasattr(self, '_file'):
 75                self.close()
 76        except Exception as e:            
 77            # don't throw an error if the file cannot be closed
 78            warnings.warn(f"Cannot close the file: {e}")
 79
 80    def close(self) -> None:
 81        self._file.close()
 82
 83    def is_read_only(self) -> bool:
 84        return sync(self._file.is_read_only())
 85
 86    def is_valid(self) -> bool:
 87        """
 88        Check if the file is a valid brim file.
 89
 90        Returns:
 91            bool: True if the file is valid, False otherwise.
 92        """
 93        # TODO validate file against https://github.com/brillouin-imaging/Brillouin-standard-file/blob/main/docs/brim_file_specs.md
 94        return True
 95
 96    @classmethod
 97    def create(cls, filename: str, store_type: StoreType = StoreType.AUTO, *, 
 98               brim_version: str = '0.1') -> 'File':
 99        """
100        Create a new brim file with the specified filename. If the file exists already it will generate an error.
101
102        Args:
103            filename (str): Path to the brim file to be created.
104            store_type (StoreType): Type of the store to use, as defined in `brimfile.file_abstraction.StoreType`. Default is 'AUTO'.
105            brim_version (str): Version of the brim file format to use. Default is '0.1'.
106
107        Returns:
108            File: An instance of the File class representing the newly created brim file.
109            store_type (str): Type of the store to use, as defined in `brimfile.file_abstraction.StoreType`. Default is 'AUTO'.
110        """
111        f = cls(filename, mode='w-', store_type=store_type)
112
113        # File version
114        sync(f._file.create_attr('/', 'brim_version', brim_version))
115
116        # Root Brillouin_data group
117        sync(f._file.create_group(brim_obj_names.Brillouin_base_path))
118
119        return f
120
121    def create_data_group(self, PSD: np.ndarray, frequency: np.ndarray, px_size_um: tuple, *, index: int = None,
122                          name: str = None, compression: FileAbstraction.Compression = FileAbstraction.Compression()) -> 'Data':
123        """
124        Adds a new data entry to the file.
125        Parameters:
126            PSD (np.ndarray): The Power Spectral Density (PSD) data to be added. It must be 4D with dimensions z, y, x, spectrum
127            frequency (np.ndarray): The frequency data corresponding to the PSD. It must be broadcastable to the PSD shape (the most common case is frequency being 1D, in which case the frequency axis is assumed the same for all the spatial coordinates)
128            px_size_um (tuple): A tuple of 3 elements, in the order z,y,x, corresponding to the pixel size in um. Unused dimensions can be set to None.
129            index (int, optional): The index for the new data group. If None, the next available index is used. Defaults to None.
130            name (str, optional): The name for the new data group. Defaults to None.
131            compression (FileAbstraction.Compression, optional): The compression method to use for the data. Defaults to FileAbstraction.Compression.DEFAULT.
132        Returns:
133            Data: The newly created Data object.
134        Raises:
135            IndexError: If the specified index already exists in the dataset.
136            ValueError: If any of the data provided is not valid or consistent
137        """
138        if PSD.ndim != 4:
139            raise ValueError(
140                "'PSD' must have 4 dimensions (z, y, x, spectrum)")
141        try:
142            np.broadcast_shapes(tuple(frequency.shape), tuple(PSD.shape))
143        except ValueError as e:
144            raise ValueError(f"frequency (shape: {frequency.shape}) is not broadcastable to PSD (shape: {PSD.shape}): {e}")
145        if len(px_size_um) != 3:
146            raise ValueError("'px_size_um' must have 3 elements (z,y,x); unused dimensions can be set to None")
147
148        return self._create_data_group_raw(PSD, frequency, scanning = None, sparse = False, px_size_um=px_size_um, 
149                                             index=index, name=name, compression=compression)
150
151    def create_data_group_sparse(self, PSD: np.ndarray, frequency: np.ndarray, scanning: dict, *, timestamp: np.ndarray = None,
152                                index: int = None, name: str = None, compression: FileAbstraction.Compression = FileAbstraction.Compression()) -> 'Data':
153        """
154        Adds a new [sparse data entry](https://github.com/brillouin-imaging/Brillouin-standard-file/blob/main/docs/brim_file_specs.md) to the file.
155        
156        Sparse data allows storage of spectra in a flattened format (first dimension is the spectrum index),
157        with spatial mapping provided separately. This is efficient for data with irregular sampling or missing pixels.
158        
159        Parameters:
160            PSD (np.ndarray): The Power Spectral Density (PSD) data to be added. First dimension is spectrum index,
161                last dimension contains the spectral data. Shape: (n_spectra, ..., n_freq_points).
162            frequency (np.ndarray): The frequency data corresponding to the PSD. Must be broadcastable to the PSD array.
163            scanning (dict): Dictionary defining the spatial mapping. Must include at least 'Spatial_map' or 'Cartesian_visualisation'.
164                See `brimfile.data.Data._add_data` docstring for detailed structure of the scanning dictionary.
165            timestamp (np.ndarray, optional): Timestamps in milliseconds for the data. Defaults to None.
166            index (int, optional): The index for the new data group. If None, the next available index is used. Defaults to None.
167            name (str, optional): The name for the new data group. Defaults to None.
168            compression (FileAbstraction.Compression, optional): The compression method to use for the data. Defaults to FileAbstraction.Compression.DEFAULT.
169        Returns:
170            Data: The newly created Data object.
171        Raises:
172            IndexError: If the specified index already exists in the dataset.
173            ValueError: If any of the data provided is not valid or consistent
174        """
175        return self._create_data_group_raw(PSD, frequency, scanning=scanning, timestamp=timestamp, sparse=True, index=index, name=name, compression=compression)   
176    
177    def _create_data_group_raw(self, PSD: np.ndarray, frequency: np.ndarray, *, scanning: dict = None, px_size_um = None, timestamp: np.ndarray = None, sparse: bool = False,
178                                index: int = None, name: str = None, compression: FileAbstraction.Compression = FileAbstraction.Compression()) -> 'Data':
179        """
180        Adds a new data entry to the file. Check the documentation for `brimfile.data.Data._add_data` for more details on the parameters.
181        Parameters:
182            PSD (np.ndarray): The Power Spectral Density (PSD) data to be added. The last dimension contains the spectra.
183            frequency (np.ndarray): The frequency data corresponding to the PSD. Must be broadcastable to the PSD array.
184            scanning (dict, optional): Spatial mapping metadata. Required for sparse=True, optional for sparse=False.
185                See `brimfile.data.Data._add_data` docstring for detailed structure.
186            px_size_um (tuple, optional): A tuple of 3 elements (z, y, x) for pixel size in μm. For non-sparse data only.
187            timestamp (np.ndarray, optional): Timestamps in milliseconds for the data. Defaults to None.
188            sparse (bool): Whether the data is sparse. See https://github.com/brillouin-imaging/Brillouin-standard-file/blob/main/docs/brim_file_specs.md for details. Defaults to False.
189            index (int, optional): The index for the new data group. If None, the next available index is used. Defaults to None.
190            name (str, optional): The name for the new data group. Defaults to None.
191            compression (FileAbstraction.Compression, optional): The compression method to use for the data. Defaults to FileAbstraction.Compression.DEFAULT.
192        Returns:
193            Data: The newly created Data object.
194        Raises:
195            IndexError: If the specified index already exists in the dataset.
196            ValueError: If any of the data provided is not valid or consistent
197        """
198        if index is not None:
199            if sync(Data._get_existing_group_name_async(self._file, index)) is not None:
200                raise IndexError(
201                    f"Data {index} already exists in {self._file.filename}")
202        else:
203            data_groups = self.list_data_groups()
204            indices = [dg['index'] for dg in data_groups]
205            indices.sort()
206            index = indices[-1] + 1 if indices else 0  # Next available index
207
208        # create the data group
209        d = Data._create_new(self._file, index, sparse, name)
210        # add the pixel size as an attribute of the data group
211        if px_size_um is not None:
212            sync(self._file.create_attr(d._group, 'element_size', tuple(px_size_um)))
213            units.add_to_attribute(self._file, d._group, 'element_size', 'um')
214        elif not sparse:
215            warnings.warn("Pixel size is not provided for non-sparse data. It is recommended to provide it for proper spatial calibration and visualization.")
216        # add the data to the data group
217        d._add_data(PSD, frequency, scanning = scanning,
218                   timestamp=timestamp, compression=compression)
219        return d
220
221    def list_data_groups(self, retrieve_custom_name=False) -> list:
222        """
223        List all data groups in the brim file.
224
225        Returns:
226            See documentation of brimfile.data.Data.list_data_groups
227        """
228        return Data.list_data_groups(self._file, retrieve_custom_name)
229
230    def get_data(self, index: int = 0) -> 'Data':
231        """
232        Retrieve a Data object for the specified index.
233
234        Args:
235            index (int): The index of the data group to retrieve.
236
237        Returns:
238            Data: The Data object corresponding to the specified index.
239        Raises:
240            IndexError: If the specified index does not exist in the dataset.
241        """        
242        return sync(Data.from_existing_async(self._file, index))
243
244    @property
245    def filename(self) -> str:
246        """
247        Get the filename of the brim file.
248
249        Returns:
250            str: The filename of the brim file.
251        """
252        return self._file.filename
253    
254    @property
255    def subtype(self) -> subtypes.SubType:
256        """
257        Get the subtype of the brim file.
258
259        Returns:
260            subtypes.SubType: The subtype of the brim file.
261        """
262        return subtypes.get_subtype(self._file)
class File:
 23class File:
 24    """
 25    Represents a brim file with Brillouin data, extending h5py.File.
 26    """
 27
 28    if "pyodide" in sys.modules:
 29        def __init__(self, file):
 30            self._file = file
 31            if not self.is_valid():
 32                raise ValueError("The brim file is not valid!")
 33    else:
 34        def __init__(self, filename: str, mode: str = 'r',
 35                     store_type: StoreType = StoreType.AUTO, * ,
 36                     validate: bool = False) -> None:
 37            """
 38            Initialize the File object.
 39
 40            Args:
 41                filename (str): Path to the brim file.
 42                mode: {'r', 'r+', 'a', 'w', 'w-'} the mode for opening the file (default is 'r' for read-only).
 43                            See the definition of `mode` in `brimfile.file_abstraction._zarrFile.__init__()` for more details.
 44                            'r' means read only (must exist); 'r+' means read/write (must exist);
 45                            'a' means read/write (create if doesn't exist); 'w' means create (overwrite if exists); 'w-' means create (fail if exists).
 46                store_type (StoreType): Type of the store to use, as defined in `brimfile.file_abstraction.StoreType`. Default is 'AUTO'.
 47                validate (bool): Whether to validate the file upon initialization. Default is False.
 48            """
 49            self._file = _AbstractFile(
 50                filename, mode=mode, store_type=store_type)
 51            if not self.is_valid():
 52                raise ValueError("The brim file is not valid!")
 53            if validate:
 54                validation_errors: list[ValidationError] = self.validate()
 55                for err in validation_errors:
 56                    if err.level == ValidationLevel.WARNING or err.level == ValidationLevel.ERROR:
 57                        warnings.warn(f"Validation warning at {err.path}: {err.message}")
 58                    elif err.level == ValidationLevel.CRITICAL:
 59                        raise ValueError(f"Validation error at {err.path}: {err.message}")
 60
 61    def validate(self) -> list[ValidationError]:
 62        """
 63        Validate the brim file and return a list of validation errors.
 64
 65        Returns:
 66            list[ValidationError]: A list of validation errors found in the brim file.
 67            If the list is empty, the file is valid.
 68        """
 69        json_descriptor = generate_json_descriptor(self._file)
 70        validation_errors: list[ValidationError] = validate_json(json_descriptor)
 71        return validation_errors
 72    
 73    def __del__(self):
 74        try:
 75            if hasattr(self, '_file'):
 76                self.close()
 77        except Exception as e:            
 78            # don't throw an error if the file cannot be closed
 79            warnings.warn(f"Cannot close the file: {e}")
 80
 81    def close(self) -> None:
 82        self._file.close()
 83
 84    def is_read_only(self) -> bool:
 85        return sync(self._file.is_read_only())
 86
 87    def is_valid(self) -> bool:
 88        """
 89        Check if the file is a valid brim file.
 90
 91        Returns:
 92            bool: True if the file is valid, False otherwise.
 93        """
 94        # TODO validate file against https://github.com/brillouin-imaging/Brillouin-standard-file/blob/main/docs/brim_file_specs.md
 95        return True
 96
 97    @classmethod
 98    def create(cls, filename: str, store_type: StoreType = StoreType.AUTO, *, 
 99               brim_version: str = '0.1') -> 'File':
100        """
101        Create a new brim file with the specified filename. If the file exists already it will generate an error.
102
103        Args:
104            filename (str): Path to the brim file to be created.
105            store_type (StoreType): Type of the store to use, as defined in `brimfile.file_abstraction.StoreType`. Default is 'AUTO'.
106            brim_version (str): Version of the brim file format to use. Default is '0.1'.
107
108        Returns:
109            File: An instance of the File class representing the newly created brim file.
110            store_type (str): Type of the store to use, as defined in `brimfile.file_abstraction.StoreType`. Default is 'AUTO'.
111        """
112        f = cls(filename, mode='w-', store_type=store_type)
113
114        # File version
115        sync(f._file.create_attr('/', 'brim_version', brim_version))
116
117        # Root Brillouin_data group
118        sync(f._file.create_group(brim_obj_names.Brillouin_base_path))
119
120        return f
121
122    def create_data_group(self, PSD: np.ndarray, frequency: np.ndarray, px_size_um: tuple, *, index: int = None,
123                          name: str = None, compression: FileAbstraction.Compression = FileAbstraction.Compression()) -> 'Data':
124        """
125        Adds a new data entry to the file.
126        Parameters:
127            PSD (np.ndarray): The Power Spectral Density (PSD) data to be added. It must be 4D with dimensions z, y, x, spectrum
128            frequency (np.ndarray): The frequency data corresponding to the PSD. It must be broadcastable to the PSD shape (the most common case is frequency being 1D, in which case the frequency axis is assumed the same for all the spatial coordinates)
129            px_size_um (tuple): A tuple of 3 elements, in the order z,y,x, corresponding to the pixel size in um. Unused dimensions can be set to None.
130            index (int, optional): The index for the new data group. If None, the next available index is used. Defaults to None.
131            name (str, optional): The name for the new data group. Defaults to None.
132            compression (FileAbstraction.Compression, optional): The compression method to use for the data. Defaults to FileAbstraction.Compression.DEFAULT.
133        Returns:
134            Data: The newly created Data object.
135        Raises:
136            IndexError: If the specified index already exists in the dataset.
137            ValueError: If any of the data provided is not valid or consistent
138        """
139        if PSD.ndim != 4:
140            raise ValueError(
141                "'PSD' must have 4 dimensions (z, y, x, spectrum)")
142        try:
143            np.broadcast_shapes(tuple(frequency.shape), tuple(PSD.shape))
144        except ValueError as e:
145            raise ValueError(f"frequency (shape: {frequency.shape}) is not broadcastable to PSD (shape: {PSD.shape}): {e}")
146        if len(px_size_um) != 3:
147            raise ValueError("'px_size_um' must have 3 elements (z,y,x); unused dimensions can be set to None")
148
149        return self._create_data_group_raw(PSD, frequency, scanning = None, sparse = False, px_size_um=px_size_um, 
150                                             index=index, name=name, compression=compression)
151
152    def create_data_group_sparse(self, PSD: np.ndarray, frequency: np.ndarray, scanning: dict, *, timestamp: np.ndarray = None,
153                                index: int = None, name: str = None, compression: FileAbstraction.Compression = FileAbstraction.Compression()) -> 'Data':
154        """
155        Adds a new [sparse data entry](https://github.com/brillouin-imaging/Brillouin-standard-file/blob/main/docs/brim_file_specs.md) to the file.
156        
157        Sparse data allows storage of spectra in a flattened format (first dimension is the spectrum index),
158        with spatial mapping provided separately. This is efficient for data with irregular sampling or missing pixels.
159        
160        Parameters:
161            PSD (np.ndarray): The Power Spectral Density (PSD) data to be added. First dimension is spectrum index,
162                last dimension contains the spectral data. Shape: (n_spectra, ..., n_freq_points).
163            frequency (np.ndarray): The frequency data corresponding to the PSD. Must be broadcastable to the PSD array.
164            scanning (dict): Dictionary defining the spatial mapping. Must include at least 'Spatial_map' or 'Cartesian_visualisation'.
165                See `brimfile.data.Data._add_data` docstring for detailed structure of the scanning dictionary.
166            timestamp (np.ndarray, optional): Timestamps in milliseconds for the data. Defaults to None.
167            index (int, optional): The index for the new data group. If None, the next available index is used. Defaults to None.
168            name (str, optional): The name for the new data group. Defaults to None.
169            compression (FileAbstraction.Compression, optional): The compression method to use for the data. Defaults to FileAbstraction.Compression.DEFAULT.
170        Returns:
171            Data: The newly created Data object.
172        Raises:
173            IndexError: If the specified index already exists in the dataset.
174            ValueError: If any of the data provided is not valid or consistent
175        """
176        return self._create_data_group_raw(PSD, frequency, scanning=scanning, timestamp=timestamp, sparse=True, index=index, name=name, compression=compression)   
177    
178    def _create_data_group_raw(self, PSD: np.ndarray, frequency: np.ndarray, *, scanning: dict = None, px_size_um = None, timestamp: np.ndarray = None, sparse: bool = False,
179                                index: int = None, name: str = None, compression: FileAbstraction.Compression = FileAbstraction.Compression()) -> 'Data':
180        """
181        Adds a new data entry to the file. Check the documentation for `brimfile.data.Data._add_data` for more details on the parameters.
182        Parameters:
183            PSD (np.ndarray): The Power Spectral Density (PSD) data to be added. The last dimension contains the spectra.
184            frequency (np.ndarray): The frequency data corresponding to the PSD. Must be broadcastable to the PSD array.
185            scanning (dict, optional): Spatial mapping metadata. Required for sparse=True, optional for sparse=False.
186                See `brimfile.data.Data._add_data` docstring for detailed structure.
187            px_size_um (tuple, optional): A tuple of 3 elements (z, y, x) for pixel size in μm. For non-sparse data only.
188            timestamp (np.ndarray, optional): Timestamps in milliseconds for the data. Defaults to None.
189            sparse (bool): Whether the data is sparse. See https://github.com/brillouin-imaging/Brillouin-standard-file/blob/main/docs/brim_file_specs.md for details. Defaults to False.
190            index (int, optional): The index for the new data group. If None, the next available index is used. Defaults to None.
191            name (str, optional): The name for the new data group. Defaults to None.
192            compression (FileAbstraction.Compression, optional): The compression method to use for the data. Defaults to FileAbstraction.Compression.DEFAULT.
193        Returns:
194            Data: The newly created Data object.
195        Raises:
196            IndexError: If the specified index already exists in the dataset.
197            ValueError: If any of the data provided is not valid or consistent
198        """
199        if index is not None:
200            if sync(Data._get_existing_group_name_async(self._file, index)) is not None:
201                raise IndexError(
202                    f"Data {index} already exists in {self._file.filename}")
203        else:
204            data_groups = self.list_data_groups()
205            indices = [dg['index'] for dg in data_groups]
206            indices.sort()
207            index = indices[-1] + 1 if indices else 0  # Next available index
208
209        # create the data group
210        d = Data._create_new(self._file, index, sparse, name)
211        # add the pixel size as an attribute of the data group
212        if px_size_um is not None:
213            sync(self._file.create_attr(d._group, 'element_size', tuple(px_size_um)))
214            units.add_to_attribute(self._file, d._group, 'element_size', 'um')
215        elif not sparse:
216            warnings.warn("Pixel size is not provided for non-sparse data. It is recommended to provide it for proper spatial calibration and visualization.")
217        # add the data to the data group
218        d._add_data(PSD, frequency, scanning = scanning,
219                   timestamp=timestamp, compression=compression)
220        return d
221
222    def list_data_groups(self, retrieve_custom_name=False) -> list:
223        """
224        List all data groups in the brim file.
225
226        Returns:
227            See documentation of brimfile.data.Data.list_data_groups
228        """
229        return Data.list_data_groups(self._file, retrieve_custom_name)
230
231    def get_data(self, index: int = 0) -> 'Data':
232        """
233        Retrieve a Data object for the specified index.
234
235        Args:
236            index (int): The index of the data group to retrieve.
237
238        Returns:
239            Data: The Data object corresponding to the specified index.
240        Raises:
241            IndexError: If the specified index does not exist in the dataset.
242        """        
243        return sync(Data.from_existing_async(self._file, index))
244
245    @property
246    def filename(self) -> str:
247        """
248        Get the filename of the brim file.
249
250        Returns:
251            str: The filename of the brim file.
252        """
253        return self._file.filename
254    
255    @property
256    def subtype(self) -> subtypes.SubType:
257        """
258        Get the subtype of the brim file.
259
260        Returns:
261            subtypes.SubType: The subtype of the brim file.
262        """
263        return subtypes.get_subtype(self._file)

Represents a brim file with Brillouin data, extending h5py.File.

File( filename: str, mode: str = 'r', store_type: brimfile.file_abstraction.StoreType = <StoreType.AUTO: 'auto'>, *, validate: bool = False)
34        def __init__(self, filename: str, mode: str = 'r',
35                     store_type: StoreType = StoreType.AUTO, * ,
36                     validate: bool = False) -> None:
37            """
38            Initialize the File object.
39
40            Args:
41                filename (str): Path to the brim file.
42                mode: {'r', 'r+', 'a', 'w', 'w-'} the mode for opening the file (default is 'r' for read-only).
43                            See the definition of `mode` in `brimfile.file_abstraction._zarrFile.__init__()` for more details.
44                            'r' means read only (must exist); 'r+' means read/write (must exist);
45                            'a' means read/write (create if doesn't exist); 'w' means create (overwrite if exists); 'w-' means create (fail if exists).
46                store_type (StoreType): Type of the store to use, as defined in `brimfile.file_abstraction.StoreType`. Default is 'AUTO'.
47                validate (bool): Whether to validate the file upon initialization. Default is False.
48            """
49            self._file = _AbstractFile(
50                filename, mode=mode, store_type=store_type)
51            if not self.is_valid():
52                raise ValueError("The brim file is not valid!")
53            if validate:
54                validation_errors: list[ValidationError] = self.validate()
55                for err in validation_errors:
56                    if err.level == ValidationLevel.WARNING or err.level == ValidationLevel.ERROR:
57                        warnings.warn(f"Validation warning at {err.path}: {err.message}")
58                    elif err.level == ValidationLevel.CRITICAL:
59                        raise ValueError(f"Validation error at {err.path}: {err.message}")

Initialize the File object.

Arguments:
  • filename (str): Path to the brim file.
  • mode: {'r', 'r+', 'a', 'w', 'w-'} the mode for opening the file (default is 'r' for read-only). See the definition of mode in brimfile.file_abstraction._zarrFile.__init__() for more details. 'r' means read only (must exist); 'r+' means read/write (must exist); 'a' means read/write (create if doesn't exist); 'w' means create (overwrite if exists); 'w-' means create (fail if exists).
  • store_type (StoreType): Type of the store to use, as defined in brimfile.file_abstraction.StoreType. Default is 'AUTO'.
  • validate (bool): Whether to validate the file upon initialization. Default is False.
def validate(self) -> list[brimfile.validation.main.ValidationError]:
61    def validate(self) -> list[ValidationError]:
62        """
63        Validate the brim file and return a list of validation errors.
64
65        Returns:
66            list[ValidationError]: A list of validation errors found in the brim file.
67            If the list is empty, the file is valid.
68        """
69        json_descriptor = generate_json_descriptor(self._file)
70        validation_errors: list[ValidationError] = validate_json(json_descriptor)
71        return validation_errors

Validate the brim file and return a list of validation errors.

Returns:

list[ValidationError]: A list of validation errors found in the brim file. If the list is empty, the file is valid.

def close(self) -> None:
81    def close(self) -> None:
82        self._file.close()
def is_read_only(self) -> bool:
84    def is_read_only(self) -> bool:
85        return sync(self._file.is_read_only())
def is_valid(self) -> bool:
87    def is_valid(self) -> bool:
88        """
89        Check if the file is a valid brim file.
90
91        Returns:
92            bool: True if the file is valid, False otherwise.
93        """
94        # TODO validate file against https://github.com/brillouin-imaging/Brillouin-standard-file/blob/main/docs/brim_file_specs.md
95        return True

Check if the file is a valid brim file.

Returns:

bool: True if the file is valid, False otherwise.

@classmethod
def create( cls, filename: str, store_type: brimfile.file_abstraction.StoreType = <StoreType.AUTO: 'auto'>, *, brim_version: str = '0.1') -> File:
 97    @classmethod
 98    def create(cls, filename: str, store_type: StoreType = StoreType.AUTO, *, 
 99               brim_version: str = '0.1') -> 'File':
100        """
101        Create a new brim file with the specified filename. If the file exists already it will generate an error.
102
103        Args:
104            filename (str): Path to the brim file to be created.
105            store_type (StoreType): Type of the store to use, as defined in `brimfile.file_abstraction.StoreType`. Default is 'AUTO'.
106            brim_version (str): Version of the brim file format to use. Default is '0.1'.
107
108        Returns:
109            File: An instance of the File class representing the newly created brim file.
110            store_type (str): Type of the store to use, as defined in `brimfile.file_abstraction.StoreType`. Default is 'AUTO'.
111        """
112        f = cls(filename, mode='w-', store_type=store_type)
113
114        # File version
115        sync(f._file.create_attr('/', 'brim_version', brim_version))
116
117        # Root Brillouin_data group
118        sync(f._file.create_group(brim_obj_names.Brillouin_base_path))
119
120        return f

Create a new brim file with the specified filename. If the file exists already it will generate an error.

Arguments:
  • filename (str): Path to the brim file to be created.
  • store_type (StoreType): Type of the store to use, as defined in brimfile.file_abstraction.StoreType. Default is 'AUTO'.
  • brim_version (str): Version of the brim file format to use. Default is '0.1'.
Returns:

File: An instance of the File class representing the newly created brim file. store_type (str): Type of the store to use, as defined in brimfile.file_abstraction.StoreType. Default is 'AUTO'.

def create_data_group( self, PSD: numpy.ndarray, frequency: numpy.ndarray, px_size_um: tuple, *, index: int = None, name: str = None, compression: brimfile.file_abstraction.FileAbstraction.Compression = <brimfile.file_abstraction.FileAbstraction.Compression object>) -> brimfile.data.Data:
122    def create_data_group(self, PSD: np.ndarray, frequency: np.ndarray, px_size_um: tuple, *, index: int = None,
123                          name: str = None, compression: FileAbstraction.Compression = FileAbstraction.Compression()) -> 'Data':
124        """
125        Adds a new data entry to the file.
126        Parameters:
127            PSD (np.ndarray): The Power Spectral Density (PSD) data to be added. It must be 4D with dimensions z, y, x, spectrum
128            frequency (np.ndarray): The frequency data corresponding to the PSD. It must be broadcastable to the PSD shape (the most common case is frequency being 1D, in which case the frequency axis is assumed the same for all the spatial coordinates)
129            px_size_um (tuple): A tuple of 3 elements, in the order z,y,x, corresponding to the pixel size in um. Unused dimensions can be set to None.
130            index (int, optional): The index for the new data group. If None, the next available index is used. Defaults to None.
131            name (str, optional): The name for the new data group. Defaults to None.
132            compression (FileAbstraction.Compression, optional): The compression method to use for the data. Defaults to FileAbstraction.Compression.DEFAULT.
133        Returns:
134            Data: The newly created Data object.
135        Raises:
136            IndexError: If the specified index already exists in the dataset.
137            ValueError: If any of the data provided is not valid or consistent
138        """
139        if PSD.ndim != 4:
140            raise ValueError(
141                "'PSD' must have 4 dimensions (z, y, x, spectrum)")
142        try:
143            np.broadcast_shapes(tuple(frequency.shape), tuple(PSD.shape))
144        except ValueError as e:
145            raise ValueError(f"frequency (shape: {frequency.shape}) is not broadcastable to PSD (shape: {PSD.shape}): {e}")
146        if len(px_size_um) != 3:
147            raise ValueError("'px_size_um' must have 3 elements (z,y,x); unused dimensions can be set to None")
148
149        return self._create_data_group_raw(PSD, frequency, scanning = None, sparse = False, px_size_um=px_size_um, 
150                                             index=index, name=name, compression=compression)

Adds a new data entry to the file.

Arguments:
  • PSD (np.ndarray): The Power Spectral Density (PSD) data to be added. It must be 4D with dimensions z, y, x, spectrum
  • frequency (np.ndarray): The frequency data corresponding to the PSD. It must be broadcastable to the PSD shape (the most common case is frequency being 1D, in which case the frequency axis is assumed the same for all the spatial coordinates)
  • px_size_um (tuple): A tuple of 3 elements, in the order z,y,x, corresponding to the pixel size in um. Unused dimensions can be set to None.
  • index (int, optional): The index for the new data group. If None, the next available index is used. Defaults to None.
  • name (str, optional): The name for the new data group. Defaults to None.
  • compression (FileAbstraction.Compression, optional): The compression method to use for the data. Defaults to FileAbstraction.Compression.DEFAULT.
Returns:

Data: The newly created Data object.

Raises:
  • IndexError: If the specified index already exists in the dataset.
  • ValueError: If any of the data provided is not valid or consistent
def create_data_group_sparse( self, PSD: numpy.ndarray, frequency: numpy.ndarray, scanning: dict, *, timestamp: numpy.ndarray = None, index: int = None, name: str = None, compression: brimfile.file_abstraction.FileAbstraction.Compression = <brimfile.file_abstraction.FileAbstraction.Compression object>) -> brimfile.data.Data:
152    def create_data_group_sparse(self, PSD: np.ndarray, frequency: np.ndarray, scanning: dict, *, timestamp: np.ndarray = None,
153                                index: int = None, name: str = None, compression: FileAbstraction.Compression = FileAbstraction.Compression()) -> 'Data':
154        """
155        Adds a new [sparse data entry](https://github.com/brillouin-imaging/Brillouin-standard-file/blob/main/docs/brim_file_specs.md) to the file.
156        
157        Sparse data allows storage of spectra in a flattened format (first dimension is the spectrum index),
158        with spatial mapping provided separately. This is efficient for data with irregular sampling or missing pixels.
159        
160        Parameters:
161            PSD (np.ndarray): The Power Spectral Density (PSD) data to be added. First dimension is spectrum index,
162                last dimension contains the spectral data. Shape: (n_spectra, ..., n_freq_points).
163            frequency (np.ndarray): The frequency data corresponding to the PSD. Must be broadcastable to the PSD array.
164            scanning (dict): Dictionary defining the spatial mapping. Must include at least 'Spatial_map' or 'Cartesian_visualisation'.
165                See `brimfile.data.Data._add_data` docstring for detailed structure of the scanning dictionary.
166            timestamp (np.ndarray, optional): Timestamps in milliseconds for the data. Defaults to None.
167            index (int, optional): The index for the new data group. If None, the next available index is used. Defaults to None.
168            name (str, optional): The name for the new data group. Defaults to None.
169            compression (FileAbstraction.Compression, optional): The compression method to use for the data. Defaults to FileAbstraction.Compression.DEFAULT.
170        Returns:
171            Data: The newly created Data object.
172        Raises:
173            IndexError: If the specified index already exists in the dataset.
174            ValueError: If any of the data provided is not valid or consistent
175        """
176        return self._create_data_group_raw(PSD, frequency, scanning=scanning, timestamp=timestamp, sparse=True, index=index, name=name, compression=compression)   

Adds a new sparse data entry to the file.

Sparse data allows storage of spectra in a flattened format (first dimension is the spectrum index), with spatial mapping provided separately. This is efficient for data with irregular sampling or missing pixels.

Arguments:
  • PSD (np.ndarray): The Power Spectral Density (PSD) data to be added. First dimension is spectrum index, last dimension contains the spectral data. Shape: (n_spectra, ..., n_freq_points).
  • frequency (np.ndarray): The frequency data corresponding to the PSD. Must be broadcastable to the PSD array.
  • scanning (dict): Dictionary defining the spatial mapping. Must include at least 'Spatial_map' or 'Cartesian_visualisation'. See brimfile.data.Data._add_data docstring for detailed structure of the scanning dictionary.
  • timestamp (np.ndarray, optional): Timestamps in milliseconds for the data. Defaults to None.
  • index (int, optional): The index for the new data group. If None, the next available index is used. Defaults to None.
  • name (str, optional): The name for the new data group. Defaults to None.
  • compression (FileAbstraction.Compression, optional): The compression method to use for the data. Defaults to FileAbstraction.Compression.DEFAULT.
Returns:

Data: The newly created Data object.

Raises:
  • IndexError: If the specified index already exists in the dataset.
  • ValueError: If any of the data provided is not valid or consistent
def list_data_groups(self, retrieve_custom_name=False) -> list:
222    def list_data_groups(self, retrieve_custom_name=False) -> list:
223        """
224        List all data groups in the brim file.
225
226        Returns:
227            See documentation of brimfile.data.Data.list_data_groups
228        """
229        return Data.list_data_groups(self._file, retrieve_custom_name)

List all data groups in the brim file.

Returns:

See documentation of brimfile.data.Data.list_data_groups

def get_data(self, index: int = 0) -> brimfile.data.Data:
231    def get_data(self, index: int = 0) -> 'Data':
232        """
233        Retrieve a Data object for the specified index.
234
235        Args:
236            index (int): The index of the data group to retrieve.
237
238        Returns:
239            Data: The Data object corresponding to the specified index.
240        Raises:
241            IndexError: If the specified index does not exist in the dataset.
242        """        
243        return sync(Data.from_existing_async(self._file, index))

Retrieve a Data object for the specified index.

Arguments:
  • index (int): The index of the data group to retrieve.
Returns:

Data: The Data object corresponding to the specified index.

Raises:
  • IndexError: If the specified index does not exist in the dataset.
filename: str
245    @property
246    def filename(self) -> str:
247        """
248        Get the filename of the brim file.
249
250        Returns:
251            str: The filename of the brim file.
252        """
253        return self._file.filename

Get the filename of the brim file.

Returns:

str: The filename of the brim file.

255    @property
256    def subtype(self) -> subtypes.SubType:
257        """
258        Get the subtype of the brim file.
259
260        Returns:
261            subtypes.SubType: The subtype of the brim file.
262        """
263        return subtypes.get_subtype(self._file)

Get the subtype of the brim file.

Returns:

subtypes.SubType: The subtype of the brim file.