Esempio n. 1
0
class DummyModel(SASModel):
    shortName = "DummyModelName"
    parameters = (
        FitParameter(name = "testp0", value = 3.4, valueRange = (.5, 6.7)),
        FitParameter(name = "testp1", value = 4, valueRange = (1, 5))
    )
    def calcIntensity(*args): pass
    def volume(*args): pass
    def formfactor(*args): pass
Esempio n. 2
0
class Kholodenko(SASModel):
    r"""Form factor of a worm-like structure after [Kholodenko93]_

    .. [Kholodenko93] `A. L. Kholodenko. Analytical calculation of the
        scattering function for polymers of arbitrary flexibility using the
        dirac propagator. Macromolecules, 26:4179–4183, 1993.
        <http://dx.doi.org/10.1021/ma00068a017>`_
    """
    shortName = "Kholodenko Worm"
    parameters = (
        FitParameter("radius",
                     Length(u'nm').toSi(1.),
                     unit=Length(u'nm'),
                     displayName="Radius",
                     generator=RandomExponential,
                     valueRange=(0., numpy.inf),
                     activeRange=Length(u'nm').toSi((1, 5))),  # preset
        FitParameter("lenKuhn",
                     Length(u'nm').toSi(1.),
                     unit=Length(u'nm'),
                     displayName="kuhn length",
                     generator=RandomUniform,
                     valueRange=(0., numpy.inf),
                     activeRange=Length(u'nm').toSi((10, 50))),  # preset
        FitParameter("lenContour",
                     Length(u'nm').toSi(2.),
                     unit=Length(u'nm'),
                     displayName="contour length",
                     generator=RandomUniform,
                     valueRange=(0., numpy.inf),
                     activeRange=Length(u'nm').toSi((100, 1000))),  # preset
    )

    def __init__(self):
        super(Kholodenko, self).__init__()
        # some presets of parameters to fit
        self.radius.setActive(True)
        self.lenKuhn.setActive(True)
        self.lenContour.setActive(True)

    def formfactor(self, dataset):
        # vectorized data and arguments
        qr = dataset.q * self.radius()  # a vector
        pcs = vectorizedPcs(qr)

        x = 3. * self.lenContour() / self.lenKuhn()
        p0 = vectorizedCoreIntegral(dataset.q, self.lenKuhn(), x)
        if len(LASTMSG):
            logging.warning("\n".join(["numpy.quad integration messages: "] +
                                      list(LASTMSG)))
        return p0 * pcs  # non-squared as opposed to SASfit

    def volume(self):
        volume = numpy.pi * self.lenContour() * self.radius()**2
        return volume
Esempio n. 3
0
class Sphere(SASModel):
    """Form factor of a sphere"""
    shortName = "Sphere"
    canSmear = True
    parameters = (
        FitParameter("radius",
                     NM.toSi(10.),
                     unit=NM,
                     displayName="Sphere radius",
                     valueRange=(0., numpy.inf),
                     activeRange=NM.toSi((1., 1000.)),
                     generator=RandomUniform,
                     decimals=9),
        Parameter("sld",
                  SLD(u'Å⁻²').toSi(1e-6),
                  unit=SLD(u'Å⁻²'),
                  displayName="scattering length density difference",
                  valueRange=(0., numpy.inf),
                  decimals=9),
    )

    def __init__(self):
        super(Sphere, self).__init__()
        self.radius.setActive(True)

    def surface(self):
        r"""Calculates the surface of a sphere defined by:

        :math:`s(r) = 4 \pi r^2`
        """
        return 4. * pi * self.radius() * self.radius()

    def volume(self):
        r"""Calculates the volume of a sphere defined by:

        :math:`v(r) = {4\pi \over 3} r^3`
        """
        result = (pi * 4. / 3.) * self.radius()**3
        return result

    def absVolume(self):
        r"""Calculates the volume of a sphere taking the scattering length
        density difference :math:`\Delta\rho` into account:

        :math:`v_{abs}(r, \Delta\rho) = v_{sph}(r) \cdot \Delta\rho^2`
        """
        return self.volume() * self.sld()**2

    def formfactor(self, dataset):
        r"""Calculates the form factor of a sphere defined by:

        :math:`F(q, r) = { 3 ~ sin(qr) - qr \cdot cos(qr) \over (qr)^3 }`
        """
        q = self.getQ(dataset)
        qr = q * self.radius()
        result = 3. * (sin(qr) - qr * cos(qr)) / (qr**3.)
        return result
