diff --git a/doc/guide/plugin.rst b/doc/guide/plugin.rst index e7c0ba4b..589d7872 100644 --- a/doc/guide/plugin.rst +++ b/doc/guide/plugin.rst @@ -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): diff --git a/sasmodels/kernel_header.c b/sasmodels/kernel_header.c index f27584d5..19793041 100644 --- a/sasmodels/kernel_header.c +++ b/sasmodels/kernel_header.c @@ -187,6 +187,11 @@ #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; } // CRUFT: support old style models with orientation received qx, qy and angles diff --git a/sasmodels/kernel_iq.c b/sasmodels/kernel_iq.c index f7e73add..668b4128 100644 --- a/sasmodels/kernel_iq.c +++ b/sasmodels/kernel_iq.c @@ -111,12 +111,6 @@ void ORTH_VEC(double *result_vec, double *vec1, double *vec2) } -// Return value restricted between low and high -static double clip(double value, double low, double high) -{ - return (value < low ? low : (value > high ? high : value)); -} - // Compute spin cross sections given in_spin and out_spin // To convert spin cross sections to sld b: // uu * (sld - m_perp_x); diff --git a/sasmodels/models/lib/magnetic_functions.c b/sasmodels/models/lib/magnetic_functions.c index 37eb6488..1213cb72 100644 --- a/sasmodels/models/lib/magnetic_functions.c +++ b/sasmodels/models/lib/magnetic_functions.c @@ -1,13 +1,3 @@ -static double clipp(double value, double low, double high) //from kernel_iq.c -{ - return (value < low ? low : (value > high ? high : value)); -} - -static double length(double x, double y) -{ - return sqrt(x*x + y*y); -} - static double fq_core_shell(double q, double sld_core, double radius, double sld_solvent, double fp_n, double sld[], double thickness[]) { @@ -56,8 +46,8 @@ static void set_weights(double in_spin, double out_spin, double weight[8]) //fro double norm=out_spin; - in_spin = clipp(sqrt(square(in_spin)), 0.0, 1.0);//opencl has ambiguities for abs() - out_spin = clipp(sqrt(square(out_spin)), 0.0, 1.0); + in_spin = clip(sqrt(square(in_spin)), 0.0, 1.0);//opencl has ambiguities for abs() + out_spin = clip(sqrt(square(out_spin)), 0.0, 1.0); if (out_spin < 0.5){norm=1-out_spin;} diff --git a/sasmodels/models/sphere.py b/sasmodels/models/sphere.py index 1088e3e5..f6407077 100644 --- a/sasmodels/models/sphere.py +++ b/sasmodels/models/sphere.py @@ -75,7 +75,6 @@ have_Fq = True radius_effective_modes = ["radius"] #single = False - def random(): """Return a random parameter set for the model.""" radius = 10**np.random.uniform(1.3, 4) @@ -106,6 +105,21 @@ def random(): 0.1, 482.93824329, 29763977.79867414, 120.0, 8087664.122641933, 1.0], [{"radius": 120., "radius_pd": 0.2, "radius_pd_n": 45}, 0.2, 1.23330406, 1850806.1197361, 120.0, 8087664.122641933, 1.0], + + # For 2-D data use (qx, qy) pairs. Since sphere is radial, just need the + # correct |q| value for the test, so use 3-4-5 triangle. The test code + # looks for tuples to detect 2-D data, so can't use simple numpy cheats. + [{"scale": 1., "background": 0., "sld": 6., "sld_solvent": 1., + "radius": 120.}, + [(0.006, 0.008), (0.06,0.08), (0.12, 0.16)], + [1.34836265e+04, 6.20114062e+00, 1.04733914e-01]], + + # TODO: magnetism smoke test. Values not validated. + [dict(radius=120, sld_M0=4, sld_mphi=20, sld_mtheta=60, + up_frac_i=0.05, up_frac_f=0.1, up_theta=-15, up_phi=10), + [(0.0, 0.01), (0.0, 0.1), (0.0, 0.2)], + [20247.206006297125, 9.312720770235483, 0.15826993186001856]], + # But note P(Q) = F2/volume # F and F^2 are "unscaled", with for n S(q) or for beta approx # I(q) = n [ + (S(q) - 1)] diff --git a/sasmodels/special.py b/sasmodels/special.py index 1b8b3483..3b963270 100644 --- a/sasmodels/special.py +++ b/sasmodels/special.py @@ -58,6 +58,9 @@ 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$. @@ -215,7 +218,7 @@ from numpy import sin, cos, tan, arcsin as asin, arccos as acos, arctan as atan from numpy import sinh, cosh, tanh, arcsinh as asinh, arccosh as acosh, arctanh as atanh from numpy import arctan2 as atan2 -from numpy import fabs, fmin, fmax, trunc, rint +from numpy import fabs, fmin, fmax, clip, trunc, rint from numpy import pi, nan, inf from scipy.special import gamma as sas_gamma from scipy.special import gammaln as sas_gammaln