Changeset 9eb3632 in sasmodels for sasmodels/kernel_iq.c
- Timestamp:
- Jul 23, 2016 12:54:17 AM (8 years ago)
- Branches:
- master, core_shell_microgels, costrafo411, magnetic_model, release_v0.94, release_v0.95, ticket-1257-vesicle-product, ticket_1156, ticket_1265_superball, ticket_822_more_unit_tests
- Children:
- 7b7da6b
- Parents:
- 6a0d6aa
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
sasmodels/kernel_iq.c
rb966a96 r9eb3632 29 29 30 30 typedef struct { 31 PARAMETER_TABLE 31 PARAMETER_TABLE; 32 32 } ParameterBlock; 33 #endif 34 35 #ifdef MAGNETIC 36 const int32_t magnetic[] = { MAGNETIC_PARS }; 37 #endif 33 #endif // _PAR_BLOCK_ 34 38 35 39 36 #ifdef MAGNETIC … … 61 58 } 62 59 63 // Convert polar to rectangular coordinates. 64 static 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 60 #endif // MAGNETIC 75 61 76 62 kernel … … 80 66 const int32_t pd_stop, // where we are stopping in the polydispersity loop 81 67 global const ProblemDetails *details, 82 // global const // TODO: make it const again! 83 double *values, 68 global const double *values, 84 69 global const double *q, // nq q values, with padding to boundary 85 global double *result, // nq+ 3return values, again with padding70 global double *result, // nq+1 return values, again with padding 86 71 const double cutoff // cutoff in the polydispersity weight product 87 72 ) 88 73 { 74 89 75 // 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 76 // walk the polydispersity cube. local_values will be aliased to pvec. 77 ParameterBlock local_values; 78 double *pvec = (double *)&local_values; 79 80 #ifdef MAGNETIC 81 // Location of the sld parameters in the parameter pvec. 82 // These parameters are updated with the effective sld due to magnetism. 83 const int32_t slds[] = { MAGNETIC_PARS }; 84 85 const double up_frac_i = values[NPARS+2]; 86 const double up_frac_f = values[NPARS+3]; 87 const double up_angle = values[NPARS+4]; 88 #define MX(_k) (values[NPARS+5+3*_k]) 89 #define MY(_k) (values[NPARS+6+3*_k]) 90 #define MZ(_k) (values[NPARS+7+3*_k]) 91 92 // TODO: could precompute these outside of the kernel. 93 // Interpret polarization cross section. 94 double uu, dd, ud, du; 95 double cos_mspin, sin_mspin; 96 spins(up_frac_i, up_frac_f, &uu, &dd, &ud, &du); 97 SINCOS(-up_angle*M_PI_180, sin_mspin, cos_mspin); 98 #endif // MAGNETIC 93 99 94 100 // Fill in the initial variables … … 98 104 #pragma omp parallel for 99 105 #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 106 for (int i=0; i < NPARS; i++) { 107 pvec[i] = values[2+i]; 108 //printf("p%d = %g\n",i, pvec[i]); 109 } 110 111 double pd_norm; 112 //printf("start: %d %d\n",pd_start, pd_stop); 113 if (pd_start == 0) { 114 pd_norm = 0.0; 139 115 #ifdef USE_OPENMP 140 116 #pragma omp parallel for 141 117 #endif 142 for (int q_index=0; q_index < nq; q_index++) { 118 for (int q_index=0; q_index < nq; q_index++) result[q_index] = 0.0; 119 //printf("initializing %d\n", nq); 120 } else { 121 pd_norm = result[nq]; 122 } 123 //printf("start %d %g %g\n", pd_start, pd_norm, result[0]); 124 125 global const double *pd_value = values + NUM_VALUES + 2; 126 global const double *pd_weight = pd_value + details->pd_sum; 127 128 // Jump into the middle of the polydispersity loop 129 #if MAX_PD>4 130 int n4=details->pd_length[4]; 131 int i4=(pd_start/details->pd_stride[4])%n4; 132 const int p4=details->pd_par[4]; 133 global const double *v4 = pd_value + details->pd_offset[4]; 134 global const double *w4 = pd_weight + details->pd_offset[4]; 135 #endif 136 #if MAX_PD>3 137 int n3=details->pd_length[3]; 138 int i3=(pd_start/details->pd_stride[3])%n3; 139 const int p3=details->pd_par[3]; 140 global const double *v3 = pd_value + details->pd_offset[3]; 141 global const double *w3 = pd_weight + details->pd_offset[3]; 142 //printf("offset %d: %d %d\n", 3, details->pd_offset[3], NUM_VALUES); 143 #endif 144 #if MAX_PD>2 145 int n2=details->pd_length[2]; 146 int i2=(pd_start/details->pd_stride[2])%n2; 147 const int p2=details->pd_par[2]; 148 global const double *v2 = pd_value + details->pd_offset[2]; 149 global const double *w2 = pd_weight + details->pd_offset[2]; 150 #endif 151 #if MAX_PD>1 152 int n1=details->pd_length[1]; 153 int i1=(pd_start/details->pd_stride[1])%n1; 154 const int p1=details->pd_par[1]; 155 global const double *v1 = pd_value + details->pd_offset[1]; 156 global const double *w1 = pd_weight + details->pd_offset[1]; 157 #endif 158 #if MAX_PD>0 159 int n0=details->pd_length[0]; 160 int i0=(pd_start/details->pd_stride[0])%n0; 161 const int p0=details->pd_par[0]; 162 global const double *v0 = pd_value + details->pd_offset[0]; 163 global const double *w0 = pd_weight + details->pd_offset[0]; 164 //printf("w0:%p, values:%p, diff:%d, %d\n",w0,values,(w0-values),NUM_VALUES); 165 #endif 166 167 168 double spherical_correction=1.0; 169 const int theta_par = details->theta_par; 170 #if MAX_PD>0 171 const int fast_theta = (theta_par == p0); 172 const int slow_theta = (theta_par >= 0 && !fast_theta); 173 #else 174 const int slow_theta = (theta_par >= 0); 175 #endif 176 177 int step = pd_start; 178 179 180 #if MAX_PD>4 181 const double weight5 = 1.0; 182 while (i4 < n4) { 183 pvec[p4] = v4[i4]; 184 double weight4 = w4[i4] * weight5; 185 //printf("step:%d level %d: p:%d i:%d n:%d value:%g weight:%g\n", step, 4, p4, i4, n4, pvec[p4], weight4); 186 #elif MAX_PD>3 187 const double weight4 = 1.0; 188 #endif 189 #if MAX_PD>3 190 while (i3 < n3) { 191 pvec[p3] = v3[i3]; 192 double weight3 = w3[i3] * weight4; 193 //printf("step:%d level %d: p:%d i:%d n:%d value:%g weight:%g\n", step, 3, p3, i3, n3, pvec[p3], weight3); 194 #elif MAX_PD>2 195 const double weight3 = 1.0; 196 #endif 197 #if MAX_PD>2 198 while (i2 < n2) { 199 pvec[p2] = v2[i2]; 200 double weight2 = w2[i2] * weight3; 201 //printf("step:%d level %d: p:%d i:%d n:%d value:%g weight:%g\n", step, 2, p2, i2, n2, pvec[p2], weight2); 202 #elif MAX_PD>1 203 const double weight2 = 1.0; 204 #endif 205 #if MAX_PD>1 206 while (i1 < n1) { 207 pvec[p1] = v1[i1]; 208 double weight1 = w1[i1] * weight2; 209 //printf("step:%d level %d: p:%d i:%d n:%d value:%g weight:%g\n", step, 1, p1, i1, n1, pvec[p1], weight1); 210 #elif MAX_PD>0 211 const double weight1 = 1.0; 212 #endif 213 if (slow_theta) { // Theta is not in inner loop 214 spherical_correction = fmax(fabs(cos(M_PI_180*pvec[theta_par])), 1.e-6); 215 } 216 #if MAX_PD>0 217 while(i0 < n0) { 218 pvec[p0] = v0[i0]; 219 double weight0 = w0[i0] * weight1; 220 //printf("step:%d level %d: p:%d i:%d n:%d value:%g weight:%g\n", step, 0, p0, i0, n0, pvec[p0], weight0); 221 if (fast_theta) { // Theta is in inner loop 222 spherical_correction = fmax(fabs(cos(M_PI_180*pvec[p0])), 1.e-6); 223 } 224 #else 225 const double weight0 = 1.0; 226 #endif 227 228 //printf("step:%d of %d, pars:",step,pd_stop); for (int i=0; i < NPARS; i++) printf("p%d=%g ",i, pvec[i]); printf("\n"); 229 //printf("sphcor: %g\n", spherical_correction); 230 231 #ifdef INVALID 232 if (!INVALID(local_values)) 233 #endif 234 { 235 // Accumulate I(q) 236 // Note: weight==0 must always be excluded 237 if (weight0 > cutoff) { 238 // spherical correction has some nasty effects when theta is +90 or -90 239 // where it becomes zero. 240 const double weight = weight0 * spherical_correction; 241 pd_norm += weight * CALL_VOLUME(local_values); 242 243 #ifdef USE_OPENMP 244 #pragma omp parallel for 245 #endif 246 for (int q_index=0; q_index<nq; q_index++) { 143 247 #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); 248 const double qx = q[2*q_index]; 249 const double qy = q[2*q_index+1]; 250 const double qsq = qx*qx + qy*qy; 251 252 // Constant across orientation, polydispersity for given qx, qy 253 double px, py, pz; 254 if (qsq > 1.e-16) { 255 px = (qy*cos_mspin + qx*sin_mspin)/qsq; 256 py = (qy*sin_mspin - qx*cos_mspin)/qsq; 257 pz = 1.0; 258 } else { 259 px = py = pz = 0.0; 260 } 261 262 double scattering = 0.0; 263 if (uu > 1.e-8) { 264 for (int sk=0; sk<NUM_MAGNETIC; sk++) { 265 const double perp = (qy*MX(sk) - qx*MY(sk)); 266 pvec[slds[sk]] = (values[slds[sk]+2] - perp*px)*uu; 267 } 268 scattering += CALL_IQ(q, q_index, local_values); 269 } 270 if (dd > 1.e-8){ 271 for (int sk=0; sk<NUM_MAGNETIC; sk++) { 272 const double perp = (qy*MX(sk) - qx*MY(sk)); 273 pvec[slds[sk]] = (values[slds[sk]+2] + perp*px)*dd; 274 } 275 scattering += CALL_IQ(q, q_index, local_values); 276 } 277 if (ud > 1.e-8){ 278 for (int sk=0; sk<NUM_MAGNETIC; sk++) { 279 const double perp = (qy*MX(sk) - qx*MY(sk)); 280 pvec[slds[sk]] = perp*py*ud; 281 } 282 scattering += CALL_IQ(q, q_index, local_values); 283 for (int sk=0; sk<NUM_MAGNETIC; sk++) { 284 pvec[slds[sk]] = MZ(sk)*pz*ud; 285 } 286 scattering += CALL_IQ(q, q_index, local_values); 287 } 288 if (du > 1.e-8) { 289 for (int sk=0; sk<NUM_MAGNETIC; sk++) { 290 const double perp = (qy*MX(sk) - qx*MY(sk)); 291 pvec[slds[sk]] = perp*py*du; 292 } 293 scattering += CALL_IQ(q, q_index, local_values); 294 for (int sk=0; sk<NUM_MAGNETIC; sk++) { 295 pvec[slds[sk]] = -MZ(sk)*pz*du; 296 } 297 scattering += CALL_IQ(q, q_index, local_values); 298 } 299 #else // !MAGNETIC 300 const double scattering = CALL_IQ(q, q_index, local_values); 301 #endif // !MAGNETIC 302 //printf("q_index:%d %g %g %g %g\n",q_index, scattering, weight, spherical_correction, weight0); 303 result[q_index] += weight * scattering; 262 304 } 263 305 } 264 p0_index = loop_index%p0_length;265 306 } 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 307 ++step; 308 #if MAX_PD>0 309 if (step >= pd_stop) break; 310 ++i0; 311 } 312 i0 = 0; 313 #endif 314 #if MAX_PD>1 315 if (step >= pd_stop) break; 316 ++i1; 317 } 318 i1 = 0; 319 #endif 320 #if MAX_PD>2 321 if (step >= pd_stop) break; 322 ++i2; 323 } 324 i2 = 0; 325 #endif 326 #if MAX_PD>3 327 if (step >= pd_stop) break; 328 ++i3; 329 } 330 i3 = 0; 331 #endif 332 #if MAX_PD>4 333 if (step >= pd_stop) break; 334 ++i4; 335 } 336 i4 = 0; 337 #endif 338 339 //printf("res: %g/%g\n", result[0], pd_norm); 364 340 // Remember the updated norm. 365 341 result[nq] = pd_norm; 366 #endif // MAX_PD > 0367 342 }
Note: See TracChangeset
for help on using the changeset viewer.