Beispiel #1
0
 def __init__(self, n=None, r=None, center=None, rotation=(0, 0, 0)):
     self.n = n
     if np.isscalar(r) or len(r) != 3:
         msg = ("r specified as {0}; "
                "r should be specified as (r_x, r_y, r_z)".format(center))
         raise InvalidScatterer(self, msg)
     self.r = r
     if np.isscalar(rotation) or len(rotation) != 3:
         msg = ("rotation specified as {0}; rotation should be "
                "specified as (alpha, beta, gamma)".format(rotation))
         raise InvalidScatterer(self, msg)
     self.rotation = rotation
     super().__init__(center)
Beispiel #2
0
 def __init__(self, s1, s2):
     # For now we just treat s1's location as the location of the composite.
     # This is probably not the best way to do it, but it is simple to
     # implement for now.
     self.location = s1.location
     self.s1 = s1
     self.s2 = s2
     if s1.num_domains > 1 or s2.num_domains > 1:
         raise InvalidScatterer(self, "Scatterer CSG is not supported for multidomain scatterers")
     if s1.n is None:
         s1 = copy(s1)
         s1.n = s2.n
     self.n = s1.n
     if s1.n != s2.n:
         raise InvalidScatterer(self, "Components of a CSG scatterer must not have different indicies")
Beispiel #3
0
 def add(self, scatterer):
     if not isinstance(scatterer, Sphere):
         raise InvalidScatterer(
             self, "Spheres expects all component " +
             "scatterers to be Spheres.\n" + repr(scatterer) +
             " is not a Sphere")
     super().add(scatterer)
Beispiel #4
0
    def translated(self, coord1, coord2=None, coord3=None):
        """
        Make a copy of this scatterer translated to a new location

        Parameters
        ----------
        x, y, z : float
            Value of the translation along each axis

        Returns
        -------
        translated : Scatterer
            A copy of this scatterer translated to a new location
        """
        if coord2 is None and len(ensure_array(coord1) == 3):
            # entered translation vector
            trans_coords = ensure_array(coord1)
        elif coord2 is not None and coord3 is not None:
            # entered 3 coords
            trans_coords = np.array([coord1, coord2, coord3])
        else:
            raise InvalidScatterer(
                self, "Cannot interpret translation coordinates")
        new = copy(self)
        new.center = self.center + trans_coords
        return new
Beispiel #5
0
    def _scat_coeffs(self, s, medium_wavevec, medium_index):
        '''
        Calculate Mie scattering coefficients.

        Parameters
        ----------
        s : :mod:`scatterer.Sphere` object
        medium_wavevec : float
            Wave vector in the medium, k = 2 * pi * n_med / lambda_0
        medium_index : float
            Medium refractive index

        Returns
        -------
        ndarray (2, n), complex
           Lorenz-Mie scattering coefficients a_n and b_n

        Notes
        -----
        See Bohren & Huffman for mathematical description.

        '''
        if (ensure_array(s.r) == 0).any():
            raise InvalidScatterer(s, "Radius is zero")
        x_arr = ensure_array(medium_wavevec * ensure_array(s.r))
        m_arr = ensure_array(ensure_array(s.n) / medium_index)

        # Check that the scatterer is in a range we can compute for
        if x_arr.max() > 1e3:
            msg =  "radius too large, field calculation would take forever"
            raise InvalidScatterer(s, msg)

        if len(x_arr) == 1 and len(m_arr) == 1:
            # Could just use scatcoeffs_multi here, but jerome is in favor of
            # keeping the simpler single layer code here
            lmax = miescatlib.nstop(x_arr[0])
            return  miescatlib.scatcoeffs(m_arr[0], x_arr[0], lmax, self.eps1,
                                          self.eps2)
        else:
            return scatcoeffs_multi(m_arr, x_arr, self.eps1, self.eps2)
Beispiel #6
0
    def __init__(self, scatterers, warn=True):
        scatterers = ensure_listlike(scatterers)
        self.warn = warn
        for s in ensure_listlike(scatterers):
            if not isinstance(s, Sphere):
                raise InvalidScatterer(
                    self, "Spheres expects all component " +
                    "scatterers to be Spheres.\n" + repr(s) +
                    " is not a Sphere")
        super().__init__(scatterers)

        if self.overlaps and self.warn:
            warnings.warn(OverlapWarning(self, self.overlaps))
Beispiel #7
0
 def __init__(self, spheres, translation=(0, 0, 0), rotation=(0, 0, 0)):
     if isinstance(spheres, Spheres):
         self.spheres = spheres
     else:
         raise InvalidScatterer(
             self,
             "RigidCluster only accepts a scatterer of class Spheres.")
     if not (len(ensure_array(translation)) == 3
             and len(ensure_array(rotation)) == 3):
         raise ValueError(
             'translation and rotation must be listlike of len 3')
     else:
         self.translation = translation
         self.rotation = rotation
Beispiel #8
0
    def __init__(self, n=None, r=.5, center=None):
        self.n = n
        self.r = r
        super().__init__(center)

        try:
            if np.any(np.array(self.r) < 0):
                raise InvalidScatterer(self, "radius is negative")
        except TypeError:
            # Simplest solution to deal with spheres with a parameter or prior
            # as arguments, just don't check them. It might be worth doing some
            # testing of the guess, but for now I am not doing that to avoid
            # introducing a dependency on something in fit
            pass
