コード例 #1
0
ファイル: source.py プロジェクト: irischang020/condor
 def __init__(self,
              wavelength,
              focus_diameter,
              pulse_energy,
              profile_model=None,
              pulse_energy_variation=None,
              pulse_energy_spread=None,
              pulse_energy_variation_n=None,
              polarization="ignore"):
     self.photon = Photon(wavelength=wavelength)
     self.pulse_energy_mean = pulse_energy
     self.set_pulse_energy_variation(pulse_energy_variation,
                                     pulse_energy_spread,
                                     pulse_energy_variation_n)
     self.profile = Profile(model=profile_model,
                            focus_diameter=focus_diameter)
     if polarization not in [
             "vertical", "horizontal", "unpolarized", "ignore"
     ]:
         log_and_raise_error(
             logger,
             "polarization = \"%s\" is an invalid input for initialization of Source instance."
         )
         return
     self.polarization = polarization
     log_debug(logger, "Source configured")
コード例 #2
0
ファイル: particle_map.py プロジェクト: FXIhub/condor
    def set_custom_geometry_by_mrcfile(self, filename, offset=None, factor=None):
        """
        Read map from the MRC file (CCP4 file format, see http://www.ccp4.ac.uk/html/maplib.html).

        The map will be preprocessed by applying an offset and rescaling and by padding the water background with zeros.

        Finally, the avereage value of the map will be rescaled by the refractive index of the associated material.

        Args:
          :filename (str): Filename of MRC file.

          :offset (float): Offset value of the map (MAP = (EM_DATA + OFFSET) X FACTOR)

          :factor (float): Rescale factor of the map (MAP = (EM_DATA + OFFSET) X FACTOR)
        """
        map3d, dx = condor.utils.emdio.read_map(filename)
        if offset is None and factor is None:
            ed_water = condor.utils.material.AtomDensityMaterial(material_type="water").get_electron_density()
            if len(self.materials) > 1:
                log_and_raise_error(
                    logger, "More than one material defined. This is incompatible with automatic scaling of an EMD map."
                )
                sys.exit(1)
            ed_particle = self.materials[0].get_electron_density()
            map3d = condor.utils.emdio.preproc_map_auto(map3d, ed_water=ed_water, ed_particle=ed_particle)
        else:
            map3d = condor.utils.emdio.perproc_map_manual(map3d, offset=offset, factor=factor)
        self.set_custom_geometry_by_array(map3d, dx)
コード例 #3
0
    def get_linear_sampling_ratio(self, wavelength = None, particle_diameter = None, particle_key = None):
        """
        Returns the linear sampling ratio :math:`o` of the diffraction pattern:

        | :math:`o=\\frac{D\\lambda}{dp}` 

        | :math:`D`: Detector distance
        | :math:`p`: Detector pixel size (edge length)
        | :math:`\\lambda`: Photon wavelength 
        | :math:`d`: Particle diameter

        """
        if wavelength is None:
            wavelength = self.source.photon.get_wavelength()
        detector_distance = self.detector.distance
        if particle_diameter is None:
            if len(self.particles) == 1:
                p = self.particles.values()[0]
            elif particle_key is None:
                log_and_raise_error(logger, "You need to specify a particle_key because there are more than one particle models.")
            else:
                p = self.particles[particle_key]
            particle_diameter = p.diameter_mean
        pN = utils.diffraction.nyquist_pixel_size(wavelength, detector_distance, particle_diameter)
        pD = self.detector.pixel_size
        ratio = pN/pD
        return ratio
コード例 #4
0
ファイル: particle_abstract.py プロジェクト: qsadhu/condor
    def add_material(self, material_type, massdensity, atomic_composition,
                     electron_density):
        """
        Initialise and add the AtomDensityMaterial / ElectronDensityMaterial class instance to the particle

        Args:
          :material_type (str): See :class:`condor.utils.material.AtomDensityMaterial`

          :massdensity (float): See :class:`condor.utils.material.AtomDensityMaterial`

          :atomic_composition (dict): See :class:`condor.utils.material.AtomDensityMaterial`

          :electron_density (float): See :class:`condor.utils.material.ElectronDensityMaterial`
        """
        if electron_density is None:
            self.materials.append(
                AtomDensityMaterial(material_type=material_type,
                                    massdensity=massdensity,
                                    atomic_composition=atomic_composition))
        else:
            if massdensity is not None or atomic_composition is not None:
                log_and_raise_error(
                    logger,
                    r"An electron density is defined so material_type, massdensity and atomic_composition have to be all 'None'."
                )
                return
            if material_type != "custom":
                log_and_raise_error(
                    logger,
                    r"An electron density is defined, so material_type must be \'custom\' but is %s."
                    % material_type)
                return
            self.materials.append(
                ElectronDensityMaterial(electron_density=electron_density))
コード例 #5
0
ファイル: material.py プロジェクト: qsadhu/condor
 def __init__(self, shape):
     if len(shape) != 3:
         log_and_raise_error(
             logger,
             "%s is an invald shape for initialisation of MaterialMap.",
             str(shape))
     self._shape = tuple(shape)
コード例 #6
0
ファイル: detector.py プロジェクト: qsadhu/condor
    def get_p_max_dist(self, cx = None, cy = None, pos = "corner", center_variation = False):
        r"""
        Return 3D position vector of the pixel furthest away from the beam center. If each of the given center position coordinates (``cx``, ``cy``) is ``None`` the beam center is assumed to be located at its mean position
        
        Kwargs:
          :cx (float): *x*-coordinate of the center position in unit pixel (default ``None``)

          :cy (float): *y*-coordinate of the center position in unit pixel (default ``None``)

          :pos (str): Position constraint can be either ``pos='corner'`` or ``pos='edge'``. (default ``'corner'``)

          :center_variation (bool): If ``True`` the beam center variation is taken into account. With respect to the mean position a maximum deviation of *factor/2* times the variational spread is assumed. The *factor* is 3 for Gaussian distributed centers and 1 for others  (default ``False``)
        """
        x, y = self._get_xy_max_dist(cx=cx, cy=cy, center_variation=center_variation)
        xm = x*self.pixel_size
        ym = y*self.pixel_size        
        log_debug(logger, "x = %.1f pix, y = %.1f pix" % (x, y))
        p = numpy.array([0.,0.,self.distance])
        if pos == "corner":
            p[0] = xm
            p[1] = ym
        elif pos == "edge":
            if abs(x) > abs(y):
                p[0] = xm
            else:
                p[1] = ym
        else:
            log_and_raise_error(logger, r"Invalid input: pos=%s. Input must be either 'corner' or 'edge'." % pos)
        return p
コード例 #7
0
def experiment_from_configdict(configdict):
    """
    Initialise Experiment instance from a dictionary

    *See also:*

      - :class:`condor.experiment.Experiment`

      - `Composing a configuration file <configdict.html>`_
    """
    # Source
    source = condor.Source(**configdict["source"])
    # Particles
    particle_keys = [k for k in configdict.keys() if k.startswith("particle")]
    particles = {}
    if len(particle_keys) == 0:
        log_and_raise_error(logger, "No particles defined.")
    for k in particle_keys:
        if k.startswith("particle_sphere"):
            particles[k] = condor.ParticleSphere(**configdict[k])
        elif k.startswith("particle_spheroid"):
            particles[k] = condor.ParticleSpheroid(**configdict[k])
        elif k.startswith("particle_map"):
            particles[k] = condor.ParticleMap(**configdict[k])
        elif k.startswith("particle_atoms"):
            particles[k] = condor.ParticleAtoms(**configdict[k])
        else:
            log_and_raise_error(logger,"Particle model for %s is not implemented." % k)
    # Detector
    detector = condor.Detector(**configdict["detector"])
    experiment = Experiment(source, particles, detector)
    return experiment
コード例 #8
0
    def get_linear_sampling_ratio(self, wavelength = None, particle_diameter = None, particle_key = None):
        """
        Returns the linear sampling ratio :math:`o` of the diffraction pattern:

        | :math:`o=\\frac{D\\lambda}{dp}` 

        | :math:`D`: Detector distance
        | :math:`p`: Detector pixel size (edge length)
        | :math:`\\lambda`: Photon wavelength 
        | :math:`d`: Particle diameter

        """
        if wavelength is None:
            wavelength = self.source.photon.get_wavelength()
        detector_distance = self.detector.distance
        if particle_diameter is None:
            if len(self.particles) == 1:
                p = self.particles.values()[0]
            elif particle_key is None:
                log_and_raise_error(logger, "You need to specify a particle_key because there are more than one particle models.")
            else:
                p = self.particles[particle_key]
            particle_diameter = p.diameter_mean
        pN = utils.diffraction.nyquist_pixel_size(wavelength, detector_distance, particle_diameter)
        pD = self.detector.pixel_size
        ratio = pN/pD
        return ratio
コード例 #9
0
    def set_custom_geometry_by_emd_id(self, emd_id, offset=None, factor=None):
        """
        Fetch map from the EMD by id code.

        The map will be preprocessed by applying an offset and rescaling and by padding the water background with zeros.

        Finally, the avereage value of the map will be rescaled by the refractive index of the associated material.

        Args:
          :emd_id (str): EMD ID code.

          :offset (float): Offset value of the map (MAP = (EM_DATA + OFFSET) X FACTOR)

          :factor (float): Rescale factor of the map (MAP = (EM_DATA + OFFSET) X FACTOR)
        """
        map3d, dx = condor.utils.emdio.fetch_map(emd_id)
        if offset is None and factor is None:
            ed_water = condor.utils.material.AtomDensityMaterial(
                material_type="water").get_electron_density()
            if len(self.materials) > 1:
                log_and_raise_error(
                    logger,
                    "More than one material defined. This is incompatible with automatic scaling of an EMD map."
                )
                sys.exit(1)
            ed_particle = self.materials[0].get_electron_density()
            map3d = condor.utils.emdio.preproc_map_auto(
                map3d, ed_water=ed_water, ed_particle=ed_particle)
        else:
            map3d = condor.utils.emdio.perproc_map_manual(map3d,
                                                          offset=offset,
                                                          factor=factor)
        self.set_custom_geometry_by_array(map3d, dx)