Esempio n. 4
0
class SphericalCoreShell(SASModel):
    r"""Form factor for a spherical core shell structure
    as defined in the SASfit manual (par. 3.1.4, Spherical Shell III).
    One modification is the ability to specify SLD for core, shell and
    solvent, identical to the notation used in the Core-shell ellipsoid.
    Compared wiht a SASfit-generated model (both with and without distribution)
    """
    shortName = "Core-Shell Sphere"
    parameters = (
        FitParameter("radius",
                     Length(u'nm').toSi(1.),
                     unit=Length(u'nm'),
                     displayName="Core Radius",
                     generator=RandomExponential,
                     valueRange=(0., numpy.inf),
                     activeRange=Length(u'nm').toSi((0.1, 1e3))),  # preset
        FitParameter("t",
                     Length(u'nm').toSi(1.),
                     unit=Length(u'nm'),
                     displayName="Thickness of Shell",
                     generator=RandomExponential,
                     valueRange=(0., numpy.inf),
                     activeRange=Length(u'nm').toSi((0.1, 1e3))),  # preset
        Parameter("eta_c",
                  SLD(u'Å⁻²').toSi(3.16e-6),
                  unit=SLD(u'Å⁻²'),
                  displayName="Core SLD",
                  generator=RandomUniform,
                  valueRange=(0, numpy.inf)),
        Parameter("eta_s",
                  SLD(u'Å⁻²').toSi(2.53e-6),
                  unit=SLD(u'Å⁻²'),
                  displayName="Shell SLD",
                  generator=RandomUniform,
                  valueRange=(0, numpy.inf)),
        Parameter("eta_sol",
                  0.,
                  unit=SLD(u'Å⁻²'),
                  displayName="Solvent SLD",
                  generator=RandomUniform,
                  valueRange=(0, numpy.inf)),
    )

    def __init__(self):
        super(SphericalCoreShell, self).__init__()
        # some presets of parameters to fit
        self.radius.setActive(True)

    def formfactor(self, dataset):
        def k(q, r, dEta):
            # modified K, taken out the volume scaling
            qr = numpy.outer(q, r)
            k = dEta * 3 * (sin(qr) - qr * cos(qr)) / (qr)**3
            return k

        # dToR = pi / 180. #degrees to radian

        vc = 4. / 3 * pi * self.radius()**3
        vt = 4. / 3 * pi * (self.radius() + self.t())**3
        vRatio = vc / vt

        ks = k(dataset.q,
               self.radius() + self.t(),
               self.eta_s() - self.eta_sol())
        kc = k(dataset.q, self.radius(), self.eta_s() - self.eta_c())
        return (ks - vRatio * kc).flatten()

    def volume(self):
        v = 4. / 3 * pi * (self.radius() + self.t())**3
        return v

    def absVolume(self):
        return self.volume()  #TODO: check how to do this.