Beispiel #9
0
    def _raw_cross_sections(
            self, scatterer, medium_wavevec, medium_index, illum_polarization):
        """
        Calculate scattering, absorption, and extinction cross
        sections, and asymmetry parameter for spherically
        symmetric scatterers.

        Parameters
        ----------
        scatterer : :mod:`scatterpy.scatterer` object
            spherically symmetric scatterer to compute for
            (Calculation would need to be implemented in a radically
            different way, via numerical quadrature, for sphere clusters)

        Returns
        -------
        cross_sections : array (4)
            Dimensional scattering, absorption, and extinction
            cross sections, and <cos \theta>

        Notes
        -----
        The radiation pressure cross section C_pr is given by
        C_pr = C_ext - <cos \theta> C_sca.

        The radiation pressure force on a sphere is

        F = (n_med I_0 C_pr) / c

        where I_0 is the incident intensity.  See van de Hulst, p. 14.
        """
        if isinstance(scatterer, Spheres):
            msg = "Use Multisphere to calculate radiometric quantities"
            raise InvalidScatterer(scatterer, msg)
        albl = self._scat_coeffs(scatterer, medium_wavevec, medium_index)

        cscat, cext, cback = miescatlib.cross_sections(albl[0], albl[1]) * \
            (2. * np.pi / medium_wavevec**2)

        cabs = cext - cscat # conservation of energy

        asym = 4. * np.pi / (medium_wavevec**2 * cscat) * \
            miescatlib.asymmetry_parameter(albl[0], albl[1])

        return np.array([cscat, cabs, cext, asym])
Beispiel #10
0
    def _scat_coeffs_internal(self, s, medium_wavevec, medium_index):
        '''
        Calculate expansion coefficients for Lorenz-Mie electric field
        inside a sphere.
        '''
        x_arr = medium_wavevec * ensure_array(s.r)
        m_arr = ensure_array(s.n) / medium_index

        # Check that the scatterer is in a range we can compute for
        if x_arr.max() > 1e3:
            msg = "radius too large, field calculation would take forever"
            raise InvalidScatterer(s, msg)

        if len(x_arr) == 1 and len(m_arr) == 1:
            # Could just use scatcoeffs_multi here, but jerome is in favor of
            # keeping the simpler single layer code here
            lmax = miescatlib.nstop(x_arr[0])
            return  miescatlib.internal_coeffs(m_arr[0], x_arr[0], lmax)
Beispiel #11
0
    def _scsmfo_setup(self, scatterer, medium_wavevec, medium_index):
        """
        Given multiple spheres, calculate amn coefficients for scattered
        field expansion in VSH using SCSMFO.

        Parameters
        ----------
        scatterer : :mod:`.scatterer` object
            scatterer or list of scatterers to compute field for

        Returns
        -------
        amn : arrays of field expansion coefficients

        """
        if isinstance(scatterer,Sphere):
            scatterer=Spheres([scatterer])
        elif not isinstance(scatterer, Spheres):
            raise TheoryNotCompatibleError(self, scatterer)
        # check for spheres being uniform
        for sph in scatterer.scatterers:
            if not np.isscalar(sph.n):
                raise TheoryNotCompatibleError(self, scatterer, "Multisphere" +
                                               " cannot compute scattering" +
                                               " from layered particles.")

        # check that the parameters are in a range where the multisphere
        # expansion will work
        for s in scatterer.scatterers:
            if s.r * medium_wavevec > 1e3:
                raise InvalidScatterer(s, "radius too large, field "+
                                            "calculation would take forever")

        # switch to centroid weighted coordinate system tmatrix code expects
        # and nondimensionalize
        centers = (scatterer.centers - scatterer.centers.mean(0)) * medium_wavevec

        m = scatterer.n / medium_index

        if (centers > 1e4).any():
            raise InvalidScatterer(scatterer, "Particle separation "
                                        "too large, calculation would take forever")
        with SuppressOutput(suppress_output=self.suppress_fortran_output):
            # The fortran code uses oppositely directed z axis (they
            # have laser propagation as positive, we have it negative),
            # so we multiply the z coordinate by -1 to correct for that.
            _, lmax, amn0, converged = scsmfo_min.amncalc(
                1, centers[:,0],  centers[:,1],
                -1.0 * centers[:,2],  m.real, m.imag,
                scatterer.r * medium_wavevec, self.niter, self.eps,
                self.qeps1, self.qeps2,  self.meth, (0,0))

        # converged == 1 if the SCSMFO iterative solver converged
        # f2py converts F77 LOGICAL to int
        if not converged:
            raise MultisphereFailure()

        # chop off unused parts of amn0, the fortran code currently has a hard
        # coded number of parameters so it will return too many coefficients.
        # We truncate here to reduce the length of stuff we have to compute with
        # later.
        limit = lmax**2 + 2*lmax
        amn = amn0[:, 0:limit, :]

        if np.isnan(amn).any():
            raise MultisphereFailure()

        return amn, lmax
Beispiel #12
0
 def __init__(self, center=None):
     if center is not None and (np.isscalar(center) or len(center) != 3):
         raise InvalidScatterer(self,"center specified as {0}, center "
             "should be specified as (x, y, z)".format(center))
     self.center = center