コード例 #10
0
ファイル: particle_map.py プロジェクト: FXIhub/condor
    def set_custom_geometry_by_emd_id(self, emd_id, offset=None, factor=None):
        """
        Fetch map from the EMD by id code.

        The map will be preprocessed by applying an offset and rescaling and by padding the water background with zeros.

        Finally, the avereage value of the map will be rescaled by the refractive index of the associated material.

        Args:
          :emd_id (str): EMD ID code.

          :offset (float): Offset value of the map (MAP = (EM_DATA + OFFSET) X FACTOR)

          :factor (float): Rescale factor of the map (MAP = (EM_DATA + OFFSET) X FACTOR)
        """
        map3d, dx = condor.utils.emdio.fetch_map(emd_id)
        if offset is None and factor is None:
            ed_water = condor.utils.material.AtomDensityMaterial(material_type="water").get_electron_density()
            if len(self.materials) > 1:
                log_and_raise_error(
                    logger, "More than one material defined. This is incompatible with automatic scaling of an EMD map."
                )
                sys.exit(1)
            ed_particle = self.materials[0].get_electron_density()
            map3d = condor.utils.emdio.preproc_map_auto(map3d, ed_water=ed_water, ed_particle=ed_particle)
        else:
            map3d = condor.utils.emdio.perproc_map_manual(map3d, offset=offset, factor=factor)
        self.set_custom_geometry_by_array(map3d, dx)
コード例 #11
0
def experiment_from_configdict(configdict):
    """
    Initialise Experiment instance from a dictionary

    *See also:*

      - :class:`condor.experiment.Experiment`

      - `Composing a configuration file <configdict.html>`_
    """
    # Source
    source = condor.Source(**configdict["source"])
    # Particles
    particle_keys = [k for k in configdict.keys() if k.startswith("particle")]
    particles = {}
    if len(particle_keys) == 0:
        log_and_raise_error(logger, "No particles defined.")
    for k in particle_keys:
        if k.startswith("particle_sphere"):
            particles[k] = condor.ParticleSphere(**configdict[k])
        elif k.startswith("particle_spheroid"):
            particles[k] = condor.ParticleSpheroid(**configdict[k])
        elif k.startswith("particle_map"):
            particles[k] = condor.ParticleMap(**configdict[k])
        elif k.startswith("particle_atoms"):
            particles[k] = condor.ParticleAtoms(**configdict[k])
        else:
            log_and_raise_error(logger,"Particle model for %s is not implemented." % k)
    # Detector
    detector = condor.Detector(**configdict["detector"])
    experiment = Experiment(source, particles, detector)
    return experiment
コード例 #12
0
 def add_material(self, material, density_map):
     if not isinstance(material, Material):
         log_and_raise_error(logger, "Cannot add material %s. It is not an instance of Material." % str(material))
     if density_map.shape != self._shape:
         log_and_raise_error(logger, "Cannot add material. Density map has incompatible shape: %s. Should be %s." % (str(density_map.shape), str(self._shape)))
     self.materials.append(material)
     self.density_maps.append(density_map)
コード例 #13
0
    def set_custom_geometry_by_h5file(self, map3d_filename, map3d_dataset, dx):
        """
        Load map from dataset in HDF5 file

        If a material is defined (``material_type`` is not ``None``) the absolute values of the map will be rescaled by the complex refractive index of the material. If no material is defined (``material_type=None``) the map will be casted to complex values and used without any rescaling.

        Args:
          :map3d_filename (str): Location of the HDF5 file that contains the map data

          :map3d_dataset (str): Dataset location in the file. The dataset must have three equal dimensions of float values.

          :dx: Grid spacing in unit meter
        """
        import h5py
        with h5py.File(map3d_filename, "r") as f:
            if map3d_dataset is not None:
                ds = map3d_dataset
            elif len(f.keys()) == 1:
                ds = f.keys()[0]
            else:
                log_and_raise_error(
                    logger, "No dataset specified where to find the map.")
            if len(f[ds].shape) == 4:
                map3d = numpy.array(f[ds][:, :, :, :])
            elif len(f[ds].shape) == 3:
                map3d = numpy.array([f[ds][:, :, :]])
            else:
                log_and_raise_error(
                    logger,
                    "Dataset has %i dimensions but it has to have either 3 or 4."
                    % len(f[ds].shape))
                return
        self.set_custom_geometry_by_array(map3d, dx)
コード例 #14
0
ファイル: particle_map.py プロジェクト: FXIhub/condor
    def set_custom_geometry_by_h5file(self, map3d_filename, map3d_dataset, dx):
        """
        Load map from dataset in HDF5 file

        If a material is defined (``material_type`` is not ``None``) the absolute values of the map will be rescaled by the complex refractive index of the material. If no material is defined (``material_type=None``) the map will be casted to complex values and used without any rescaling.

        Args:
          :map3d_filename (str): Location of the HDF5 file that contains the map data

          :map3d_dataset (str): Dataset location in the file. The dataset must have three equal dimensions of float values.

          :dx: Grid spacing in unit meter
        """
        import h5py

        with h5py.File(map3d_filename, "r") as f:
            if map3d_dataset is not None:
                ds = map3d_dataset
            elif len(f.keys()) == 1:
                ds = f.keys()[0]
            else:
                log_and_raise_error(logger, "No dataset specified where to find the map.")
            if len(f[ds].shape) == 4:
                map3d = numpy.array(f[ds][:, :, :, :])
            elif len(f[ds].shape) == 3:
                map3d = numpy.array([f[ds][:, :, :]])
            else:
                log_and_raise_error(
                    logger, "Dataset has %i dimensions but it has to have either 3 or 4." % len(f[ds].shape)
                )
                return
        self.set_custom_geometry_by_array(map3d, dx)
コード例 #15
0
ファイル: detector.py プロジェクト: FXIhub/condor
    def get_p_max_dist(self, cx = None, cy = None, pos = "corner", center_variation = False):
        r"""
        Return 3D position vector of the pixel furthest away from the beam center. If each of the given center position coordinates (``cx``, ``cy``) is ``None`` the beam center is assumed to be located at its mean position
        
        Kwargs:
          :cx (float): *x*-coordinate of the center position in unit pixel (default ``None``)

          :cy (float): *y*-coordinate of the center position in unit pixel (default ``None``)

          :pos (str): Position constraint can be either ``pos='corner'`` or ``pos='edge'``. (default ``'corner'``)

          :center_variation (bool): If ``True`` the beam center variation is taken into account. With respect to the mean position a maximum deviation of *factor/2* times the variational spread is assumed. The *factor* is 3 for Gaussian distributed centers and 1 for others  (default ``False``)
        """
        x, y = self._get_xy_max_dist(cx=cx, cy=cy, center_variation=center_variation)
        xm = x*self.pixel_size
        ym = y*self.pixel_size        
        log_debug(logger, "x = %.1f pix, y = %.1f pix" % (x, y))
        p = numpy.array([0.,0.,self.distance])
        if pos == "corner":
            p[0] = xm
            p[1] = ym
        elif pos == "edge":
            if abs(x) > abs(y):
                p[0] = xm
            else:
                p[1] = ym
        else:
            log_and_raise_error(logger, r"Invalid input: pos=%s. Input must be either 'corner' or 'edge'." % pos)
        return p
コード例 #16
0
ファイル: detector.py プロジェクト: FXIhub/condor
 def generate_qmap_3d(self, wavelength, qn=None, qmax=None, extrinsic_rotation=None, order='xyz'):
     if qn is None and qmax is None:
         qn = max([self._nx, self._ny])
         qmax = self.get_q_max(wavelength, pos="edge")
     elif qn is not None and qmax is not None:
         pass
     else:
         log_and_raise_error(logger, "Either none or both optional arguments qn and qmax have to be passed to this function.")
         return
     return condor.utils.scattering_vector.generate_qmap_3d(qn=qn, qmax=qmax, extrinsic_rotation=extrinsic_rotation, order=order)
コード例 #17
0
 def get_next_number_of_particles(self):
     """
     Iterate the number of partices
     """
     if self.arrival == "random":
         return int(numpy.random.poisson(self.number))
     elif self.arrival == "synchronised":
         return int(numpy.round(self.number))
     else:
         log_and_raise_error(logger, "self.arrival=%s is invalid. Has to be either \'synchronised\' or \'random\'." % self.arrival)
コード例 #18
0
ファイル: source.py プロジェクト: FXIhub/condor
 def __init__(self, wavelength, focus_diameter, pulse_energy, profile_model=None, pulse_energy_variation=None, pulse_energy_spread=None, pulse_energy_variation_n=None, polarization="ignore"):
     self.photon = Photon(wavelength=wavelength)
     self.pulse_energy_mean = pulse_energy
     self.set_pulse_energy_variation(pulse_energy_variation, pulse_energy_spread, pulse_energy_variation_n)
     self.profile = Profile(model=profile_model, focus_diameter=focus_diameter)
     if polarization not in ["vertical", "horizontal", "unpolarized", "ignore"]:
         log_and_raise_error(logger, "polarization = \"%s\" is an invalid input for initialization of Source instance.")
         return
     self.polarization = polarization
     log_debug(logger, "Source configured")