Esempio n. 5
0
class CylindersIsotropic(SASModel):
    r"""Form factor of cylinders
    previous version (length-fixed) checked against SASfit
    """
    shortName = "Isotropic Cylinders"
    parameters = (FitParameter("radius",
                               Length(u'nm').toSi(1.),
                               unit=Length(u'nm'),
                               displayName="Cylinder Radius",
                               generator=RandomExponential,
                               valueRange=(Length(u'nm').toSi(0.1),
                                           numpy.inf)),
                  Parameter(
                      "useAspect",
                      True,
                      displayName="Use aspect ratio (checked) or length "),
                  FitParameter("length",
                               Length(u'nm').toSi(10.),
                               unit=Length(u'nm'),
                               displayName="Length L of the Cylinder",
                               generator=RandomExponential,
                               valueRange=(Length(u'nm').toSi(0.1),
                                           Length(u'nm').toSi(1e10))),
                  FitParameter("aspect",
                               10.0,
                               displayName="Aspect ratio of the Cylinder",
                               generator=RandomExponential,
                               valueRange=(1e-3, 1e3)),
                  FitParameter(
                      "psiAngle",
                      0.1,
                      unit=Angle(u'°'),
                      displayName="Internal Parameter, not user adjustable",
                      generator=RandomUniform,
                      valueRange=(0.01, 2 * pi + 0.01)),
                  Parameter("psiAngleDivisions",
                            303.,
                            displayName="Orientation Integration Divisions",
                            valueRange=(1, numpy.inf)),
                  Parameter("sld",
                            SLD(u'Å⁻²').toSi(1e-6),
                            unit=SLD(u'Å⁻²'),
                            displayName="Scattering length density difference",
                            valueRange=(0, numpy.inf)))

    def __init__(self):
        super(CylindersIsotropic, self).__init__()
        # some presets of parameters to fit
        self.radius.setActive(True)

    def formfactor(self, dataset):
        # psi and phi defined in fig. 1, Pauw et al, J. Appl. Cryst. 2010
        # used in the equation for a cylinder from Pedersen, 1997

        psiRange = self.psiAngle.valueRange()
        psi = numpy.linspace(psiRange[0], psiRange[1],
                             self.psiAngleDivisions())

        if self.useAspect():
            halfLength = self.radius() * self.aspect()
        else:
            halfLength = 0.5 * self.length()
        qRsina = numpy.outer(dataset.q, self.radius() * sin((psi)))
        qLcosa = numpy.outer(dataset.q, halfLength * cos((psi)))
        fsplit = (
            (2. * scipy.special.j1(qRsina) / qRsina * sinc(qLcosa / pi)) *
            sqrt(abs(sin((psi)))[newaxis, :] + 0. * qRsina))
        #integrate over orientation
        return numpy.sqrt(numpy.mean(fsplit**2, axis=1))  # should be length q

    def volume(self):
        if self.useAspect():
            halfLength = self.radius() * self.aspect()
        else:
            halfLength = self.length() / 2.
        v = pi * self.radius()**2 * (halfLength * 2.)
        return v

    def absVolume(self):
        return self.volume() * self.sld()**2
