source: sasmodels/doc/developer/overview.rst @ e077231

core_shell_microgelsmagnetic_modelticket-1257-vesicle-productticket_1156ticket_1265_superballticket_822_more_unit_tests
Last change on this file since e077231 was 2a7e20e, checked in by Paul Kienzle <pkienzle@…>, 7 years ago

update developer docs with current interpretation of orientation; describe the scripts in the explore directory

  • Property mode set to 100644
File size: 17.8 KB
RevLine 
[2e66ef5]1.. py:currentmodule:: sasmodels
2
3***************************
4Code Overview
5***************************
6
7Computational kernels
8---------------------
9
[870a2f4]10* :mod:`core`
11* :mod:`modelinfo`
12* :mod:`kernel`
13* :mod:`product`
14* :mod:`mixture`
15
[2e66ef5]16At the heart of *sasmodels* are the individual computational kernels.  These
17functions take a particular $q$ value and a set of parameter values and
18return the expected scattering for that $q$. The instructions for writing
19a kernel are documented in :ref:`Writing_a_Plugin`.  The source code for
[870a2f4]20the kernels is stored in :mod:`models`.
[2e66ef5]21
[870a2f4]22The primary interface to the models is through :mod:`core`, which
[2e66ef5]23provides functions for listing available models, loading the model definition
[870a2f4]24and compiling the model.  Use :func:`core.load_model` to load in
25a model definition and compile it.  This makes use of
26:func:`core.load_model_info` to load the model definition and
27:func:`core.build_model` to turn it into a computational kernel model
28:class:`kernel.KernelModel`.
29
30The :class:`modelinfo.ModelInfo` class defines the properties
31of the model including the available model parameters
32:class:`modelinfo.ParameterTable` with individual parameter attributes
33such as units and hard limits defined in :class:`modelinfo.Parameter`.
34
35The :class:`product.ProductModel` and :class:`mixture.MixtureModel` classes
36are derived models, created automatically for models with names like
37"hardsphere*sphere" and "cylinder+sphere".
38
39Data loaders
40------------
41
42* :mod:`data`
43
44In order to test models a minimal set of data management routines is
45provided in :mod:`data`.  In particular, it provides mock :class:`data.Data1D`
46and :class:`data.Data2D` classes which mimic those classes in *SasView*.
47The functions :func:`data.empty_data1D` and :func:`data.empty_data2D`
48are handy for creating containers with a particular set of $q$, $\Delta q$
49points which can later be evaluated, and :func:`data.plot_theory` to show
50the result.  If *SasView* is available on the path then :func:`data.load_data`
51can be used to load any data type defined in *SasView*.  The function
52:func:`data.plot_data` can plot that data alone without the theory value.
53
54Kernel execution
55----------------
56
57* :mod:`resolution`
58* :mod:`resolution2d`
59* :mod:`sesans`
60* :mod:`weights`
61* :mod:`details`
62* :mod:`direct_model`
63* :mod:`bumps_model`
64* :mod:`sasview_model`
65
66To execute a computational kernel at a particular set of $q$ values, the
67use :meth:`kernel.KernelModel.make_kernel`, which returns a callable
68:class:`kernel.Kernel` for that $q$ vector (or a pair of $q_x$, $q_y$
69for 2-D datasets).
70
71The calculated $q$ values should include the measured
72data points as well as additional $q$ values required to properly compute the
73$q$ resolution function.  The *Resolution* subclasses in :mod:`resolution`
74define the *q_calc* attribute for this purpose.  These are
75:class:`resolution.Perfect1D` for perfect resolution,
76:class:`resolution.Pinhole1D` for the usual SANS pinhole aperture,
77:class:`resolution.Slit1D` for the usual USANS slit aperture and
78:class:`resolution2d.Pinhole2D` for 2-D pinhole data.
79In addition, :class:`resolution2d.Slit2D` defines 1-D slit smeared data
80for oriented samples, which require calculation at particular $q_x$ and
81$q_y$ values instead of $|q|$ as is the case for orientationally averaged
82USANS.  The :class:`sesans.SesansTransform` class acts like a 1-D resolution,
83having a *q_calc* attribute that defines the calculated $q$ values for
84the SANS models that get converted to spin-echo values by the
85:meth:`sesnas.SesansTransform.apply` method.
86
87Polydispersity is defined by :class:`weights.Dispersion` classes,
88:class:`weights.RectangleDispersion`, :class:`weights.ArrayDispersion`,
89:class:`weights.LogNormalDispersion`, :class:`weights.GaussianDispersion`,
90:class:`weights.SchulzDispersion`.  The :func:`weights.get_weights`
91function creates a dispersion object of the class matching
92:attr:`weights.Dispersion.type`, and calls it with the current value
93of the parameter.  This returns a vector of values and weights for a
94weighted average polydispersity.
95
96In order to call the :class:`kernel.Kernel`, the values and weights for
97all parameters must be composed into a :class:`details.CallDetails` object.
98This is a compact vector representation of the entire polydispersity
99loop that can be passed easily to the kernel.  Additionally, the magnetic
100parameters must be converted from polar to cartesian coordinates.  This
101work is done by the :func:`details.make_kernel_args` function, which returns
102values that can be sent directly to the kernel.  It uses
103:func:`details.make_details` to set the details object and
104:func:`details.convert_magnetism` for the coordinate transform.
105
106In the end, making a simple theory function evaluation requires a lot of
107setup. To make calling them a little easier, the *DirectModel* and
108*BumpsModel* interfaces are provided.  See :ref:`Scripting_Interface`
109for an example.
110
111The :class:`direct_model.DirectModel` interface accepts a data object
112and a kernel model.  Within the class,
113the :meth:`direct_model.DataMixin._interpret_data` method is called to
114query the data and set the resolution.
115The :meth:`direct_model.DataMixin._calc_theory` takes a set of parameter
116values, builds the kernel arguments, calls the kernel, and applies the
117resolution function, returning the predicted value for the data $q$ values.
118The :class:`bumps_model.Experiment` class is like the DirectModel class,
119but it defines a Fitness class that can be handed directly to the
120bumps optimization and uncertainty analysis program.
121
122The :class:`sasview_model.SasviewModel` class defines a SasView 4.x
123compatible interface to the sasmodels definitions, allowing sasmodels
124to be used directly from SasView.  Over time the SasView shim should
125disappear as SasView access the :class:`modelinfo.ModelInfo` and
126computational kernels directly.
127
128Kernel execution
129----------------
130
131* :mod:`kernelcl`
132* :mod:`kerneldll`
133* :mod:`kernelpy`
134* :mod:`generate`
[2e66ef5]135
136
137The kernel functions for the most part do not define polydispersity,
[870a2f4]138resolution or magnetism directly.  Instead sasmodels automatically
139applies these, calling the computation kernel as needed.
140
141The outermost loop is the resolution calculation.  For the 1-D case
142this computes a single vector of $I(q)$ values and applies the convolution
143to the resulting set.  Since the same $I(q)$ vector is used to compute the
144convolution at each point, it can be precomputed before the convolution,
145and so the convolution is reasonably efficient.  The 2-D case is not
146that efficient, and instead recomputes the entire shifted/scaled set
147of $q_x$, $q_y$ values many times, or very many times depending on the
148accuracy requested.
149
150Polydispersity is handled as a mesh over the polydisperse parameters.
151This is the next level of the loop.  For C kernels run in a DLL or
152using OpenCL, the polydisperisty loop is generated separately for each
153model as C code.  Inside the polydispersity loop there is a loop over
154the magnetic cross sections for magnetic models, updating the SLD
155parameters with the effective magnetic SLD for that particular $q$
156value. For OpenCL, each $q$ value loops over the
157polydispersity mesh on a separate processor. For DLL, the outer loop
158cycles through polydispersity, and the inner loop distributes q values
159amongst the processors.  Like the DLL, the Python kernel execution
160cycles over the polydisperse parameters and the magnetic cross sections,
161calling the computation kernel with a vector of $q$ values.  Assuming
162the kernel code accepts vectors, this can be fast enough (though it is
163painfully slow if not vectorized).
164
165Further details are provided in the next section,
166:ref:`Calculator_Interface`
167
[eda8b30]168.. _orientation_developer:
169
[da5536f]170Orientation and Numerical Integration
171-------------------------------------
172
[e964ab1]173For 2d data from oriented anisotropic particles, the mean particle
174orientation is defined by angles $\theta$, $\phi$ and $\Psi$, which are not
175in general the same as similarly named angles in many form factors. The
176wikipedia page on Euler angles (https://en.wikipedia.org/wiki/Euler_angles)
177lists the different conventions available. To quote: "Different authors may
178use different sets of rotation axes to define Euler angles, or different
179names for the same angles. Therefore, any discussion employing Euler angles
[da5536f]180should always be preceded by their definition."
181
[3d40839]182We are using the $z$-$y$-$z$ convention with extrinsic rotations
183$\Psi$-$\theta$-$\phi$ for the particle orientation and $x$-$y$-$z$
184convention with extrinsic rotations $\Psi$-$\theta$-$\phi$ for jitter, with
185jitter applied before particle orientation.
[e964ab1]186
[2a7e20e]187When computing the orientation dispersity integral, the weights for
188the individual points depends on the map projection used to translate jitter
189angles into latitude/longitude.  The choice of projection is set by
190*sasmodels.generate.PROJECTION*, with the default *PROJECTION=1* for
191equirectangular and *PROJECTION=2* for sinusoidal.  The more complicated
192Guyou and Postel projections are not implemented. See explore.jitter.draw_mesh
193for details.
194
[e964ab1]195For numerical integration within form factors etc. sasmodels is mostly using
196Gaussian quadrature with 20, 76 or 150 points depending on the model. It also
197makes use of symmetries such as calculating only over one quadrant rather
198than the whole sphere. There is often a U-substitution replacing $\theta$
199with $cos(\theta)$ which changes the limits of integration from 0 to $\pi/2$
200to 0 to 1 and also conveniently absorbs the $sin(\theta)$ scale factor in the
[3d40839]201integration. This can cause confusion if checking equations to include in a
202paper or thesis! Most models use the same core kernel code expressed in terms
203of the rotated view ($q_a$, $q_b$, $q_c$) for both the 1D and the 2D models,
204but there are also historical quirks such as the parallelepiped model, which
205has a useless transformation representing $j_0(a q_a)$ as $j_0(b q_a a/b)$.
[da5536f]206
[3d40839]207Useful testing routines include:
[da5536f]208
[2a7e20e]209The *sascomp* utility is used to view and compare models with different
210parameters and calculation engines. The usual case is to simply plot a
211model that you are developing::
212
213    python sascomp path/to/model.py
214
215Once the obvious problems are addressed, check the numerical precision
216across a variety of randomly generated inputs::
217
218    python sascomp -engine=single,double path/to/model.py -sets=10
219
220You can compare different parameter values for the same or different models.
221For example when looking along the long axis of a cylinder ($\theta=0$),
222dispersity in $\theta$ should be equivalent to dispersity in $\phi$
223when $\phi=90$::
224
225    python sascomp -2d cylinder theta=0 phi=0,90 theta_pd_type=rectangle \\
226    phi_pd_type=rectangle phi_pd=10,1 theta_pd=1,10 length=500 radius=10
227
228It turns out that they are not because the equirectangular map projection
229weights the points by $\cos(\theta)$ so $\Delta\theta$ is not identical
230to $\Delta\phi$.  Setting *PROJECTION=2* in :mod:`sasmodels.generate` helps
231somewhat.  Postel would help even more in this case, though leading
232to distortions elsewhere.  See :mod:`sasmodels.compare` for many more details.
233
234*sascomp -ngauss=n* allows you to set the number of quadrature points used
235for the 1D integration for any model.  For example, a carbon nanotube with
236length 10 $\mu$\ m and radius 1 nm is not computed correctly at high $q$::
[e964ab1]237
[2a7e20e]238    python sascomp cylinder length=100000 radius=10 -ngauss=76,500 -double -highq
239
240Note: ticket 702 gives some forms for long rods and thin disks that may
241be used in place of the integration, depending on $q$, radius and length; if
242the cylinder model is updated with these corrections then above call will show
243no difference.
244
245*explore/check1d.py* uses sasmodels 1D integration and compares that with a
[e964ab1]246rectangle distribution in $\theta$ and $\phi$, with $\theta$ limits set to
[3d40839]247$\pm 90/\sqrt(3)$ and $\phi$ limits set to $\pm 180/\sqrt(3)$ [The rectangle
[e964ab1]248weight function uses the fact that the distribution width column is labelled
[3d40839]249sigma to decide that the 1-$\sigma$ width of a rectangular distribution needs to
[e964ab1]250be multiplied by $\sqrt(3)$ to get the corresponding gaussian equivalent, or
251similar reasoning.] This should rotate the sample through the entire
[3d40839]252$\theta$-$\phi$ surface according to the pattern that you see in jitter.py when
[2a7e20e]253you use 'rectangle' rather than 'gaussian' for its distribution without
254changing the viewing angle. In order to match the 1-D pattern for an arbitrary
255viewing angle on triaxial shapes, we need to integrate
[3d40839]256over $\theta$, $\phi$ and $\Psi$.
257
[2a7e20e]258*sascomp -sphere=n* uses the same rectangular distribution as check1d to
259compute the pattern of the $q_x$-$q_y$ grid.  This ought to produce a
260spherically symmetric pattern.
261
262*explore/precision.py* investigates the accuracy of individual functions
263on the different execution platforms.  This includes the basic special
264functions as well as more complex expressions used in scattering.  In many
265cases the OpenCL function in sasmodels will use a polynomial approximation
266over part of the range to improve accuracy, as shown in::
267
268    python explore/precision.py 3j1/x
269
270*explore/realspace.py* allows you to set up a Monte Carlo simulation of your
271model by sampling random points within and computing the 1D and 2D scattering
272patterns.  This was used to check the core shell parallelepiped example.  This
273is similar to the general sas calculator in sasview, though it uses different
274code.
275
276*explore/jitter.py* is for exploring different options for handling
277orientation and orientation dispersity.  It uses *explore/guyou.py* to
278generate the Guyou projection.
279
280*explore/asymint.py* is a direct implementation of the 1D integration for
281a number of different models.  It calculates the integral for a particular
282$q$ using several different integration schemes, including mpmath with 100
283bits of precision (33 digits), so you can use it to check the target values
284for the 1D model tests.  This is not a general purpose tool; you will need to
285edit the file to change the model parameters. It does not currently
286apply the $u=cos(\theta)$ substitution that many models are using
287internally so the 76-point gaussian quadrature may not match the value
288produced by the eqivalent model from sasmodels.
289
290*explore/symint.py* is for rotationally symmetric models (just cylinder for
291now), so it can compute an entire curve rather than a single $q$ point.  It
292includes code to compare the long cylinder approximation to cylinder.
293
294*explore/rpa.py* is for checking different implementations of the RPA model
295against calculations for specific blends.  This is a work in (suspended)
296progress.
297
298There are a few modules left over in *explore* that can probably be removed.
299*angular_pd.py* was an early version of *jitter.py**sc.py* and *sc.c*
300was used to explore different calculations for paracrystal models; it has
301been absorbed into *asymint.py*. *transform_angles.py* translates orientation
302parameters from the SasView 3.x definition to sasmodels.
303
304*explore/angles.py* generates code for the view and jitter transformations.
305Keep this around since it may be needed if we add new projections.
[870a2f4]306
307Testing
308-------
309
310* :mod:`model_test`
311* :mod:`compare`
312* :mod:`compare_many`
313* :mod:`rst2html`
314* :mod:`list_pars`
315
316Individual models should all have test values to make sure that the
317evaluation is correct.  This is particularly important in the context
318of OpenCL since sasmodels doesn't control the compiler or the hardware,
319and since GPUs are notorious for preferring speed over precision.  The
320tests can be run as a group using :mod:`model_test` as main::
321
322    $ python -m sasmodels.model_test all
323
324Individual models can be listed instead of *all*, which is convenient when
325adding new models.
326
327The :mod:`compare` module, usually invoked using *./sascomp* provides a
328rich interface for exploring model accuracy, execution speed and parameter
329ranges.  It also allows different models to be compared.
330The :mod:`compare_many` module does batch comparisons, keeping a list of
331the particular random seeds which lead to large differences in output
332between different computing platforms.
333
334The :mod:`rst2html` module provides tools for converting model docs to
335html and viewing the html.  This is used by :mod:`compare` to display
336the model description, such as::
337
338    $ ./sascomp -html sphere
339
340This makes debugging the latex much faster, though this may require
341Qt in order for mathjax to work correctly.
342
343When run as main, it can display arbitrary ReStructuredText files. E.g.,
344
345::
346
347    $ python -m sasmodels.rst2html doc/developer/overview.rst
348
349This is handy for sorting out rst and latex syntax.  With some work
350the results could be improved so that it recognizes sphinx roles
351such as *mod*, *class* and *func*, and so that it uses the style sheets
352from the sasmodels docs.
353
354The :mod:`list_pars` module lists all instances of parameters across
355all models.  This helps to make sure that similar parameters have
356similar names across the different models.  With the verbose flag,
357the particular models which use each named parameter are listed.
358
359
360Model conversion
361----------------
362
363* :mod:`convert`
364* :mod:`conversion_table`
365
366Model definitions are not static.  As needs change or problems are found,
367models may be updated with new model names or may be reparameterized
368with new parameter definitions.  For example, in translating the
369Teubner-Strey model from SasView 3.x to sasmodels, the definition
370in terms of *drho*, *k*, *c1*, *c2*, *a2* and prefactor was replaced
371by the defintion in terms of *volfraction_a*, *xi*, *d*, *sld_a* and
372*sld_b*.  Within :mod:`convert`, the *_hand_convert_3_1_2_to_4_1*
373function must be called when loading a 3.x model definition to update it to
3744.1, and then the model should be further updated to 4.2, 5.0, and so on.
375The :func:`convert.convert_model` function does this, using the conversion
376table in :mod:`conversion_table` (which handled the major renaming from
377SasView 3.x to sasmodels), and using the internal *_hand_convert* function
378for the more complicated cases.
379
380Other
381-----
382
383* :mod:`exception`
384* :mod:`alignment`
385
386The :func:`exception.annotate_exception` function annotates the current
387exception with a context string, such as "while opening myfile.dat" without
388adjusting the traceback.
389
390The :mod:`alignment` module is unused.
Note: See TracBrowser for help on using the repository browser.