コード例 #19
0
ファイル: detector.py プロジェクト: qsadhu/condor
 def generate_qmap_3d(self, wavelength, qn=None, qmax=None, extrinsic_rotation=None, order='xyz'):
     if qn is None and qmax is None:
         qn = max([self._nx, self._ny])
         qmax = self.get_q_max(wavelength, pos="edge")
     elif qn is not None and qmax is not None:
         pass
     else:
         log_and_raise_error(logger, "Either none or both optional arguments qn and qmax have to be passed to this function.")
         return
     return condor.utils.scattering_vector.generate_qmap_3d(qn=qn, qmax=qmax, extrinsic_rotation=extrinsic_rotation, order=order)
コード例 #20
0
ファイル: detector.py プロジェクト: Rydeness/condor
 def _init_mask(self, mask, mask_is_cxi_bitmask, mask_filename,
                mask_dataset, nx, ny, x_gap_size_in_pixel,
                y_gap_size_in_pixel, cx_hole, cy_hole,
                hole_diameter_in_pixel):
     if mask is not None or (mask_filename is not None
                             and mask_dataset is not None):
         if mask is not None:
             # Copy mask from array
             self._mask = numpy.array(mask, dtype=numpy.uint16)
         else:
             # Read mask from file
             import h5py
             with h5py.File(mask_filename, "r") as f:
                 self._mask = numpy.array(f[mask_dataset][:, :],
                                          dtype=numpy.uint16)
         if not mask_is_cxi_bitmask:
             # Convert maskt to CXI bit format
             self._mask = (self._mask == 0) * PixelMask.PIXEL_IS_MISSING
     elif nx is not None and ny is not None:
         # Initialise empty mask
         self._mask = numpy.zeros(shape=(int(ny + y_gap_size_in_pixel),
                                         int(nx + x_gap_size_in_pixel)),
                                  dtype=numpy.uint16)
     else:
         log_and_raise_error(
             logger,
             r"Either 'mask' or 'nx' and 'ny' have to be specified.")
         sys.exit(1)
     self._nx = self._mask.shape[1]
     self._ny = self._mask.shape[0]
     # Mask out pixels in gaps
     if y_gap_size_in_pixel > 0:
         cy = int(numpy.ceil((self._ny - 1) / 2.))
         gy = int(numpy.round(y_gap_size_in_pixel))
         self._mask[cy - gy / 2:cy - gy / 2 +
                    gy, :] |= PixelMask.PIXEL_IS_MISSING
     if x_gap_size_in_pixel > 0:
         cx = int(numpy.ceil((self._nx - 1) / 2.))
         gx = int(numpy.round(x_gap_size_in_pixel))
         self._mask[:, cx - gx / 2:cx - gx / 2 +
                    gx] |= PixelMask.PIXEL_IS_MISSING
     # Mask out pixels in hole
     if hole_diameter_in_pixel > 0:
         if cx_hole is None:
             cx_hole = (self._nx - 1) / 2.
         if cy_hole is None:
             cy_hole = (self._ny - 1) / 2.
         Y, X = numpy.indices((self._ny, self._nx), dtype=numpy.float64)
         X = X - cx_hole
         Y = Y - cy_hole
         R = numpy.sqrt(X**2 + Y**2)
         tmp = R <= hole_diameter_in_pixel / 2.0
         if tmp.sum() > 0:
             self._mask[tmp] |= PixelMask.PIXEL_IS_MISSING
コード例 #21
0
 def __init__(self,
              pdb_filename = None, pdb_id = None,
              atomic_numbers = None, atomic_positions = None,
              rotation_values = None, rotation_formalism = None, rotation_mode = "extrinsic",
              number = 1., arrival = "synchronised",
              position = None,  position_variation = None, position_spread = None, position_variation_n = None):
     try:
         import spsim
     except Exception,e:
         print str(e)
         log_and_raise_error(logger, "Cannot import spsim module. This module is necessary to simulate diffraction for particle model of discrete atoms. Please install spsim from https://github.com/FilipeMaia/spsim and try again.")
         return
コード例 #22
0
    def set_atomic_concentration(self, element, relative_concentration):
        r"""
        Set the concentration of a given atomic species

        Args:
          :element (str): Atomic species (e.g. ``'H'`` for hydrogen)

          :relative_concentration (float): Relative quantity of atoms of the given atomic species with respect to the others (e.g. for water: hydrogen concentration ``2.``, oxygen concentration ``1.``)
        """
        if element not in atomic_names:
            log_and_raise_error(logger, "Cannot add element \"%s\". Invalid name." % element)
        self._atomic_composition[element] = relative_concentration
コード例 #23
0
 def get_resolution(self, wavelength = None, cx = None, cy = None, pos="corner", convention="full_period"):
     if wavelength is None:
         wavelength = self.source.photon.get_wavelength()
     dx = self.detector.get_resolution_element_x(wavelength, cx=cx, cy=cy)
     dy = self.detector.get_resolution_element_y(wavelength, cx=cx, cy=cy)
     dxdy = numpy.array([dx, dy])
     if convention == "full_period":
         return dxdy*2
     elif convention == "half_period":
         return dxdy
     else:
         log_and_raise_error(logger, "Invalid input: convention=%s. Must be either \"full_period\" or \"half_period\"." % convention)
         return
コード例 #24
0
 def get_resolution(self, wavelength = None, cx = None, cy = None, pos="corner", convention="full_period"):
     if wavelength is None:
         wavelength = self.source.photon.get_wavelength()
     dx = self.detector.get_resolution_element_x(wavelength, cx=cx, cy=cy)
     dy = self.detector.get_resolution_element_y(wavelength, cx=cx, cy=cy)
     dxdy = numpy.array([dx, dy])
     if convention == "full_period":
         return dxdy*2
     elif convention == "half_period":
         return dxdy
     else:
         log_and_raise_error(logger, "Invalid input: convention=%s. Must be either \"full_period\" or \"half_period\"." % convention)
         return
コード例 #25
0
ファイル: material.py プロジェクト: qsadhu/condor
 def add_material(self, material, density_map):
     if not isinstance(material, Material):
         log_and_raise_error(
             logger,
             "Cannot add material %s. It is not an instance of Material." %
             str(material))
     if density_map.shape != self._shape:
         log_and_raise_error(
             logger,
             "Cannot add material. Density map has incompatible shape: %s. Should be %s."
             % (str(density_map.shape), str(self._shape)))
     self.materials.append(material)
     self.density_maps.append(density_map)
コード例 #26
0
ファイル: material.py プロジェクト: qsadhu/condor
    def set_atomic_concentration(self, element, relative_concentration):
        r"""
        Set the concentration of a given atomic species

        Args:
          :element (str): Atomic species (e.g. ``'H'`` for hydrogen)

          :relative_concentration (float): Relative quantity of atoms of the given atomic species with respect to the others (e.g. for water: hydrogen concentration ``2.``, oxygen concentration ``1.``)
        """
        if element not in atomic_names:
            log_and_raise_error(
                logger, "Cannot add element \"%s\". Invalid name." % element)
        self._atomic_composition[element] = relative_concentration
コード例 #27
0
ファイル: source.py プロジェクト: FXIhub/condor
 def _get_next_pulse_energy(self):
     p = self._pulse_energy_variation.get(self.pulse_energy_mean)
     # Non-random
     if self._pulse_energy_variation._mode in [None,"range"]:
         if p <= 0:
             log_and_raise_error(logger, "Pulse energy smaller-equals zero. Change your configuration.")
         else:
             return p
     # Random
     else:
         if p <= 0.:
             log_warning(logger, "Pulse energy smaller-equals zero. Try again.")
             self._get_next_pulse_energy()
         else:
             return p
コード例 #28
0
    def set_atoms_from_arrays(self, atomic_numbers, atomic_positions):
        r"""
        Specify atomic positions from atomic numbers and atomic positions

        Args:
          :atomic_numbers (array): Integer array of atomic numbers specifies the element species of each atom. Array shape: (:math:`N`,) with :math:`N` denoting the number of atoms.

          :atomic_position (array): Float array of atomic positions [:math:`x`, :math:`y`, :math:`z`] in unit meter. Array shape: (:math:`N`, 3,) with :math:`N` denoting the number of atoms
        """
        N1 = len(atomic_numbers)
        N2 = len(atomic_positions)
        if N1 != N2:
            log_and_raise_error(logger, "Cannot set atoms. atomic_numbers and atomic_positions have to have the same length")
        self._atomic_positions = numpy.array(atomic_positions)
        self._atomic_numbers   = numpy.array(atomic_numbers)
コード例 #29
0
 def _get_next_diameter(self):
     d = self._diameter_variation.get(self.diameter_mean)
     # Non-random diameter
     if self._diameter_variation._mode in [None,"range"]:
         if d <= 0:
             log_and_raise_error(logger,"Sample diameter smaller-equals zero. Change your configuration.")
         else:
             return d
     # Random diameter
     else:
         if d <= 0.:
             log_warning(logger, "Sample diameter smaller-equals zero. Try again.")
             return self._get_next_diameter()
         else:
             return d
コード例 #30
0
ファイル: particle_spheroid.py プロジェクト: FXIhub/condor
 def _get_next_flattening(self):
     f = self._flattening_variation.get(self.flattening_mean)
     # Non-random 
     if self._flattening_variation._mode in [None, "range"]:
         if f <= 0:
             log_and_raise_error(logger, "Spheroid flattening smaller-equals zero. Change your configuration.")
         else:
             return f
     # Random 
     else:
         if f <= 0.:
             log_warning(logger, "Spheroid flattening smaller-equals zero. Try again.")
             return self._get_next_flattening()
         else:
             return f