Esempio n. 6
0
class CylindersIsotropic(SASModel):
    r"""Form factor of cylinders
    which are radially isotropic (so not spherically isotropic!)
    !!!completed but not verified!!!
    """
    shortName = "Cylinders defined by aspect ratio"
    parameters = (
        FitParameter("radius",
                     Length("nm").toSi(1.),
                     unit=Length("nm"),
                     displayName="Cylinder radius",
                     valueRange=(0., numpy.inf),
                     activeRange=Length("nm").toSi((0.1, 1e3)),
                     suffix="nm"),
        FitParameter("aspect",
                     10.0,
                     displayName="Aspect ratio L/(2R) of the cylinder",
                     valueRange=(0., numpy.inf),
                     activeRange=(1.0, 20),
                     suffix="-"),
        FitParameter("psiAngle",
                     Angle(u"°").toSi(10.),
                     unit=Angle(u"°"),
                     displayName="in-plane cylinder rotation",
                     valueRange=(0., Angle(u"°").toSi(180.))),
        FitParameter("psiAngleDivisions",
                     303.,
                     displayName="in-plane angle divisions",
                     valueRange=(0, numpy.inf),
                     suffix="-"),
    )

    def __init__(self):
        super(CylindersIsotropic, self).__init__()
        # some presets of parameters to fit
        self.radius.setActive(True)
        self.aspect.setActive(False)  # not expected to vary
        self.psiAngle.setActive(True)  # better when random
        self.psiAngleDivisions.setActive(False)  # not expected to vary

    def formfactor(self, dataset):
        #psi and phi defined in fig. 1, Pauw et al, J. Appl. Cryst. 2010
        #used in the equation for a cylinder from Pedersen, 1997

        dToR = pi / 180.  #degrees to radian
        psiRange = self.psiAngle.valueRange()
        psi = numpy.linspace(psiRange[0], psiRange[1],
                             self.psiAngleDivisions())

        ##replicate so we cover all possible combinations of psi, phi and psi
        #psiLong=psi[ numpy.sort( numpy.array( range(
        #    (len(psi)*len(q))
        #    ) ) %len(psi) ) ] #indexed to 111222333444 etc
        #qLong=q[ numpy.array( range(
        #    (len(psi)*len(q))
        #    ) ) %len(q) ] #indexed to 1234123412341234 etc

        #this is wrong for spherical symmetry:
        #qRsina=numpy.outer(q,radi*sin(((psi-psiA)*dToR)%180))
        #qLcosa=numpy.outer(q,radi*asp*cos(((psi-psiA)*dToR)%180))
        #fsplit=( 2*scipy.special.j1(qRsina)/qRsina * sin(qLcosa)/qLcosa )*sqrt((sin((psi-psiA)*dToR))[newaxis,:]%180+0*qRsina)
        qRsina = numpy.outer(dataset.q,
                             self.radius() * sin((psi * dToR) % 180.))
        qLcosa = numpy.outer(
            dataset.q,
            self.radius() * self.aspect() * cos((psi * dToR) % 180.))
        fsplit = (
            (2 * scipy.special.j1(qRsina) / qRsina * sin(qLcosa) / qLcosa) *
            sqrt((sin(psi * dToR))[newaxis, :] % 180 + 0. * qRsina))
        #integrate over orientation
        return numpy.sqrt(numpy.mean(fsplit**2, axis=1))  #should be length q

    def volume(self):
        v = pi * self.radius()**2 * (2. * self.radius() * self.aspect())
        return v
Esempio n. 7
0
class CylindersRadiallyIsotropic(SASModel):
    r"""Form factor of cylinders
    which are radially isotropic (so not spherically isotropic!)
    !!!completed but not verified!!!
    """
    shortName = "Radially (in-plane) isotropic cylinders"
    parameters = (
        FitParameter("radius",
                     Length(u'nm').toSi(1.),
                     unit=Length(u'nm'),
                     displayName="Cylinder radius",
                     generator=RandomExponential,
                     valueRange=Length(u'nm').toSi((0.1, numpy.inf)),
                     activeRange=Length(u'nm').toSi((0.1, 1e3))),  # preset
        FitParameter("aspect",
                     10.0,
                     displayName="Aspect ratio L/(2R) of the cylinder",
                     generator=RandomUniform,
                     valueRange=(0.1, numpy.inf),
                     activeRange=(1.0, 20.)),  # preset
        FitParameter("psiAngle",
                     0.17,
                     unit=Angle(u'°'),
                     displayName="in-plane cylinder rotation",
                     generator=RandomUniform,
                     valueRange=(0.01, 2 * pi + 0.01)),
        Parameter(
            "psiAngleDivisions",
            303.,  #setting to int gives OverflowError
            displayName="in-plane angle divisions",
            valueRange=(1, numpy.inf)),
        Parameter("sld",
                  SLD(u'Å⁻²').toSi(1e-6),
                  unit=SLD(u'Å⁻²'),
                  displayName="scattering length density difference",
                  valueRange=(0., numpy.inf)))

    def __init__(self):
        super(CylindersRadiallyIsotropic, self).__init__()
        # some presets of parameters to fit
        self.radius.setActive(True)
        self.aspect.setActive(False)  # not expected to vary
        self.psiAngle.setActive(True)  # better when random

    def formfactor(self, dataset):
        #psi and phi defined in fig. 1, Pauw et al, J. Appl. Cryst. 2010
        #used in the equation for a cylinder from Pedersen, 1997

        # dToR = pi/180. #degrees to radian not necessary since unit conversion
        psiRange = self.psiAngle.valueRange()
        psi = numpy.linspace(psiRange[0], psiRange[1],
                             self.psiAngleDivisions())

        ##replicate so we cover all possible combinations of psi, phi and psi
        #psiLong=psi[ numpy.sort( numpy.array( range(
        #    (len(psi)*len(q))
        #    ) ) %len(psi) ) ] #indexed to 111222333444 etc
        #qLong=q[ numpy.array( range(
        #    (len(psi)*len(q))
        #    ) ) %len(q) ] #indexed to 1234123412341234 etc

        #rotation can be used to get slightly better results, but
        #ONLY FOR RADIAL SYMMETRY, NOT SPHERICAL.
        qRsina = numpy.outer(dataset.q,
                             self.radius() * sin(((psi - self.psiAngle()))))
        qLcosa = numpy.outer(
            dataset.q,
            self.radius() * self.aspect() * cos(((psi - self.psiAngle()))))
        #leave the rotation out of it for now.
        #qRsina=numpy.outer(q,radi*sin(((psi)*dToR)))
        #qLcosa=numpy.outer(q,radi*asp*cos(((psi)*dToR)))
        fsplit = (2. * scipy.special.j1(qRsina) / qRsina * sin(qLcosa) /
                  qLcosa)
        #integrate over orientation
        return numpy.sqrt(numpy.mean(fsplit**2, axis=1))  # should be length q

    def volume(self):
        v = pi * self.radius()**2 * (2. * self.radius() * self.aspect())
        return v

    def absVolume(self):
        return self.volume() * self.sld()**2
