Changeset be3cce9 in sasmodels


Ignore:
Timestamp:
Sep 11, 2017 7:15:05 AM (7 years ago)
Author:
lewis
Branches:
master, core_shell_microgels, costrafo411, magnetic_model, ticket-1257-vesicle-product, ticket_1156, ticket_1265_superball, ticket_822_more_unit_tests
Children:
6726fb9
Parents:
a85a569 (diff), ab60822 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge branch 'master' into ticket-767

Files:
5 added
3 deleted
25 edited

Legend:

Unmodified
Added
Removed
  • .travis.yml

    rb419c2d r947a61e  
    11language: python 
    2  
    3 sudo:  false 
    4  
     2sudo: false 
    53matrix: 
    64  include: 
    7     - os: linux 
    8       env: 
    9         - PY=2.7 
    10     - os: linux 
    11       env: 
    12         - PY=3.6 
    13     - os: osx 
    14       language: generic 
    15       env: 
    16         - PY=2.7 
    17     - os: osx 
    18       language: generic 
    19       env: 
    20         - PY=3.5 
    21  
    22 # whitelist 
     5  - os: linux 
     6    env: 
     7    - PY=2.7 
     8  - os: linux 
     9    env: 
     10    - PY=3.6 
     11  - os: osx 
     12    language: generic 
     13    env: 
     14    - PY=2.7 
     15  - os: osx 
     16    language: generic 
     17    env: 
     18    - PY=3.5 
    2319branches: 
    2420  only: 
    25     - master 
    26  
     21  - master 
    2722addons: 
    2823  apt: 
    29     packages: 
    30       opencl-headers 
    31  
     24    packages: opencl-headers 
    3225before_install: 
    33   - echo $TRAVIS_OS_NAME 
    34  
    35   - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then 
    36       wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh; 
    37     fi; 
    38   - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then 
    39       wget https://repo.continuum.io/miniconda/Miniconda3-latest-MacOSX-x86_64.sh -O miniconda.sh; 
    40     fi; 
    41  
    42   - bash miniconda.sh -b -p $HOME/miniconda 
    43   - export PATH="$HOME/miniconda/bin:$PATH" 
    44   - hash -r 
    45   - conda update --yes conda 
    46  
    47   # Useful for debugging any issues with conda 
    48   - conda info -a 
    49  
    50   - conda install --yes python=$PY numpy scipy cython mako cffi 
    51  
    52   # Not testing with opencl below, so don't need to install it 
    53   #- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then 
    54   #    pip install pyopencl; 
    55   #  fi; 
    56  
     26- openssl aes-256-cbc -K $encrypted_fe6026add10a_key -iv $encrypted_fe6026add10a_iv 
     27  -in .travis/travis_rsa.enc -out .travis/travis_rsa -d 
     28- echo $TRAVIS_OS_NAME 
     29- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh 
     30  -O miniconda.sh; fi; 
     31- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then wget https://repo.continuum.io/miniconda/Miniconda3-latest-MacOSX-x86_64.sh 
     32  -O miniconda.sh; fi; 
     33- bash miniconda.sh -b -p $HOME/miniconda 
     34- export PATH="$HOME/miniconda/bin:$PATH" 
     35- hash -r 
     36- conda update --yes conda 
     37- conda info -a 
     38- conda install --yes python=$PY numpy scipy cython mako cffi 
    5739install: 
    58   - pip install bumps 
    59   - pip install unittest-xml-reporting 
    60  
     40- pip install bumps 
     41- pip install unittest-xml-reporting 
    6142script: 
    6243- python --version 
    6344- python -m sasmodels.model_test -v dll all 
    64  
     45before_deploy: 
     46- echo -e "Host danse.chem.utk.edu\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config 
     47deploy: 
     48    skip_cleanup: true 
     49    provider: script 
     50    script: /bin/sh -ex ./deploy.sh 
     51    on: 
     52        branch: master 
    6553notifications: 
    6654  slack: 
  • doc/conf.py

    r8ae8532 r30b60d2  
    211211#latex_preamble = '' 
    212212LATEX_PREAMBLE=r""" 
     213\newcommand{\lt}{<} 
     214\newcommand{\gt}{>} 
    213215\renewcommand{\AA}{\text{\r{A}}} % Allow \AA in math mode 
    214216\usepackage[utf8]{inputenc}      % Allow unicode symbols in text 
  • doc/guide/magnetism/magnetism.rst

    r990d8df r64eecf7  
    1616 
    1717.. figure:: 
    18     mag_img/mag_vector.bmp 
     18    mag_img/mag_vector.png 
    1919 
    2020The magnetic scattering length density is then 
     
    3636 
    3737.. figure:: 
    38     mag_img/M_angles_pic.bmp 
     38    mag_img/M_angles_pic.png 
    3939 
    4040If the angles of the $Q$ vector and the spin-axis $x'$ to the $x$ - axis are 
  • doc/guide/plugin.rst

    r870a2f4 r30b60d2  
    117117Models that do not conform to these requirements will *never* be incorporated 
    118118into the built-in library. 
    119  
    120 More complete documentation for the sasmodels package can be found at 
    121 `<http://www.sasview.org/sasmodels>`_. In particular, 
    122 `<http://www.sasview.org/sasmodels/api/generate.html#module-sasmodels.generate>`_ 
    123 describes the structure of a model. 
    124119 
    125120 
     
    613608 
    614609    sas_gamma(x): 
    615         Gamma function $\text{sas_gamma}(x) = \Gamma(x)$. 
     610        Gamma function sas_gamma\ $(x) = \Gamma(x)$. 
    616611 
    617612        The standard math function, tgamma(x) is unstable for $x < 1$ 
     
    623618    sas_erf(x), sas_erfc(x): 
    624619        Error function 
    625         $\text{sas_erf}(x) = \frac{2}{\sqrt\pi}\int_0^x e^{-t^2}\,dt$ 
     620        sas_erf\ $(x) = \frac{2}{\sqrt\pi}\int_0^x e^{-t^2}\,dt$ 
    626621        and complementary error function 
    627         $\text{sas_erfc}(x) = \frac{2}{\sqrt\pi}\int_x^{\infty} e^{-t^2}\,dt$. 
     622        sas_erfc\ $(x) = \frac{2}{\sqrt\pi}\int_x^{\infty} e^{-t^2}\,dt$. 
    628623 
    629624        The standard math functions erf(x) and erfc(x) are slower and broken 
     
    634629 
    635630    sas_J0(x): 
    636         Bessel function of the first kind $\text{sas_J0}(x)=J_0(x)$ where 
     631        Bessel function of the first kind sas_J0\ $(x)=J_0(x)$ where 
    637632        $J_0(x) = \frac{1}{\pi}\int_0^\pi \cos(x\sin(\tau))\,d\tau$. 
    638633 
     
    643638 
    644639    sas_J1(x): 
    645         Bessel function of the first kind  $\text{sas_J1}(x)=J_1(x)$ where 
     640        Bessel function of the first kind  sas_J1\ $(x)=J_1(x)$ where 
    646641        $J_1(x) = \frac{1}{\pi}\int_0^\pi \cos(\tau - x\sin(\tau))\,d\tau$. 
    647642 
     
    652647 
    653648    sas_JN(n, x): 
    654         Bessel function of the first kind and integer order $n$: 
    655         $\text{sas_JN}(n, x)=J_n(x)$ where 
     649        Bessel function of the first kind and integer order $n$, 
     650        sas_JN\ $(n, x) =J_n(x)$ where 
    656651        $J_n(x) = \frac{1}{\pi}\int_0^\pi \cos(n\tau - x\sin(\tau))\,d\tau$. 
    657         If $n$ = 0 or 1, it uses sas_J0(x) or sas_J1(x), respectively. 
     652        If $n$ = 0 or 1, it uses sas_J0($x$) or sas_J1($x$), respectively. 
    658653 
    659654        The standard math function jn(n, x) is not available on all platforms. 
     
    663658 
    664659    sas_Si(x): 
    665         Sine integral $\text{Si}(x) = \int_0^x \tfrac{\sin t}{t}\,dt$. 
     660        Sine integral Si\ $(x) = \int_0^x \tfrac{\sin t}{t}\,dt$. 
    666661 
    667662        This function uses Taylor series for small and large arguments: 
     
    688683    sas_3j1x_x(x): 
    689684        Spherical Bessel form 
    690         $\text{sph_j1c}(x) = 3 j_1(x)/x = 3 (\sin(x) - x \cos(x))/x^3$, 
     685        sph_j1c\ $(x) = 3 j_1(x)/x = 3 (\sin(x) - x \cos(x))/x^3$, 
    691686        with a limiting value of 1 at $x=0$, where $j_1(x)$ is the spherical 
    692687        Bessel function of the first kind and first order. 
     
    699694 
    700695    sas_2J1x_x(x): 
    701         Bessel form $\text{sas_J1c}(x) = 2 J_1(x)/x$, with a limiting value 
     696        Bessel form sas_J1c\ $(x) = 2 J_1(x)/x$, with a limiting value 
    702697        of 1 at $x=0$, where $J_1(x)$ is the Bessel function of first kind 
    703698        and first order. 
  • doc/guide/resolution.rst

    rf8a2baa r30b60d2  
    212212elliptical Gaussian distribution. The $A$ is a normalization factor. 
    213213 
    214 .. figure:: resolution_2d_rotation.gif 
     214.. figure:: resolution_2d_rotation.png 
    215215 
    216216    Coordinate axis rotation for 2D resolution calculation. 
  • doc/rst_prolog

    ra0fb06a r30b60d2  
    11.. Set up some substitutions to make life easier... 
    2 .. Remove |biggamma|, etc. when they are no longer needed. 
    32 
    4  
    5 .. |alpha| unicode:: U+03B1 
    6 .. |beta| unicode:: U+03B2 
    7 .. |gamma| unicode:: U+03B3 
    8 .. |delta| unicode:: U+03B4 
    9 .. |epsilon| unicode:: U+03B5 
    10 .. |zeta| unicode:: U+03B6 
    11 .. |eta| unicode:: U+03B7 
    12 .. |theta| unicode:: U+03B8 
    13 .. |iota| unicode:: U+03B9 
    14 .. |kappa| unicode:: U+03BA 
    15 .. |lambda| unicode:: U+03BB 
    16 .. |mu| unicode:: U+03BC 
    17 .. |nu| unicode:: U+03BD 
    18 .. |xi| unicode:: U+03BE 
    19 .. |omicron| unicode:: U+03BF 
    20 .. |pi| unicode:: U+03C0 
    21 .. |rho| unicode:: U+03C1 
    22 .. |sigma| unicode:: U+03C3 
    23 .. |tau| unicode:: U+03C4 
    24 .. |upsilon| unicode:: U+03C5 
    25 .. |phi| unicode:: U+03C6 
    26 .. |chi| unicode:: U+03C7 
    27 .. |psi| unicode:: U+03C8 
    28 .. |omega| unicode:: U+03C9 
    29  
    30  
    31 .. |biggamma| unicode:: U+0393 
    32 .. |bigdelta| unicode:: U+0394 
    33 .. |bigzeta| unicode:: U+039E 
    34 .. |bigpsi| unicode:: U+03A8 
    35 .. |bigphi| unicode:: U+03A6 
    36 .. |bigsigma| unicode:: U+03A3 
    37 .. |Gamma| unicode:: U+0393 
    38 .. |Delta| unicode:: U+0394 
    39 .. |Zeta| unicode:: U+039E 
    40 .. |Psi| unicode:: U+03A8 
    41  
    42  
    43 .. |drho| replace:: |Delta|\ |rho| 
    443.. |Ang| unicode:: U+212B 
    454.. |Ang^-1| replace:: |Ang|\ :sup:`-1` 
     
    5716.. |cm^-3| replace:: cm\ :sup:`-3` 
    5817.. |sr^-1| replace:: sr\ :sup:`-1` 
    59 .. |P0| replace:: P\ :sub:`0`\ 
    60 .. |A2| replace:: A\ :sub:`2`\ 
    61  
    62  
    63 .. |equiv| unicode:: U+2261 
    64 .. |noteql| unicode:: U+2260 
    65 .. |TM| unicode:: U+2122 
    66  
    6718 
    6819.. |cdot| unicode:: U+00B7 
  • sasmodels/generate.py

    r573ffab r30b60d2  
    210210 
    211211# Conversion from units defined in the parameter table for each model 
    212 # to units displayed in the sphinx documentation.  
     212# to units displayed in the sphinx documentation. 
    213213# This section associates the unit with the macro to use to produce the LaTex 
    214214# code.  The macro itself needs to be defined in sasmodels/doc/rst_prolog. 
     
    216216# NOTE: there is an RST_PROLOG at the end of this file which is NOT 
    217217# used for the bundled documentation. Still as long as we are defining the macros 
    218 # in two places any new addition should define the macro in both places.  
     218# in two places any new addition should define the macro in both places. 
    219219RST_UNITS = { 
    220220    "Ang": "|Ang|", 
     
    898898.. |cm^-3| replace:: cm\ :sup:`-3` 
    899899.. |sr^-1| replace:: sr\ :sup:`-1` 
    900 .. |P0| replace:: P\ :sub:`0`\ 
    901  
    902 .. |equiv| unicode:: U+2261 
    903 .. |noteql| unicode:: U+2260 
    904 .. |TM| unicode:: U+2122 
    905900 
    906901.. |cdot| unicode:: U+00B7 
  • sasmodels/models/binary_hard_sphere.py

    r8f04da4 r30b60d2  
    2323    :nowrap: 
    2424 
    25     \begin{align} 
     25    \begin{align*} 
    2626    x &= \frac{(\phi_2 / \phi)\alpha^3}{(1-(\phi_2/\phi) + (\phi_2/\phi) 
    2727    \alpha^3)} \\ 
    2828    \phi &= \phi_1 + \phi_2 = \text{total volume fraction} \\ 
    2929    \alpha &= R_1/R_2 = \text{size ratio} 
    30     \end{align} 
     30    \end{align*} 
    3131 
    3232The 2D scattering intensity is the same as 1D, regardless of the orientation of 
  • sasmodels/models/core_shell_bicelle.py

    ra151caa r30b60d2  
    4141 
    4242    I(Q,\alpha) = \frac{\text{scale}}{V_t} \cdot 
    43         F(Q,\alpha)^2.sin(\alpha) + \text{background} 
     43        F(Q,\alpha)^2 \cdot sin(\alpha) + \text{background} 
    4444 
    4545where 
    4646 
    4747.. math:: 
     48    :nowrap: 
    4849 
    49     \begin{align} 
     50    \begin{align*} 
    5051    F(Q,\alpha) = &\bigg[ 
    5152    (\rho_c - \rho_f) V_c \frac{2J_1(QRsin \alpha)}{QRsin\alpha}\frac{sin(QLcos\alpha/2)}{Q(L/2)cos\alpha} \\ 
     
    5354    &+(\rho_r - \rho_s) V_t \frac{2J_1(Q(R+t_r)sin\alpha)}{Q(R+t_r)sin\alpha}\frac{sin(Q(L/2+t_f)cos\alpha)}{Q(L/2+t_f)cos\alpha} 
    5455    \bigg] 
    55     \end{align} 
     56    \end{align*} 
    5657 
    5758where $V_t$ is the total volume of the bicelle, $V_c$ the volume of the core, 
  • sasmodels/models/core_shell_bicelle_elliptical.py

    r8f04da4 r30b60d2  
    4242 
    4343    I(Q,\alpha,\psi) = \frac{\text{scale}}{V_t} \cdot 
    44         F(Q,\alpha, \psi)^2.sin(\alpha) + \text{background} 
     44        F(Q,\alpha, \psi)^2 \cdot sin(\alpha) + \text{background} 
    4545 
    46 where a numerical integration of $F(Q,\alpha, \psi)^2.sin(\alpha)$ is carried out over \alpha and \psi for: 
     46where a numerical integration of $F(Q,\alpha, \psi)^2 \cdot sin(\alpha)$ is carried out over \alpha and \psi for: 
    4747 
    4848.. math:: 
     49    :nowrap: 
    4950 
    50         \begin{align} 
     51    \begin{align*} 
    5152    F(Q,\alpha,\psi) = &\bigg[ 
    5253    (\rho_c - \rho_f) V_c \frac{2J_1(QR'sin \alpha)}{QR'sin\alpha}\frac{sin(QLcos\alpha/2)}{Q(L/2)cos\alpha} \\ 
     
    5455    &+(\rho_r - \rho_s) V_t \frac{2J_1(Q(R'+t_r)sin\alpha)}{Q(R'+t_r)sin\alpha}\frac{sin(Q(L/2+t_f)cos\alpha)}{Q(L/2+t_f)cos\alpha} 
    5556    \bigg] 
    56     \end{align} 
     57    \end{align*} 
    5758 
    5859where 
  • sasmodels/models/core_shell_ellipsoid.py

    r9f6823b r30b60d2  
    4444 
    4545.. math:: 
    46     \begin{align} 
     46    :nowrap: 
     47 
     48    \begin{align*} 
    4749    F(q,\alpha) = &f(q,radius\_equat\_core,radius\_equat\_core.x\_core,\alpha) \\ 
    4850    &+ f(q,radius\_equat\_core + thick\_shell,radius\_equat\_core.x\_core + thick\_shell.x\_polar\_shell,\alpha) 
    49     \end{align} 
     51    \end{align*} 
    5052 
    5153where 
  • sasmodels/models/fractal_core_shell.py

    r8f04da4 r64eecf7  
    3131$\rho_{solv}$ are the scattering length densities of the core, shell, and 
    3232solvent respectively, $r_c$ and $r_s$ are the radius of the core and the radius 
    33 of the whole particle respectively, $D_f$ is the fractal dimension, and |xi| the 
     33of the whole particle respectively, $D_f$ is the fractal dimension, and $\xi$ the 
    3434correlation length. 
    3535 
  • sasmodels/models/hollow_rectangular_prism.py

    r8f04da4 r30b60d2  
    3131  :nowrap: 
    3232 
    33   \begin{align} 
     33  \begin{align*} 
    3434  A_{P\Delta}(q) & =  A B C 
    3535    \left[\frac{\sin \bigl( q \frac{C}{2} \cos\theta \bigr)} 
     
    4747    \left[ \frac{\sin \bigl[ q \bigl(\frac{B}{2}-\Delta\bigr) \sin\theta \cos\phi \bigr]} 
    4848    {q \bigl(\frac{B}{2}-\Delta\bigr) \sin\theta \cos\phi} \right] 
    49   \end{align} 
     49  \end{align*} 
    5050 
    5151where $A$, $B$ and $C$ are the external sides of the parallelepiped fulfilling 
  • sasmodels/models/line.py

    r48462b0 rc63a7c8  
    1515 
    1616.. math:: 
     17 
    1718    I(q) = \text{scale} (I(qx) \cdot I(qy)) + \text{background} 
    1819 
  • sasmodels/models/multilayer_vesicle.py

    r142a8e2 r64eecf7  
    3333.. math:: 
    3434 
    35      r_i &= r_c + (i-1)(t_s + t_w) && \text{ solvent radius before shell } i \\ 
    36      R_i &= r_i + t_s && \text{ shell radius for shell } i 
     35     r_i &= r_c + (i-1)(t_s + t_w) \text{ solvent radius before shell } i \\ 
     36     R_i &= r_i + t_s \text{ shell radius for shell } i 
    3737 
    3838$\phi$ is the volume fraction of particles, $V(r)$ is the volume of a sphere 
  • sasmodels/models/parallelepiped.py

    r8f04da4 r30b60d2  
    2020   Parallelepiped with the corresponding definition of sides. 
    2121 
    22 .. note:: 
    23  
    24 The three dimensions of the parallelepiped (strictly here a cuboid) may be given in 
    25 $any$ size order. To avoid multiple fit solutions, especially 
    26 with Monte-Carlo fit methods, it may be advisable to restrict their ranges. There may 
    27 be a number of closely similar "best fits", so some trial and error, or fixing of some 
    28 dimensions at expected values, may help. 
     22The three dimensions of the parallelepiped (strictly here a cuboid) may be 
     23given in *any* size order. To avoid multiple fit solutions, especially 
     24with Monte-Carlo fit methods, it may be advisable to restrict their ranges. 
     25There may be a number of closely similar "best fits", so some trial and 
     26error, or fixing of some dimensions at expected values, may help. 
    2927 
    3028The 1D scattering intensity $I(q)$ is calculated as: 
  • sasmodels/models/rpa.py

    r4f9e288 r30b60d2  
    3030    These case numbers are different from those in the NIST SANS package! 
    3131 
    32 The models are based on the papers by Akcasu et al. [#Akcasu]_ and by 
    33 Hammouda [#Hammouda]_ assuming the polymer follows Gaussian statistics such 
     32The models are based on the papers by Akcasu *et al.* and by 
     33Hammouda assuming the polymer follows Gaussian statistics such 
    3434that $R_g^2 = n b^2/6$ where $b$ is the statistical segment length and $n$ is 
    3535the number of statistical segment lengths. A nice tutorial on how these are 
    3636constructed and implemented can be found in chapters 28 and 39 of Boualem 
    37 Hammouda's 'SANS Toolbox'[#toolbox]_. 
     37Hammouda's 'SANS Toolbox'. 
    3838 
    3939In brief the macroscopic cross sections are derived from the general forms 
     
    4949  are calculated with respect to component D).** So the scattering contrast 
    5050  for a C/D blend = [SLD(component C) - SLD(component D)]\ :sup:`2`. 
    51 * Depending on which case is being used, the number of fitting parameters can  
     51* Depending on which case is being used, the number of fitting parameters can 
    5252  vary. 
    5353 
     
    5757      component are obtained from other methods and held fixed while The *scale* 
    5858      parameter should be held equal to unity. 
    59     * The variables are normally the segment lengths (b\ :sub:`a`, b\ :sub:`b`, 
    60       etc) and $\chi$ parameters (K\ :sub:`ab`, K\ :sub:`ac`, etc). 
    61  
     59    * The variables are normally the segment lengths ($b_a$, $b_b$, 
     60      etc.) and $\chi$ parameters ($K_{ab}$, $K_{ac}$, etc). 
    6261 
    6362References 
    6463---------- 
    6564 
    66 .. [#Akcasu] A Z Akcasu, R Klein and B Hammouda, *Macromolecules*, 26 (1993) 
    67    4136. 
    68 .. [#Hammouda] B. Hammouda, *Advances in Polymer Science* 106 (1993) 87. 
    69 .. [#toolbox] https://www.ncnr.nist.gov/staff/hammouda/the_sans_toolbox.pdf 
     65A Z Akcasu, R Klein and B Hammouda, *Macromolecules*, 26 (1993) 4136. 
     66 
     67B. Hammouda, *Advances in Polymer Science* 106 (1993) 87. 
     68 
     69B. Hammouda, *SANS Toolbox* 
     70https://www.ncnr.nist.gov/staff/hammouda/the_sans_toolbox.pdf. 
    7071 
    7172Authorship and Verification 
  • sasmodels/models/star_polymer.py

    r142a8e2 r30b60d2  
    77emanating from a common central (in the case of this model) point.  It is 
    88derived as a special case of on the Benoit model for general branched 
    9 polymers\ [#CITBenoit]_ as also used by Richter ''et. al.''\ [#CITRichter]_ 
     9polymers\ [#CITBenoit]_ as also used by Richter *et al.*\ [#CITRichter]_ 
    1010 
    1111For a star with $f$ arms the scattering intensity $I(q)$ is calculated as 
  • sasmodels/models/surface_fractal.py

    r48462b0 r30b60d2  
    99 
    1010.. math:: 
     11    :nowrap: 
    1112 
     13    \begin{align*} 
    1214    I(q) &= \text{scale} \times P(q)S(q) + \text{background} \\ 
    1315    P(q) &= F(qR)^2 \\ 
     
    1517    S(q) &= \Gamma(5-D_S)\xi^{\,5-D_S}\left[1+(q\xi)^2 \right]^{-(5-D_S)/2} 
    1618            \sin\left[-(5-D_S) \tan^{-1}(q\xi) \right] q^{-1} \\ 
    17     \text{scale} &= \text{scale_factor}\, N V^2(\rho_\text{particle} - \rho_\text{solvent})^2 \\ 
     19    \text{scale} &= \text{scale factor}\, N V^1(\rho_\text{particle} - \rho_\text{solvent})^2 \\ 
    1820    V &= \frac{4}{3}\pi R^3 
     21    \end{align*} 
    1922 
    2023where $R$ is the radius of the building block, $D_S$ is the **surface** fractal 
  • sasmodels/core.py

    r142a8e2 ra85a569  
    1010 
    1111import os 
     12import re 
    1213from os.path import basename, dirname, join as joinpath 
    1314from glob import glob 
     
    2122from . import kernelpy 
    2223from . import kerneldll 
     24from . import custom 
    2325 
    2426if os.environ.get("SAS_OPENCL", "").lower() == "none": 
     
    3032    except Exception: 
    3133        HAVE_OPENCL = False 
     34 
     35CUSTOM_MODEL_PATH = os.environ.get('SAS_MODELPATH', "") 
     36if CUSTOM_MODEL_PATH == "": 
     37    path = joinpath(os.path.expanduser("~"), ".sasmodels", "custom_models") 
     38    if not os.path.isdir(path): 
     39        os.makedirs(path) 
     40    CUSTOM_MODEL_PATH = path 
    3241 
    3342try: 
     
    125134                       dtype=dtype, platform=platform) 
    126135 
    127  
    128 def load_model_info(model_name): 
     136def load_model_info(model_string): 
    129137    # type: (str) -> modelinfo.ModelInfo 
    130138    """ 
    131139    Load a model definition given the model name. 
    132140 
    133     *model_name* is the name of the model, or perhaps a model expression 
    134     such as sphere*hardsphere or sphere+cylinder. 
     141    *model_string* is the name of the model, or perhaps a model expression 
     142    such as sphere*cylinder or sphere+cylinder. Use '@' for a structure 
     143    factor product, e.g. sphere@hardsphere. Custom models can be specified by 
     144    prefixing the model name with 'custom.', e.g. 'custom.MyModel+sphere'. 
    135145 
    136146    This returns a handle to the module defining the model.  This can be 
    137147    used with functions in generate to build the docs or extract model info. 
    138148    """ 
    139     parts = model_name.split('+') 
    140     if len(parts) > 1: 
    141         model_info_list = [load_model_info(p) for p in parts] 
    142         return mixture.make_mixture_info(model_info_list) 
    143  
    144     parts = model_name.split('*') 
    145     if len(parts) > 1: 
    146         if len(parts) > 2: 
    147             raise ValueError("use P*S to apply structure factor S to model P") 
    148         P_info, Q_info = [load_model_info(p) for p in parts] 
     149    if '@' in model_string: 
     150        parts = model_string.split('@') 
     151        if len(parts) != 2: 
     152            raise ValueError("Use P@S to apply a structure factor S to model P") 
     153        P_info, Q_info = [load_model_info(part) for part in parts] 
    149154        return product.make_product_info(P_info, Q_info) 
    150155 
    151     kernel_module = generate.load_kernel_module(model_name) 
    152     return modelinfo.make_model_info(kernel_module) 
     156    product_parts = [] 
     157    addition_parts = [] 
     158 
     159    addition_parts_names = model_string.split('+') 
     160    if len(addition_parts_names) >= 2: 
     161        addition_parts = [load_model_info(part) for part in addition_parts_names] 
     162    elif len(addition_parts_names) == 1: 
     163        product_parts_names = model_string.split('*') 
     164        if len(product_parts_names) >= 2: 
     165            product_parts = [load_model_info(part) for part in product_parts_names] 
     166        elif len(product_parts_names) == 1: 
     167            if "custom." in product_parts_names[0]: 
     168                # Extract ModelName from "custom.ModelName" 
     169                pattern = "custom.([A-Za-z0-9_-]+)" 
     170                result = re.match(pattern, product_parts_names[0]) 
     171                if result is None: 
     172                    raise ValueError("Model name in invalid format: " + product_parts_names[0]) 
     173                model_name = result.group(1) 
     174                # Use ModelName to find the path to the custom model file 
     175                model_path = joinpath(CUSTOM_MODEL_PATH, model_name + ".py") 
     176                if not os.path.isfile(model_path): 
     177                    raise ValueError("The model file {} doesn't exist".format(model_path)) 
     178                kernel_module = custom.load_custom_kernel_module(model_path) 
     179                return modelinfo.make_model_info(kernel_module) 
     180            # Model is a core model 
     181            kernel_module = generate.load_kernel_module(product_parts_names[0]) 
     182            return modelinfo.make_model_info(kernel_module) 
     183 
     184    model = None 
     185    if len(product_parts) > 1: 
     186        model = mixture.make_mixture_info(product_parts, operation='*') 
     187    if len(addition_parts) > 1: 
     188        if model is not None: 
     189            addition_parts.append(model) 
     190        model = mixture.make_mixture_info(addition_parts, operation='+') 
     191    return model 
    153192 
    154193 
  • sasmodels/mixture.py

    r6dc78e4 r31ae428  
    2525    pass 
    2626 
    27 def make_mixture_info(parts): 
     27def make_mixture_info(parts, operation='+'): 
    2828    # type: (List[ModelInfo]) -> ModelInfo 
    2929    """ 
    3030    Create info block for mixture model. 
    3131    """ 
    32     flatten = [] 
    33     for part in parts: 
    34         if part.composition and part.composition[0] == 'mixture': 
    35             flatten.extend(part.composition[1]) 
    36         else: 
    37             flatten.append(part) 
    38     parts = flatten 
    39  
    4032    # Build new parameter list 
    4133    combined_pars = [] 
    4234    demo = {} 
    43     for k, part in enumerate(parts): 
     35 
     36    model_num = 0 
     37    all_parts = copy(parts) 
     38    is_flat = False 
     39    while not is_flat: 
     40        is_flat = True 
     41        for part in all_parts: 
     42            if part.composition and part.composition[0] == 'mixture' and \ 
     43                len(part.composition[1]) > 1: 
     44                all_parts += part.composition[1] 
     45                all_parts.remove(part) 
     46                is_flat = False 
     47 
     48    # When creating a mixture model that is a sum of product models (ie (1*2)+(3*4)) 
     49    # the parameters for models 1 & 2 will be prefixed with A & B respectively, 
     50    # but so will the parameters for models 3 & 4. We need to rename models 3 & 4 
     51    # so that they are prefixed with C & D to avoid overlap of parameter names. 
     52    used_prefixes = [] 
     53    for part in parts: 
     54        i = 0 
     55        if part.composition and part.composition[0] == 'mixture': 
     56            npars_list = [info.parameters.npars for info in part.composition[1]] 
     57            for npars in npars_list: 
     58                # List of params of one of the constituent models of part 
     59                submodel_pars = part.parameters.kernel_parameters[i:i+npars] 
     60                # Prefix of the constituent model 
     61                prefix = submodel_pars[0].name[0] 
     62                if prefix not in used_prefixes: # Haven't seen this prefix so far 
     63                    used_prefixes.append(prefix) 
     64                    i += npars 
     65                    continue 
     66                while prefix in used_prefixes: 
     67                    # This prefix has been already used, so change it to the 
     68                    # next letter that hasn't been used 
     69                    prefix = chr(ord(prefix) + 1) 
     70                used_prefixes.append(prefix) 
     71                prefix += "_" 
     72                # Update the parameters of this constituent model to use the 
     73                # new prefix 
     74                for par in submodel_pars: 
     75                    par.id = prefix + par.id[2:] 
     76                    par.name = prefix + par.name[2:] 
     77                    if par.length_control is not None: 
     78                        par.length_control = prefix + par.length_control[2:] 
     79                i += npars 
     80 
     81    for part in parts: 
    4482        # Parameter prefix per model, A_, B_, ... 
    4583        # Note that prefix must also be applied to id and length_control 
    4684        # to support vector parameters 
    47         prefix = chr(ord('A')+k) + '_' 
    48         scale =  Parameter(prefix+'scale', default=1.0, 
    49                            description="model intensity for " + part.name) 
    50         combined_pars.append(scale) 
     85        prefix = '' 
     86        if not part.composition: 
     87            # Model isn't a composition model, so it's parameters don't have a 
     88            # a prefix. Add the next available prefix 
     89            prefix = chr(ord('A')+len(used_prefixes)) 
     90            used_prefixes.append(prefix) 
     91            prefix += '_' 
     92             
     93        if operation == '+': 
     94            # If model is a sum model, each constituent model gets its own scale parameter 
     95            scale_prefix = prefix 
     96            if prefix == '' and part.operation == '*': 
     97                # `part` is a composition product model. Find the prefixes of 
     98                # it's parameters to form a new prefix for the scale, eg: 
     99                # a model with A*B*C will have ABC_scale 
     100                sub_prefixes = [] 
     101                for param in part.parameters.kernel_parameters: 
     102                    # Prefix of constituent model 
     103                    sub_prefix = param.id.split('_')[0] 
     104                    if sub_prefix not in sub_prefixes: 
     105                        sub_prefixes.append(sub_prefix) 
     106                # Concatenate sub_prefixes to form prefix for the scale 
     107                scale_prefix = ''.join(sub_prefixes) + '_' 
     108            scale =  Parameter(scale_prefix + 'scale', default=1.0, 
     109                            description="model intensity for " + part.name) 
     110            combined_pars.append(scale) 
    51111        for p in part.parameters.kernel_parameters: 
    52112            p = copy(p) 
     
    63123 
    64124    model_info = ModelInfo() 
    65     model_info.id = '+'.join(part.id for part in parts) 
    66     model_info.name = ' + '.join(part.name for part in parts) 
     125    model_info.id = operation.join(part.id for part in parts) 
     126    model_info.operation = operation 
     127    model_info.name = '(' + operation.join(part.name for part in parts) + ')' 
    67128    model_info.filename = None 
    68129    model_info.title = 'Mixture model with ' + model_info.name 
     
    116177        self.kernels = kernels 
    117178        self.dtype = self.kernels[0].dtype 
     179        self.operation = model_info.operation 
    118180        self.results = []  # type: List[np.ndarray] 
    119181 
     
    124186        # remember the parts for plotting later 
    125187        self.results = []  # type: List[np.ndarray] 
    126         offset = 2 # skip scale & background 
    127188        parts = MixtureParts(self.info, self.kernels, call_details, values) 
    128189        for kernel, kernel_details, kernel_values in parts: 
    129190            #print("calling kernel", kernel.info.name) 
    130191            result = kernel(kernel_details, kernel_values, cutoff, magnetic) 
    131             #print(kernel.info.name, result) 
    132             total += result 
     192            result = np.array(result).astype(kernel.dtype) 
     193            # print(kernel.info.name, result) 
     194            if self.operation == '+': 
     195                total += result 
     196            elif self.operation == '*': 
     197                if np.all(total) == 0.0: 
     198                    total = result 
     199                else: 
     200                    total *= result 
    133201            self.results.append(result) 
    134202 
     
    171239 
    172240        self.part_num += 1 
    173         self.par_index += info.parameters.npars + 1 
     241        self.par_index += info.parameters.npars 
     242        if self.model_info.operation == '+': 
     243            self.par_index += 1 # Account for each constituent model's scale param 
    174244        self.mag_index += 3 * len(info.parameters.magnetism_index) 
    175245 
     
    182252        # which includes the initial scale and background parameters. 
    183253        # We want the index into the weight length/offset for each parameter. 
    184         # Exclude the initial scale and background, so subtract two, but each 
    185         # component has its own scale factor which we need to skip when 
    186         # constructing the details for the kernel, so add one, giving a 
    187         # net subtract one. 
    188         index = slice(par_index - 1, par_index - 1 + info.parameters.npars) 
     254        # Exclude the initial scale and background, so subtract two. If we're 
     255        # building an addition model, each component has its own scale factor 
     256        # which we need to skip when constructing the details for the kernel, so 
     257        # add one, giving a net subtract one. 
     258        diff = 1 if self.model_info.operation == '+' else 2 
     259        index = slice(par_index - diff, par_index - diff + info.parameters.npars) 
    189260        length = full.length[index] 
    190261        offset = full.offset[index] 
     
    196267    def _part_values(self, info, par_index, mag_index): 
    197268        # type: (ModelInfo, int, int) -> np.ndarray 
    198         #print(info.name, par_index, self.values[par_index:par_index + info.parameters.npars + 1]) 
    199         scale = self.values[par_index] 
    200         pars = self.values[par_index + 1:par_index + info.parameters.npars + 1] 
     269        # Set each constituent model's scale to 1 if this is a multiplication model 
     270        scale = self.values[par_index] if self.model_info.operation == '+' else 1.0 
     271        diff = 1 if self.model_info.operation == '+' else 0 # Skip scale if addition model 
     272        pars = self.values[par_index + diff:par_index + info.parameters.npars + diff] 
    201273        nmagnetic = len(info.parameters.magnetism_index) 
    202274        if nmagnetic: 
  • sasmodels/model_test.py

    rbedb9b0 r65314f7  
    201201                ({}, 'VR', None), 
    202202                ] 
    203  
    204             tests = smoke_tests + self.info.tests 
     203            tests = smoke_tests 
     204            if self.info.tests is not None: 
     205                tests += self.info.tests 
    205206            try: 
    206207                model = build_model(self.info, dtype=self.dtype, 
     
    371372        stream.writeln(traceback.format_exc()) 
    372373        return 
    373  
    374374    # Run the test suite 
    375375    suite.run(result) 
  • sasmodels/modelinfo.py

    r0bdddc2 ra85a569  
    727727    models when the model is first called, not when the model is loaded. 
    728728    """ 
     729    if hasattr(kernel_module, "model_info"): 
     730        # Custom sum/multi models 
     731        return kernel_module.model_info 
    729732    info = ModelInfo() 
    730733    #print("make parameter table", kernel_module.parameters) 
  • sasmodels/product.py

    r8f04da4 ra85a569  
    7777 
    7878    model_info = ModelInfo() 
    79     model_info.id = '*'.join((p_id, s_id)) 
    80     model_info.name = '*'.join((p_name, s_name)) 
     79    model_info.id = '@'.join((p_id, s_id)) 
     80    model_info.name = '@'.join((p_name, s_name)) 
    8181    model_info.filename = None 
    8282    model_info.title = 'Product of %s and %s'%(p_name, s_name) 
  • sasmodels/sasview_model.py

    r724257c rbcdd6c9  
    120120    else: 
    121121        model_info = modelinfo.make_model_info(kernel_module) 
    122         model = _make_model_from_info(model_info) 
     122        model = make_model_from_info(model_info) 
    123123    model.timestamp = getmtime(path) 
    124124 
     
    142142 
    143143 
     144def make_model_from_info(model_info): 
     145    # type: (ModelInfo) -> SasviewModelType 
     146    """ 
     147    Convert *model_info* into a SasView model wrapper. 
     148    """ 
     149    def __init__(self, multiplicity=None): 
     150        SasviewModel.__init__(self, multiplicity=multiplicity) 
     151    attrs = _generate_model_attributes(model_info) 
     152    attrs['__init__'] = __init__ 
     153    attrs['filename'] = model_info.filename 
     154    ConstructedModel = type(model_info.name, (SasviewModel,), attrs) # type: SasviewModelType 
     155    return ConstructedModel 
     156 
     157 
    144158def _make_standard_model(name): 
    145159    # type: (str) -> SasviewModelType 
     
    153167    kernel_module = generate.load_kernel_module(name) 
    154168    model_info = modelinfo.make_model_info(kernel_module) 
    155     return _make_model_from_info(model_info) 
    156  
    157  
     169    return make_model_from_info(model_info) 
     170 
     171     
    158172def _register_old_models(): 
    159173    # type: () -> None 
     
    187201    model_info = product.make_product_info(form_factor._model_info, 
    188202                                           structure_factor._model_info) 
    189     ConstructedModel = _make_model_from_info(model_info) 
     203    ConstructedModel = make_model_from_info(model_info) 
    190204    return ConstructedModel() 
    191205 
    192 def _make_model_from_info(model_info): 
    193     # type: (ModelInfo) -> SasviewModelType 
    194     """ 
    195     Convert *model_info* into a SasView model wrapper. 
    196     """ 
    197     def __init__(self, multiplicity=None): 
    198         SasviewModel.__init__(self, multiplicity=multiplicity) 
    199     attrs = _generate_model_attributes(model_info) 
    200     attrs['__init__'] = __init__ 
    201     attrs['filename'] = model_info.filename 
    202     ConstructedModel = type(model_info.name, (SasviewModel,), attrs) # type: SasviewModelType 
    203     return ConstructedModel 
    204206 
    205207def _generate_model_attributes(model_info): 
     
    603605        if hasattr(self._model_info, "composition") \ 
    604606           and self._model_info.composition is not None: 
    605             p_model = _make_model_from_info(self._model_info.composition[1][0])() 
    606             s_model = _make_model_from_info(self._model_info.composition[1][1])() 
     607            p_model = make_model_from_info(self._model_info.composition[1][0])() 
     608            s_model = make_model_from_info(self._model_info.composition[1][1])() 
    607609        return p_model, s_model 
    608610 
Note: See TracChangeset for help on using the changeset viewer.