コード例 #31
0
    def set_alignment(self, rotation_values, rotation_formalism, rotation_mode):
        """
        Set rotation scheme of the partice

        Args:        
          :rotation_values: Array of rotation parameters. For simulating patterns of many shots this can be also a sequence of rotation parameters. Input ``None`` for no rotation and for random rotation formalisms. For more documentation see :class:`condor.utils.rotation.Rotations` (default ``None``)  

          :rotation_mode (str): If the rotation shall be assigned to the particle choose ``\'extrinsic\'``. Choose ``\'intrinsic\'`` if the coordinate system shall be rotated (default ``\'extrinsic\'``)

        """
        # Check input
        if rotation_mode not in ["extrinsic","intrinsic"]:
            log_and_raise_error(logger, "%s is not a valid rotation mode for alignment." % rotation_mode)
            sys.exit(1)
        self._rotation_mode = rotation_mode
        self._rotations = condor.utils.rotation.Rotations(values=rotation_values, formalism=rotation_formalism)
コード例 #32
0
 def _get_material_conf(self):
     conf = {}
     for m_i in self.materials:
         conf_i = m_i.get_conf()
         if isinstance(m_i, AtomDensityMaterial):
             conf_i["electron_density"] = None
         elif isinstance(m_i, ElectronDensityMaterial):
             conf_i["material_type"] = None
             conf_i["massdensity"] = None
             conf_i["atomic_composition"] = None
         else:
             log_and_raise_error(logger, "Material has the wrong class: %s" % str(m_i))
         for k,v in conf_i.items():
             if k not in conf:
                 conf[k] = []
             conf[k].append(conf_i[k])
     return conf
コード例 #33
0
    def __init__(self, material_type, massdensity = None, atomic_composition = None):
        AbstractMaterial.__init__(self)
        
        self.clear_atomic_composition()

        if atomic_composition is not None and massdensity is not None and (material_type is None or material_type == "custom"):
            for element,concentration in atomic_composition.items():
                self.set_atomic_concentration(element, concentration)
            self.massdensity = massdensity

        elif material_type is not None and atomic_composition is None and massdensity is None:
            for element, concentration in MaterialType.atomic_compositions[material_type].items():
                self.set_atomic_concentration(element, concentration)
            self.massdensity = MaterialType.mass_densities[material_type]

        else:
            log_and_raise_error(logger, "Invalid arguments in Material initialization.")
コード例 #34
0
ファイル: source.py プロジェクト: irischang020/condor
    def get_intensity(self, position, unit="ph/m2", pulse_energy=None):
        """
        Calculate the intensity at a given position in the focus

        Args:
          :position: Coordinates [*x*, *y*, *z*] of the position where the intensity shall be calculated
           
        Kwargs:
          :unit (str): Intensity unit (default ``\'ph/m2\'``)

            *Choose one of the following options:*
        
              - ``\'ph/m2\'``

              - ``\'J/m2\'``

              - ``\'J/um2\'``

              - ``\'mJ/um2\'``

              - ``\'ph/um2\'``

           :pulse_energy (float): Pulse energy of that particular pulse in unit Joule. If ``None`` the mean of the pulse energy will be used (default ``None``)
        """
        # Assuming
        # 1) Radially symmetric profile that is invariant along the beam axis within the sample volume
        # 2) The variation of intensity are on much larger scale than the dimension of the particle size (i.e. flat wavefront)
        r = numpy.sqrt(position[1]**2 + position[2]**2)
        I = (self.profile.get_radial())(r) * (pulse_energy
                                              if pulse_energy is not None else
                                              self.pulse_energy_mean)
        if unit == "J/m2":
            pass
        elif unit == "ph/m2":
            I /= self.photon.get_energy()
        elif unit == "J/um2":
            I *= 1.E-12
        elif unit == "mJ/um2":
            I *= 1.E-9
        elif unit == "ph/um2":
            I /= self.photon.get_energy()
            I *= 1.E-12
        else:
            log_and_raise_error(logger, "%s is not a valid unit." % unit)
            return
        return I
コード例 #35
0
    def set_atoms_from_arrays(self, atomic_numbers, atomic_positions):
        r"""
        Specify atomic positions from atomic numbers and atomic positions

        Args:
          :atomic_numbers (array): Integer array of atomic numbers specifies the element species of each atom. Array shape: (:math:`N`,) with :math:`N` denoting the number of atoms.

          :atomic_position (array): Float array of atomic positions [:math:`x`, :math:`y`, :math:`z`] in unit meter. Array shape: (:math:`N`, 3,) with :math:`N` denoting the number of atoms
        """
        N1 = len(atomic_numbers)
        N2 = len(atomic_positions)
        if N1 != N2:
            log_and_raise_error(
                logger,
                "Cannot set atoms. atomic_numbers and atomic_positions have to have the same length"
            )
        self._atomic_positions = numpy.array(atomic_positions)
        self._atomic_numbers = numpy.array(atomic_numbers)
コード例 #36
0
ファイル: source.py プロジェクト: FXIhub/condor
    def get_intensity(self, position, unit = "ph/m2", pulse_energy = None):
        """
        Calculate the intensity at a given position in the focus

        Args:
          :position: Coordinates [*x*, *y*, *z*] of the position where the intensity shall be calculated
           
        Kwargs:
          :unit (str): Intensity unit (default ``\'ph/m2\'``)

            *Choose one of the following options:*
        
              - ``\'ph/m2\'``

              - ``\'J/m2\'``

              - ``\'J/um2\'``

              - ``\'mJ/um2\'``

              - ``\'ph/um2\'``

           :pulse_energy (float): Pulse energy of that particular pulse in unit Joule. If ``None`` the mean of the pulse energy will be used (default ``None``)
        """
        # Assuming
        # 1) Radially symmetric profile that is invariant along the beam axis within the sample volume
        # 2) The variation of intensity are on much larger scale than the dimension of the particle size (i.e. flat wavefront)
        r = numpy.sqrt(position[1]**2 + position[2]**2)
        I = (self.profile.get_radial())(r) * (pulse_energy if pulse_energy is not None else self.pulse_energy_mean)
        if unit == "J/m2":
            pass
        elif unit == "ph/m2":
            I /= self.photon.get_energy() 
        elif unit == "J/um2":
            I *= 1.E-12
        elif unit == "mJ/um2":
            I *= 1.E-9
        elif unit == "ph/um2":
            I /= self.photon.get_energy()
            I *= 1.E-12
        else:
            log_and_raise_error(logger, "%s is not a valid unit." % unit)
            return
        return I
コード例 #37
0
ファイル: detector.py プロジェクト: FXIhub/condor
 def _init_mask(self, mask, mask_is_cxi_bitmask, mask_filename, mask_dataset, nx, ny, x_gap_size_in_pixel, y_gap_size_in_pixel, cx_hole, cy_hole, hole_diameter_in_pixel):
     if mask is not None or (mask_filename is not None and mask_dataset is not None):
         if mask is not None:
             # Copy mask from array
             self._mask = numpy.array(mask, dtype=numpy.uint16)
         else:
             # Read mask from file
             import h5py
             with h5py.File(mask_filename,"r") as f:
                 self._mask = numpy.array(f[mask_dataset][:,:], dtype=numpy.uint16)
         if not mask_is_cxi_bitmask:
             # Convert maskt to CXI bit format
             self._mask = (self._mask == 0) * PixelMask.PIXEL_IS_MISSING
     elif nx is not None and ny is not None:
         # Initialise empty mask
         self._mask = numpy.zeros(shape=(int(ny+y_gap_size_in_pixel), int(nx+x_gap_size_in_pixel)),dtype=numpy.uint16)
     else:
         log_and_raise_error(logger, r"Either 'mask' or 'nx' and 'ny' have to be specified.")
         sys.exit(1)
     self._nx = self._mask.shape[1]
     self._ny = self._mask.shape[0]
     # Mask out pixels in gaps
     if y_gap_size_in_pixel > 0:
         cy = int(numpy.ceil((self._ny-1)/2.))
         gy = int(numpy.round(y_gap_size_in_pixel))
         self._mask[cy-gy/2:cy-gy/2+gy,:] |= PixelMask.PIXEL_IS_MISSING
     if x_gap_size_in_pixel > 0:
         cx = int(numpy.ceil((self._nx-1)/2.))
         gx = int(numpy.round(x_gap_size_in_pixel))
         self._mask[:,cx-gx/2:cx-gx/2+gx] |= PixelMask.PIXEL_IS_MISSING
     # Mask out pixels in hole    
     if hole_diameter_in_pixel > 0:
         if cx_hole is None:
             cx_hole = (self._nx-1)/2.
         if cy_hole is None:
             cy_hole = (self._ny-1)/2.
         Y,X = numpy.indices((self._ny,self._nx), dtype=numpy.float64)
         X = X-cx_hole
         Y = Y-cy_hole
         R = numpy.sqrt(X**2 + Y**2)
         tmp = R<=hole_diameter_in_pixel/2.0
         if tmp.sum() > 0:
             self._mask[tmp] |= PixelMask.PIXEL_IS_MISSING