Esempio n. 8
0
class EllipsoidalCoreShell(SASModel):
    r"""Form factor for an ellipsoidal core shell structure
    as defined in the SASfit manual (par. 3.2.3)
    Tested 2014-01-21 against SASfit function with good agreement.
    """
    shortName = "Core-Shell Ellipsoid"
    parameters = (
            FitParameter("a", Length(u'nm').toSi(1.), unit = Length(u'nm'),
                    displayName = "Principal Core Radius",
                    generator = RandomExponential,
                    valueRange = (0., numpy.inf),
                    activeRange = Length(u'nm').toSi((0.1, 1e3))), # preset
            FitParameter("b", Length(u'nm').toSi(10.), unit = Length(u'nm'),
                    displayName = "Equatorial Core Radius",
                    generator = RandomExponential,
                    valueRange = (0., numpy.inf),
                    activeRange = Length(u'nm').toSi((1.0, 1e4))), # preset
            FitParameter("t", Length(u'nm').toSi(1.), unit = Length(u'nm'),
                    displayName = "Thickness of Shell",
                    generator = RandomExponential,
                    valueRange = (0., numpy.inf),
                    activeRange = Length(u'nm').toSi((0.1, 1e3))), # preset
            Parameter("eta_c", SLD(u'Å⁻²').toSi(3.15e-6), unit = SLD(u'Å⁻²'),
                    displayName = "Core SLD",
                    generator = RandomUniform,
                    valueRange = (0, numpy.inf)),
            Parameter("eta_s", SLD(u'Å⁻²').toSi(2.53e-6), unit = SLD(u'Å⁻²'),
                    displayName = "Shell SLD",
                    generator = RandomUniform,
                    valueRange = (0, numpy.inf)),
            Parameter("eta_sol", 0., unit = SLD(u'Å⁻²'),
                    displayName = "Solvent SLD",
                    generator = RandomUniform,
                    valueRange = (0, numpy.inf)),
            Parameter("intDiv", 100,
                    displayName = "Orientation Integration Divisions",
                    generator = RandomUniform,
                    valueRange = (0, 1e4)),
    )

    def __init__(self):
        super(EllipsoidalCoreShell, self).__init__()
        # some presets of parameters to fit
        self.a.setActive(True)

    def formfactor(self, dataset):
        def j1(x):
            return ( sin(x) - x * cos(x) ) / (x**2)

        def calcXc(q, a, b, mu):
            sfact = sqrt(
                    a**2 * mu**2 + b**2 * (1 - mu**2)
                    )
            return numpy.outer(q, sfact)

        def calcXt(q, a, b, t, mu):
            sfact = sqrt(
                    (a + t)**2 * mu**2 + (b + t)**2 * (1 - mu**2)
                    )
            return numpy.outer(q, sfact)

        intVal = numpy.linspace(0., 1., self.intDiv())

        vc = 4./3. * pi *  self.a() * self.b() **2.
        vt = 4./3. * pi * (self.a() + self.t()) * (self.b() + self.t()) **2.
        vRatio = vc / vt

        xc = calcXc(dataset.q, self.a(), self.b(), intVal)
        xt = calcXt(dataset.q, self.a(), self.b(), self.t(), intVal)
        fsplit = (
                (self.eta_c() - self.eta_s()) * vRatio *
                ( 3 * j1( xc ) / xc ) +
                (self.eta_s() - self.eta_sol()) * 1. *
                ( 3 * j1( xt ) / xt )
                )
        # integrate over orientation
        return numpy.sqrt(numpy.mean(fsplit**2, axis=1)) # should be length q

    def volume(self):
        v = 4./3 * pi * (self.a() + self.t()) * (self.b() + self.t())**2
        return v

    def absVolume(self):
        return self.volume() #TODO: check how to do this.
