Source code for jscatter.formfactor.bodies

# -*- coding: utf-8 -*-
# written by Ralf Biehl at the Forschungszentrum Jülich ,
# Jülich Center for Neutron Science 1 and Institute of Complex Systems 1
#    Jscatter is a program to read, analyse and plot data
#    Copyright (C) 2015-2024  Ralf Biehl
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

import inspect
import os


import numpy as np
import scipy.special as special

from .. import formel
from .. import structurefactor as sf
from ..dataarray import dataArray as dA
from .composed import multiShellCylinder, multiShellEllipsoid
from .formfactoramplitudes import fa_sphere as _fa_sphere, fq_cuboid as _fq_cuboid, fq_prism as _fq_prism
from .formfactoramplitudes import fq_triellipsoid

from .cloudscattering import cloudScattering

__all__ = ['sphere', 'disc', 'cylinder', 'cuboid', 'ellipsoid', 'triaxialEllipsoid', 'superball', 'prism']


_path_ = os.path.realpath(os.path.dirname(__file__))

# variable to allow printout for debugging as if debug:print 'message'
debug = False


[docs] def sphere(q, radius, contrast=1): r""" Scattering of a single homogeneous sphere. Parameters ---------- q : float Wavevector in units of 1/nm radius : float Radius in units nm contrast : float, default=1 Difference in scattering length to the solvent = contrast Returns ------- dataArray Columns [q, Iq, fa] Iq scattering intensity - fa formfactor amplitude - .I0 forward scattering Notes ----- .. math:: I(q)= \rho^2V^2\left[\frac{3(sin(qR) - qR cos(qR))}{(qR)^3}\right]^2 with contrast :math:`\rho` and sphere volume :math:`V=\frac{4\pi}{3}R^3` The first minimum of the form factor is at qR=4.493 Examples -------- :: import jscatter as js import numpy as np q=js.loglist(0.1,5,300) p=js.grace() R=3 sp=js.ff.sphere(q, R) p.plot(sp.X*R,sp.Y,li=1) p.yaxis(label='I(q)',scale='l',min=1e-4,max=1e5) p.xaxis(label='qR',scale='l',min=0.1*R,max=5*R) p.legend(x=0.15,y=0.1) #p.save(js.examples.imagepath+'/sphere.jpg') .. image:: ../../examples/images/sphere.jpg :align: center :width: 50 % :alt: sphere References ---------- .. [1] Guinier, A. and G. Fournet, "Small-Angle Scattering of X-Rays", John Wiley and Sons, New York, (1955). """ R = radius qr = np.atleast_1d(q) * R fa0 = (4 / 3. * np.pi * R ** 3 * contrast) # forward scattering amplitude q=0 faQR = fa0 * _fa_sphere(qr) result = dA(np.c_[q, faQR ** 2, faQR].T) result.columnname = 'q; Iq; fa' result.setColumnIndex(iey=None) result.radius = radius result.I0 = fa0**2 result.fa0 = fa0 result.contrast = contrast result.modelname = inspect.currentframe().f_code.co_name return result
[docs] def disc(q, R, D, SLD, solventSLD=0, alpha=None): """ Disc form factor . Parameters ---------- q : array Wavevectors, units 1/nm R : float Radius in nm. D : float Thickness of the disc in units nm. SLD,solventSLD : float Scattering length density in nm^-2. alpha : float, [float,float] , unit rad Orientation, angle between the cylinder axis and the scattering vector q. 0 means parallel, pi/2 is perpendicular If alpha =[start,end] is integrated between start,end start > 0, end < pi/2 Notes ----- For details see :py:func:`~jscatter.formfactor.composed.multiShellDisc`. Examples -------- Simple disc :: import jscatter as js import numpy as np x=np.r_[0.0:10:0.01] p=js.grace() # single disc bshell = js.ff.multiShellDisc(x,2,2,6.39e-4) p[0].plot(bshell, le='disc') p[0].yaxis(label='I(q)',scale='l',min=1e-7,max=0.01) p[0].xaxis(label='q / nm\S-1',scale='l',min=0.1,max=10) p[0].legend(x=2,y=0.003) p[0].title('simple disc') # p.save(js.examples.imagepath+'/simpleDisc.jpg') .. image:: ../../examples/images/simpleDisc.jpg :align: center :width: 50 % :alt: simpleDisc """ if alpha is None: alpha = [0, np.pi / 2] return multiShellCylinder(q, D/2, [R], [SLD], solventSLD=solventSLD, alpha=alpha)
[docs] def cylinder(q, L, radius, SLD=1e-3, solventSLD=0, alpha=None, nalpha=90, h=None): r""" Cylinder form factor including cap. Based on multiShellCylinder (see there for detailed description of parameters). Parameters ---------- q : array Scattering vector i units 1/nm. L : float Length in nm. radius : float Radius in nm. h : float Cap geometry SLD : float Cylinder scattering length density in units nm^-2. solventSLD : float Solvent scattering length density in units nm^-2. h : float, default=None Geometry of the cap with radii R=(r**2+h**2)**0.5 in units nm. h is distance of cap center with radius R from the flat cylinder cap and r as radius of the cylinder. - None No cap, flat end as default. - 0 cap radius equal cylinder radius - >0 cap radius larger cylinder radius as barbell - <0 cap radius smaller cylinder radius as lens cap alpha : float, [float,float] , default [0,pi/2], unit rad Orientation, angle between the cylinder axis and the scattering vector q in units rad. 0 means parallel, pi/2 is perpendicular If alpha =[start,end] is integrated between start,end start > 0, end < pi/2 nalpha : int, default 90 Number of points in Gauss integration along alpha. Notes ----- Compared to SASview (5.0) this yields a factor 2 less intensity. Correctness can be checked as the forward scattering .I0 is independent of orientation and should be equal V² (V is volume) if SLD=1 and solvent SLD=0. Definition of parameters can be seen in this figure ignoring the outer shell. See :py:func:`~jscatter.formfactor.composed.multiShellCylinder` : .. image:: barbell.png :align: center :height: 150px :alt: Image of barbell Examples -------- The typical **long cylinder** formfactor with a linear region for long cylinders. :: import jscatter as js import numpy as np q=js.loglist(0.01,8,500) p=js.grace() p.multi(1,2) R=2 for L in [20,40,150]: cc=js.ff.cylinder(q,L=L,radius=R) p[0].plot(cc,li=-1,sy=0,le='L ={0:.0f} R={1:.1f}'.format(L,R)) L=60 for R in [1,2,4]: cc=js.ff.cylinder(q,L=L/R**2,radius=R) p[1].plot(cc,li=-1,sy=0,le='L ={0:.2f} R={1:.1f}'.format(L/R**2,R)) p[0].yaxis(label='I(q)',scale='l',min=1e-6,max=10) p[0].xaxis(label='q / nm\S-1',scale='l',min=0.01,max=6) p[1].yaxis(label='I(q)',scale='l',min=1e-7,max=1) p[1].xaxis(label='q / nm\S-1',scale='l',min=0.01,max=6) p[1].text(r'forward scattering I0\n=(SLD*L\xp\f{}R\S2\N)\S2\N = 0.035530',x=0.02,y=0.1) p.title('cylinder') p[0].legend(x=0.012,y=0.001) p[1].legend(x=0.012,y=0.0001) #p.save(js.examples.imagepath+'/cylinder.jpg') .. image:: ../../examples/images/cylinder.jpg :align: center :width: 50 % :alt: cylinder The following **short cylinders** highlight the peak shape which can differ from expectations. Dependent on R, L interferences we see flattened peaks and that consecutive peaks also differ in shape. :: import jscatter as js import numpy as np q=js.loglist(0.1,3,200) p=js.grace() L=25 for R in np.r_[5:12:2]: cc=js.ff.cylinder(q,L=L,radius=R) p.plot(cc.X,cc.Y/cc.I0,li=[1,3,-1],sy=0,le='L ={0:.2f} R={1:.1f}'.format(L,R)) p.yaxis(label='I(q)',scale='l') p.xaxis(label='q / nm\S-1',scale='n',min=0.1,max=3.3) p.title('short cylinders') p.legend(x=2,y=0.02) #p.save(js.examples.imagepath+'/cylindershort.jpg') .. image:: ../../examples/images/cylindershort.jpg :align: center :width: 50 % :alt: cylindershort References ---------- .. [1] Guinier, A. and G. Fournet, "Small-Angle Scattering of X-Rays", John Wiley and Sons, New York, (1955) .. [2] http://www.ncnr.nist.gov/resources/sansmodels/Cylinder.html """ if alpha is None: alpha = [0, np.pi / 2] return multiShellCylinder(q, L, [radius], [SLD], h=h, solventSLD=solventSLD, alpha=alpha, nalpha=nalpha)
[docs] def cuboid(q, a, b=None, c=None, SLD=1, solventSLD=0, N=30): r""" Formfactor of rectangular cuboid with different edge lengths. Parameters ---------- q : array Wavevector in 1/nm a,b,c : float, None Edge length, for a=b=c its a cube, Units in nm. If b=None b=a. If c=None c=b. SLD : float, default =1 Scattering length density of cuboid.unit nm^-2 e.g. SiO2 = 4.186*1e-6 A^-2 = 4.186*1e-4 nm^-2 for neutrons solventSLD : float, default =0 Scattering length density of solvent. unit nm^-2 e.g. D2O = 6.335*1e-6 A^-2 = 6.335*1e-4 nm^-2 for neutrons N : int Order for Gaussian integration over both phi and theta. Returns ------- dataArray Columns [q,Iq] - .I0 forward scattering - .edges - .contrast Notes ----- .. math:: I(q)=\rho^2V_{cube}^2 \int_{0}^{2\pi}\int_{0}^{\pi} \lvert sinc(q_xa/2 ) sinc(q_yb/2) sinc(q_zc/2)\rvert^2 \sin\theta d\theta d\phi with :math:`q = (q_x,q_y,q_z) = (q\sin\theta\cos\phi,q\sin\theta\sin\phi,q\cos\theta)` and contrast :math:`\rho` [1]_. In [1]_ the edge length is only half of it. Examples -------- :: import jscatter as js import numpy as np q=np.r_[0.1:5:0.01] p=js.grace() p.plot(js.ff.cuboid(q,60,4,6)) p.plot(js.ff.cuboid(q,10,4,60)) p.plot(js.ff.cuboid(q,11,11,11),li=1) p.yaxis(scale='l',label='I(q)') p.xaxis(scale='l',label='q / nm\S-1') p.title('cuboid') #p.save(js.examples.imagepath+'/cuboid.jpg') .. image:: ../../examples/images/cuboid.jpg :width: 50 % :align: center :alt: cuboid References ---------- .. [1] Analysis of small-angle scattering data from colloids and polymer solutions: modeling and least-squares fitting Pedersen, Jan Skov Advances in Colloid and Interface Science 70, 171 (1997) http://dx.doi.org/10.1016/S0001-8686(97)00312-6 """ if b is None: b = a if c is None: c = b sld = SLD - solventSLD V = a * b * c q = np.atleast_1d(q) # integrate by Gauss quadrature rule fq = formel.pQFGxD(_fq_cuboid, [0, 0], [np.pi/2, np.pi/2], ['p', 't'], q=q, a=a, b=b, c=c, n=N) * 8 / (4 * np.pi) I0 = V ** 2 * sld ** 2 result = dA(np.c_[q, I0 * fq].T) result.setColumnIndex(iey=None) result.columnname = 'q; Iq' result.modelname = inspect.currentframe().f_code.co_name result.I0 = I0 result.edges = [a, b, c] result.contrast = sld return result
[docs] def ellipsoid(q, Ra, Rb, SLD=1, solventSLD=0, alpha=None, tol=1e-6): r""" Form factor for a simple ellipsoid (ellipsoid of revolution). Parameters ---------- q : float Scattering vector unit e.g. 1/A or 1/nm 1/Ra Ra : float Radius rotation axis units in 1/unit(q) Rb : float Radius rotated axis units in 1/unit(q) SLD : float, default =1 Scattering length density of unit nm^-2 e.g. SiO2 = 4.186*1e-6 A^-2 = 4.186*1e-4 nm^-2 for neutrons solventSLD : float, default =0 Scattering length density of solvent. unit nm^-2 e.g. D2O = 6.335*1e-6 A^-2 = 6.335*1e-4 nm^-2 for neutrons alpha : [float,float] , default [0,90] Angle between rotation axis Ra and scattering vector q in unit grad Between these angles orientation is averaged alpha=0 axis and q are parallel, other orientation is averaged tol : float relative tolerance for integration between alpha Returns ------- dataArray Columns [q; Iq; beta ] - .RotationAxisRadius - .RotatedAxisRadius - .EllipsoidVolume - .I0 forward scattering q=0 - beta is asymmetry factor according to [3]_. :math:`\beta = |<F(Q)>|^2/<|F(Q)|^2>` with scattering amplitude :math:`F(Q)` and form factor :math:`P(Q)=<|F(Q)|^2>` Notes ----- See :py:func:`~jscatter.formfactor.bodies.triaxialEllipsoid` with Rb=Rc for the equation. Examples -------- Simple ellipsoid in vacuum:: import jscatter as js import numpy as np x=np.r_[0.1:10:0.01] Rp=6. Re=8. elli = js.ff.ellipsoid(x,Rp,Re,1) # plot it p=js.grace() p.plot(elli) p.yaxis(scale='l',label='I(q)',min=0.01,max=100) p.xaxis(scale='l',label='q / nm\S-1',min=0.1,max=10) p.title('ellipsoid') # p.save(js.examples.imagepath+'/ellipsoid.jpg') .. image:: ../../examples/images/ellipsoid.jpg :width: 50 % :align: center :alt: ellipsoid References ---------- .. [1] Structure Analysis by Small-Angle X-Ray and Neutron Scattering Feigin, L. A, and D. I. Svergun, Plenum Press, New York, (1987). .. [2] http://www.ncnr.nist.gov/resources/sansmodels/Ellipsoid.html .. [3] M. Kotlarchyk and S.-H. Chen, J. Chem. Phys. 79, 2461 (1983). """ if alpha is None: alpha = [0, 90] result = multiShellEllipsoid(q, Ra, Rb, shellSLD=SLD, solventSLD=solventSLD, alpha=alpha, tol=tol) attr = result.attr result.EllipsoidVolume = result.outerVolume result.RotationAxisRadius = Ra result.RotatedAxisRadius = Rb result.contrast = result.shellcontrast result.angles = alpha attr.remove('columnname') attr.remove('I0') for at in attr: delattr(result, at) result.modelname = inspect.currentframe().f_code.co_name return result
[docs] def triaxialEllipsoid(q, Ra, Rb, Rc, SLD=1, solventSLD=0, n=25): r""" Formfactor triaxial ellipsoid. Parameters ---------- q : array Scattering vector in units 1/nm. Ra,Rb,Rc : float Half axes = radii in units nm. SLD : float Scattering length density in unit nm^-2. solventSLD : float Scattering length density of solvent in unit nm^-2 . n : int Order for Gaussian integration over both phi and theta. Returns ------- dataArray Columns [q; Iq] - .Ra, .Rb, .Rc - .volume - .I0 forward scattering q=0 Notes ----- According to [1]_ the triaxial ellipsoid is .. math:: I(q) = V^2 \rho^2 \int_0^{2\pi} \int_0^{\pi} \phi(QR_a)^2 sin(\theta) d\theta d\phi with :math:`u=QR_a` and :math:`Q = (q_x^2 + (R_b/R_aq_y)^2 + (R_c/R_aq_z)^2 )^{(1/2)}` and .. math:: \Phi(u) = \frac{3(sin(u)-u cos(u))}{u^3} also contrast :math:`\rho`, ellipsoid volume :math:`V = 4/3\pi R_a R_b R_c` Examples -------- Triaxial ellipsoid in vacuum :: import jscatter as js import numpy as np q=np.r_[0.1:6:0.01] p=js.grace() elli = js.ff.triaxialEllipsoid(q,3,3,3) p.plot(elli, sy=3, le='sphere') p.plot(js.ff.sphere(q,radius=3),li=[1,2,1],sy=0) # rotation ellipsoid relli = js.ff.triaxialEllipsoid(q,3,4,4) p.plot(relli, sy=2, le='rot ellipsoid') p.plot(js.ff.ellipsoid(q,Ra=3,Rb=4),li=[1,2,1],sy=0) telli = js.ff.triaxialEllipsoid(q,3,4,5) p.plot(telli, sy=[3,0.2,3], le='triaxial ellipsoid') p.yaxis(scale='l',label='I(q)',min=0.01,max=1e5) p.xaxis(scale='l',label='q / nm\S-1',min=0.1,max=10) p.legend(x=0.15,y=100) p.title('triaxial ellipsoid model comparison ') p.subtitle('lines are standard models for sphere and ellipsoid') #p.save(js.examples.imagepath+'/triaxialEllipsoid(.jpg') .. image:: ../../examples/images/triaxialEllipsoid(.jpg :width: 50 % :align: center :alt: triaxialEllipsoid References ---------- .. [1] Generalizing small-angle scattering form factors with linear transformations Matt Thompson J. Appl. Cryst. (2020). 53, 1387-1391 https://doi.org/10.1107/S1600576720010389 """ # forward scattering Q=0 ------------- V = 4 / 3. * np.pi * Ra * Rb * Rc contrast = SLD - solventSLD # integration over orientations for all q res = formel.pQFGxD(fq_triellipsoid, [0, 0], [np.pi/2, np.pi/2], ['p', 't'], n=n, q=q, Ra=Ra, Rb=Rb, Rc=Rc) res *= 8 / (4 * np.pi) result = dA(np.c_[q, V**2 * contrast**2 * res].T) result.setColumnIndex(iey=None) result.columnname = 'q; Iq' result.volume = V result.Ra = Ra result.Rb = Rb result.Rc = Rc result.I0 = V**2 * contrast**2 result.modelname = inspect.currentframe().f_code.co_name return result
[docs] def superball(q, R, p, SLD=1, solventSLD=0, nGrid=12, returngrid=False): r""" A superball is a general mathematical shape that can be used to describe rounded cubes, sphere and octahedron's. The shape parameter p continuously changes from star, octahedron, sphere to cube. Parameters ---------- q : array Wavevector in 1/nm R : float, None 2R = edge length p : float, 0<p<100 Parameter that describes shape - p=0 empty space - p<0.5 concave octahedron's - p=0.5 octahedron - 0.5<p<1 convex octahedron's - p=1 spheres - p>1 rounded cubes - p->inf cubes SLD : float, default =1 Scattering length density of cuboid. unit nm^-2 solventSLD : float, default =0 Scattering length density of solvent. unit nm^-2 nGrid : int Number of gridpoints in superball volume is ~ nGrid**3. The accuracy can be increased increasing the number of grid points dependent on needed q range. Orientational average is done with 2(nGrid*4)+1 orientations on Fibonacci lattice. returngrid : bool Return only grid as lattice object. The a visualisation can be done using grid.show() Returns ------- dataArray Columns [q,Iq, beta] Notes ----- The shape is described by .. math:: |x|^{2p} + |y|^{2p} + |z|^{2p} \le |R|^{2p} which results in a sphere for p=1. The numerical integration is done by a pseudorandom grid of scatterers. .. image:: ../../examples/images/superballfig.jpg :width: 100 % :align: center :alt: superballfig Examples -------- Visualisation as shown above :: import jscatter as js import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D fig = plt.figure(figsize=[15,3]) q=np.r_[0:5:0.1] R=3 for i,p in enumerate([0.2,0.5,1,1.3,20],1): ax = fig.add_subplot(1,5,i, projection='3d') grid=js.ff.superball(q,R,p=p,nGrid=40,returngrid=True) grid.filter(lambda xyz: np.linalg.norm(xyz)) grid.show(fig=fig, ax=ax,atomsize=0.2) ax.set_title(f'p={p:.2f}') #fig.savefig(js.examples.imagepath+'/superballfig.jpg') Compare to extreme cases of sphere (p=1) and cube (p->inf , use here 100) to estimate the needed accuracy in your Q range. :: import jscatter as js import numpy as np # q=np.r_[0:3.5:0.02] R=6 nGrid=25 p=js.grace() p.multi(2,1) p[0].yaxis(scale='l',label='I(q)') ss=js.ff.superball(q,R,p=1,nGrid=12) p[0].plot(ss,legend='superball p=1 nGrid=12 default') ss=js.ff.superball(q,R,p=1,nGrid=25) p[0].plot(ss,legend='superball p=1 nGrid=25') p[0].plot(js.ff.sphere(q,R),li=1,sy=0,legend='sphere ff') p[0].legend(x=2,y=5e5) # p[1].yaxis(scale='l',label='I(q)') p[1].xaxis(scale='n',label='q / nm\S-1') cc=js.ff.superball(q,R,p=100) p[1].plot(cc,sy=[1,0.3,1],legend='superball p=100 nGrid=12') cc=js.ff.superball(q,R,p=100,nGrid=25) p[1].plot(cc,sy=[1,0.3,2],legend='superball p=100 nGrid=25') p[1].plot(js.ff.cuboid(q,2*R),li=4,sy=0,legend='cuboid') p[1].legend(x=2,y=9e5) p[0].title('Superball with transition from sphere to cuboid') p[0].subtitle('p=1 sphere; p>1 round cube; p>20 cube ') #p.save(js.examples.imagepath+'/superball.jpg') .. image:: ../../examples/images/superball.jpg :width: 50 % :align: center :alt: superball **Superball scaling** with :math:`q/p^{1/3}` close to sphere shape with p=1. Small deviations from sphere (as a kind of long wavelength roughness) cannot be discriminated from polydispersity or small ellipsoidality. :: import jscatter as js import numpy as np q=np.r_[0:5:0.02] R=3 Fq=js.dL() for i,p in enumerate([0.5,0.8,0.9,1,1.115,1.3,2],1): fq=js.ff.superball(q,R,p=p,nGrid=20) Fq.append(fq) pp=js.grace() pp.multi(2,1,vgap=0.2) for fq in Fq[1:-1]: pp[0].plot(fq.X,fq.Y/fq.Y[0],sy=[-1,0.2,-1],le=f'{fq.rounding_p:.2g}') pp[1].plot(fq.X*fq.rounding_p**(1/3),fq.Y/fq.Y[0],sy=[-1,0.2,-1],le=f'{fq.rounding_p:.2g}') fq=Fq[0] pp[0].plot(fq.X,fq.Y/fq.Y[0],sy=0,li=[1,2,-1],le=f'{fq.rounding_p:.2g}') pp[1].plot(fq.X*fq.rounding_p**(1/3),fq.Y/fq.Y[0],sy=0,li=[1,2,-1],le=f'{fq.rounding_p:.2g}') fq=Fq[-1] pp[0].plot(fq.X,fq.Y/fq.Y[0],sy=0,li=[3,2,-1],le=f'{fq.rounding_p:.2g}') pp[1].plot(fq.X*fq.rounding_p**(1/3),fq.Y/fq.Y[0],sy=0,li=[3,2,-1],le=f'{fq.rounding_p:.2g}') pp[0].legend(x=0.2,y=0.05) pp[0].yaxis(label='I(q)',scale='l') pp[1].yaxis(label='I(q)',scale='l') pp[0].xaxis(label='q / nm') pp[1].xaxis(label=r'q/p\S1/3\N') pp[0].title('superball scaling') pp[0].subtitle('p=0.5 octahedron, p=1 sphere, p>10 cube') pp[1].text(r'q scaled by p\S-1/3\nclose to p=1 I(q) scales to similar shape ',x=4,y=0.1) pp[0].text('original',x=4,y=0.1) #p.save(js.examples.imagepath+'/superballscaling.jpg') .. image:: ../../examples/images/superballscaling.jpg :width: 50 % :align: center :alt: superballscaling References ---------- .. [1] Periodic lattices of arbitrary nano-objects: modeling and applications for self-assembled systems Yager, K.G.; Zhang, Y.; Lu, F.; Gang, O. Journal of Applied Crystallography 2014, 47, 118–129. doi: 10.1107/S160057671302832X .. [2] http://gisaxs.com/index.php/Form_Factor:Superball """ p2 = abs(2. * min(p, 101.)) R = abs(R) q = np.atleast_1d(q) contrast = SLD - solventSLD # volume according to Soft Matter, 2012, 8, 8826-8834, DOI: 10.1039/C2SM25813G frac = special.gamma(1 + 1 / p2) ** 3 / special.gamma(1 + 3 / p2) V = 8 * R ** 3 * frac # superball surface radius for a point, # a definition of radius in p2 exponent as # r = lambda xyz: (np.abs(xyz[:, :3]) ** p2).sum(axis=1) ** (1. / p2) # The same is calculated in numpy.linalg.norm(xyz,ord=p2,axis=1) but faster # The integration using pseudorandom grid is as fast as 3D GaussIntegration of same quality looking at high Q # accuracy (deviation from analytic sphere/cube) # pseudorandom grid grid = sf.pseudoRandomLattice([2 * R, 2 * R, 2 * R], int(nGrid ** 3 / frac), b=0) grid.move([-R, -R, -R]) # move to zero center # select according to p2 norm <R grid.set_bsel(1, np.linalg.norm(grid.XYZall, ord=p2, axis=1) < R) grid.prune(grid.ball > 0) if returngrid: return grid # calc scattering result = cloudScattering(q, grid, relError=nGrid * 4) result.columnname = 'q; Iq; beta; fa' result.Y = result.Y * V ** 2 * contrast ** 2 result.modelname = inspect.currentframe().f_code.co_name result.R = R result.Volume = V result.rounding_p = p2 / 2. result.contrast = contrast result.I0 = V ** 2 * contrast ** 2 return result
[docs] def prism(q, R, H, SLD=1, solventSLD=0, relError=300): r""" Formfactor of prism (equilateral triangle) . Parameters ---------- q : array 3xN R : float Edge length of equilateral triangle in units nm. H : float Height in units nm SLD : float, default =1 Scattering length density unit nm^-2 e.g. SiO2 = 4.186*1e-6 A^-2 = 4.186*1e-4 nm^-2 for neutrons solventSLD : float, default =0 Scattering length density of solvent. unit nm^-2 e.g. D2O = 6.335*1e-6 A^-2 = 6.335*1e-4 nm^-2 for neutrons relError : float, default 300 Determines how points on sphere are selected for integration - >=1 Fibonacci Lattice with relError*2+1 points (min 15 points) - 0<1 Pseudo random points on sphere (see randomPointsOnSphere). Stops if relative improvement in mean is less than relError (uses steps of 20*ncpu new points). Final error is (stddev of N points) /sqrt(N) as for Monte Carlo methods. even if it is not a correct 1-sigma error in this case. Returns ------- dataArray [q, fq] Notes ----- With contrast :math:`\rho` and wavevector :math:`q=[q_x,q_y,q_z]` the scattering amplitude :math:`F_a(q)` is .. math:: F_a(q_x,q_y,q_z) = \rho \frac{2 \sqrt{3} e^{-iq_yR/ \sqrt{3}} H} {q_x (q_x^2-3q_y^2)} \ (q_x e^{i q_yR\sqrt{3}} - q_xcos(q_xR) - i\sqrt{3} q_ysin(q_xR)) sinc(q_zH/2) and :math:`F(q)=<F_a(q)F^*_a(q)>=<|F_a(q)|^2>` Examples -------- :: import jscatter as js q = js.loglist(0.1,5,100) p = js.grace() fq = js.ff.prism(q,3,3) p.plot(fq.X,fq.Y/fq.I0) p.yaxis(scale='log') References ---------- .. [1] DNA-Nanoparticle Superlattices Formed From Anisotropic Building Blocks Jones et al Nature Materials 9, 913–917 (2010), doi: 10.1038/nmat2870 """ V = np.sqrt(3)*R*R*H sld = SLD - solventSLD I0 = V*V*sld*sld fq, err = formel.sphereAverage(funktion=_fq_prism, Q=q, R=R, H=H, passPoints=True, relError=relError).reshape(2, -1) result = dA(np.c_[q, sld**2 * fq].T) result.setColumnIndex(iey=None) result.columnname = 'q; Iq' result.modelname = inspect.currentframe().f_code.co_name result.I0 = I0 result.height = H result.edge = R result.volume = V result.contrast = sld return result