コード例 #38
0
    def set_custom_geometry_by_array(self, map3d, dx):
        """
        Set map from numpy array

        Args:
          :map3d (array): 4D numpy array (material index, z, y, x) of float values. If a material is defined (material not ``None``) the values of the map scale the complex refractive index of the material. If no material is defined (materials is ``None``) the map will be casted to complex values and used without any rescaling.

          :dx (float): Grid spacing in unit meter
        """
        # Check shape
        s = numpy.array(map3d.shape)
        if numpy.any(s[-3:] != s[-1]):
            log_and_raise_error(
                logger,
                "Condor only accepts maps with equal spatial dimensions. Current shape is: %s"
                % str(s[-3:]))
        if self.materials is None:
            # Complex map(s) = refractive index map
            # Check input
            if len(s) == 3:
                map3d = [map3d]
            if len(s) < 3 or len(s) > 4:
                log_and_raise_error(
                    logger,
                    "map3d has %i dimensions but should have 3 or 4." % len(s))
                return
            # Load map(s)
            _map3d = numpy.asarray(map3d)
        else:
            # Real map(s) to be scaled by material's complext refractive index
            # Check input
            if len(s) not in [3, 4]:
                log_and_raise_error(
                    logger,
                    "map3d has %i dimensions but it has to have either 3 or 4."
                    % len(s))
                return
            # Load map(s)
            if len(s) == 3:
                n_mat = len(self.materials)
                s = numpy.array([n_mat] + list(s))
                _map3d = numpy.array(n_mat * [map3d], dtype=numpy.float64)
            else:
                if s[0] != len(self.materials):
                    log_and_raise_error(
                        logger,
                        "The first dimension of the map (%i) does not equal the number of specified materials (%i)."
                        % (s[0], len(self.materials)))
                    return
                _map3d = numpy.asarray(map3d, dtype=numpy.float64)
        self._map3d_orig = _map3d
        self._dx_orig = dx
        self._set_cache(_map3d, dx, geometry="custom")
コード例 #39
0
ファイル: source.py プロジェクト: irischang020/condor
 def _get_next_pulse_energy(self):
     p = self._pulse_energy_variation.get(self.pulse_energy_mean)
     # Non-random
     if self._pulse_energy_variation._mode in [None, "range"]:
         if p <= 0:
             log_and_raise_error(
                 logger,
                 "Pulse energy smaller-equals zero. Change your configuration."
             )
         else:
             return p
     # Random
     else:
         if p <= 0.:
             log_warning(logger,
                         "Pulse energy smaller-equals zero. Try again.")
             self._get_next_pulse_energy()
         else:
             return p
コード例 #40
0
ファイル: particle_atoms.py プロジェクト: FXIhub/condor
 def __init__(self,
              pdb_filename = None, pdb_id = None,
              atomic_numbers = None, atomic_positions = None,
              rotation_values = None, rotation_formalism = None, rotation_mode = "extrinsic",
              number = 1., arrival = "synchronised",
              position = None,  position_variation = None, position_spread = None, position_variation_n = None):
     try:
         import spsim
     except Exception as e:
         print(str(e))
         log_and_raise_error(logger, "Cannot import spsim module. This module is necessary to simulate diffraction for particle model of discrete atoms. Please install spsim from https://github.com/FilipeMaia/spsim and try again.")
         return
     # Initialise base class
     AbstractParticle.__init__(self,
                               rotation_values=rotation_values, rotation_formalism=rotation_formalism, rotation_mode=rotation_mode,                                            
                               number=number, arrival=arrival,
                               position=position, position_variation=position_variation, position_spread=position_spread, position_variation_n=position_variation_n)
     self._atomic_positions  = None
     self._atomic_numbers    = None
     self._pdb_filename      = None
     self._diameter_mean    = None
     if pdb_filename is not None:
         log_debug(logger, "Attempt reading atoms from PDB file %s." % pdb_filename)
         if (pdb_id is not None or atomic_numbers is not None or atomic_positions is not None):
             log_and_raise_error(logger, "Atom configuration is ambiguous. pdb_filename is specified but also at least one of the following arguments: atomic_numbers, atomic_positions, pdb_id.")
             sys.exit(1)
         elif not os.path.isfile(pdb_filename):
             log_and_raise_error(logger, "Cannot initialize particle model. PDB file %s does not exist." % pdb_filename)
             sys.exit(1)
         else:
             self.set_atoms_from_pdb_file(pdb_filename)
     elif pdb_id is not None:
         log_debug(logger, "Attempt fetching PDB entry of ID=%s" % pdb_id)
         if (atomic_numbers is not None or atomic_positions is not None):
             log_and_raise_error(logger, "Atom configuration is ambiguous. pdb_id is specified but also at least one of the following arguments: atomic_numbers, atomic_positions.")
             sys.exit(1)
         else:
             self.set_atoms_from_pdb_id(pdb_id)
     elif atomic_numbers is not None and atomic_positions is not None:
         log_debug(logger, "Attempt reading atoms from lists/attays.")
         self.set_atoms_from_arrays(atomic_numbers, atomic_positions)
     else:
         log_and_raise_error(logger, "Cannot initialise particle model. The atomic positions have to be specified either by a pdb_filename, pdb_id or by atomic_numbers and atomic_positions.")
コード例 #41
0
 def _get_next_flattening(self):
     f = self._flattening_variation.get(self.flattening_mean)
     # Non-random
     if self._flattening_variation._mode in [None, "range"]:
         if f <= 0:
             log_and_raise_error(
                 logger,
                 "Spheroid flattening smaller-equals zero. Change your configuration."
             )
         else:
             return f
     # Random
     else:
         if f <= 0.:
             log_warning(
                 logger,
                 "Spheroid flattening smaller-equals zero. Try again.")
             return self._get_next_flattening()
         else:
             return f
コード例 #42
0
 def __init__(self, source, particles, detector):
     self.source    = source
     for n,p in particles.items():
         if n.startswith("particle_sphere"):
             if not isinstance(p, condor.particle.ParticleSphere):
                 log_and_raise_error(logger, "Particle %s is not a condor.particle.ParticleSphere instance." % n)
         elif n.startswith("particle_spheroid"):
             if not isinstance(p, condor.particle.ParticleSpheroid):
                 log_and_raise_error(logger, "Particle %s is not a condor.particle.ParticleSpheroid instance." % n)
         elif n.startswith("particle_map"):
             if not isinstance(p, condor.particle.ParticleMap):
                 log_and_raise_error(logger, "Particle %s is not a condor.particle.ParticleMap instance." % n)
         elif n.startswith("particle_atoms"):
             if not isinstance(p, condor.particle.ParticleAtoms):
                 log_and_raise_error(logger, "Particle %s is not a condor.particle.ParticleAtoms instance." % n)
         else:
             log_and_raise_error(logger, "The particle model name %s is invalid. The name has to start with either particle_sphere, particle_spheroid, particle_map or particle_atoms.")
     self.particles = particles
     self.detector  = detector
     self._qmap_cache = {}
コード例 #43
0
 def __init__(self, source, particles, detector):
     self.source    = source
     for n,p in particles.items():
         if n.startswith("particle_sphere"):
             if not isinstance(p, condor.particle.ParticleSphere):
                 log_and_raise_error(logger, "Particle %s is not a condor.particle.ParticleSphere instance." % n)
         elif n.startswith("particle_spheroid"):
             if not isinstance(p, condor.particle.ParticleSpheroid):
                 log_and_raise_error(logger, "Particle %s is not a condor.particle.ParticleSpheroid instance." % n)
         elif n.startswith("particle_map"):
             if not isinstance(p, condor.particle.ParticleMap):
                 log_and_raise_error(logger, "Particle %s is not a condor.particle.ParticleMap instance." % n)
         elif n.startswith("particle_atoms"):
             if not isinstance(p, condor.particle.ParticleAtoms):
                 log_and_raise_error(logger, "Particle %s is not a condor.particle.ParticleAtoms instance." % n)
         else:
             log_and_raise_error(logger, "The particle model name %s is invalid. The name has to start with either particle_sphere, particle_spheroid, particle_map or particle_atoms.")
     self.particles = particles
     self.detector  = detector
     self._qmap_cache = {}
コード例 #44
0
ファイル: detector.py プロジェクト: FXIhub/condor
    def get_mask(self,intensities=None, boolmask=False):
        """
        Return mask. The mask has information about the status of each individual detector pixel. The output can be either a CXI bitmask (default) or a boolean mask
    
        For further information and the full bitcode go to :class:`condor.utils.pixelmask.PixelMask`
       
        Kwargs:
          :intensities: Numpy array of photon intensities for masking saturated pixels (default ``None``)

          :boolmask (bool): If ``True`` the output will be a boolean array. Mask values are converted to ``True`` if no bit is set and to ``False`` otherwise
        """
        if intensities is not None:
            if not condor.utils.testing.same_shape(intensities, self._mask):
                log_and_raise_error(logger, "Intensities and mask do not have the same shape")
        M = self._mask.copy()
        if self.saturation_level is not None and intensities is not None:
            M[intensities >= self.saturation_level] |= PixelMask.PIXEL_IS_SATURATED
        if boolmask:
            return numpy.array(M == 0,dtype="bool")
        else:
            return M
コード例 #45
0
ファイル: detector.py プロジェクト: qsadhu/condor
    def get_mask(self,intensities=None, boolmask=False):
        """
        Return mask. The mask has information about the status of each individual detector pixel. The output can be either a CXI bitmask (default) or a boolean mask
    
        For further information and the full bitcode go to :class:`condor.utils.pixelmask.PixelMask`
       
        Kwargs:
          :intensities: Numpy array of photon intensities for masking saturated pixels (default ``None``)

          :boolmask (bool): If ``True`` the output will be a boolean array. Mask values are converted to ``True`` if no bit is set and to ``False`` otherwise
        """
        if intensities is not None:
            if not condor.utils.testing.same_shape(intensities, self._mask):
                log_and_raise_error(logger, "Intensities and mask do not have the same shape")
        M = self._mask.copy()
        if self.saturation_level is not None and intensities is not None:
            M[intensities >= self.saturation_level] |= PixelMask.PIXEL_IS_SATURATED
        if boolmask:
            return numpy.array(M == 0,dtype="bool")
        else:
            return M