Esempio n. 9
0
class CylindersRadiallyIsotropicTilted(SASModel):
    r"""Form factor of cylinders *UNFINISHED*
    which are radially isotropic (so not spherically isotropic!)
    """
    shortName = "Cylinders defined by aspect ratio"
    parameters = (
        FitParameter("radius",
                     1.0,
                     displayName="Cylinder radius",
                     valueRange=(0.1, numpy.inf),
                     activeRange=(0.1, 1e3),
                     suffix="nm"),
        FitParameter("aspect",
                     10.0,
                     displayName="Aspect ratio L/(2R) of the cylinder",
                     valueRange=(0.1, numpy.inf),
                     activeRange=(1, 20),
                     suffix="-"),
        FitParameter("psiAngle",
                     0.1,
                     displayName="in-plane cylinder rotation",
                     valueRange=(0.1, 180.1),
                     suffix="deg."),
        FitParameter("psiAngleDivisions",
                     303.,
                     displayName="in-plane angle divisions",
                     valueRange=(1, numpy.inf),
                     suffix="-"),
        FitParameter(
            "phiDistWidth",
            10.0,
            displayName="out-of-plane axis distribution width",
            #with 90 degrees fully out-of-plane (parallel to beam):
            valueRange=(0.1, 90.1),
            suffix="deg."),
        FitParameter("phiDistDivisions",
                     9.,
                     displayName="out of plane integration divisions",
                     valueRange=(1, numpy.inf),
                     suffix="-"),
    )

    def __init__(self):
        super(CylindersRadiallyIsotropicTilted, self).__init__()
        # some presets of parameters to fit
        self.radius.setActive(True)
        self.aspect.setActive(False)  # not expected to vary
        self.psiAngle.setActive(False)  # keep out for now.
        self.psiAngleDivisions.setActive(False)  # not expected to vary
        self.phiDistWidth.setActive(False)  # gaussian width
        self.phiDistDivisions.setActive(False)  # integration steps

    def formfactor(self, dataset):
        #the remaining values are never active fitting parameters
        #psi and phi defined in fig. 1, Pauw et al, J. Appl. Cryst. 2010
        #also shown in Figure 3.98 in the SASfit manual
        #used in the equation for a cylinder from Pedersen, 1997

        dToR = pi / 180.  #degrees to radian
        psiRange = self.psiAngle.valueRange()
        psi = numpy.linspace(psiRange[0], psiRange[1],
                             self.psiAngleDivisions())
        # determine the divisions to integrate phi over: equal probability
        x = linspace(0, 0.99, self.phiDistDivisions() + 1.)  # zero added
        phiAngles = scipy.stats.norm.interval(x)[
            1]  # only get the positive values
        # now calculate the centroid value for each integration bin
        phiCtr = scipy.stats.norm.interval(x[:-1] + diff(x) / 2.)[1]
        # each of these integration bins is equally probable. Can integrate
        # using mean function.

        # rotation can be used to get slightly better results, but
        # ONLY FOR RADIAL SYMMETRY, NOT SPHERICAL.
        fcyl = 0.
        for pIdx in range(len(phiCtr)):
            # TODO: Implementing from equations 3.263 in SASfit manual
            # leave the cylinder axis arbitrary psi rotation out of it for now.
            # calculate cos(gamma)
            # since we do not want to describe theta, we use an angle
            # omega describing cylinder axis projection in x-z plane (=psi),
            # combined with
            # phi describing cylinder axis projection onto x-y plane
            # theta=omega/cos(phi) (probably)
            # cosGammaP=sin(psi*dToR)*cos(psi*dToR)*cos(phiCtr[pidX]*dToR)\
            #         + cos(psi*dToR)*sin(psi*dToR)
            # cosGammaM=
            qRsina = numpy.outer(dataset.q, self.radius() * sin(psi * dToR))
            # approximation for small tilts, adjusts the length of the cylinder only!
            qLcosa = numpy.outer(
                dataset.q,
                self.radius() * self.aspect() * cos(phiCtr[pIdx] * dToR) *
                cos(psi * dToR))

            fsplit = (2. * scipy.special.j1(qRsina) / qRsina *
                      sinc(qLcosa / pi))
            # integrate over orientation
            fcyl += numpy.sqrt(numpy.mean(fsplit**2, axis=1)) / len(
                phiCtr)  # should be length q

        return fcyl

    def volume(self):
        v = pi * self.radius()**2 * (2. * self.radius() * self.aspect())
        return v
