source: sasmodels/sasmodels/kernel_iq.c @ 32e3c9b

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

dll version of magnetic sld

  • Property mode set to 100644
File size: 12.5 KB
Line 
1
2/*
3    ##########################################################
4    #                                                        #
5    #   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!   #
6    #   !!                                              !!   #
7    #   !!  KEEP THIS CODE CONSISTENT WITH KERNELPY.PY  !!   #
8    #   !!                                              !!   #
9    #   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!   #
10    #                                                        #
11    ##########################################################
12*/
13
14#ifndef _PAR_BLOCK_ // protected block so we can include this code twice.
15#define _PAR_BLOCK_
16
17typedef struct {
18#if MAX_PD > 0
19    int32_t pd_par[MAX_PD];     // id of the nth polydispersity variable
20    int32_t pd_length[MAX_PD];  // length of the nth polydispersity weight vector
21    int32_t pd_offset[MAX_PD];  // offset of pd weights in the value & weight vector
22    int32_t pd_stride[MAX_PD];  // stride to move to the next index at this level
23#endif // MAX_PD > 0
24    int32_t pd_prod;            // total number of voxels in hypercube
25    int32_t pd_sum;             // total length of the weights vector
26    int32_t num_active;         // number of non-trivial pd loops
27    int32_t theta_par;          // id of spherical correction variable
28} ProblemDetails;
29
30typedef struct {
31    PARAMETER_TABLE;
32} ParameterBlock;
33#endif
34
35#ifdef MAGNETIC
36const int32_t magnetic[] = { MAGNETIC_PARS };
37#endif
38
39#ifdef MAGNETIC
40// Return value restricted between low and high
41static double clip(double value, double low, double high)
42{
43    return (value < low ? low : (value > high ? high : value));
44}
45
46// Compute spin cross sections given in_spin and out_spin
47// To convert spin cross sections to sld b:
48//     uu * (sld - m_sigma_x);
49//     dd * (sld + m_sigma_x);
50//     ud * (m_sigma_y + 1j*m_sigma_z);
51//     du * (m_sigma_y - 1j*m_sigma_z);
52static void spins(double in_spin, double out_spin,
53    double *uu, double *dd, double *ud, double *du)
54{
55    in_spin = clip(in_spin, 0.0, 1.0);
56    out_spin = clip(out_spin, 0.0, 1.0);
57        *uu = sqrt(sqrt(in_spin * out_spin));
58        *dd = sqrt(sqrt((1.0-in_spin) * (1.0-out_spin)));
59        *ud = sqrt(sqrt(in_spin * (1.0-out_spin)));
60        *du = sqrt(sqrt((1.0-in_spin) * out_spin));
61}
62
63// Convert polar to rectangular coordinates.
64static void polrec(double r, double theta, double phi,
65    double *x, double *y, double *z)
66{
67    double cos_theta, sin_theta, cos_phi, sin_phi;
68    SINCOS(theta*M_PI_180, sin_theta, cos_theta);
69    SINCOS(phi*M_PI_180, sin_phi, cos_phi);
70    *x = r * cos_theta * cos_phi;
71    *y = r * sin_theta;
72    *z = -r * cos_theta * sin_phi;
73}
74#endif
75
76kernel
77void KERNEL_NAME(
78    int32_t nq,                 // number of q values
79    const int32_t pd_start,     // where we are in the polydispersity loop
80    const int32_t pd_stop,      // where we are stopping in the polydispersity loop
81    global const ProblemDetails *details,
82   // global const  // TODO: make it const again!
83    double *values,
84    global const double *q, // nq q values, with padding to boundary
85    global double *result,  // nq+3 return values, again with padding
86    const double cutoff     // cutoff in the polydispersity weight product
87    )
88{
89  // Storage for the current parameter values.  These will be updated as we
90  // walk the polydispersity cube.
91  ParameterBlock local_values;  // current parameter values
92  double *pvec = (double *)(&local_values);  // Alias named parameters with a vector
93
94  // Fill in the initial variables
95  //   values[0] is scale
96  //   values[1] is background
97  #ifdef USE_OPENMP
98  #pragma omp parallel for
99  #endif
100  for (int k=0; k < NPARS; k++) {
101    pvec[k] = values[k+2];
102  }
103#ifdef MAGNETIC
104  const double up_frac_i = values[NPARS+2];
105  const double up_frac_f = values[NPARS+3];
106  const double up_angle = values[NPARS+4];
107  #define MX(_k) (values[NPARS+5+3*_k])
108  #define MY(_k) (values[NPARS+6+3*_k])
109  #define MZ(_k) (values[NPARS+7+3*_k])
110
111  // TODO: precompute this on the python side
112  // Convert polar to rectangular coordinates in place.
113  if (pd_start == 0) {  // Update in place; only do this for the first hunk!
114//printf("spin: %g %g %g\n", up_frac_i, up_frac_f, up_angle);
115    for (int mag=0; mag < NUM_MAGNETIC; mag++) {
116//printf("mag %d: %g %g %g\n", mag, MX(mag), MY(mag), MZ(mag));
117        polrec(MX(mag), MY(mag), MZ(mag), &MX(mag), &MY(mag), &MZ(mag));
118//printf("   ==>: %g %g %g\n", MX(mag), MY(mag), MZ(mag));
119    }
120  }
121  // Interpret polarization cross section.
122  double uu, dd, ud, du;
123  double cos_mspin, sin_mspin;
124  spins(up_frac_i, up_frac_f, &uu, &dd, &ud, &du);
125  SINCOS(-up_angle*M_PI_180, sin_mspin, cos_mspin);
126#endif
127
128  // Monodisperse computation
129  if (details->num_active == 0) {
130    double norm, scale, background;
131    #ifdef INVALID
132    if (INVALID(local_values)) { return; }
133    #endif
134
135    norm = CALL_VOLUME(local_values);
136    scale = values[0];
137    background = values[1];
138
139    #ifdef USE_OPENMP
140    #pragma omp parallel for
141    #endif
142    for (int q_index=0; q_index < nq; q_index++) {
143#ifdef MAGNETIC
144      const double qx = q[2*q_index];
145      const double qy = q[2*q_index+1];
146      const double qsq = qx*qx + qy*qy;
147
148      // Constant across orientation, polydispersity for given qx, qy
149      double px, py, pz;
150      if (qsq > 1e-16) {
151        px = (qy*cos_mspin + qx*sin_mspin)/qsq;
152        py = (qy*sin_mspin - qx*cos_mspin)/qsq;
153        pz = 1.0;
154      } else {
155        px = py = pz = 0.0;
156      }
157
158      double scattering = 0.0;
159      if (uu > 1e-8) {
160        for (int mag=0; mag<NUM_MAGNETIC; mag++) {
161            const double perp = (qy*MX(mag) - qx*MY(mag));
162            pvec[magnetic[mag]] = (values[magnetic[mag]+2] - perp*px)*uu;
163        }
164        scattering += CALL_IQ(q, q_index, local_values);
165      }
166      if (dd > 1e-8){
167        for (int mag=0; mag<NUM_MAGNETIC; mag++) {
168            const double perp = (qy*MX(mag) - qx*MY(mag));
169            pvec[magnetic[mag]] = (values[magnetic[mag]+2] + perp*px)*dd;
170        }
171        scattering += CALL_IQ(q, q_index, local_values);
172      }
173      if (ud > 1e-8){
174        for (int mag=0; mag<NUM_MAGNETIC; mag++) {
175            const double perp = (qy*MX(mag) - qx*MY(mag));
176            pvec[magnetic[mag]] = perp*py*ud;
177        }
178        scattering += CALL_IQ(q, q_index, local_values);
179        for (int mag=0; mag<NUM_MAGNETIC; mag++) {
180            pvec[magnetic[mag]] = MZ(mag)*pz*ud;
181        }
182        scattering += CALL_IQ(q, q_index, local_values);
183      }
184      if (du > 1e-8) {
185        for (int mag=0; mag<NUM_MAGNETIC; mag++) {
186            const double perp = (qy*MX(mag) - qx*MY(mag));
187            pvec[magnetic[mag]] = perp*py*du;
188        }
189        scattering += CALL_IQ(q, q_index, local_values);
190        for (int mag=0; mag<NUM_MAGNETIC; mag++) {
191            pvec[magnetic[mag]] = -MZ(mag)*pz*du;
192        }
193        scattering += CALL_IQ(q, q_index, local_values);
194      }
195#else
196      double scattering = CALL_IQ(q, q_index, local_values);
197#endif
198      result[q_index] = (norm>0. ? scale*scattering/norm + background : background);
199    }
200    return;
201  }
202
203#if MAX_PD > 0
204
205#if MAGNETIC
206  const double *pd_value = values+2+NPARS+3+3*NUM_MAGNETIC;
207#else
208  const double *pd_value = values+2+NPARS;
209#endif
210  const double *pd_weight = pd_value+details->pd_sum;
211
212  // need product of weights at every Iq calc, so keep product of
213  // weights from the outer loops so that weight = partial_weight * fast_weight
214  double pd_norm;
215  double partial_weight; // product of weight w4*w3*w2 but not w1
216  double spherical_correction; // cosine correction for latitude variation
217  double weight; // product of partial_weight*w1*spherical_correction
218
219  // Number of elements in the longest polydispersity loop
220  const int p0_par = details->pd_par[0];
221  const int p0_length = details->pd_length[0];
222  const int p0_offset = details->pd_offset[0];
223  const int p0_is_theta = (p0_par == details->theta_par);
224  int p0_index;
225
226  // Trigger the reset behaviour that happens at the end the fast loop
227  // by setting the initial index >= weight vector length.
228  p0_index = p0_length;
229
230  // Default the spherical correction to 1.0 in case it is not otherwise set
231  spherical_correction = 1.0;
232
233  // Since we are no longer looping over the entire polydispersity hypercube
234  // for each q, we need to track the result and normalization values between
235  // calls.  This means initializing them to 0 at the start and accumulating
236  // them between calls.
237  pd_norm = (pd_start == 0 ? 0.0 : result[nq]);
238
239  if (pd_start == 0) {
240    #ifdef USE_OPENMP
241    #pragma omp parallel for
242    #endif
243    for (int q_index=0; q_index < nq; q_index++) {
244      result[q_index] = 0.0;
245    }
246  }
247
248  // Loop over the weights then loop over q, accumulating values
249  for (int loop_index=pd_start; loop_index < pd_stop; loop_index++) {
250    // check if fast loop needs to be reset
251    if (p0_index == p0_length) {
252
253      // Compute position in polydispersity hypercube and partial weight
254      partial_weight = 1.0;
255      for (int k=1; k < details->num_active; k++) {
256        int pk = details->pd_par[k];
257        int index = details->pd_offset[k] + (loop_index/details->pd_stride[k])%details->pd_length[k];
258        pvec[pk] = pd_value[index];
259        partial_weight *= pd_weight[index];
260        if (pk == details->theta_par) {
261          spherical_correction = fmax(fabs(cos(M_PI_180*pvec[pk])), 1.e-6);
262        }
263      }
264      p0_index = loop_index%p0_length;
265    }
266
267    // Update parameter p0
268    weight = partial_weight*pd_weight[p0_offset + p0_index];
269    pvec[p0_par] = pd_value[p0_offset + p0_index];
270    if (p0_is_theta) {
271      spherical_correction = fmax(fabs(cos(M_PI_180*pvec[p0_par])), 1.e-6);
272    }
273    p0_index++;
274
275    #ifdef INVALID
276    if (INVALID(local_values)) continue;
277    #endif
278
279    // Accumulate I(q)
280    // Note: weight==0 must always be excluded
281    if (weight > cutoff) {
282      // spherical correction has some nasty effects when theta is +90 or -90
283      // where it becomes zero.  If the entirety of the correction
284      weight *= spherical_correction;
285      pd_norm += weight * CALL_VOLUME(local_values);
286
287      #ifdef USE_OPENMP
288      #pragma omp parallel for
289      #endif
290      for (int q_index=0; q_index < nq; q_index++) {
291#ifdef MAGNETIC
292        const double qx = q[2*q_index];
293        const double qy = q[2*q_index+1];
294        const double qsq = qx*qx + qy*qy;
295
296        // Constant across orientation, polydispersity for given qx, qy
297        double px, py, pz;
298        if (qsq > 1e-16) {
299          px = (qy*cos_mspin + qx*sin_mspin)/qsq;
300          py = (qy*sin_mspin - qx*cos_mspin)/qsq;
301          pz = 1.0;
302        } else {
303          px = py = pz = 0.0;
304        }
305
306        double scattering = 0.0;
307        if (uu > 1e-8) {
308          for (int mag=0; mag<NUM_MAGNETIC; mag++) {
309              const double perp = (qy*MX(mag) - qx*MY(mag));
310              pvec[magnetic[mag]] = (values[magnetic[mag]+2] - perp*px)*uu;
311          }
312          scattering += CALL_IQ(q, q_index, local_values);
313        }
314        if (dd > 1e-8){
315          for (int mag=0; mag<NUM_MAGNETIC; mag++) {
316              const double perp = (qy*MX(mag) - qx*MY(mag));
317              pvec[magnetic[mag]] = (values[magnetic[mag]+2] + perp*px)*dd;
318          }
319          scattering += CALL_IQ(q, q_index, local_values);
320        }
321        if (ud > 1e-8){
322          for (int mag=0; mag<NUM_MAGNETIC; mag++) {
323              const double perp = (qy*MX(mag) - qx*MY(mag));
324              pvec[magnetic[mag]] = perp*py*ud;
325          }
326          scattering += CALL_IQ(q, q_index, local_values);
327          for (int mag=0; mag<NUM_MAGNETIC; mag++) {
328              pvec[magnetic[mag]] = MZ(mag)*pz*ud;
329          }
330          scattering += CALL_IQ(q, q_index, local_values);
331        }
332        if (du > 1e-8) {
333          for (int mag=0; mag<NUM_MAGNETIC; mag++) {
334              const double perp = (qy*MX(mag) - qx*MY(mag));
335              pvec[magnetic[mag]] = perp*py*du;
336          }
337          scattering += CALL_IQ(q, q_index, local_values);
338          for (int mag=0; mag<NUM_MAGNETIC; mag++) {
339              pvec[magnetic[mag]] = -MZ(mag)*pz*du;
340          }
341          scattering += CALL_IQ(q, q_index, local_values);
342        }
343#else
344        double scattering = CALL_IQ(q, q_index, local_values);
345#endif
346        result[q_index] += weight*scattering;
347      }
348    }
349  }
350
351  if (pd_stop >= details->pd_prod) {
352    // End of the PD loop we can normalize
353    double scale, background;
354    scale = values[0];
355    background = values[1];
356    #ifdef USE_OPENMP
357    #pragma omp parallel for
358    #endif
359    for (int q_index=0; q_index < nq; q_index++) {
360      result[q_index] = (pd_norm>0. ? scale*result[q_index]/pd_norm + background : background);
361    }
362  }
363
364  // Remember the updated norm.
365  result[nq] = pd_norm;
366#endif // MAX_PD > 0
367}
Note: See TracBrowser for help on using the repository browser.