コード例 #46
0
ファイル: particle_map.py プロジェクト: FXIhub/condor
    def set_custom_geometry_by_array(self, map3d, dx):
        """
        Set map from numpy array

        Args:
          :map3d (array): 4D numpy array (material index, z, y, x) of float values. If a material is defined (material not ``None``) the values of the map scale the complex refractive index of the material. If no material is defined (materials is ``None``) the map will be casted to complex values and used without any rescaling.

          :dx (float): Grid spacing in unit meter
        """
        # Check shape
        s = numpy.array(map3d.shape)
        if numpy.any(s[-3:] != s[-1]):
            log_and_raise_error(
                logger, "Condor only accepts maps with equal spatial dimensions. Current shape is: %s" % str(s[-3:])
            )
        if self.materials is None:
            # Complex map(s) = refractive index map
            # Check input
            if len(s) == 3:
                map3d = [map3d]
            if len(s) < 3 or len(s) > 4:
                log_and_raise_error(logger, "map3d has %i dimensions but should have 3 or 4." % len(s))
                return
            # Load map(s)
            _map3d = numpy.asarray(map3d)
        else:
            # Real map(s) to be scaled by material's complext refractive index
            # Check input
            if len(s) not in [3, 4]:
                log_and_raise_error(logger, "map3d has %i dimensions but it has to have either 3 or 4." % len(s))
                return
            # Load map(s)
            if len(s) == 3:
                n_mat = len(self.materials)
                s = numpy.array([n_mat] + list(s))
                _map3d = numpy.array(n_mat * [map3d], dtype=numpy.float64)
            else:
                if s[0] != len(self.materials):
                    log_and_raise_error(
                        logger,
                        "The first dimension of the map (%i) does not equal the number of specified materials (%i)."
                        % (s[0], len(self.materials)),
                    )
                    return
                _map3d = numpy.asarray(map3d, dtype=numpy.float64)
        self._map3d_orig = _map3d
        self._dx_orig = dx
        self._set_cache(_map3d, dx, geometry="custom")
コード例 #47
0
 def __init__(self,
              pdb_filename=None,
              pdb_id=None,
              atomic_numbers=None,
              atomic_positions=None,
              rotation_values=None,
              rotation_formalism=None,
              rotation_mode="extrinsic",
              number=1.,
              arrival="synchronised",
              position=None,
              position_variation=None,
              position_spread=None,
              position_variation_n=None):
     try:
         import spsim
     except Exception, e:
         print str(e)
         log_and_raise_error(
             logger,
             "Cannot import spsim module. This module is necessary to simulate diffraction for particle model of discrete atoms. Please install spsim from https://github.com/FilipeMaia/spsim and try again."
         )
         return
コード例 #48
0
    def add_material(self, material_type, massdensity, atomic_composition, electron_density):
        """
        Initialise and add the AtomDensityMaterial / ElectronDensityMaterial class instance to the particle

        Args:
          :material_type (str): See :class:`condor.utils.material.AtomDensityMaterial`

          :massdensity (float): See :class:`condor.utils.material.AtomDensityMaterial`

          :atomic_composition (dict): See :class:`condor.utils.material.AtomDensityMaterial`

          :electron_density (float): See :class:`condor.utils.material.ElectronDensityMaterial`
        """
        if electron_density is None:
            self.materials.append(AtomDensityMaterial(material_type=material_type, massdensity=massdensity, atomic_composition=atomic_composition))
        else:
            if massdensity is not None or atomic_composition is not None:
                log_and_raise_error(logger, r"An electron density is defined so material_type, massdensity and atomic_composition have to be all 'None'.")
                return
            if material_type != "custom":
                log_and_raise_error(logger, r"An electron density is defined, so material_type must be \'custom\' but is %s." % material_type)
                return
            self.materials.append(ElectronDensityMaterial(electron_density=electron_density))
コード例 #49
0
ファイル: material.py プロジェクト: qsadhu/condor
    def __init__(self,
                 material_type,
                 massdensity=None,
                 atomic_composition=None):
        AbstractMaterial.__init__(self)

        self.clear_atomic_composition()

        if atomic_composition is not None and massdensity is not None and (
                material_type is None or material_type == "custom"):
            for element, concentration in atomic_composition.items():
                self.set_atomic_concentration(element, concentration)
            self.massdensity = massdensity

        elif material_type is not None and atomic_composition is None and massdensity is None:
            for element, concentration in MaterialType.atomic_compositions[
                    material_type].items():
                self.set_atomic_concentration(element, concentration)
            self.massdensity = MaterialType.mass_densities[material_type]

        else:
            log_and_raise_error(
                logger, "Invalid arguments in Material initialization.")
コード例 #50
0
    def set_custom_geometry_by_mrcfile(self,
                                       filename,
                                       offset=None,
                                       factor=None):
        """
        Read map from the MRC file (CCP4 file format, see http://www.ccp4.ac.uk/html/maplib.html).

        The map will be preprocessed by applying an offset and rescaling and by padding the water background with zeros.

        Finally, the avereage value of the map will be rescaled by the refractive index of the associated material.

        Args:
          :filename (str): Filename of MRC file.

          :offset (float): Offset value of the map (MAP = (EM_DATA + OFFSET) X FACTOR)

          :factor (float): Rescale factor of the map (MAP = (EM_DATA + OFFSET) X FACTOR)
        """
        map3d, dx = condor.utils.emdio.read_map(filename)
        if offset is None and factor is None:
            ed_water = condor.utils.material.AtomDensityMaterial(
                material_type="water").get_electron_density()
            if len(self.materials) > 1:
                log_and_raise_error(
                    logger,
                    "More than one material defined. This is incompatible with automatic scaling of an EMD map."
                )
                sys.exit(1)
            ed_particle = self.materials[0].get_electron_density()
            map3d = condor.utils.emdio.preproc_map_auto(
                map3d, ed_water=ed_water, ed_particle=ed_particle)
        else:
            map3d = condor.utils.emdio.perproc_map_manual(map3d,
                                                          offset=offset,
                                                          factor=factor)
        self.set_custom_geometry_by_array(map3d, dx)
コード例 #51
0
    def get_new_map(self, O, dx_required, dx_suggested):
        """
        Return new map with given parameters

        Args:

          :O (dict): Parameter dictionary as returned from :meth:`condor.particle.particle_map.get_next`

          :dx_required (float): Required resolution (grid spacing) of the map. An error is raised if the resolution of the map has too low resolution

          :dx_suggested (float): Suggested resolution (grid spacing) of the map. If the map has a very high resolution it will be interpolated to a the suggested resolution value
        """

        if O["geometry"] in ["icosahedron", "sphere", "spheroid", "cube"]:

            if not self._is_map_in_cache(O, dx_required):

                dx = dx_suggested
                n_mat = len(self.materials)

                if O["geometry"] == "icosahedron":
                    m_tmp = self._get_map_icosahedron(O["diameter"] / 2., dx)

                elif O["geometry"] == "spheroid":
                    a = condor.utils.spheroid_diffraction.to_spheroid_semi_diameter_a(
                        O["diameter"], O["flattening"])
                    c = condor.utils.spheroid_diffraction.to_spheroid_semi_diameter_c(
                        O["diameter"], O["flattening"])
                    m_tmp = self._get_map_spheroid(a, c, dx)

                elif O["geometry"] == "sphere":
                    m_tmp = self._get_map_sphere(O["diameter"] / 2., dx)

                elif O["geometry"] == "cube":
                    m_tmp = self._get_map_cube(O["diameter"] / 2., dx)

                else:
                    log_and_raise_error(
                        logger,
                        "Particle map geometry \"%s\" is not implemented. Change your configuration and try again."
                        % O["geometry"])
                    sys.exit(1)

                m = numpy.array(n_mat * [m_tmp])

                self._set_cache(map3d=m,
                                dx=dx,
                                geometry=O["geometry"],
                                diameter=O["diameter"],
                                flattening=(None if O["geometry"] != "spheroid"
                                            else O["flattening"]))

            else:

                log_debug(
                    logger,
                    "No need for calculating a new map. Reading map from cache."
                )
                m = self._cache["map3d"]
                dx = self._cache["dx"]

        elif O["geometry"] == "custom":

            rescale_factor = O["diameter"] / self.diameter_mean
            dx_rescaled = self._cache["dx"] * rescale_factor

            # Current map too coarsely sampled?
            if (dx_rescaled / dx_required > 1.) and not numpy.isclose(
                    dx_rescaled / dx_required, 1.):

                # Cached map (original) also too coarsely sampled?
                if self._dx_orig / dx_required > 1. and not numpy.isclose(
                        self._dx_orig / dx_required, 1.):
                    # Not fine enough -> exit
                    log_and_raise_error(
                        logger,
                        "Resolution of given custom map is insufficient for simulation. Required is at most %e m vs. provided %e m."
                        % (dx_required, self._dx_orig))
                    sys.exit(1)
                else:
                    # Change back to original fine map
                    self._set_cache(map3d=self._map3d_orig,
                                    dx=self._dx_orig,
                                    geometry="custom")

            # Can we downsample current map?
            # MAX: We would do this only for performance reasons but have not found a good way of downsampling without introducing artifacts
            #if (dx_suggested/dx_rescaled >= 2.) and (dx_suggested/self._dx_orig >= 2.) and ENABLE_MAP_INTERPOLATION:
            #    print "ENABLE_MAP_INTERPOLATION=%i" % ENABLE_MAP_INTERPOLATION
            #    N1 = self._map3d_orig.shape[0]
            #    m1 = numpy.zeros(shape=(N1,N1,N1), dtype=numpy.float64)
            #    m1[:self._map3d_orig.shape[0],:self._map3d_orig.shape[0],:self._map3d_orig.shape[0]] = self._map3d_orig[:,:,:]
            #    fm1 = numpy.fft.fftshift(numpy.fft.ifftn(m1))
            #    N1 = m1.shape[0]
            #    N2 = int(numpy.ceil(N1 * self._dx_orig / dx_suggested))
            #    x1 = numpy.linspace(-0.5,0.5,N2)*(1-0.5/N2)
            #    Z,Y,X = numpy.meshgrid(x1,x1,x1,indexing="ij")
            #    coords = numpy.array([[z,y,x] for z,y,x in zip(Z.ravel(),Y.ravel(),X.ravel())])
            #    m2 = abs(numpy.fft.fftshift(condor.utils.nfft.nfft(fm1,coords).reshape((N2,N2,N2))))
            #    #from pylab import *
            #    #imsave("m1.png", m1.sum(0))
            #    #imsave("m2.png", m2.sum(0))
            #    self._dx    = self._dx_orig * float(N1)/float(N2)
            #    self._map3d = m2 / m2.sum() * m1.sum()

            m = self._cache["map3d"]
            dx = rescale_factor * self._cache["dx"]

        return m, dx
