Skip to content

Commit

Permalink
Merge branch 'master' into pr-python-fq
Browse files Browse the repository at this point in the history
  • Loading branch information
pkienzle authored Nov 26, 2024
2 parents ad9fb9a + 049de31 commit aa03630
Show file tree
Hide file tree
Showing 26 changed files with 676 additions and 119 deletions.
24 changes: 17 additions & 7 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,34 @@ jobs:
strategy:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
python-version: ["3.7", "3.8", "3.9", "3.10"]
python-version: ["3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Free Disk Space (Ubuntu)
if: ${{ matrix.os == 'ubuntu-latest' }}
uses: jlumbroso/free-disk-space@main
with:
haskell: false
large-packages: false

- name: setup apt dependencies for Linux
if: ${{ matrix.os == 'ubuntu-latest' }}
run: |
sudo apt-get update
sudo apt-get install opencl-headers ocl-icd-opencl-dev libpocl2
sudo apt update
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
python -m pip install wheel setuptools
python -m pip install mako
python -m pip install numpy scipy matplotlib docutils pytest sphinx bumps unittest-xml-reporting tinycc
python -m pip install numpy scipy matplotlib docutils pytest sphinx bumps==0.* unittest-xml-reporting tinycc siphash24
- name: setup pyopencl on Linux + macOS
if: ${{ matrix.os != 'windows-latest' }}
Expand All @@ -45,9 +51,11 @@ jobs:
choco install opencl-intel-cpu-runtime
python -m pip install --only-binary=pyopencl --find-links http://www.silx.org/pub/wheelhouse/ --trusted-host www.silx.org pyopencl
- name: Test with pytest
- name: Test with pytest (only on Windows for now since PoCL is failing on Ubuntu)

env:
PYOPENCL_COMPILER_OUTPUT: 1
SAS_OPENCL: none
run: |
# other CI uses the following, but `setup.py test` is a deprecated way
# of running tests
Expand All @@ -57,6 +65,8 @@ jobs:
- name: check that the docs build (linux only)
if: ${{ matrix.os == 'ubuntu-latest' }}
env:
SAS_OPENCL: none
run: |
make -j 4 -C doc SPHINXOPTS="-W --keep-going -n" html
Expand Down
10 changes: 9 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
Release notes
=============

v1.0.7 2023-02-??
v1.0.8 2024-09-26
-----------------
* New model: Bulk ferromagnets model from marketplace
* Doc update: Archive built docs on Github
* Doc update: Display math correctly
* Fix error in FCC paracrystaline models
* Fix parameter name checking in kernel call

v1.0.7 2023-03-23
------------------
* Doc upate: corefunc and optimizer documentation
* Doc update: various models (cylinder, gel_fit, paracrystal, core_shell_ellipsoid)
Expand Down
3 changes: 1 addition & 2 deletions LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
Copyright (c) 2009-2022, SasView Developers

Copyright (c) 2009-2024, SasView Developers

All rights reserved.

Expand Down
37 changes: 3 additions & 34 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

nitpick_ignore = [
('py:class', 'argparse.Namespace'),
('py:class', 'bumps.parameter.Parameter'),
('py:class', 'collections.OrderedDict'),
('py:class', 'cuda.Context'),
('py:class', 'cuda.Function'),
Expand All @@ -40,6 +41,7 @@
('py:class', 'pyopencl._cl.Device'),
('py:class', 'pyopencl._cl.Kernel'),
('py:class', 'QWebView'),
('py:class', 'types.ModuleType'),
('py:class', 'unittest.suite.TestSuite'),
('py:class', 'wx.Frame'),
# autodoc and namedtuple is completely broken
Expand All @@ -55,6 +57,7 @@
('py:class', 'module'),
('py:class', 'SesansData'),
('py:class', 'SourceModule'),
('py:class', 'TestCondition'),
# KernelModel and Calculator breaking on git actions tests, even though
# KernelModel is already listed. astropy example sometimes includes full
# path to complaining symbol. Let's see if that helps here:
Expand Down Expand Up @@ -141,40 +144,6 @@
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []

nitpick_ignore = [
('py:class', 'argparse.Namespace'),
('py:class', 'bumps.parameter.Parameter'),
('py:class', 'collections.OrderedDict'),
('py:class', 'cuda.Context'),
('py:class', 'cuda.Function'),
('py:class', 'np.dtype'),
('py:class', 'numpy.dtype'),
('py:class', 'np.ndarray'),
('py:class', 'numpy.ndarray'),
('py:class', 'pyopencl.Program'),
('py:class', 'pyopencl._cl.Context'),
('py:class', 'pyopencl._cl.CommandQueue'),
('py:class', 'pyopencl._cl.Device'),
('py:class', 'pyopencl._cl.Kernel'),
('py:class', 'QWebView'),
('py:class', 'unittest.suite.TestSuite'),
('py:class', 'wx.Frame'),
# autodoc and namedtuple is completely broken
('py:class', 'integer -- return number of occurrences of value'),
('py:class', 'integer -- return first index of value.'),
# autodoc doesn't handle these type definitions
('py:class', 'Data'),
('py:class', 'Data1D'),
('py:class', 'Data2D'),
('py:class', 'Kernel'),
('py:class', 'ModelInfo'),
('py:class', 'module'),
('py:class', 'SesansData'),
('py:class', 'SourceModule'),
('py:class', 'TestCondition'),
]



# -- Options for HTML output ---------------------------------------------------

Expand Down
4 changes: 3 additions & 1 deletion doc/guide/plugin.rst
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ value at a time::

Iq.vectorized = False

Return np.NaN if the parameters are not valid (e.g., cap_radius < radius in
Return np.nan if the parameters are not valid (e.g., cap_radius < radius in
barbell). If I(q; pars) is NaN for any $q$, then those parameters will be
ignored, and not included in the calculation of the weighted polydispersity.

Expand Down Expand Up @@ -851,6 +851,8 @@ Some non-standard constants and functions are also provided:
$x^2$
cube(x):
$x^3$
clip(a, a_min, a_max):
$\min(\max(a, a_\text{min}), a_\text{max})$, or NaN if $a$ is NaN.
sas_sinx_x(x):
$\sin(x)/x$, with limit $\sin(0)/0 = 1$.
powr(x, y):
Expand Down
54 changes: 51 additions & 3 deletions explore/precision.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,13 +190,13 @@ def plotdiff(x, target, actual, label, diff):
if diff == "relative":
err = np.array([(abs((t-a)/t) if t != 0 else a) for t, a in zip(target, actual)], 'd')
#err = np.clip(err, 0, 1)
pylab.loglog(x, err, '-', label=label)
pylab.loglog(x, err, '-', label=label, alpha=0.7)
elif diff == "absolute":
err = np.array([abs((t-a)) for t, a in zip(target, actual)], 'd')
pylab.loglog(x, err, '-', label=label)
pylab.loglog(x, err, '-', label=label, alpha=0.7)
else:
limits = np.min(target), np.max(target)
pylab.semilogx(x, np.clip(actual, *limits), '-', label=label)
pylab.semilogx(x, np.clip(actual, *limits), '-', label=label, alpha=0.7)

def make_ocl(function, name, source=[]):
class Kernel(object):
Expand Down Expand Up @@ -412,6 +412,54 @@ def add_function(name, mp_function, np_function, ocl_function,
np_function=lambda x: np.fmod(x, 2*np.pi),
ocl_function=make_ocl("return fmod(q, 2*M_PI);", "sas_fmod"),
)

def sas_langevin(x):
scalar = np.isscalar(x)
if scalar:
x = np.array([x]) # should inherit dtype for single if given single
f = np.empty_like(x)
cutoff = 0.1 if f.dtype == np.float64 else 1.0
#cutoff *= 10
index = x < cutoff
xp = x[index]
xpsq = xp*xp
f[index] = xp / (3. + xpsq / (5. + xpsq/(7. + xpsq/(9.))))
# 4 terms gets to 1e-7 single, 1e-14 double. Can get to 1e-15 double by adding
# another 4 terms and setting cutoff at 1.0. Not worthwhile. Instead we would
# need an expansion about x somewhere between 1 and 10 for the interval [0.1, 100.]
#f[index] = xp / (3. + xpsq / (5. + xpsq/(7. + xpsq/(9. + xpsq/(11.0 + xpsq/(13. + xpsq/(15. + xpsq/17.)))))))
xp = x[~index]
f[~index] = 1/np.tanh(xp) - 1/xp
return f[0] if scalar else f

def sas_langevin_x(x):
scalar = np.isscalar(x)
if scalar:
x = np.array([x]) # should inherit dtype for single if given single
f = np.empty_like(x)
cutoff = 0.1 if f.dtype == np.float64 else 1.0
index = x < cutoff
xp = x[index]
xpsq = xp*xp
f[index] = 1. / (3. + xpsq / (5. + xpsq/(7. + xpsq/(9.))))
xp = x[~index]
f[~index] = (1/np.tanh(xp) - 1/xp)/xp
return f[0] if scalar else f

add_function(
name="langevin(x)",
mp_function=lambda x: (1/mp.tanh(x) - 1/x),
np_function=sas_langevin,
#ocl_function=make_ocl("return q < 0.7 ? q*(1./3. + q*q*(-1./45. + q*q*(2./945. + q*q*(-1./4725.) + q*q*(2./93555.)))) : 1/tanh(q) - 1/q;", "sas_langevin"),
ocl_function=make_ocl("return q < 1e-5 ? q/3. : 1/tanh(q) - 1/q;", "sas_langevin"),
)
add_function(
name="langevin(x)/x",
mp_function=lambda x: (1/mp.tanh(x) - 1/x)/x,
#np_function=lambda x: sas_langevin(x)/x, # Note: need to test for x=0
np_function=sas_langevin_x,
ocl_function=make_ocl("return q < 1e-5 ? 1./3. : (1/tanh(q) - 1/q)/q;", "sas_langevin_x"),
)
add_function(
name="gauss_coil",
mp_function=lambda x: 2*(mp.exp(-x**2) + x**2 - 1)/x**4,
Expand Down
4 changes: 2 additions & 2 deletions explore/realspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ def pad_vectors(boundary, *vectors):
if new_size > old_size:
new = np.empty(new_size, dtype=old.dtype)
new[:old_size] = old
new[old_size:] = np.NaN
new[old_size:] = np.nan
yield new
else:
yield old
Expand Down Expand Up @@ -1115,7 +1115,7 @@ def _sasmodels_Iqxy(kernel, qx, qy, pars, view):
# calculator avoids masked values; instead set masked values to NaN
result = np.empty_like(qx)
result[calculator.index] = Iqxy
result[~calculator.index] = np.NaN
result[~calculator.index] = np.nan
return result

def wrap_sasmodel(name, **pars):
Expand Down
2 changes: 1 addition & 1 deletion sasmodels/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
OpenCL drivers are available. See :mod:`.generate` for details on
defining new models.
"""
__version__ = "1.0.6"
__version__ = "1.0.8"

def data_files():
"""
Expand Down
6 changes: 3 additions & 3 deletions sasmodels/compare_many.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,9 @@ def try_model(fn, pars):
traceback.print_exc()
print("when comparing %s for %d"%(name, seed))
if hasattr(data, 'qx_data'):
result = np.NaN*data.data
result = np.nan*data.data
else:
result = np.NaN*data.x
result = np.nan*data.x
return result
def check_model(pars):
"""
Expand All @@ -165,7 +165,7 @@ def check_model(pars):
except Exception as exc:
#raise
print('"Error: %s"'%str(exc).replace('"', "'"))
print('"good","%d of %d","max diff",%g' % (0, N, np.NaN))
print('"good","%d of %d","max diff",%g' % (0, N, np.nan))
return
expected = max(PRECISION[base], PRECISION[comp])

Expand Down
6 changes: 3 additions & 3 deletions sasmodels/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,8 @@ def __init__(self, x=None, y=None, dx=None, dy=None):
self.y, self.dy = _as_numpy(y), _as_numpy(dy)
self.dxl = None
self.filename = None
self.qmin = self.x.min() if self.x is not None else np.NaN
self.qmax = self.x.max() if self.x is not None else np.NaN
self.qmin = self.x.min() if self.x is not None else np.nan
self.qmax = self.x.max() if self.x is not None else np.nan
# TODO: why is 1D mask False and 2D mask True?
self.mask = (np.isnan(y) if y is not None
else np.zeros_like(x, 'b') if x is not None
Expand Down Expand Up @@ -314,7 +314,7 @@ class Source(object):
"""
def __init__(self):
# type: () -> None
self.wavelength = np.NaN
self.wavelength = np.nan
self.wavelength_unit = "A"

class Sample(object):
Expand Down
46 changes: 46 additions & 0 deletions sasmodels/kernel_header.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,54 @@
#endif
inline double square(double x) { return x*x; }
inline double cube(double x) { return x*x*x; }
// clip() follows numpy.clip() semantics, returning (x < low ? low : x > high ? high : x)
// OpenCL/CUDA clamp() returns fmin(fmax(x, low), high)
// C++(17) clamp() matches numpy.clip()
// If x is NaN numpy.clip() returns NaN but OpenCL clamp() returns low.
inline double clip(double x, double low, double high) { return x < low ? low : x > high ? high : x; }
inline double sas_sinx_x(double x) { return x==0 ? 1.0 : sin(x)/x; }

// vector algebra
static void SET_VEC(double *vector, double v0, double v1, double v2)
{
vector[0] = v0;
vector[1] = v1;
vector[2] = v2;
}

static void SCALE_VEC(double *vector, double a)
{
vector[0] = a*vector[0];
vector[1] = a*vector[1];
vector[2] = a*vector[2];
}

static void ADD_VEC(double *result_vec, double *vec1, double *vec2)
{
result_vec[0] = vec1[0] + vec2[0];
result_vec[1] = vec1[1] + vec2[1];
result_vec[2] = vec1[2] + vec2[2];
}

static double SCALAR_VEC( double *vec1, double *vec2)
{
return vec1[0] * vec2[0] + vec1[1] * vec2[1] + vec1[2] * vec2[2];
}

static double MAG_VEC( double *vec)
{
return sqrt(SCALAR_VEC(vec,vec));
}


static void ORTH_VEC(double *result_vec, double *vec1, double *vec2)
{
double scale = SCALAR_VEC(vec1,vec2) / SCALAR_VEC(vec2,vec2);
result_vec[0] = vec1[0] - scale * vec2[0];
result_vec[1] = vec1[1] - scale * vec2[1];
result_vec[2] = vec1[2] - scale * vec2[2];
}

// CRUFT: support old style models with orientation received qx, qy and angles

// To rotate from the canonical position to theta, phi, psi, first rotate by
Expand Down
Loading

0 comments on commit aa03630

Please sign in to comment.