Skip to content

Commit

Permalink
Merge branch 'master' into use-dQ-Data-slit-length-and-width-switched
Browse files Browse the repository at this point in the history
  • Loading branch information
caitwolf authored May 11, 2023
2 parents fd03bda + 7ceea84 commit a6d8ec0
Show file tree
Hide file tree
Showing 21 changed files with 703 additions and 335 deletions.
11 changes: 11 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
Release notes
=============

v1.0.7 2023-02-??
------------------
* Doc upate: corefunc and optimizer documentation
* Doc update: various models (cylinder, gel_fit, paracrystal, core_shell_ellipsoid)
* Fix error in BCC and FCC paracrystaline models
* Fix rare race condition causing errors
* Fix to allow multiple scattering scripts to run
* Fix problem with models containing complex amplitudes not compiling on the fly
* Restructuring and cross-linking of sasmodels docs
* Update web links and contributor list

v1.0.6 2022-03-17
------------------
* implements generalized 3D description of magnetic SANS
Expand Down
54 changes: 42 additions & 12 deletions doc/genmodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import re
import shutil
import argparse
import subprocess

# CRUFT: python 2.7 backport of makedirs(path, exist_ok=False)
if sys.version_info[0] >= 3:
Expand Down Expand Up @@ -148,8 +149,10 @@ def make_figure(model_info, opts):
"""
import matplotlib.pyplot as plt

print("Build model")
model = core.build_model(model_info)

print("Set up figure")
fig_height = 3.0 # in
fig_left = 0.6 # in
fig_right = 0.5 # in
Expand All @@ -167,8 +170,10 @@ def make_figure(model_info, opts):
ax_width = ax_height/ratio # square axes
fig = plt.figure(figsize=aspect)
ax2d = fig.add_axes([0.5+ax_left, ax_bottom, ax_width, ax_height])
print("2D plot")
plot_2d(model, opts, ax2d)
ax1d = fig.add_axes([ax_left, ax_bottom, ax_width, ax_height])
print("1D plot")
plot_1d(model, opts, ax1d)
#ax.set_aspect('square')
else:
Expand All @@ -182,11 +187,14 @@ def make_figure(model_info, opts):
aspect = (fig_width, fig_height)
fig = plt.figure(figsize=aspect)
ax1d = fig.add_axes([ax_left, ax_bottom, ax_width, ax_height])
print("1D plot")
plot_1d(model, opts, ax1d)

if model_info.profile:
print("Profile inset")
plot_profile_inset(model_info, ax1d)

print("Save")
# Save image in model/img
makedirs(joinpath(TARGET_DIR, 'img'), exist_ok=True)
path = joinpath(TARGET_DIR, 'img', figfile(model_info))
Expand Down Expand Up @@ -311,7 +319,9 @@ def make_figure_cached(model_info, opts):
# check if we are caching
cache_dir = os.environ.get('SASMODELS_BUILD_CACHE', None)
if cache_dir is None:
print("Nothing cashed, creating...")
make_figure(model_info, opts)
print("Made a figure")
return

# TODO: changing default parameters won't trigger a rebuild.
Expand Down Expand Up @@ -387,31 +397,57 @@ def process_model(py_file, force=False):
}

# Generate the RST file and the figure. Order doesn't matter.
print("generating", rst_file)
print("generating rst", rst_file)
print("1: docs")
gen_docs(model_info, rst_file)
print("2: figure", end='')
if force:
print()
make_figure(model_info, PLOT_OPTS)
else:
print(" (cached)")
make_figure_cached(model_info, PLOT_OPTS)
print("Done process_model")

return rst_file

def run_sphinx(rst_files, output):
"""
Use sphinx to build *rst_files*, storing the html in *output*.
"""

print("Building index...")

conf_dir = dirname(realpath(__file__))
with open(joinpath(TARGET_DIR, 'index.rst'), 'w') as fid:
fid.write(".. toctree::\n\n")
for path in rst_files:
fid.write(" %s\n"%basename(path))

print("Running sphinx command...")

command = [
sys.executable,
"-m", "sphinx",
"-c", conf_dir,
TARGET_DIR,
output,
]
os.system(" ".join(repr(s) for s in command))

process = subprocess.Popen(command, shell=False, stdout=subprocess.PIPE)

# Make sure we can see process output in real time
while True:

output = process.stdout.readline()

if process.poll() is not None:
break

if output:
print(output.strip())



def main():
"""
Expand Down Expand Up @@ -442,19 +478,13 @@ def main():
sys.exit(1)
makedirs(TARGET_DIR, exist_ok=True)