コード例 #52
0
class ParticleAtoms(AbstractParticle):
    """
    Class for a particle model

    *Model:* Discrete atomic positions

    Kwargs:
      :pdb_filename (str): See :meth:`set_atoms_from_pdb_file` (default ``None``)

      :pdb_id (str): See :meth:`set_atoms_from_pdb_id` (default ``None``)

      :atomic_numbers (array): See :meth:`set_atoms_from_arrays` (default ``None``)
    
      :atomic_positions (array): See :meth:`set_atoms_from_arrays` (default ``None``)

      .. note:: The atomic positions have to be specified either by a ``pdb_filename`` or by ``atomic_numbers`` and  ``atomic_positions``.

      :rotation_values (array): See :meth:`condor.particle.particle_abstract.AbstractParticle.set_alignment` (default ``None``)

      :rotation_formalism (str): See :meth:`condor.particle.particle_abstract.AbstractParticle.set_alignment` (default ``None``)

      :rotation_mode (str): See :meth:`condor.particle.particle_abstract.AbstractParticle.set_alignment` (default ``None``)

      :number (float): Expectation value for the number of particles in the interaction volume. (defaukt ``1.``)

      :arrival (str): Arrival of particles at the interaction volume can be either ``'random'`` or ``'synchronised'``. If ``sync`` at every event the number of particles in the interaction volume equals the rounded value of ``number``. If ``'random'`` the number of particles is Poissonian and ``number`` is the expectation value. (default ``'synchronised'``)

      :position (array): See :class:`condor.particle.particle_abstract.AbstractParticle` (default ``None``)

      :position_variation (str): See :meth:`condor.particle.particle_abstract.AbstractParticle.set_position_variation` (default ``None``)

      :position_spread (float): See :meth:`condor.particle.particle_abstract.AbstractParticle.set_position_variation` (default ``None``)

      :position_variation_n (int): See :meth:`condor.particle.particle_abstract.AbstractParticle.set_position_variation` (default ``None``)
    """
    def __init__(self,
                 pdb_filename=None,
                 pdb_id=None,
                 atomic_numbers=None,
                 atomic_positions=None,
                 rotation_values=None,
                 rotation_formalism=None,
                 rotation_mode="extrinsic",
                 number=1.,
                 arrival="synchronised",
                 position=None,
                 position_variation=None,
                 position_spread=None,
                 position_variation_n=None):
        try:
            import spsim
        except Exception, e:
            print str(e)
            log_and_raise_error(
                logger,
                "Cannot import spsim module. This module is necessary to simulate diffraction for particle model of discrete atoms. Please install spsim from https://github.com/FilipeMaia/spsim and try again."
            )
            return
        # Initialise base class
        AbstractParticle.__init__(self,
                                  rotation_values=rotation_values,
                                  rotation_formalism=rotation_formalism,
                                  rotation_mode=rotation_mode,
                                  number=number,
                                  arrival=arrival,
                                  position=position,
                                  position_variation=position_variation,
                                  position_spread=position_spread,
                                  position_variation_n=position_variation_n)
        self._atomic_positions = None
        self._atomic_numbers = None
        self._pdb_filename = None
        self._diameter_mean = None
        if pdb_filename is not None:
            log_debug(logger,
                      "Attempt reading atoms from PDB file %s." % pdb_filename)
            if (pdb_id is not None or atomic_numbers is not None
                    or atomic_positions is not None):
                log_and_raise_error(
                    logger,
                    "Atom configuration is ambiguous. pdb_filename is specified but also at least one of the following arguments: atomic_numbers, atomic_positions, pdb_id."
                )
                sys.exit(1)
            elif not os.path.isfile(pdb_filename):
                log_and_raise_error(
                    logger,
                    "Cannot initialize particle model. PDB file %s does not exist."
                    % pdb_filename)
                sys.exit(1)
            else:
                self.set_atoms_from_pdb_file(pdb_filename)
        elif pdb_id is not None:
            log_debug(logger, "Attempt fetching PDB entry of ID=%s" % pdb_id)
            if (atomic_numbers is not None or atomic_positions is not None):
                log_and_raise_error(
                    logger,
                    "Atom configuration is ambiguous. pdb_id is specified but also at least one of the following arguments: atomic_numbers, atomic_positions."
                )
                sys.exit(1)
            else:
                self.set_atoms_from_pdb_id(pdb_id)
        elif atomic_numbers is not None and atomic_positions is not None:
            log_debug(logger, "Attempt reading atoms from lists/attays.")
            self.set_atoms_from_arrays(atomic_numbers, atomic_positions)
        else:
            log_and_raise_error(
                logger,
                "Cannot initialise particle model. The atomic positions have to be specified either by a pdb_filename, pdb_id or by atomic_numbers and atomic_positions."
            )
コード例 #53
0
ファイル: particle_map.py プロジェクト: FXIhub/condor
    def __init__(
        self,
        geometry,
        diameter=None,
        diameter_variation=None,
        diameter_spread=None,
        diameter_variation_n=None,
        dx=None,
        map3d=None,
        map3d_filename=None,
        map3d_dataset=None,
        emd_id=None,
        rotation_values=None,
        rotation_formalism=None,
        rotation_mode="extrinsic",
        flattening=0.75,
        number=1.0,
        arrival="synchronised",
        position=None,
        position_variation=None,
        position_spread=None,
        position_variation_n=None,
        material_type=None,
        massdensity=None,
        atomic_composition=None,
        electron_density=None,
    ):
        # Initialise base class
        AbstractContinuousParticle.__init__(
            self,
            diameter=diameter,
            diameter_variation=diameter_variation,
            diameter_spread=diameter_spread,
            diameter_variation_n=diameter_variation_n,
            rotation_values=rotation_values,
            rotation_formalism=rotation_formalism,
            rotation_mode=rotation_mode,
            number=number,
            arrival=arrival,
            position=position,
            position_variation=position_variation,
            position_spread=position_spread,
            position_variation_n=position_variation_n,
            material_type=material_type,
            massdensity=massdensity,
            atomic_composition=atomic_composition,
            electron_density=electron_density,
        )

        # Check for valid geometry
        if geometry not in ["icosahedron", "cube", "sphere", "spheroid", "custom"]:
            log_and_raise_error(
                logger,
                "Cannot initialize %s because '%s' is not a valid argument for 'geometry'."
                % (kwargs["geometry"], self.__class__.__name__),
            )
            sys.exit(1)
        self.geometry = geometry

        # Has effect only for spheroids
        self.flattening = flattening

        # Init chache
        self._cache = {}
        self._dx_orig = None
        self._map3d_orig = None

        if geometry == "custom":
            if map3d is not None:
                if dx is None:
                    log_and_raise_error(
                        logger, "Cannot initialize custom geometry with 'map3d' but without grid spacing 'dx'."
                    )
                    sys.exit(1)
                else:
                    log_debug(logger, "Attempting to initialise custom geometry with 'map3d'.")
                    if map3d_filename is not None or map3d_dataset is not None or emd_id is not None:
                        log_and_raise_error(
                            logger, "Cannot initialize custom geometry because of ambiguous keyword arguments."
                        )
                        sys.exit(1)
                    self.set_custom_geometry_by_array(map3d, dx)
            elif map3d_filename is not None and map3d_dataset is not None:
                if dx is None:
                    log_and_raise_error(
                        logger,
                        "You are trying to initialise the map with an HDF5 file. You also need to provide the grid spacing 'dx'",
                    )
                    sys.exit(1)
                log_debug(
                    logger, "Attempting to initialise custom geometry with 'map3d_filename', 'map3d_dataset' and 'dx'."
                )
                if not map3d_filename.endswith(".h5"):
                    log_and_raise_error(logger, "Map file is not an HDF5 file!")
                    sys.exit(1)
                if map3d is not None or emd_id is not None:
                    log_and_raise_error(
                        logger, "Cannot initialize custom geometry because of ambiguous keyword arguments."
                    )
                    sys.exit(1)
                self.set_custom_geometry_by_h5file(map3d_filename, map3d_dataset, dx)
            elif map3d_filename is not None:
                if not map3d_filename.endswith(".map") and not map3d_filename.endswith(".mrc"):
                    log_and_raise_error(logger, "Map file is not an MRC/MAP file!")
                    sys.exit(1)
                self.set_custom_geometry_by_mrcfile(map3d_filename)
            elif emd_id is not None:
                log_debug(logger, "Attempting to initialise custom geometry with 'emd_id'.")
                if map3d_filename is not None or map3d_dataset is not None or map3d is not None or dx is not None:
                    log_and_raise_error(
                        logger, "Cannot initialize custom geometry because of ambiguous keyword arguments."
                    )
                    sys.exit(1)
                self.set_custom_geometry_by_emd_id(emd_id)

        if diameter is None:
            self.diameter_mean = self._dx_orig * self._map3d_orig.shape[-1]
