source: sasmodels/sasmodels/kernel_iq.c @ 0f00d95

core_shell_microgelscostrafo411magnetic_modelrelease_v0.94release_v0.95ticket-1257-vesicle-productticket_1156ticket_1265_superballticket_822_more_unit_tests
Last change on this file since 0f00d95 was 0f00d95, checked in by Paul Kienzle <pkienzle@…>, 8 years ago

tweak spherical correction implementation

  • Property mode set to 100644
File size: 11.3 KB
RevLine 
[2e44ac7]1
2/*
3    ##########################################################
4    #                                                        #
5    #   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!   #
6    #   !!                                              !!   #
7    #   !!  KEEP THIS CODE CONSISTENT WITH KERNELPY.PY  !!   #
8    #   !!                                              !!   #
9    #   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!   #
10    #                                                        #
11    ##########################################################
12*/
13
[03cac08]14#ifndef _PAR_BLOCK_ // protected block so we can include this code twice.
15#define _PAR_BLOCK_
[2e44ac7]16
17typedef struct {
[60eab2a]18#if MAX_PD > 0
[a6f9577]19    int32_t pd_par[MAX_PD];     // id of the nth polydispersity variable
[5cf3c33]20    int32_t pd_length[MAX_PD];  // length of the nth polydispersity weight vector
[0a7e5eb4]21    int32_t pd_offset[MAX_PD];  // offset of pd weights in the value & weight vector
[5cf3c33]22    int32_t pd_stride[MAX_PD];  // stride to move to the next index at this level
[60eab2a]23#endif // MAX_PD > 0
[a738209]24    int32_t pd_prod;            // total number of voxels in hypercube
25    int32_t pd_sum;             // total length of the weights vector
[5ff1b03]26    int32_t num_active;         // number of non-trivial pd loops
[0a7e5eb4]27    int32_t theta_par;          // id of spherical correction variable
[2e44ac7]28} ProblemDetails;
29
30typedef struct {
[56547a8]31    PARAMETER_TABLE
[2e44ac7]32} ParameterBlock;
[9eb3632]33#endif // _PAR_BLOCK_
[03cac08]34
[32e3c9b]35
[a4280bd]36#if defined(MAGNETIC) && NUM_MAGNETIC>0
37
[32e3c9b]38// Return value restricted between low and high
39static double clip(double value, double low, double high)
40{
[b966a96]41  return (value < low ? low : (value > high ? high : value));
[32e3c9b]42}
43
44// Compute spin cross sections given in_spin and out_spin
45// To convert spin cross sections to sld b:
46//     uu * (sld - m_sigma_x);
47//     dd * (sld + m_sigma_x);
48//     ud * (m_sigma_y + 1j*m_sigma_z);
49//     du * (m_sigma_y - 1j*m_sigma_z);
[a4280bd]50static void set_spins(double in_spin, double out_spin, double spins[4])
[32e3c9b]51{
[b966a96]52  in_spin = clip(in_spin, 0.0, 1.0);
53  out_spin = clip(out_spin, 0.0, 1.0);
[a4280bd]54  spins[0] = sqrt(sqrt((1.0-in_spin) * (1.0-out_spin))); // dd
55  spins[1] = sqrt(sqrt((1.0-in_spin) * out_spin));       // du
56  spins[2] = sqrt(sqrt(in_spin * (1.0-out_spin)));       // ud
57  spins[3] = sqrt(sqrt(in_spin * out_spin));             // uu
58}
59
60static double mag_sld(double qx, double qy, double p,
61                       double mx, double my, double sld)
62{
63    const double perp = qy*mx - qx*my;
64    return sld + perp*p;
[32e3c9b]65}
66
[9eb3632]67#endif // MAGNETIC
[2e44ac7]68
[03cac08]69kernel
70void KERNEL_NAME(
[5cf3c33]71    int32_t nq,                 // number of q values
72    const int32_t pd_start,     // where we are in the polydispersity loop
73    const int32_t pd_stop,      // where we are stopping in the polydispersity loop
[6e7ff6d]74    global const ProblemDetails *details,
[9eb3632]75    global const double *values,
[2e44ac7]76    global const double *q, // nq q values, with padding to boundary
[9eb3632]77    global double *result,  // nq+1 return values, again with padding
[303d8d6]78    const double cutoff     // cutoff in the polydispersity weight product
[2e44ac7]79    )
80{
[9eb3632]81
[10ddb64]82  // Storage for the current parameter values.  These will be updated as we
[9eb3632]83  // walk the polydispersity cube.  local_values will be aliased to pvec.
84  ParameterBlock local_values;
85  double *pvec = (double *)&local_values;
[2e44ac7]86
[a4280bd]87#if defined(MAGNETIC) && NUM_MAGNETIC>0
[9eb3632]88  // Location of the sld parameters in the parameter pvec.
89  // These parameters are updated with the effective sld due to magnetism.
[a4280bd]90  #if NUM_MAGNETIC > 3
[9eb3632]91  const int32_t slds[] = { MAGNETIC_PARS };
[a4280bd]92  #endif
[32e3c9b]93
[9eb3632]94  // TODO: could precompute these outside of the kernel.
[32e3c9b]95  // Interpret polarization cross section.
[a4280bd]96  //     up_frac_i = values[NUM_PARS+2];
97  //     up_frac_f = values[NUM_PARS+3];
98  //     up_angle = values[NUM_PARS+4];
99  double spins[4];
[32e3c9b]100  double cos_mspin, sin_mspin;
[a4280bd]101  set_spins(values[NUM_PARS+2], values[NUM_PARS+3], spins);
102  SINCOS(-values[NUM_PARS+4]*M_PI_180, sin_mspin, cos_mspin);
[9eb3632]103#endif // MAGNETIC
[3044216]104
[9eb3632]105  // Fill in the initial variables
106  //   values[0] is scale
107  //   values[1] is background
108  #ifdef USE_OPENMP
109  #pragma omp parallel for
110  #endif
[a4280bd]111  for (int i=0; i < NUM_PARS; i++) {
[9eb3632]112    pvec[i] = values[2+i];
113//printf("p%d = %g\n",i, pvec[i]);
114  }
[f2f67a6]115
[9eb3632]116  double pd_norm;
117//printf("start: %d %d\n",pd_start, pd_stop);
118  if (pd_start == 0) {
119    pd_norm = 0.0;
[2e44ac7]120    #ifdef USE_OPENMP
121    #pragma omp parallel for
122    #endif
[9eb3632]123    for (int q_index=0; q_index < nq; q_index++) result[q_index] = 0.0;
124//printf("initializing %d\n", nq);
125  } else {
126    pd_norm = result[nq];
[2e44ac7]127  }
[9eb3632]128//printf("start %d %g %g\n", pd_start, pd_norm, result[0]);
129
[7b7da6b]130#if MAX_PD>0
[9eb3632]131  global const double *pd_value = values + NUM_VALUES + 2;
132  global const double *pd_weight = pd_value + details->pd_sum;
[7b7da6b]133#endif
[9eb3632]134
135  // Jump into the middle of the polydispersity loop
136#if MAX_PD>4
137  int n4=details->pd_length[4];
138  int i4=(pd_start/details->pd_stride[4])%n4;
139  const int p4=details->pd_par[4];
140  global const double *v4 = pd_value + details->pd_offset[4];
141  global const double *w4 = pd_weight + details->pd_offset[4];
142#endif
143#if MAX_PD>3
144  int n3=details->pd_length[3];
145  int i3=(pd_start/details->pd_stride[3])%n3;
146  const int p3=details->pd_par[3];
147  global const double *v3 = pd_value + details->pd_offset[3];
148  global const double *w3 = pd_weight + details->pd_offset[3];
149//printf("offset %d: %d %d\n", 3, details->pd_offset[3], NUM_VALUES);
150#endif
151#if MAX_PD>2
152  int n2=details->pd_length[2];
153  int i2=(pd_start/details->pd_stride[2])%n2;
154  const int p2=details->pd_par[2];
155  global const double *v2 = pd_value + details->pd_offset[2];
156  global const double *w2 = pd_weight + details->pd_offset[2];
157#endif
158#if MAX_PD>1
159  int n1=details->pd_length[1];
160  int i1=(pd_start/details->pd_stride[1])%n1;
161  const int p1=details->pd_par[1];
162  global const double *v1 = pd_value + details->pd_offset[1];
163  global const double *w1 = pd_weight + details->pd_offset[1];
164#endif
165#if MAX_PD>0
166  int n0=details->pd_length[0];
167  int i0=(pd_start/details->pd_stride[0])%n0;
168  const int p0=details->pd_par[0];
169  global const double *v0 = pd_value + details->pd_offset[0];
170  global const double *w0 = pd_weight + details->pd_offset[0];
171//printf("w0:%p, values:%p, diff:%d, %d\n",w0,values,(w0-values),NUM_VALUES);
172#endif
[2e44ac7]173
[5ff1b03]174
[9eb3632]175#if MAX_PD>0
[0f00d95]176  const int theta_par = details->theta_par;
[9eb3632]177  const int fast_theta = (theta_par == p0);
178  const int slow_theta = (theta_par >= 0 && !fast_theta);
[0f00d95]179  double spherical_correction = 1.0;
[32e3c9b]180#else
[0f00d95]181  // Note: if not polydisperse the weights cancel and we don't need the
182  // spherical correction.
183  const double spherical_correction = 1.0;
[32e3c9b]184#endif
[3044216]185
[9eb3632]186  int step = pd_start;
[ae2b6b5]187
[a738209]188
[9eb3632]189#if MAX_PD>4
190  const double weight5 = 1.0;
191  while (i4 < n4) {
192    pvec[p4] = v4[i4];
193    double weight4 = w4[i4] * weight5;
194//printf("step:%d level %d: p:%d i:%d n:%d value:%g weight:%g\n", step, 4, p4, i4, n4, pvec[p4], weight4);
195#elif MAX_PD>3
196    const double weight4 = 1.0;
197#endif
198#if MAX_PD>3
199  while (i3 < n3) {
200    pvec[p3] = v3[i3];
201    double weight3 = w3[i3] * weight4;
202//printf("step:%d level %d: p:%d i:%d n:%d value:%g weight:%g\n", step, 3, p3, i3, n3, pvec[p3], weight3);
203#elif MAX_PD>2
204    const double weight3 = 1.0;
205#endif
206#if MAX_PD>2
207  while (i2 < n2) {
208    pvec[p2] = v2[i2];
209    double weight2 = w2[i2] * weight3;
210//printf("step:%d level %d: p:%d i:%d n:%d value:%g weight:%g\n", step, 2, p2, i2, n2, pvec[p2], weight2);
211#elif MAX_PD>1
212    const double weight2 = 1.0;
213#endif
214#if MAX_PD>1
215  while (i1 < n1) {
216    pvec[p1] = v1[i1];
217    double weight1 = w1[i1] * weight2;
218//printf("step:%d level %d: p:%d i:%d n:%d value:%g weight:%g\n", step, 1, p1, i1, n1, pvec[p1], weight1);
219#elif MAX_PD>0
220    const double weight1 = 1.0;
221#endif
222#if MAX_PD>0
[0f00d95]223  if (slow_theta) { // Theta is not in inner loop
224    spherical_correction = fmax(fabs(cos(M_PI_180*pvec[theta_par])), 1.e-6);
225  }
[9eb3632]226  while(i0 < n0) {
227    pvec[p0] = v0[i0];
228    double weight0 = w0[i0] * weight1;
229//printf("step:%d level %d: p:%d i:%d n:%d value:%g weight:%g\n", step, 0, p0, i0, n0, pvec[p0], weight0);
230    if (fast_theta) { // Theta is in inner loop
231      spherical_correction = fmax(fabs(cos(M_PI_180*pvec[p0])), 1.e-6);
[5ff1b03]232    }
[9eb3632]233#else
234    const double weight0 = 1.0;
235#endif
[5ff1b03]236
[a4280bd]237//printf("step:%d of %d, pars:",step,pd_stop); for (int i=0; i < NUM_PARS; i++) printf("p%d=%g ",i, pvec[i]); printf("\n");
[9eb3632]238//printf("sphcor: %g\n", spherical_correction);
[ae2b6b5]239
[3044216]240    #ifdef INVALID
[9eb3632]241    if (!INVALID(local_values))
[3044216]242    #endif
[9eb3632]243    {
244      // Accumulate I(q)
245      // Note: weight==0 must always be excluded
246      if (weight0 > cutoff) {
[a3a0c5c]247        // spherical correction is set at a minimum of 1e-6, otherwise there
248        // would be problems looking at models with theta=90.
[9eb3632]249        const double weight = weight0 * spherical_correction;
250        pd_norm += weight * CALL_VOLUME(local_values);
251
252        #ifdef USE_OPENMP
253        #pragma omp parallel for
254        #endif
255        for (int q_index=0; q_index<nq; q_index++) {
[a4280bd]256#if defined(MAGNETIC) && NUM_MAGNETIC > 0
[9eb3632]257          const double qx = q[2*q_index];
258          const double qy = q[2*q_index+1];
259          const double qsq = qx*qx + qy*qy;
260
261          // Constant across orientation, polydispersity for given qx, qy
[a4280bd]262          double scattering = 0.0;
263          // TODO: what is the magnetic scattering at q=0
[9eb3632]264          if (qsq > 1.e-16) {
[a4280bd]265            double p[4];
266            p[0] = (qy*cos_mspin + qx*sin_mspin)/qsq;
267            p[3] = -p[0];
268            p[1] = p[2] = (qy*sin_mspin - qx*cos_mspin)/qsq;
[9eb3632]269
[a4280bd]270            for (int index=0; index<4; index++) {
271              const double xs = spins[index];
272              if (xs > 1.e-8) {
273                const int spin_flip = (index==1) || (index==2);
274                const double pk = p[index];
275                for (int axis=0; axis<=spin_flip; axis++) {
276                  #define M1 NUM_PARS+5
277                  #define M2 NUM_PARS+8
278                  #define M3 NUM_PARS+13
279                  #define SLD(_M_offset, _sld_offset) \
280                      pvec[_sld_offset] = xs * (axis \
281                      ? (index==1 ? -values[_M_offset+2] : values[_M_offset+2]) \
282                      : mag_sld(qx, qy, pk, values[_M_offset], values[_M_offset+1], \
283                                (spin_flip ? 0.0 : values[_sld_offset+2])))
284                  #if NUM_MAGNETIC==1
285                      SLD(M1, MAGNETIC_PAR1);
286                  #elif NUM_MAGNETIC==2
287                      SLD(M1, MAGNETIC_PAR1);
288                      SLD(M2, MAGNETIC_PAR2);
289                  #elif NUM_MAGNETIC==3
290                      SLD(M1, MAGNETIC_PAR1);
291                      SLD(M2, MAGNETIC_PAR2);
292                      SLD(M3, MAGNETIC_PAR3);
293                  #else
294                  for (int sk=0; sk<NUM_MAGNETIC; sk++) {
295                      SLD(M1+3*sk, slds[sk]);
296                  }
297                  #endif
298                  scattering += CALL_IQ(q, q_index, local_values);
299                }
300              }
[9eb3632]301            }
[32e3c9b]302          }
[9eb3632]303#else  // !MAGNETIC
304          const double scattering = CALL_IQ(q, q_index, local_values);
305#endif // !MAGNETIC
306//printf("q_index:%d %g %g %g %g\n",q_index, scattering, weight, spherical_correction, weight0);
307          result[q_index] += weight * scattering;
[32e3c9b]308        }
[3044216]309      }
[03cac08]310    }
[9eb3632]311    ++step;
312#if MAX_PD>0
313    if (step >= pd_stop) break;
314    ++i0;
[2e44ac7]315  }
[9eb3632]316  i0 = 0;
317#endif
318#if MAX_PD>1
319    if (step >= pd_stop) break;
320    ++i1;
321  }
322  i1 = 0;
323#endif
324#if MAX_PD>2
325    if (step >= pd_stop) break;
326    ++i2;
[2e44ac7]327  }
[9eb3632]328  i2 = 0;
329#endif
330#if MAX_PD>3
331    if (step >= pd_stop) break;
332    ++i3;
333  }
334  i3 = 0;
335#endif
336#if MAX_PD>4
337    if (step >= pd_stop) break;
338    ++i4;
339  }
340  i4 = 0;
341#endif
[f2f67a6]342
[9eb3632]343//printf("res: %g/%g\n", result[0], pd_norm);
[f2f67a6]344  // Remember the updated norm.
[a738209]345  result[nq] = pd_norm;
[2e44ac7]346}
Note: See TracBrowser for help on using the repository browser.