source: sasmodels/sasmodels/kernel_iq.c @ a738209

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

simplify kernels by remove coordination parameter logic

  • Property mode set to 100644
File size: 6.4 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
36kernel
37void KERNEL_NAME(
38    int32_t nq,                 // number of q values
39    const int32_t pd_start,     // where we are in the polydispersity loop
40    const int32_t pd_stop,      // where we are stopping in the polydispersity loop
41    global const ProblemDetails *details,
42    global const double *values,
43    global const double *q, // nq q values, with padding to boundary
44    global double *result,  // nq+3 return values, again with padding
45    const double cutoff     // cutoff in the polydispersity weight product
46    )
47{
48  // Storage for the current parameter values.  These will be updated as we
49  // walk the polydispersity cube.
50  ParameterBlock local_values;  // current parameter values
51  double *pvec = (double *)(&local_values);  // Alias named parameters with a vector
52
53  // Fill in the initial variables
54  #ifdef USE_OPENMP
55  #pragma omp parallel for
56  #endif
57  for (int k=0; k < NPARS; k++) {
58    pvec[k] = values[k+2];
59  }
60
61  // Monodisperse computation
62  if (details->num_active == 0) {
63    double norm, scale, background;
64    #ifdef INVALID
65    if (INVALID(local_values)) { return; }
66    #endif
67
68    norm = CALL_VOLUME(local_values);
69    scale = values[0];
70    background = values[1];
71
72    #ifdef USE_OPENMP
73    #pragma omp parallel for
74    #endif
75    for (int q_index=0; q_index < nq; q_index++) {
76      double scattering = CALL_IQ(q, q_index, local_values);
77      result[q_index] = (norm>0. ? scale*scattering/norm + background : background);
78    }
79    return;
80  }
81
82#if MAX_PD > 0
83
84  const double *pd_value = values+2+NPARS;
85  const double *pd_weight = pd_value+details->pd_sum;
86
87  // need product of weights at every Iq calc, so keep product of
88  // weights from the outer loops so that weight = partial_weight * fast_weight
89  double pd_norm;
90  double partial_weight; // product of weight w4*w3*w2 but not w1
91  double spherical_correction; // cosine correction for latitude variation
92  double weight; // product of partial_weight*w1*spherical_correction
93
94  // Number of elements in the longest polydispersity loop
95  const int p0_par = details->pd_par[0];
96  const int p0_length = details->pd_length[0];
97  const int p0_offset = details->pd_offset[0];
98  const int p0_is_theta = (p0_par == details->theta_par);
99  int p0_index;
100
101  // Trigger the reset behaviour that happens at the end the fast loop
102  // by setting the initial index >= weight vector length.
103  p0_index = p0_length;
104
105  // Default the spherical correction to 1.0 in case it is not otherwise set
106  spherical_correction = 1.0;
107
108  // Since we are no longer looping over the entire polydispersity hypercube
109  // for each q, we need to track the result and normalization values between
110  // calls.  This means initializing them to 0 at the start and accumulating
111  // them between calls.
112  pd_norm = (pd_start == 0 ? 0.0 : result[nq]);
113
114  if (pd_start == 0) {
115    #ifdef USE_OPENMP
116    #pragma omp parallel for
117    #endif
118    for (int q_index=0; q_index < nq; q_index++) {
119      result[q_index] = 0.0;
120    }
121  }
122
123  // Loop over the weights then loop over q, accumulating values
124  for (int loop_index=pd_start; loop_index < pd_stop; loop_index++) {
125    // check if fast loop needs to be reset
126    if (p0_index == p0_length) {
127
128      // Compute position in polydispersity hypercube and partial weight
129      partial_weight = 1.0;
130      for (int k=1; k < details->num_active; k++) {
131        int pk = details->pd_par[k];
132        int index = details->pd_offset[k] + (loop_index/details->pd_stride[k])%details->pd_length[k];
133        pvec[pk] = pd_value[index];
134        partial_weight *= pd_weight[index];
135        if (pk == details->theta_par) {
136          spherical_correction = fmax(fabs(cos(M_PI_180*pvec[pk])), 1.e-6);
137        }
138      }
139      p0_index = loop_index%p0_length;
140    }
141
142    // Update parameter p0
143    weight = partial_weight*pd_weight[p0_offset + p0_index];
144    pvec[p0_par] = pd_value[p0_offset + p0_index];
145    if (p0_is_theta) {
146      spherical_correction = fmax(fabs(cos(M_PI_180*pvec[p0_par])), 1.e-6);
147    }
148    p0_index++;
149
150    #ifdef INVALID
151    if (INVALID(local_values)) continue;
152    #endif
153
154    // Accumulate I(q)
155    // Note: weight==0 must always be excluded
156    if (weight > cutoff) {
157      // spherical correction has some nasty effects when theta is +90 or -90
158      // where it becomes zero.  If the entirety of the correction
159      weight *= spherical_correction;
160      pd_norm += weight * CALL_VOLUME(local_values);
161
162      #ifdef USE_OPENMP
163      #pragma omp parallel for
164      #endif
165      for (int q_index=0; q_index < nq; q_index++) {
166        const double scattering = CALL_IQ(q, q_index, local_values);
167        result[q_index] += weight*scattering;
168      }
169    }
170  }
171
172  if (pd_stop >= details->pd_prod) {
173    // End of the PD loop we can normalize
174    double scale, background;
175    scale = values[0];
176    background = values[1];
177    #ifdef USE_OPENMP
178    #pragma omp parallel for
179    #endif
180    for (int q_index=0; q_index < nq; q_index++) {
181      result[q_index] = (pd_norm>0. ? scale*result[q_index]/pd_norm + background : background);
182    }
183  }
184
185  // Remember the updated norm.
186  result[nq] = pd_norm;
187#endif // MAX_PD > 0
188}
Note: See TracBrowser for help on using the repository browser.