コード例 #54
0
ファイル: particle_map.py プロジェクト: FXIhub/condor
    def get_new_map(self, O, dx_required, dx_suggested):
        """
        Return new map with given parameters

        Args:

          :O (dict): Parameter dictionary as returned from :meth:`condor.particle.particle_map.get_next`

          :dx_required (float): Required resolution (grid spacing) of the map. An error is raised if the resolution of the map has too low resolution

          :dx_suggested (float): Suggested resolution (grid spacing) of the map. If the map has a very high resolution it will be interpolated to a the suggested resolution value
        """

        if O["geometry"] in ["icosahedron", "sphere", "spheroid", "cube"]:

            if not self._is_map_in_cache(O, dx_required):

                dx = dx_suggested
                n_mat = len(self.materials)

                if O["geometry"] == "icosahedron":
                    m_tmp = self._get_map_icosahedron(O["diameter"] / 2.0, dx)

                elif O["geometry"] == "spheroid":
                    a = condor.utils.spheroid_diffraction.to_spheroid_semi_diameter_a(O["diameter"], O["flattening"])
                    c = condor.utils.spheroid_diffraction.to_spheroid_semi_diameter_c(O["diameter"], O["flattening"])
                    m_tmp = self._get_map_spheroid(a, c, dx)

                elif O["geometry"] == "sphere":
                    m_tmp = self._get_map_sphere(O["diameter"] / 2.0, dx)

                elif O["geometry"] == "cube":
                    m_tmp = self._get_map_cube(O["diameter"] / 2.0, dx)

                else:
                    log_and_raise_error(
                        logger,
                        'Particle map geometry "%s" is not implemented. Change your configuration and try again.'
                        % O["geometry"],
                    )
                    sys.exit(1)

                m = numpy.array(n_mat * [m_tmp])

                self._set_cache(
                    map3d=m,
                    dx=dx,
                    geometry=O["geometry"],
                    diameter=O["diameter"],
                    flattening=(None if O["geometry"] != "spheroid" else O["flattening"]),
                )

            else:

                log_debug(logger, "No need for calculating a new map. Reading map from cache.")
                m = self._cache["map3d"]
                dx = self._cache["dx"]

        elif O["geometry"] == "custom":

            dx_needed = O["diameter"] / self.diameter_mean * self._cache["dx"]

            # Map fine enough?
            if dx_needed / dx_required >= 1.0:

                if self._dx_orig / dx_required >= 1.0:
                    # Not fine enough -> exit
                    log_and_raise_error(
                        logger,
                        "Resolution of given custom map is insufficient for simulation. required %e m vs. provided %e m."
                        % (dx_required, self._dx_orig),
                    )
                    sys.exit(1)

                # Change back to original fine map
                self.set_cache(map3d=self._map3d_orig, dx=self._dx_orig, geometry="custom")

            # Can we downsample current map?
            # if (dx_suggested/dx_needed >= 2.) and (dx_suggested/self._dx_orig >= 2.) and ENABLE_MAP_INTERPOLATION:
            #    print "ENABLE_MAP_INTERPOLATION=%i" % ENABLE_MAP_INTERPOLATION
            #    N1 = self._map3d_orig.shape[0]
            #    m1 = numpy.zeros(shape=(N1,N1,N1), dtype=numpy.float64)
            #    m1[:self._map3d_orig.shape[0],:self._map3d_orig.shape[0],:self._map3d_orig.shape[0]] = self._map3d_orig[:,:,:]
            #    fm1 = numpy.fft.fftshift(numpy.fft.ifftn(m1))
            #    N1 = m1.shape[0]
            #    N2 = int(numpy.ceil(N1 * self._dx_orig / dx_suggested))
            #    x1 = numpy.linspace(-0.5,0.5,N2)*(1-0.5/N2)
            #    Z,Y,X = numpy.meshgrid(x1,x1,x1,indexing="ij")
            #    coords = numpy.array([[z,y,x] for z,y,x in zip(Z.ravel(),Y.ravel(),X.ravel())])
            #    m2 = abs(numpy.fft.fftshift(condor.utils.nfft.nfft(fm1,coords).reshape((N2,N2,N2))))
            #    #from pylab import *
            #    #imsave("m1.png", m1.sum(0))
            #    #imsave("m2.png", m2.sum(0))
            #    self._dx    = self._dx_orig * float(N1)/float(N2)
            #    self._map3d = m2 / m2.sum() * m1.sum()

            m = self._cache["map3d"]
            dx = O["diameter"] / self.diameter_mean * self._cache["dx"]

        return m, dx
コード例 #55
0
 def __init__(self, shape):
     if len(shape) != 3:
         log_and_raise_error(logger, "%s is an invald shape for initialisation of MaterialMap.", str(shape))
     self._shape = tuple(shape)
コード例 #56
0
    def __init__(self,
                 geometry,
                 diameter=None,
                 diameter_variation=None,
                 diameter_spread=None,
                 diameter_variation_n=None,
                 dx=None,
                 map3d=None,
                 map3d_filename=None,
                 map3d_dataset=None,
                 emd_id=None,
                 rotation_values=None,
                 rotation_formalism=None,
                 rotation_mode="extrinsic",
                 flattening=0.75,
                 number=1.,
                 arrival="synchronised",
                 position=None,
                 position_variation=None,
                 position_spread=None,
                 position_variation_n=None,
                 material_type=None,
                 massdensity=None,
                 atomic_composition=None,
                 electron_density=None):
        # Initialise base class
        AbstractContinuousParticle.__init__(
            self,
            diameter=diameter,
            diameter_variation=diameter_variation,
            diameter_spread=diameter_spread,
            diameter_variation_n=diameter_variation_n,
            rotation_values=rotation_values,
            rotation_formalism=rotation_formalism,
            rotation_mode=rotation_mode,
            number=number,
            arrival=arrival,
            position=position,
            position_variation=position_variation,
            position_spread=position_spread,
            position_variation_n=position_variation_n,
            material_type=material_type,
            massdensity=massdensity,
            atomic_composition=atomic_composition,
            electron_density=electron_density)

        # Check for valid geometry
        if geometry not in [
                "icosahedron", "cube", "sphere", "spheroid", "custom"
        ]:
            log_and_raise_error(
                logger,
                "Cannot initialize %s because \'%s\' is not a valid argument for \'geometry\'."
                % (kwargs["geometry"], self.__class__.__name__))
            sys.exit(1)
        self.geometry = geometry

        # Has effect only for spheroids
        self.flattening = flattening

        # Init chache
        self._cache = {}
        self._dx_orig = None
        self._map3d_orig = None

        if geometry == "custom":
            if map3d is not None:
                if dx is None:
                    log_and_raise_error(
                        logger,
                        "Cannot initialize custom geometry with \'map3d\' without known grid spacing (\'dx\')."
                    )
                    sys.exit(1)
                else:
                    log_debug(
                        logger,
                        "Attempting to initialise custom geometry with \'map3d\'."
                    )
                    if map3d_filename is not None or map3d_dataset is not None or emd_id is not None:
                        log_and_raise_error(
                            logger,
                            "Cannot initialize custom geometry because of ambiguous keyword arguments."
                        )
                        sys.exit(1)
                    self.set_custom_geometry_by_array(map3d, dx)
            elif map3d_filename is not None and map3d_dataset is not None:
                if dx is None:
                    log_and_raise_error(
                        logger,
                        "You are trying to initialise the map with an HDF5 file. You also need to provide the grid spacing \'dx\'"
                    )
                    sys.exit(1)
                log_debug(
                    logger,
                    "Attempting to initialise custom geometry with \'map3d_filename\', \'map3d_dataset\' and \'dx\'."
                )
                if not map3d_filename.endswith(".h5"):
                    log_and_raise_error(logger,
                                        "Map file is not an HDF5 file!")
                    sys.exit(1)
                if map3d is not None or emd_id is not None:
                    log_and_raise_error(
                        logger,
                        "Cannot initialize custom geometry because of ambiguous keyword arguments."
                    )
                    sys.exit(1)
                self.set_custom_geometry_by_h5file(map3d_filename,
                                                   map3d_dataset, dx)
            elif map3d_filename is not None:
                if not map3d_filename.endswith(
                        ".map") and not map3d_filename.endswith(".mrc"):
                    log_and_raise_error(logger,
                                        "Map file is not an MRC/MAP file!")
                    sys.exit(1)
                self.set_custom_geometry_by_mrcfile(map3d_filename)
            elif emd_id is not None:
                log_debug(
                    logger,
                    "Attempting to initialise custom geometry with \'emd_id\'."
                )
                if map3d_filename is not None or map3d_dataset is not None or map3d is not None or dx is not None:
                    log_and_raise_error(
                        logger,
                        "Cannot initialize custom geometry because of ambiguous keyword arguments."
                    )
                    sys.exit(1)
                self.set_custom_geometry_by_emd_id(emd_id)

        if diameter is None:
            self.diameter_mean = self._dx_orig * self._map3d_orig.shape[-1]