Esempio n. 10
0
class LMADenseSphere(SASModel):
    """Form factor of a sphere convoluted with a structure factor,
    equations 15-17 from Pedersen, J. Appl. Cryst. 27 (1994), 595--608. 
    Correct eqn given in Kinning and Thomas, Macromolecules 17 (1984) 1712.
    Internally set parameters are volume fraction of the hard spheres,
    and the multiplication factor /mf/ for an additional stand-off distance
    between the hard spheres: Rh=mf*R where Rh is the hard-sphere radius
    ("interaction radius") used in the structure factor, R is the radius of
    the sphere, and mf is the multiplication factor.
    """
    canSmear = True
    shortName = "LMADenseSphere"
    parameters = (
            FitParameter("radius", 
                    Length(u"nm").toSi(1.), 
                    unit = Length(u"nm"),
                    displayName = "Sphere radius",
                    valueRange = (0., np.inf),
                    generator = RandomUniform,
                    decimals = 9),
            FitParameter("volFrac", 
                    Fraction(u"%").toSi(10), 
                    unit = Fraction(u"%"),
                    displayName = "Volume fraction of spheres",
                    valueRange = (Fraction(u"%").toSi(0.001), 
                        Fraction(u"%").toSi(100.)),
                    generator = RandomUniform,
                    decimals = 9),
            Parameter("mf", 
                    -1., # auto
                    displayName = "standoff multiplier (-1 = auto)",
                    valueRange = (-1., 1.e6),
                    unit = NoUnit(u''),
                    decimals = 9,
                    displayValues = {-1.: "auto"}),
            Parameter("sld", 
                    SLD(u'Å⁻²').toSi(1e-6), 
                    unit = SLD(u'Å⁻²'),
                    displayName = "Scattering length density difference",
                    valueRange = (0., np.inf),
                    decimals = 9)
            )

    def __init__(self):
        super(LMADenseSphere, self).__init__()
        # some presets of parameters to fit
        self.radius.setActive(True)

    def volume(self):
        result = (pi*4./3.) * self.radius()**3
        return result
    
    def absVolume(self):
        return self.volume() * self.sld()**2

    def formfactor(self, dataset):
        
        q = self.getQ(dataset)
        SFmu = self.volFrac()
        SFmf = self.mf()
        if SFmf == -1:
            SFmf = (0.634 / SFmu) **(1. / 3)

        def SFG(A, SFmu):
            alpha = ( 1 + 2 * SFmu )**2 / ( 1 - SFmu )**4
            beta = -6 * SFmu * ( 1 + SFmu / 2 )**2 / ( 1 - SFmu )**4
            gamma = SFmu * alpha / 2
            G = (
                    alpha * ( sin(A) - A * cos(A) ) / A**2 +
                    beta *(2 * A * sin(A) + (2 - A**2) * cos(A) - 2) / A**3 +
                    gamma*( -1 * A**4 * cos(A) + 4 * ((3 * A**2 - 6) * cos(A)
                        + (A**3 - 6 * A) * sin(A) + 6 ) ) / A**5
                    )
            return G


        qr = q * self.radius()
        result = 3. * (sin(qr) - qr * cos(qr)) / (qr**3.)
        #now we introduce the structure factor
        rhsq = 2. * q * (SFmf * self.radius())
        G = SFG(rhsq, SFmu)
        S = (( 1. + 24. * SFmu * G / rhsq ))**(-1)
        #print (S < 0).sum()
        # the above structure factor needs to be the square root as it is
        # taken into the form factor. Eventually, we want to calculate the
        # scattering as FF**2 * S, which we are now achieving as
        # (FF * S**0.5)**2. The minus sign in the exponent of the above
        # equation comes from the original equation in the literature.
        result = np.sqrt(result**2 * S)
        return result
