Changeset be3cce9 in sasmodels
- Timestamp:
- Sep 11, 2017 7:15:05 AM (7 years ago)
- 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. - Files:
-
- 5 added
- 3 deleted
- 25 edited
Legend:
- Unmodified
- Added
- Removed
-
.travis.yml
rb419c2d r947a61e 1 1 language: python 2 3 sudo: false 4 2 sudo: false 5 3 matrix: 6 4 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 23 19 branches: 24 20 only: 25 - master 26 21 - master 27 22 addons: 28 23 apt: 29 packages: 30 opencl-headers 31 24 packages: opencl-headers 32 25 before_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 57 39 install: 58 - pip install bumps 59 - pip install unittest-xml-reporting 60 40 - pip install bumps 41 - pip install unittest-xml-reporting 61 42 script: 62 43 - python --version 63 44 - python -m sasmodels.model_test -v dll all 64 45 before_deploy: 46 - echo -e "Host danse.chem.utk.edu\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config 47 deploy: 48 skip_cleanup: true 49 provider: script 50 script: /bin/sh -ex ./deploy.sh 51 on: 52 branch: master 65 53 notifications: 66 54 slack: -
doc/conf.py
r8ae8532 r30b60d2 211 211 #latex_preamble = '' 212 212 LATEX_PREAMBLE=r""" 213 \newcommand{\lt}{<} 214 \newcommand{\gt}{>} 213 215 \renewcommand{\AA}{\text{\r{A}}} % Allow \AA in math mode 214 216 \usepackage[utf8]{inputenc} % Allow unicode symbols in text -
doc/guide/magnetism/magnetism.rst
r990d8df r64eecf7 16 16 17 17 .. figure:: 18 mag_img/mag_vector. bmp18 mag_img/mag_vector.png 19 19 20 20 The magnetic scattering length density is then … … 36 36 37 37 .. figure:: 38 mag_img/M_angles_pic. bmp38 mag_img/M_angles_pic.png 39 39 40 40 If the angles of the $Q$ vector and the spin-axis $x'$ to the $x$ - axis are -
doc/guide/plugin.rst
r870a2f4 r30b60d2 117 117 Models that do not conform to these requirements will *never* be incorporated 118 118 into the built-in library. 119 120 More complete documentation for the sasmodels package can be found at121 `<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.124 119 125 120 … … 613 608 614 609 sas_gamma(x): 615 Gamma function $\text{sas_gamma}(x) = \Gamma(x)$.610 Gamma function sas_gamma\ $(x) = \Gamma(x)$. 616 611 617 612 The standard math function, tgamma(x) is unstable for $x < 1$ … … 623 618 sas_erf(x), sas_erfc(x): 624 619 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$ 626 621 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$. 628 623 629 624 The standard math functions erf(x) and erfc(x) are slower and broken … … 634 629 635 630 sas_J0(x): 636 Bessel function of the first kind $\text{sas_J0}(x)=J_0(x)$ where631 Bessel function of the first kind sas_J0\ $(x)=J_0(x)$ where 637 632 $J_0(x) = \frac{1}{\pi}\int_0^\pi \cos(x\sin(\tau))\,d\tau$. 638 633 … … 643 638 644 639 sas_J1(x): 645 Bessel function of the first kind $\text{sas_J1}(x)=J_1(x)$ where640 Bessel function of the first kind sas_J1\ $(x)=J_1(x)$ where 646 641 $J_1(x) = \frac{1}{\pi}\int_0^\pi \cos(\tau - x\sin(\tau))\,d\tau$. 647 642 … … 652 647 653 648 sas_JN(n, x): 654 Bessel function of the first kind and integer order $n$ :655 $\text{sas_JN}(n, x)=J_n(x)$ where649 Bessel function of the first kind and integer order $n$, 650 sas_JN\ $(n, x) =J_n(x)$ where 656 651 $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. 658 653 659 654 The standard math function jn(n, x) is not available on all platforms. … … 663 658 664 659 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$. 666 661 667 662 This function uses Taylor series for small and large arguments: … … 688 683 sas_3j1x_x(x): 689 684 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$, 691 686 with a limiting value of 1 at $x=0$, where $j_1(x)$ is the spherical 692 687 Bessel function of the first kind and first order. … … 699 694 700 695 sas_2J1x_x(x): 701 Bessel form $\text{sas_J1c}(x) = 2 J_1(x)/x$, with a limiting value696 Bessel form sas_J1c\ $(x) = 2 J_1(x)/x$, with a limiting value 702 697 of 1 at $x=0$, where $J_1(x)$ is the Bessel function of first kind 703 698 and first order. -
doc/guide/resolution.rst
rf8a2baa r30b60d2 212 212 elliptical Gaussian distribution. The $A$ is a normalization factor. 213 213 214 .. figure:: resolution_2d_rotation. gif214 .. figure:: resolution_2d_rotation.png 215 215 216 216 Coordinate axis rotation for 2D resolution calculation. -
doc/rst_prolog
ra0fb06a r30b60d2 1 1 .. Set up some substitutions to make life easier... 2 .. Remove |biggamma|, etc. when they are no longer needed.3 2 4 5 .. |alpha| unicode:: U+03B16 .. |beta| unicode:: U+03B27 .. |gamma| unicode:: U+03B38 .. |delta| unicode:: U+03B49 .. |epsilon| unicode:: U+03B510 .. |zeta| unicode:: U+03B611 .. |eta| unicode:: U+03B712 .. |theta| unicode:: U+03B813 .. |iota| unicode:: U+03B914 .. |kappa| unicode:: U+03BA15 .. |lambda| unicode:: U+03BB16 .. |mu| unicode:: U+03BC17 .. |nu| unicode:: U+03BD18 .. |xi| unicode:: U+03BE19 .. |omicron| unicode:: U+03BF20 .. |pi| unicode:: U+03C021 .. |rho| unicode:: U+03C122 .. |sigma| unicode:: U+03C323 .. |tau| unicode:: U+03C424 .. |upsilon| unicode:: U+03C525 .. |phi| unicode:: U+03C626 .. |chi| unicode:: U+03C727 .. |psi| unicode:: U+03C828 .. |omega| unicode:: U+03C929 30 31 .. |biggamma| unicode:: U+039332 .. |bigdelta| unicode:: U+039433 .. |bigzeta| unicode:: U+039E34 .. |bigpsi| unicode:: U+03A835 .. |bigphi| unicode:: U+03A636 .. |bigsigma| unicode:: U+03A337 .. |Gamma| unicode:: U+039338 .. |Delta| unicode:: U+039439 .. |Zeta| unicode:: U+039E40 .. |Psi| unicode:: U+03A841 42 43 .. |drho| replace:: |Delta|\ |rho|44 3 .. |Ang| unicode:: U+212B 45 4 .. |Ang^-1| replace:: |Ang|\ :sup:`-1` … … 57 16 .. |cm^-3| replace:: cm\ :sup:`-3` 58 17 .. |sr^-1| replace:: sr\ :sup:`-1` 59 .. |P0| replace:: P\ :sub:`0`\60 .. |A2| replace:: A\ :sub:`2`\61 62 63 .. |equiv| unicode:: U+226164 .. |noteql| unicode:: U+226065 .. |TM| unicode:: U+212266 67 18 68 19 .. |cdot| unicode:: U+00B7 -
sasmodels/generate.py
r573ffab r30b60d2 210 210 211 211 # 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. 213 213 # This section associates the unit with the macro to use to produce the LaTex 214 214 # code. The macro itself needs to be defined in sasmodels/doc/rst_prolog. … … 216 216 # NOTE: there is an RST_PROLOG at the end of this file which is NOT 217 217 # 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. 219 219 RST_UNITS = { 220 220 "Ang": "|Ang|", … … 898 898 .. |cm^-3| replace:: cm\ :sup:`-3` 899 899 .. |sr^-1| replace:: sr\ :sup:`-1` 900 .. |P0| replace:: P\ :sub:`0`\901 902 .. |equiv| unicode:: U+2261903 .. |noteql| unicode:: U+2260904 .. |TM| unicode:: U+2122905 900 906 901 .. |cdot| unicode:: U+00B7 -
sasmodels/models/binary_hard_sphere.py
r8f04da4 r30b60d2 23 23 :nowrap: 24 24 25 \begin{align }25 \begin{align*} 26 26 x &= \frac{(\phi_2 / \phi)\alpha^3}{(1-(\phi_2/\phi) + (\phi_2/\phi) 27 27 \alpha^3)} \\ 28 28 \phi &= \phi_1 + \phi_2 = \text{total volume fraction} \\ 29 29 \alpha &= R_1/R_2 = \text{size ratio} 30 \end{align }30 \end{align*} 31 31 32 32 The 2D scattering intensity is the same as 1D, regardless of the orientation of -
sasmodels/models/core_shell_bicelle.py
ra151caa r30b60d2 41 41 42 42 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} 44 44 45 45 where 46 46 47 47 .. math:: 48 :nowrap: 48 49 49 \begin{align }50 \begin{align*} 50 51 F(Q,\alpha) = &\bigg[ 51 52 (\rho_c - \rho_f) V_c \frac{2J_1(QRsin \alpha)}{QRsin\alpha}\frac{sin(QLcos\alpha/2)}{Q(L/2)cos\alpha} \\ … … 53 54 &+(\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} 54 55 \bigg] 55 \end{align }56 \end{align*} 56 57 57 58 where $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 42 42 43 43 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} 45 45 46 where a numerical integration of $F(Q,\alpha, \psi)^2 .sin(\alpha)$ is carried out over \alpha and \psi for:46 where a numerical integration of $F(Q,\alpha, \psi)^2 \cdot sin(\alpha)$ is carried out over \alpha and \psi for: 47 47 48 48 .. math:: 49 :nowrap: 49 50 50 \begin{align}51 \begin{align*} 51 52 F(Q,\alpha,\psi) = &\bigg[ 52 53 (\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} \\ … … 54 55 &+(\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} 55 56 \bigg] 56 \end{align }57 \end{align*} 57 58 58 59 where -
sasmodels/models/core_shell_ellipsoid.py
r9f6823b r30b60d2 44 44 45 45 .. math:: 46 \begin{align} 46 :nowrap: 47 48 \begin{align*} 47 49 F(q,\alpha) = &f(q,radius\_equat\_core,radius\_equat\_core.x\_core,\alpha) \\ 48 50 &+ f(q,radius\_equat\_core + thick\_shell,radius\_equat\_core.x\_core + thick\_shell.x\_polar\_shell,\alpha) 49 \end{align }51 \end{align*} 50 52 51 53 where -
sasmodels/models/fractal_core_shell.py
r8f04da4 r64eecf7 31 31 $\rho_{solv}$ are the scattering length densities of the core, shell, and 32 32 solvent 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|the33 of the whole particle respectively, $D_f$ is the fractal dimension, and $\xi$ the 34 34 correlation length. 35 35 -
sasmodels/models/hollow_rectangular_prism.py
r8f04da4 r30b60d2 31 31 :nowrap: 32 32 33 \begin{align }33 \begin{align*} 34 34 A_{P\Delta}(q) & = A B C 35 35 \left[\frac{\sin \bigl( q \frac{C}{2} \cos\theta \bigr)} … … 47 47 \left[ \frac{\sin \bigl[ q \bigl(\frac{B}{2}-\Delta\bigr) \sin\theta \cos\phi \bigr]} 48 48 {q \bigl(\frac{B}{2}-\Delta\bigr) \sin\theta \cos\phi} \right] 49 \end{align }49 \end{align*} 50 50 51 51 where $A$, $B$ and $C$ are the external sides of the parallelepiped fulfilling -
sasmodels/models/line.py
r48462b0 rc63a7c8 15 15 16 16 .. math:: 17 17 18 I(q) = \text{scale} (I(qx) \cdot I(qy)) + \text{background} 18 19 -
sasmodels/models/multilayer_vesicle.py
r142a8e2 r64eecf7 33 33 .. math:: 34 34 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 } i35 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 37 37 38 38 $\phi$ is the volume fraction of particles, $V(r)$ is the volume of a sphere -
sasmodels/models/parallelepiped.py
r8f04da4 r30b60d2 20 20 Parallelepiped with the corresponding definition of sides. 21 21 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. 22 The three dimensions of the parallelepiped (strictly here a cuboid) may be 23 given in *any* size order. To avoid multiple fit solutions, especially 24 with Monte-Carlo fit methods, it may be advisable to restrict their ranges. 25 There may be a number of closely similar "best fits", so some trial and 26 error, or fixing of some dimensions at expected values, may help. 29 27 30 28 The 1D scattering intensity $I(q)$ is calculated as: -
sasmodels/models/rpa.py
r4f9e288 r30b60d2 30 30 These case numbers are different from those in the NIST SANS package! 31 31 32 The models are based on the papers by Akcasu et al. [#Akcasu]_and by33 Hammouda [#Hammouda]_assuming the polymer follows Gaussian statistics such32 The models are based on the papers by Akcasu *et al.* and by 33 Hammouda assuming the polymer follows Gaussian statistics such 34 34 that $R_g^2 = n b^2/6$ where $b$ is the statistical segment length and $n$ is 35 35 the number of statistical segment lengths. A nice tutorial on how these are 36 36 constructed and implemented can be found in chapters 28 and 39 of Boualem 37 Hammouda's 'SANS Toolbox' [#toolbox]_.37 Hammouda's 'SANS Toolbox'. 38 38 39 39 In brief the macroscopic cross sections are derived from the general forms … … 49 49 are calculated with respect to component D).** So the scattering contrast 50 50 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 52 52 vary. 53 53 … … 57 57 component are obtained from other methods and held fixed while The *scale* 58 58 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). 62 61 63 62 References 64 63 ---------- 65 64 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 65 A Z Akcasu, R Klein and B Hammouda, *Macromolecules*, 26 (1993) 4136. 66 67 B. Hammouda, *Advances in Polymer Science* 106 (1993) 87. 68 69 B. Hammouda, *SANS Toolbox* 70 https://www.ncnr.nist.gov/staff/hammouda/the_sans_toolbox.pdf. 70 71 71 72 Authorship and Verification -
sasmodels/models/star_polymer.py
r142a8e2 r30b60d2 7 7 emanating from a common central (in the case of this model) point. It is 8 8 derived as a special case of on the Benoit model for general branched 9 polymers\ [#CITBenoit]_ as also used by Richter ''et. al.''\ [#CITRichter]_9 polymers\ [#CITBenoit]_ as also used by Richter *et al.*\ [#CITRichter]_ 10 10 11 11 For a star with $f$ arms the scattering intensity $I(q)$ is calculated as -
sasmodels/models/surface_fractal.py
r48462b0 r30b60d2 9 9 10 10 .. math:: 11 :nowrap: 11 12 13 \begin{align*} 12 14 I(q) &= \text{scale} \times P(q)S(q) + \text{background} \\ 13 15 P(q) &= F(qR)^2 \\ … … 15 17 S(q) &= \Gamma(5-D_S)\xi^{\,5-D_S}\left[1+(q\xi)^2 \right]^{-(5-D_S)/2} 16 18 \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 \\ 18 20 V &= \frac{4}{3}\pi R^3 21 \end{align*} 19 22 20 23 where $R$ is the radius of the building block, $D_S$ is the **surface** fractal -
sasmodels/core.py
r142a8e2 ra85a569 10 10 11 11 import os 12 import re 12 13 from os.path import basename, dirname, join as joinpath 13 14 from glob import glob … … 21 22 from . import kernelpy 22 23 from . import kerneldll 24 from . import custom 23 25 24 26 if os.environ.get("SAS_OPENCL", "").lower() == "none": … … 30 32 except Exception: 31 33 HAVE_OPENCL = False 34 35 CUSTOM_MODEL_PATH = os.environ.get('SAS_MODELPATH', "") 36 if 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 32 41 33 42 try: … … 125 134 dtype=dtype, platform=platform) 126 135 127 128 def load_model_info(model_name): 136 def load_model_info(model_string): 129 137 # type: (str) -> modelinfo.ModelInfo 130 138 """ 131 139 Load a model definition given the model name. 132 140 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'. 135 145 136 146 This returns a handle to the module defining the model. This can be 137 147 used with functions in generate to build the docs or extract model info. 138 148 """ 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] 149 154 return product.make_product_info(P_info, Q_info) 150 155 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 153 192 154 193 -
sasmodels/mixture.py
r6dc78e4 r31ae428 25 25 pass 26 26 27 def make_mixture_info(parts ):27 def make_mixture_info(parts, operation='+'): 28 28 # type: (List[ModelInfo]) -> ModelInfo 29 29 """ 30 30 Create info block for mixture model. 31 31 """ 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 = flatten39 40 32 # Build new parameter list 41 33 combined_pars = [] 42 34 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: 44 82 # Parameter prefix per model, A_, B_, ... 45 83 # Note that prefix must also be applied to id and length_control 46 84 # 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) 51 111 for p in part.parameters.kernel_parameters: 52 112 p = copy(p) … … 63 123 64 124 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) + ')' 67 128 model_info.filename = None 68 129 model_info.title = 'Mixture model with ' + model_info.name … … 116 177 self.kernels = kernels 117 178 self.dtype = self.kernels[0].dtype 179 self.operation = model_info.operation 118 180 self.results = [] # type: List[np.ndarray] 119 181 … … 124 186 # remember the parts for plotting later 125 187 self.results = [] # type: List[np.ndarray] 126 offset = 2 # skip scale & background127 188 parts = MixtureParts(self.info, self.kernels, call_details, values) 128 189 for kernel, kernel_details, kernel_values in parts: 129 190 #print("calling kernel", kernel.info.name) 130 191 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 133 201 self.results.append(result) 134 202 … … 171 239 172 240 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 174 244 self.mag_index += 3 * len(info.parameters.magnetism_index) 175 245 … … 182 252 # which includes the initial scale and background parameters. 183 253 # 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) 189 260 length = full.length[index] 190 261 offset = full.offset[index] … … 196 267 def _part_values(self, info, par_index, mag_index): 197 268 # 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] 201 273 nmagnetic = len(info.parameters.magnetism_index) 202 274 if nmagnetic: -
sasmodels/model_test.py
rbedb9b0 r65314f7 201 201 ({}, 'VR', None), 202 202 ] 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 205 206 try: 206 207 model = build_model(self.info, dtype=self.dtype, … … 371 372 stream.writeln(traceback.format_exc()) 372 373 return 373 374 374 # Run the test suite 375 375 suite.run(result) -
sasmodels/modelinfo.py
r0bdddc2 ra85a569 727 727 models when the model is first called, not when the model is loaded. 728 728 """ 729 if hasattr(kernel_module, "model_info"): 730 # Custom sum/multi models 731 return kernel_module.model_info 729 732 info = ModelInfo() 730 733 #print("make parameter table", kernel_module.parameters) -
sasmodels/product.py
r8f04da4 ra85a569 77 77 78 78 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)) 81 81 model_info.filename = None 82 82 model_info.title = 'Product of %s and %s'%(p_name, s_name) -
sasmodels/sasview_model.py
r724257c rbcdd6c9 120 120 else: 121 121 model_info = modelinfo.make_model_info(kernel_module) 122 model = _make_model_from_info(model_info)122 model = make_model_from_info(model_info) 123 123 model.timestamp = getmtime(path) 124 124 … … 142 142 143 143 144 def 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 144 158 def _make_standard_model(name): 145 159 # type: (str) -> SasviewModelType … … 153 167 kernel_module = generate.load_kernel_module(name) 154 168 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 158 172 def _register_old_models(): 159 173 # type: () -> None … … 187 201 model_info = product.make_product_info(form_factor._model_info, 188 202 structure_factor._model_info) 189 ConstructedModel = _make_model_from_info(model_info)203 ConstructedModel = make_model_from_info(model_info) 190 204 return ConstructedModel() 191 205 192 def _make_model_from_info(model_info):193 # type: (ModelInfo) -> SasviewModelType194 """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.filename202 ConstructedModel = type(model_info.name, (SasviewModel,), attrs) # type: SasviewModelType203 return ConstructedModel204 206 205 207 def _generate_model_attributes(model_info): … … 603 605 if hasattr(self._model_info, "composition") \ 604 606 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])() 607 609 return p_model, s_model 608 610
Note: See TracChangeset
for help on using the changeset viewer.