if args.cpus == -1:
cpus = int(os.environ.get("SASMODELS_BUILD_CPUS", "0"))
else:
cpus = args.cpus
if cpus != 1 and not args.force:
import multiprocessing
p = multiprocessing.Pool(cpus if cpus > 0 else None)
rst_files = p.map(process_model, args.files)
else:
rst_files = [process_model(py_file, args.force)
print("** 'Normal' processing **")
rst_files = [process_model(py_file, args.force)
for py_file in args.files]
print("normal .rst file processing complete")

if args.sphinx:
print("running sphinx")
run_sphinx(rst_files, args.build)


Expand Down
144 changes: 11 additions & 133 deletions doc/guide/fitting_sq.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
.. _Interaction_Models:

Fitting Models with Structure Factors
-------------------------------------
=====================================

**Interaction models** (previously called product models), or $P@S$ models
for short, multiply the form factor $P(Q)$ by the structure factor $S(Q)$,
modulated by the **effective radius** of the form factor. For the theory
behind this, see :ref:`PStheory` later.
behind this, see :ref:`PStheory` .

Parameters
^^^^^^^^^^
Expand Down Expand Up @@ -151,152 +151,30 @@ Many parameters are common amongst $P@S$ models, but take on specific meanings:
More mode options may appear in future as more complicated operations are
added.

.. _PStheory:

Theory
^^^^^^

Scattering at vector $\mathbf Q$ for an individual particle with
shape parameters $\mathbf\xi$ and contrast $\rho_c(\mathbf r, \mathbf\xi)$
is computed from the square of the amplitude, $F(\mathbf Q, \mathbf\xi)$, as

.. math::
I(\mathbf Q) = F(\mathbf Q, \mathbf\xi) F^*(\mathbf Q, \mathbf\xi)
\big/ V(\mathbf\xi)
with the particle volume $V(\mathbf \xi)$ and

.. math::
F(\mathbf Q, \mathbf\xi) = \int_{\mathbb R^3} \rho_c(\mathbf r, \mathbf\xi)
e^{i \mathbf Q \cdot \mathbf r} \,\mathrm d \mathbf r = F
The 1-D scattering pattern for monodisperse particles uses the orientation
average in spherical coordinates,

.. math::
I(Q) = n \langle F F^*\rangle = \frac{n}{4\pi}
\int_{\theta=0}^{\pi} \int_{\phi=0}^{2\pi}
F F^* \sin(\theta) \,\mathrm d\phi \mathrm d\theta
where $F(\mathbf Q,\mathbf\xi)$ uses
$\mathbf Q = [Q \sin\theta\cos\phi, Q \sin\theta\sin\phi, Q \cos\theta]^T$.
A $u$-substitution may be used, with $\alpha = \cos \theta$,
$\surd(1 - \alpha^2) = \sin \theta$, and
$\mathrm d\alpha = -\sin\theta\,\mathrm d\theta$.
Here,

.. math:: n = V_f/V(\mathbf\xi)

is the number density of scatterers estimated from the volume fraction $V_f$
of particles in solution. In this formalism, each incoming
wave interacts with exactly one particle before being scattered into the
detector. All interference effects are within the particle itself.
The detector accumulates counts in proportion to the relative probability
at each pixel. The extension to heterogeneous systems is simply a matter of
adding the scattering patterns in proportion to the number density of each
particle. That is, given shape parameters $\mathbf\xi$ with probability
$P_\mathbf{\xi}$,

.. math::
I(Q) = \int_\Xi n(\mathbf\xi) \langle F F^* \rangle \,\mathrm d\xi
= V_f\frac{\int_\Xi P_\mathbf{\xi} \langle F F^* \rangle
\,\mathrm d\mathbf\xi}{\int_\Xi P_\mathbf\xi V(\mathbf\xi)\,\mathrm d\mathbf\xi}
This approximation is valid in the dilute limit, where particles are
sufficiently far apart that the interaction between them can be ignored.

As concentration increases, a structure factor term $S(Q)$ can be included,
giving the monodisperse approximation for the interaction between particles,
with

.. math:: I(Q) = n \langle F F^* \rangle S(Q)

For particles without spherical symmetry, the decoupling approximation
is more accurate, with

.. math::
I(Q) = n [\langle F F^* \rangle
+ \langle F \rangle \langle F \rangle^* (S(Q) - 1)]
Or equivalently,

.. math:: I(Q) = P(Q)[1 + \beta\,(S(Q) - 1)]

with the form factor $P(Q) = n \langle F F^* \rangle$ and
$\beta = \langle F \rangle \langle F \rangle^* \big/ \langle F F^* \rangle$.
These approximations can be extended to heterogeneous systems using averages
over size, $\langle \cdot \rangle_\mathbf\xi = \int_\Xi P_\mathbf\xi \langle\cdot\rangle\,\mathrm d\mathbf\xi \big/ \int_\Xi P_\mathbf\xi \,\mathrm d\mathbf\xi$ and setting
$n = V_f\big/\langle V \rangle_\mathbf\xi$.

Further improvements can be made using the local monodisperse
approximation (LMA) or using partial structure factors as done in [#bressler]_,
but these are not implemented in this code.

For hollow shapes, *volfraction* is computed from the material in the
shell rather than the shell plus solvent inside the shell. Using
$V_e(\mathbf\xi)$ as the enclosed volume of the shell plus solvent and
$V_c(\mathbf\xi)$ as the core volume of solvent inside the shell, we
can compute the average enclosed and shell volumes as

.. math::
:nowrap:
\begin{align*}
\langle V_e \rangle &= \frac{
\int_\Xi P_\mathbf\xi V_e(\mathbf\xi)\,\mathrm d\mathbf\xi
}{ \int_\Xi P_\mathbf\xi\,\mathrm d\mathbf \xi } \\
\langle V_s \rangle &= \frac{
\int_\Xi P_\mathbf\xi (V_e(\mathbf\xi) - V_c(\mathbf\xi))\,\mathrm d\mathbf\xi
}{ \int_\Xi P_\mathbf\xi\,\mathrm d\mathbf \xi }
\end{align*}
Given $n$ particles and a total solvent volume $V_\text{out}$ outside the
shells, the volume fraction of the shell, $\phi_s$ and the shell plus
enclosed solvent $\phi_e$ are

.. math::
:nowrap:
\begin{align*}
\phi_s &= \frac{n \langle V_s \rangle}{n \langle V_s \rangle + n \langle V_c \rangle + V_\text{out}}
= \frac{n \langle V_s \rangle}{V_\text{total}} \\
\phi_e &= \frac{n \langle V_e \rangle}{n \langle V_e \rangle + V_\text{out}}
= \frac{n \langle V_e \rangle}{V_\text{total}}
\end{align*}
Dividing gives

.. math::
.. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
\frac{\phi_S}{\phi_P} = \frac{\langle V_e \rangle}{\langle V_s \rangle}
Related sections
^^^^^^^^^^^^^^^^

so the enclosed volume fraction can be computed from the shell volume fraction
and the form:shell volume ratio as
See also:

.. math::
:ref:`PStheory`

\phi_S = \phi_P \langle V_e \rangle \big/ \langle V_s \rangle
:ref:`polydispersityhelp`

.. note::
:ref:`Resolution_Smearing`

Prior to Sasmodels v1.0.5 (Nov 2020), the intermediate $P(Q)$ returned by
the interaction calculator did not incorporate the volume normalization and
so $I(Q) \ne P(Q) S(Q)$. This became apparent when $P(Q)$ and $I(Q)$ were
plotted together. Further details can be found `here <https://github.com/SasView/sasview/issues/1698>`_.
:ref:`orientation`

References
^^^^^^^^^^

.. [#kotlarchyk] Kotlarchyk, M.; Chen, S.-H. *J. Chem. Phys.*, 1983, 79, 2461
.. [#bressler] Bressler I., Kohlbrecher J., Thunemann A.F.
*J. Appl. Crystallogr.* 48 (2015) 1587-1598
.. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
*Document History*

| 2019-03-31 Paul Kienzle, Steve King & Richard Heenan
| 2021-11-03 Steve King
| 2022-10-30 Steve King
13 changes: 7 additions & 6 deletions doc/guide/index.rst
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
****************
SAS Models Guide
****************
***************
Sasmodels Guide
***************

.. toctree::
:numbered: 4
:maxdepth: 4

intro.rst
install.rst
scripting.rst
gpu_setup.rst
theory.rst
pd/polydispersity.rst
resolution.rst
plugin.rst
fitting_sq.rst
magnetism/magnetism.rst
orientation/orientation.rst
magnetism/magnetism.rst
plugin.rst
sesans/sans_to_sesans.rst
sesans/sesans_fitting.rst
scripting.rst
refs.rst
4 changes: 2 additions & 2 deletions doc/guide/install.rst
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
********************
sasmodels Setup
Sasmodels Setup
********************


sasmodels Installation
Sasmodels Installation
**********************
Sasmodels can be installed using a simple pip installation::

Expand Down
Loading

0 comments on commit a6d8ec0

Please sign in to comment.