Esempio n. 11
0
class GaussianChain(SASModel):
    r"""Form factor of flexible polymer chains which are not selfavoiding
    and obey Gaussian statistics after [Debye47]_

    See also: http://sasfit.sf.net/manual/Gaussian_Chain#Gauss_2

    .. [Debye47] `P. Debye, Mollecular-weight determination by light
        scattering, Journal of Physical and Colloid Chemistry, 51:18--32,
        1947. <http://dx.doi.org/10.1021/j150451a002>`_

    I_0 = (bp - (k * Rg^2) * eta_s)^2 with k = 1 nm.
    k * Rg^2 = volume approximation
    """
    shortName = "Gaussian Chain"
    parameters = (
        FitParameter("rg",
                     Length(u'nm').toSi(1.),
                     unit=Length(u'nm'),
                     displayName="radius of gyration, Rg",
                     generator=RandomExponential,
                     valueRange=(0., numpy.inf),
                     activeRange=Length(u'nm').toSi((1.0, 1e2))),  # preset
        FitParameter("bp",
                     Length(u'nm').toSi(100.),
                     unit=Length(u'nm'),
                     displayName="scattering length of the polymer",
                     generator=RandomUniform,
                     valueRange=(0., numpy.inf),
                     activeRange=Length(u'nm').toSi((0.1, 1e3))),  # preset
        FitParameter("etas",
                     SLD(u'Å⁻²').toSi(1e-6),
                     unit=SLD(u'Å⁻²'),
                     displayName="scattering length density of the solvent",
                     generator=RandomUniform,
                     valueRange=(0., numpy.inf),
                     activeRange=SLD(u'Å⁻²').toSi((0.1, 10.))),  # preset
        FitParameter("k",
                     1.0,
                     displayName="volumetric scaling factor of Rg",
                     generator=RandomUniform,
                     valueRange=(0., numpy.inf),
                     activeRange=(0.1, 10.)),  # preset
    )

    def __init__(self):
        super(GaussianChain, self).__init__()
        # some presets of parameters to fit
        self.rg.setActive(True)

    def formfactor(self, dataset):
        # vectorized data
        beta = self.bp() - (self.k() * self.rg()**2) * self.etas()
        u = (dataset.q * self.rg())**2
        result = numpy.sqrt(2.) * numpy.sqrt(numpy.expm1(-u) + u) / u
        result *= beta
        result[dataset.q <= 0.0] = beta
        return result

    def volume(self):
        v = self.k() * self.rg()**2
        return v

    @mixedmethod
    def fixTestParams(self, params):
        # order and meaning differs from sasfit Gauss2 model
        vol = params['etas']
        params['etas'] = params['k']
        params['k'] = vol / params['rg']**2.
        return params