Main Page Namespace List Class Hierarchy Alphabetical List Compound List File List Namespace Members Compound Members File Members Related Pages

MeasureQCP.C

Go to the documentation of this file.
00001 /***************************************************************************
00002 *cr
00003 *cr (C) Copyright 1995-2019 The Board of Trustees of the
00004 *cr University of Illinois
00005 *cr All Rights Reserved
00006 *cr
00007 ***************************************************************************/
00008 
00009 /***************************************************************************
00010 * RCS INFORMATION:
00011 *
00012 * $RCSfile: MeasureQCP.C,v $
00013 * $Author: johns $ $Locker: $ $State: Exp $
00014 * $Revision: 1.35 $ $Date: 2020年10月15日 16:07:31 $
00015 *
00016 ***************************************************************************
00017 * DESCRIPTION:
00018 * Code to compute RMSD values for unaligned structures without 
00019 * actually performing the alginment, particularly useful for 
00020 * computing large dissimilarity matrices required for 
00021 * trajectory clustering analysis
00022 *
00023 ***************************************************************************/
00024 
00025 #include <stdio.h>
00026 #include <stdlib.h>
00027 
00028 #define VMDQCPUSESSE 1
00029 // #define VMDQCPUSEAVX2 1
00030 #if defined(VMDUSEAVX512)
00031 #define VMDQCPUSEAVX512 1
00032 #endif
00033 
00034 #define VMDQCPUSETHRPOOL 1
00035 
00036 #if VMDQCPUSESSE && defined(__SSE2__)
00037 #include <emmintrin.h>
00038 #endif
00039 #if VMDQCPUSEAVX2 && defined(__AVX__) && defined(__AVX2__)
00040 #include <immintrin.h>
00041 #endif
00042 #if VMDQCPUSEAVX512 && defined(__AVX512F__)
00043 #include <immintrin.h>
00044 #endif
00045 #if (defined(VMDQCPUSEVSX) && defined(__VSX__))
00046 #if defined(__GNUC__) && defined(__VEC__)
00047 #include <altivec.h>
00048 #endif
00049 #endif
00050 
00051 #include <math.h>
00052 #include "Measure.h"
00053 #include "AtomSel.h"
00054 #include "utilities.h"
00055 #include "ResizeArray.h"
00056 #include "MoleculeList.h"
00057 #include "Inform.h"
00058 #include "Timestep.h"
00059 #include "CUDAAccel.h"
00060 #include "CUDAMeasureQCP.h"
00061 #include "VMDApp.h"
00062 #include "WKFThreads.h"
00063 #include "WKFUtils.h"
00064 
00065 #if VMDQCPUSEAVX512 && defined(__AVX512F__)
00066 
00067 static double hadd8_m512d(__m512d sum8) {
00068 // __m512d tmp = sum8;
00069 // __m512d hsum4 = _mm512_add_pd(tmp, _mm512_permute2f128_pd(tmp, tmp, 0x1));
00070 // __m512d hsum2 = _mm256_castpd256_pd128(hsum4);
00071 // __m512d sum2 = _mm_hadd_pd(hsum2, hsum2);
00072 // return _mm_cvtsd_f64(sum2);
00073 return 0.0;
00074 }
00075 
00076 
00077 // AVX2 + FMA + 32-byte-aligned SOA-format memory buffers
00078 static double InnerProductSOA_avx512(double *A,
00079 float *crdx1, float *crdy1, float *crdz1,
00080 float *crdx2, float *crdy2, float *crdz2,
00081 const int cnt, const float *weight) {
00082 __m512d va0 = _mm512_set1_pd(0.0);
00083 __m512d va1 = _mm512_set1_pd(0.0);
00084 __m512d va2 = _mm512_set1_pd(0.0);
00085 __m512d va3 = _mm512_set1_pd(0.0);
00086 __m512d va4 = _mm512_set1_pd(0.0);
00087 __m512d va5 = _mm512_set1_pd(0.0);
00088 __m512d va6 = _mm512_set1_pd(0.0);
00089 __m512d va7 = _mm512_set1_pd(0.0);
00090 __m512d va8 = _mm512_set1_pd(0.0);
00091 __m512d vG1 = _mm512_set1_pd(0.0);
00092 __m512d vG2 = _mm512_set1_pd(0.0);
00093 
00094 if (weight != NULL) {
00095 for (int i=0; i<cnt; i+=8) {
00096 __m256 xa8f = _mm256_load_ps(crdx1 + i); // load 8-float vectors
00097 __m256 ya8f = _mm256_load_ps(crdy1 + i);
00098 __m256 za8f = _mm256_load_ps(crdz1 + i);
00099 
00100 __m512d xa8 = _mm512_cvtps_pd(xa8f); // convert from float to doubles
00101 __m512d ya8 = _mm512_cvtps_pd(ya8f);
00102 __m512d za8 = _mm512_cvtps_pd(za8f);
00103 
00104 __m512d gatmp = _mm512_mul_pd(xa8, xa8);
00105 gatmp = _mm512_fmadd_pd(ya8, ya8, gatmp);
00106 gatmp = _mm512_fmadd_pd(za8, za8, gatmp);
00107 
00108 __m256 xb8f = _mm256_load_ps(crdx2 + i); // load 8-float vectors
00109 __m256 yb8f = _mm256_load_ps(crdy2 + i);
00110 __m256 zb8f = _mm256_load_ps(crdz2 + i);
00111 
00112 __m512d xb8 = _mm512_cvtps_pd(xb8f); // convert from float to doubles
00113 __m512d yb8 = _mm512_cvtps_pd(yb8f);
00114 __m512d zb8 = _mm512_cvtps_pd(zb8f);
00115 
00116 __m512d gbtmp = _mm512_mul_pd(xb8, xb8);
00117 gbtmp = _mm512_fmadd_pd(yb8, yb8, gbtmp);
00118 gbtmp = _mm512_fmadd_pd(zb8, zb8, gbtmp);
00119 
00120 __m256 w8f = _mm256_load_ps(weight + i); // load 8-float vector
00121 __m512d w8 = _mm512_cvtps_pd(w8f); // convert from float to double
00122 
00123 vG1 = _mm512_fmadd_pd(w8, gatmp, vG1);
00124 vG2 = _mm512_fmadd_pd(w8, gbtmp, vG2);
00125 
00126 va0 = _mm512_fmadd_pd(xa8, xb8, va0);
00127 va1 = _mm512_fmadd_pd(xa8, yb8, va1);
00128 va2 = _mm512_fmadd_pd(xa8, zb8, va2);
00129 
00130 va3 = _mm512_fmadd_pd(ya8, xb8, va3);
00131 va4 = _mm512_fmadd_pd(ya8, yb8, va4);
00132 va5 = _mm512_fmadd_pd(ya8, zb8, va5);
00133 
00134 va6 = _mm512_fmadd_pd(za8, xb8, va6);
00135 va7 = _mm512_fmadd_pd(za8, yb8, va7);
00136 va8 = _mm512_fmadd_pd(za8, zb8, va8);
00137 }
00138 } else {
00139 for (int i=0; i<cnt; i+=8) {
00140 __m256 xa8f = _mm256_load_ps(crdx1 + i); // load 8-float vectors
00141 __m256 ya8f = _mm256_load_ps(crdy1 + i);
00142 __m256 za8f = _mm256_load_ps(crdz1 + i);
00143 
00144 __m512d xa8 = _mm512_cvtps_pd(xa8f); // convert from float to doubles
00145 __m512d ya8 = _mm512_cvtps_pd(ya8f);
00146 __m512d za8 = _mm512_cvtps_pd(za8f);
00147 
00148 vG1 = _mm512_fmadd_pd(xa8, xa8, vG1);
00149 vG1 = _mm512_fmadd_pd(ya8, ya8, vG1);
00150 vG1 = _mm512_fmadd_pd(za8, za8, vG1);
00151 
00152 __m256 xb8f = _mm256_load_ps(crdx2 + i); // load 8-float vectors
00153 __m256 yb8f = _mm256_load_ps(crdy2 + i);
00154 __m256 zb8f = _mm256_load_ps(crdz2 + i);
00155 
00156 __m512d xb8 = _mm512_cvtps_pd(xb8f); // convert from float to doubles
00157 __m512d yb8 = _mm512_cvtps_pd(yb8f);
00158 __m512d zb8 = _mm512_cvtps_pd(zb8f);
00159 
00160 vG2 = _mm512_fmadd_pd(xb8, xb8, vG2);
00161 vG2 = _mm512_fmadd_pd(yb8, yb8, vG2);
00162 vG2 = _mm512_fmadd_pd(zb8, zb8, vG2);
00163 
00164 va0 = _mm512_fmadd_pd(xa8, xb8, va0);
00165 va1 = _mm512_fmadd_pd(xa8, yb8, va1);
00166 va2 = _mm512_fmadd_pd(xa8, zb8, va2);
00167 
00168 va3 = _mm512_fmadd_pd(ya8, xb8, va3);
00169 va4 = _mm512_fmadd_pd(ya8, yb8, va4);
00170 va5 = _mm512_fmadd_pd(ya8, zb8, va5);
00171 
00172 va6 = _mm512_fmadd_pd(za8, xb8, va6);
00173 va7 = _mm512_fmadd_pd(za8, yb8, va7);
00174 va8 = _mm512_fmadd_pd(za8, zb8, va8);
00175 }
00176 }
00177 
00178 A[0] = hadd8_m512d(va0);
00179 A[1] = hadd8_m512d(va1);
00180 A[2] = hadd8_m512d(va2);
00181 A[3] = hadd8_m512d(va3);
00182 A[4] = hadd8_m512d(va4);
00183 A[5] = hadd8_m512d(va5);
00184 A[6] = hadd8_m512d(va6);
00185 A[7] = hadd8_m512d(va7);
00186 A[8] = hadd8_m512d(va8);
00187 
00188 double G1 = hadd8_m512d(vG1);
00189 double G2 = hadd8_m512d(vG2);
00190 
00191 return (G1 + G2) * 0.5;
00192 }
00193 
00194 #endif
00195 
00196 #if VMDQCPUSEAVX2 && defined(__AVX__) && defined(__AVX2__)
00197 
00198 static double hadd4_m256d(__m256d sum4) {
00199 __m256d tmp = sum4;
00200 __m256d hsum4 = _mm256_add_pd(tmp, _mm256_permute2f128_pd(tmp, tmp, 0x1));
00201 __m128d hsum2 = _mm256_castpd256_pd128(hsum4);
00202 __m128d sum2 = _mm_hadd_pd(hsum2, hsum2);
00203 return _mm_cvtsd_f64(sum2);
00204 }
00205 
00206 
00207 // AVX2 + FMA + 32-byte-aligned SOA-format memory buffers
00208 static double InnerProductSOA_avx2(double *A,
00209 float *crdx1, float *crdy1, float *crdz1,
00210 float *crdx2, float *crdy2, float *crdz2,
00211 const int cnt, const float *weight) {
00212 __m256d va0 = _mm256_set1_pd(0.0);
00213 __m256d va1 = _mm256_set1_pd(0.0);
00214 __m256d va2 = _mm256_set1_pd(0.0);
00215 __m256d va3 = _mm256_set1_pd(0.0);
00216 __m256d va4 = _mm256_set1_pd(0.0);
00217 __m256d va5 = _mm256_set1_pd(0.0);
00218 __m256d va6 = _mm256_set1_pd(0.0);
00219 __m256d va7 = _mm256_set1_pd(0.0);
00220 __m256d va8 = _mm256_set1_pd(0.0);
00221 __m256d vG1 = _mm256_set1_pd(0.0);
00222 __m256d vG2 = _mm256_set1_pd(0.0);
00223 
00224 if (weight != NULL) {
00225 for (int i=0; i<cnt; i+=4) {
00226 __m128 xa4f = _mm_load_ps(crdx1 + i); // load 4-float vectors
00227 __m128 ya4f = _mm_load_ps(crdy1 + i);
00228 __m128 za4f = _mm_load_ps(crdz1 + i);
00229 
00230 __m256d xa4 = _mm256_cvtps_pd(xa4f); // convert from float to doubles
00231 __m256d ya4 = _mm256_cvtps_pd(ya4f);
00232 __m256d za4 = _mm256_cvtps_pd(za4f);
00233 
00234 __m256d gatmp = _mm256_mul_pd(xa4, xa4);
00235 gatmp = _mm256_fmadd_pd(ya4, ya4, gatmp);
00236 gatmp = _mm256_fmadd_pd(za4, za4, gatmp);
00237 
00238 __m128 xb4f = _mm_load_ps(crdx2 + i); // load 4-float vectors
00239 __m128 yb4f = _mm_load_ps(crdy2 + i);
00240 __m128 zb4f = _mm_load_ps(crdz2 + i);
00241 
00242 __m256d xb4 = _mm256_cvtps_pd(xb4f); // convert from float to doubles
00243 __m256d yb4 = _mm256_cvtps_pd(yb4f);
00244 __m256d zb4 = _mm256_cvtps_pd(zb4f);
00245 
00246 __m256d gbtmp = _mm256_mul_pd(xb4, xb4);
00247 gbtmp = _mm256_fmadd_pd(yb4, yb4, gbtmp);
00248 gbtmp = _mm256_fmadd_pd(zb4, zb4, gbtmp);
00249 
00250 __m128 w4f = _mm_load_ps(weight + i); // load 4-float vector
00251 __m256d w4 = _mm256_cvtps_pd(w4f); // convert from float to double
00252 
00253 vG1 = _mm256_fmadd_pd(w4, gatmp, vG1);
00254 vG2 = _mm256_fmadd_pd(w4, gbtmp, vG2);
00255 
00256 va0 = _mm256_fmadd_pd(xa4, xb4, va0);
00257 va1 = _mm256_fmadd_pd(xa4, yb4, va1);
00258 va2 = _mm256_fmadd_pd(xa4, zb4, va2);
00259 
00260 va3 = _mm256_fmadd_pd(ya4, xb4, va3);
00261 va4 = _mm256_fmadd_pd(ya4, yb4, va4);
00262 va5 = _mm256_fmadd_pd(ya4, zb4, va5);
00263 
00264 va6 = _mm256_fmadd_pd(za4, xb4, va6);
00265 va7 = _mm256_fmadd_pd(za4, yb4, va7);
00266 va8 = _mm256_fmadd_pd(za4, zb4, va8);
00267 }
00268 } else {
00269 for (int i=0; i<cnt; i+=4) {
00270 __m128 xa4f = _mm_load_ps(crdx1 + i); // load 4-float vectors
00271 __m128 ya4f = _mm_load_ps(crdy1 + i);
00272 __m128 za4f = _mm_load_ps(crdz1 + i);
00273 
00274 __m256d xa4 = _mm256_cvtps_pd(xa4f); // convert from float to doubles
00275 __m256d ya4 = _mm256_cvtps_pd(ya4f);
00276 __m256d za4 = _mm256_cvtps_pd(za4f);
00277 
00278 vG1 = _mm256_fmadd_pd(xa4, xa4, vG1);
00279 vG1 = _mm256_fmadd_pd(ya4, ya4, vG1);
00280 vG1 = _mm256_fmadd_pd(za4, za4, vG1);
00281 
00282 __m128 xb4f = _mm_load_ps(crdx2 + i); // load 4-float vectors
00283 __m128 yb4f = _mm_load_ps(crdy2 + i);
00284 __m128 zb4f = _mm_load_ps(crdz2 + i);
00285 
00286 __m256d xb4 = _mm256_cvtps_pd(xb4f); // convert from float to doubles
00287 __m256d yb4 = _mm256_cvtps_pd(yb4f);
00288 __m256d zb4 = _mm256_cvtps_pd(zb4f);
00289 
00290 vG2 = _mm256_fmadd_pd(xb4, xb4, vG2);
00291 vG2 = _mm256_fmadd_pd(yb4, yb4, vG2);
00292 vG2 = _mm256_fmadd_pd(zb4, zb4, vG2);
00293 
00294 va0 = _mm256_fmadd_pd(xa4, xb4, va0);
00295 va1 = _mm256_fmadd_pd(xa4, yb4, va1);
00296 va2 = _mm256_fmadd_pd(xa4, zb4, va2);
00297 
00298 va3 = _mm256_fmadd_pd(ya4, xb4, va3);
00299 va4 = _mm256_fmadd_pd(ya4, yb4, va4);
00300 va5 = _mm256_fmadd_pd(ya4, zb4, va5);
00301 
00302 va6 = _mm256_fmadd_pd(za4, xb4, va6);
00303 va7 = _mm256_fmadd_pd(za4, yb4, va7);
00304 va8 = _mm256_fmadd_pd(za4, zb4, va8);
00305 }
00306 }
00307 
00308 A[0] = hadd4_m256d(va0);
00309 A[1] = hadd4_m256d(va1);
00310 A[2] = hadd4_m256d(va2);
00311 A[3] = hadd4_m256d(va3);
00312 A[4] = hadd4_m256d(va4);
00313 A[5] = hadd4_m256d(va5);
00314 A[6] = hadd4_m256d(va6);
00315 A[7] = hadd4_m256d(va7);
00316 A[8] = hadd4_m256d(va8);
00317 
00318 double G1 = hadd4_m256d(vG1);
00319 double G2 = hadd4_m256d(vG2);
00320 
00321 return (G1 + G2) * 0.5;
00322 }
00323 
00324 #endif
00325 
00326 
00327 // plain C++ version of inner product for SOA coordinate storage
00328 static double InnerProductSOA(double *A,
00329 float *crdx1, float *crdy1, float *crdz1,
00330 float *crdx2, float *crdy2, float *crdz2,
00331 const int cnt, const float *weight) {
00332 double G1=0.0, G2 = 0.0;
00333 memset(A, 0, sizeof(double) * 9);
00334 
00335 double x1, x2, y1, y2, z1, z2;
00336 double a0, a1, a2, a3, a4, a5, a6, a7, a8;
00337 a0=a1=a2=a3=a4=a5=a6=a7=a8=0.0;
00338 if (weight != NULL) {
00339 for (int i=0; i<cnt; i++) {
00340 double w = weight[i];
00341 x1 = crdx1[i];
00342 y1 = crdy1[i];
00343 z1 = crdz1[i];
00344 
00345 G1 += w * (x1*x1 + y1*y1 + z1*z1);
00346 
00347 x2 = crdx2[i];
00348 y2 = crdy2[i];
00349 z2 = crdz2[i];
00350 
00351 G2 += w * (x2*x2 + y2*y2 + z2*z2);
00352 
00353 a0 += x1 * x2;
00354 a1 += x1 * y2;
00355 a2 += x1 * z2;
00356 
00357 a3 += y1 * x2;
00358 a4 += y1 * y2;
00359 a5 += y1 * z2;
00360 
00361 a6 += z1 * x2;
00362 a7 += z1 * y2;
00363 a8 += z1 * z2;
00364 }
00365 } else {
00366 for (int i=0; i<cnt; i++) {
00367 x1 = crdx1[i];
00368 y1 = crdy1[i];
00369 z1 = crdz1[i];
00370 
00371 G1 += x1*x1 + y1*y1 + z1*z1;
00372 
00373 x2 = crdx2[i];
00374 y2 = crdy2[i];
00375 z2 = crdz2[i];
00376 
00377 G2 += x2*x2 + y2*y2 + z2*z2;
00378 
00379 a0 += x1 * x2;
00380 a1 += x1 * y2;
00381 a2 += x1 * z2;
00382 
00383 a3 += y1 * x2;
00384 a4 += y1 * y2;
00385 a5 += y1 * z2;
00386 
00387 a6 += z1 * x2;
00388 a7 += z1 * y2;
00389 a8 += z1 * z2;
00390 }
00391 }
00392 
00393 A[0] = a0;
00394 A[1] = a1;
00395 A[2] = a2;
00396 
00397 A[3] = a3;
00398 A[4] = a4;
00399 A[5] = a5;
00400 
00401 A[6] = a6;
00402 A[7] = a7;
00403 A[8] = a8;
00404 
00405 return (G1 + G2) * 0.5;
00406 }
00407 
00408 //
00409 // OpenACC version of inner product for SOA coordinate storage
00410 //
00411 // use pgc++ -m64 -Minfo=accel -ta=nvidia -O -acc
00412 #if defined(__PGIC__) && defined(_OPENACC)
00413 
00414 #if 0
00415 static void vecadd_acc(void) {
00416 printf("****** OpenACC test vecadd_acc()...\n");
00417 
00418 // Size of vectors
00419 int n = 10000;
00420 
00421 // Input vectors
00422 double *restrict a;
00423 double *restrict b;
00424 
00425 // Output vector
00426 double *restrict c;
00427 
00428 // Size, in bytes, of each vector
00429 size_t bytes = n*sizeof(double);
00430 
00431 // Allocate memory for each vector
00432 a = (double*)malloc(bytes);
00433 b = (double*)malloc(bytes);
00434 c = (double*)malloc(bytes);
00435 
00436 // Initialize content of input vectors, vector a[i] = sin(i)^2 vector b[i] = cos(i)^2
00437 int i;
00438 for (i=0; i<n; i++) {
00439 a[i] = sin(i)*sin(i);
00440 b[i] = cos(i)*cos(i);
00441 } 
00442 
00443 // sum component wise and save result into vector c
00444 #pragma acc kernels copyin(a[0:n],b[0:n]), copyout(c[0:n])
00445 for (i=0; i<n; i++) {
00446 c[i] = a[i] + b[i];
00447 }
00448 
00449 // Sum up vector c and print result divided by n, this should equal 1 within error
00450 double sum = 0.0;
00451 for(i=0; i<n; i++) {
00452 sum += c[i];
00453 }
00454 sum = sum/n;
00455 printf("****** final result: %f *******\n", sum);
00456 
00457 // Release memory
00458 free(a);
00459 free(b);
00460 free(c);
00461 
00462 printf("****** OpenACC test vecadd_acc() done.\n");
00463 }
00464 #endif
00465 
00466 //
00467 // Use 1-D loop rather than 2-D to please PGI OpenACC so it doesn't
00468 // complain about loop-carried dependencies, this appears to be the only
00469 // successful method to achieve a good quality parallelization.
00470 //
00471 #define LOOP1D 1
00472 
00473 #if defined(LOOP1D)
00474 #if defined(__PGIC__) && defined(_OPENACC)
00475 #pragma acc routine seq
00476 #endif
00477 static void acc_idx2sub_tril(long N, long ind, long *J, long *I) {
00478 long i, j;
00479 i = long(floor((2*N+1 - sqrt((2*N+1)*(2*N+1) - 8*ind)) / 2));
00480 j = ind - N*i + i*(i-1)/2 + i;
00481 
00482 *I = i;
00483 *J = j;
00484 }
00485 #endif
00486 
00487 static void rmsdmat_qcp_acc(int cnt, int padcnt, int framecrdsz, 
00488 int framecount, 
00489 #if defined(LOOP1D)
00490 const float * restrict crds, 
00491 #else
00492 const float * crds, 
00493 #endif
00494 // const float *weight,
00495 float * rmsdmat) {
00496 printf("OpenACC rmsdmat_qcp_acc()...\n");
00497 printf("ACC cnt: %d padcnt: %d\n", cnt, padcnt);
00498 
00499 printf("Copying input arrays to accelerators...\n");
00500 long totalsz = 3L * framecrdsz * framecount;
00501 printf("ACC copysz: %ld (3 * %d * %d)\n", totalsz, framecrdsz, framecount);
00502 
00503 long matcnt = framecount * framecount;
00504 printf("ACC matcnt: %ld\n", matcnt);
00505 
00506 printf("Running OpenACC kernels...\n");
00507 #if defined(LOOP1D)
00508 long i, j, k;
00509 #pragma acc kernels copyin(crds[0:totalsz]), copy(rmsdmat[0:matcnt])
00510 for (k=0; k<(framecount*(framecount-1))/2; k++) {
00511 acc_idx2sub_tril(long(framecount-1), k, &i, &j);
00512 long x1addr = j * 3L * framecrdsz;
00513 {
00514 #else
00515 long i, j;
00516 #pragma acc kernels copyin(crds[0:totalsz]), copy(rmsdmat[0:matcnt])
00517 for (j=0; j<framecount; j++) {
00518 long x1addr = j * 3L * framecrdsz;
00519 
00520 for (i=0; i<j; i++) {
00521 #endif
00522 // calculate the (weighted) inner product of two structures
00523 long x2addr = i * 3L * framecrdsz;
00524 
00525 double G1=0.0, G2=0.0;
00526 
00527 double a0, a1, a2, a3, a4, a5, a6, a7, a8;
00528 a0=a1=a2=a3=a4=a5=a6=a7=a8=0.0;
00529 #if 0
00530 if (weight != NULL) {
00531 double x1, x2, y1, y2, z1, z2;
00532 #pragma acc loop
00533 for (long l=0; l<cnt; l++) {
00534 double w = weight[l];
00535 x1 = crds[l + x1addr];
00536 y1 = crds[l + x1addr + framecrdsz];
00537 z1 = crds[l + x1addr + framecrdsz*2];
00538 
00539 G1 += w * (x1*x1 + y1*y1 + z1*z1);
00540 
00541 x2 = crds[l + x2addr];
00542 y2 = crds[l + x2addr + framecrdsz];
00543 z2 = crds[l + x2addr + framecrdsz*2];
00544 
00545 G2 += w * (x2*x2 + y2*y2 + z2*z2);
00546 
00547 a0 += x1 * x2;
00548 a1 += x1 * y2;
00549 a2 += x1 * z2;
00550 
00551 a3 += y1 * x2;
00552 a4 += y1 * y2;
00553 a5 += y1 * z2;
00554 
00555 a6 += z1 * x2;
00556 a7 += z1 * y2;
00557 a8 += z1 * z2;
00558 }
00559 } else {
00560 #endif
00561 double x1, x2, y1, y2, z1, z2;
00562 #pragma acc loop vector(256)
00563 //#pragma acc loop vector(256) reduction(+:a0),reduction(+:a1),reduction(+:a2),reduction(+:a3),reduction(+:a4),reduction(+:a5),reduction(+:a6),reduction(+:a7),reduction(+:a8),reduction(+:G1),reduction(+:G2)
00564 for (long l=0; l<cnt; l++) {
00565 x1 = crds[l + x1addr];
00566 y1 = crds[l + x1addr + framecrdsz];
00567 z1 = crds[l + x1addr + framecrdsz*2];
00568 
00569 G1 += x1*x1 + y1*y1 + z1*z1;
00570 
00571 x2 = crds[l + x2addr];
00572 y2 = crds[l + x2addr + framecrdsz];
00573 z2 = crds[l + x2addr + framecrdsz*2];
00574 
00575 G2 += x2*x2 + y2*y2 + z2*z2;
00576 
00577 a0 += x1 * x2;
00578 a1 += x1 * y2;
00579 a2 += x1 * z2;
00580 
00581 a3 += y1 * x2;
00582 a4 += y1 * y2;
00583 a5 += y1 * z2;
00584 
00585 a6 += z1 * x2;
00586 a7 += z1 * y2;
00587 a8 += z1 * z2;
00588 }
00589 #if 0
00590 }
00591 #endif
00592 
00593 double A[9];
00594 A[0] = a0;
00595 A[1] = a1;
00596 A[2] = a2;
00597 
00598 A[3] = a3;
00599 A[4] = a4;
00600 A[5] = a5;
00601 
00602 A[6] = a6;
00603 A[7] = a7;
00604 A[8] = a8;
00605 
00606 double E0 = (G1 + G2) * 0.5;
00607 
00608 // calculate the RMSD & rotational matrix
00609 float rmsd;
00610 FastCalcRMSDAndRotation(NULL, A, &rmsd, E0, cnt, -1);
00611 #if defined(LOOP1D)
00612 rmsdmat[k]=rmsd; // store linearized triangle
00613 #else
00614 rmsdmat[j*framecount + i]=rmsd;
00615 #endif
00616 }
00617 }
00618 
00619 printf("ACC done.\n");
00620 }
00621 
00622 #endif
00623 
00624 
00625 #if 0
00626 static double InnerProductAOS(double *A, double *coords1, double *coords2,
00627 const int cnt, const double *weight) {
00628 double G1=0.0, G2=0.0;
00629 memset(A, 0, sizeof(double) * 9);
00630 
00631 long i;
00632 double x1, x2, y1, y2, z1, z2;
00633 if (weight != NULL) {
00634 for (i=0; i<cnt; i++) {
00635 double w = weight[i];
00636 long idx = i*3;
00637 x1 = coords1[idx ];
00638 y1 = coords1[idx+1];
00639 z1 = coords1[idx+2];
00640 
00641 G1 += w * (x1*x1 + y1*y1 + z1*z1);
00642 
00643 x2 = coords2[idx ];
00644 y2 = coords2[idx+1];
00645 z2 = coords2[idx+2];
00646 
00647 G2 += w * (x2*x2 + y2*y2 + z2*z2);
00648 
00649 A[0] += (x1 * x2);
00650 A[1] += (x1 * y2);
00651 A[2] += (x1 * z2);
00652 
00653 A[3] += (y1 * x2);
00654 A[4] += (y1 * y2);
00655 A[5] += (y1 * z2);
00656 
00657 A[6] += (z1 * x2);
00658 A[7] += (z1 * y2);
00659 A[8] += (z1 * z2);
00660 }
00661 } else {
00662 for (i=0; i<cnt; i++) {
00663 long idx = i*3;
00664 x1 = coords1[idx ];
00665 y1 = coords1[idx+1];
00666 z1 = coords1[idx+2];
00667 
00668 G1 += x1*x1 + y1*y1 + z1*z1;
00669 
00670 x2 = coords2[idx ];
00671 y2 = coords2[idx+1];
00672 z2 = coords2[idx+2];
00673 
00674 G2 += x2*x2 + y2*y2 + z2*z2;
00675 
00676 A[0] += (x1 * x2);
00677 A[1] += (x1 * y2);
00678 A[2] += (x1 * z2);
00679 
00680 A[3] += (y1 * x2);
00681 A[4] += (y1 * y2);
00682 A[5] += (y1 * z2);
00683 
00684 A[6] += (z1 * x2);
00685 A[7] += (z1 * y2);
00686 A[8] += (z1 * z2);
00687 }
00688 }
00689 
00690 return (G1 + G2) * 0.5;
00691 }
00692 #endif
00693 
00694 
00695 void com_soa(int cnt, 
00696 float *&soax, float *&soay, float *&soaz,
00697 double &comx, double &comy, double &comz,
00698 const float *weight) {
00699 comx=comy=comz=0.0;
00700 
00701 if (weight != NULL) {
00702 double wsum = 0.0;
00703 
00704 // compute weighted center of mass
00705 int i;
00706 for (i=0; i<cnt; i++) {
00707 double w = weight[i];
00708 wsum += w;
00709 
00710 comx += soax[i] * w;
00711 comy += soay[i] * w;
00712 comz += soaz[i] * w;
00713 }
00714 double wsumnorm = 1.0 / wsum;
00715 comx *= wsumnorm; 
00716 comy *= wsumnorm;
00717 comz *= wsumnorm;
00718 } else {
00719 // compute unweighted center of mass
00720 int i;
00721 for (i=0; i<cnt; i++) {
00722 comx += soax[i];
00723 comy += soay[i];
00724 comz += soaz[i];
00725 }
00726 double avenorm = 1.0 / ((double) cnt);
00727 comx *= avenorm; 
00728 comy *= avenorm;
00729 comz *= avenorm;
00730 }
00731 }
00732 
00733 
00734 
00735 int center_convert_soa(const AtomSel *sel, int num, const float *framepos,
00736 const float *weight, 
00737 float *&soax, float *&soay, float *&soaz) {
00738 // allocate temporary working arrays, plus required SIMD padding
00739 int cnt = sel->selected;
00740 soax = (float *) calloc(1, (cnt + 16)*sizeof(float));
00741 soay = (float *) calloc(1, (cnt + 16)*sizeof(float));
00742 soaz = (float *) calloc(1, (cnt + 16)*sizeof(float));
00743 
00744 int selind = sel->firstsel; // start from the first selected atom
00745 double comx=0.0, comy=0.0, comz=0.0;
00746 
00747 int i;
00748 for (i=0; i<cnt; i++) {
00749 // find next 'on' atom in selection
00750 // loop is safe since we already stop the on cnt > 0 above
00751 while (!sel->on[selind])
00752 selind++;
00753 
00754 // compact selection and convert AOS to SOA storage on-the-fly
00755 long addr = 3*selind;
00756 float tx = framepos[addr ];
00757 float ty = framepos[addr + 1];
00758 float tz = framepos[addr + 2];
00759 
00760 comx += tx;
00761 comy += ty;
00762 comz += tz;
00763 
00764 soax[i] = tx;
00765 soay[i] = ty; 
00766 soaz[i] = tz;
00767 
00768 selind++; // advance to next atom
00769 }
00770 
00771 double avenorm = 1.0 / ((double) cnt);
00772 comx *= avenorm; // compute unweighted center of mass
00773 comy *= avenorm;
00774 comz *= avenorm;
00775 
00776 #if 0
00777 printf("center_convert_soa(): structure com: %g %g %g\n", comx, comy, comz);
00778 #endif
00779 
00780 // translate center of mass to the origin
00781 for (i=0; i<cnt; i++) {
00782 soax[i] -= float(comx);
00783 soay[i] -= float(comy);
00784 soaz[i] -= float(comz);
00785 }
00786 
00787 #if 0
00788 // check post-translation com 
00789 com_soa(cnt, soax, soay, soaz, comx, comy, comz, weight); 
00790 printf("center_convert_soa(): centered com: %lg %lg %lg\n", comx, comy, comz);
00791 #endif 
00792 
00793 return 0;
00794 }
00795 
00796 
00797 int center_convert_single_soa(const AtomSel *sel, int num, 
00798 const float *framepos,
00799 const float *weight, 
00800 float *soax, float *soay, float *soaz) {
00801 // allocate temporary working arrays, plus required SIMD padding
00802 int cnt = sel->selected;
00803 int selind = sel->firstsel; // start from the first selected atom
00804 double comx=0.0, comy=0.0, comz=0.0;
00805 
00806 int i;
00807 for (i=0; i<cnt; i++) {
00808 // find next 'on' atom in selection
00809 // loop is safe since we already stop the on cnt > 0 above
00810 while (!sel->on[selind])
00811 selind++;
00812 
00813 // compact selection and convert AOS to SOA storage on-the-fly
00814 long addr = 3*selind;
00815 float tx = framepos[addr ];
00816 float ty = framepos[addr + 1];
00817 float tz = framepos[addr + 2];
00818 
00819 comx += tx;
00820 comy += ty;
00821 comz += tz;
00822 
00823 soax[i] = tx;
00824 soay[i] = ty; 
00825 soaz[i] = tz;
00826 
00827 selind++; // advance to next atom
00828 }
00829 
00830 double avenorm = 1.0 / ((double) cnt);
00831 comx *= avenorm; // compute unweighted center of mass
00832 comy *= avenorm;
00833 comz *= avenorm;
00834 
00835 // translate center of mass to the origin
00836 for (i=0; i<cnt; i++) {
00837 soax[i] -= float(comx);
00838 soay[i] -= float(comy);
00839 soaz[i] -= float(comz);
00840 }
00841 
00842 return 0;
00843 }
00844 
00845 
00846 int measure_rmsd_qcp(VMDApp *app,
00847 const AtomSel *sel1, const AtomSel *sel2,
00848 int num, const float *framepos1, const float *framepos2,
00849 float *weight, float *rmsd) {
00850 if (!sel1 || !sel2) return MEASURE_ERR_NOSEL;
00851 if (sel1->selected < 1 || sel2->selected < 1) return MEASURE_ERR_NOSEL;
00852 if (!weight || !rmsd) return MEASURE_ERR_NOWEIGHT;
00853 
00854 // the number of selected atoms must be the same
00855 if (sel1->selected != sel2->selected) return MEASURE_ERR_MISMATCHEDCNT;
00856 
00857 #if 0
00858 // need to know how to traverse the list of weights
00859 // there could be 1 weight per atom (sel_flg == 1) or
00860 // 1 weight per selected atom (sel_flg == 0)
00861 int sel_flg;
00862 if (num == sel1->num_atoms) {
00863 sel_flg = 1; // using all elements
00864 } else {
00865 sel_flg = 0; // using elements from selection
00866 }
00867 #endif
00868 
00869 //
00870 // compute CoM for each selection while copying them into target bufs 
00871 //
00872 float *sel1x, *sel1y, *sel1z, *sel2x, *sel2y, *sel2z;
00873 center_convert_soa(sel1, num, framepos1, weight, sel1x, sel1y, sel1z);
00874 center_convert_soa(sel2, num, framepos2, weight, sel2x, sel2y, sel2z);
00875 
00876 // calculate the (weighted) inner product of two structures
00877 double E0 = 0;
00878 double A[9];
00879 E0 = InnerProductSOA(A, 
00880 sel1x, sel1y, sel1z,
00881 sel2x, sel2y, sel2z,
00882 sel1->selected, NULL /* weight */);
00883 
00884 #if 0
00885 printf("QCP inner product results:\n");
00886 printf(" E0: %g\n", E0);
00887 int i;
00888 for (i=0; i<9; i+=3) 
00889 printf("A[%d-%d]: %g %g %g\n", i, i+2, A[i], A[i+1], A[i+2]);
00890 printf("\n");
00891 #endif
00892 
00893 // calculate the RMSD & rotational matrix
00894 FastCalcRMSDAndRotation(NULL, A, rmsd, E0, sel1->selected, -1);
00895 
00896 free(sel1x);
00897 free(sel1y);
00898 free(sel1z);
00899 
00900 free(sel2x);
00901 free(sel2y);
00902 free(sel2z);
00903 
00904 return MEASURE_NOERR; // and say rmsd is OK
00905 }
00906 
00907 
00908 #if 0
00909 // compute linear array index from lower-triangular indices i,j 
00910 static int sub2idx_tril(long N, long i, long j, long *ind) {
00911 // *ind = i + (j-1)*N - j*(j-1)/2;
00912 *ind = j + N*i - i*(i-1)/2;
00913 return 0;
00914 }
00915 #endif
00916 
00917 // compute lower-triangular indices i,j from linear array index
00918 static int idx2sub_tril(long N, long ind, long *J, long *I) {
00919 long i, j;
00920 
00921 if (ind > (N*(N+1)/2)) {
00922 return -1; // out of bounds
00923 }
00924 
00925 // XXX deal with ambiguous types for sqrt() on Solaris
00926 double tmp2np1 = 2*N+1;
00927 i = long(floor((tmp2np1 - sqrt(tmp2np1*tmp2np1 - 8.0*ind)) / 2));
00928 // i = long(floor((2*N+1 - sqrt((2*N+1)*(2*N+1) - 8*ind)) / 2));
00929 j = ind - N*i + i*(i-1)/2 + i;
00930 
00931 *I = i;
00932 *J = j+1;
00933 
00934 return 0;
00935 }
00936 
00937 
00938 typedef struct {
00939 const AtomSel *sel;
00940 int first;
00941 int last;
00942 int step;
00943 float *rmsdmat;
00944 int padcnt;
00945 int framecrdsz;
00946 float *crds;
00947 #if (VMDQCPUSEAVX512 && defined(__AVX512F__))
00948 int useavx512;
00949 #endif
00950 #if (VMDQCPUSESSE && defined(__SSE2__)) || (VMDQCPUSEAVX2 && defined(__AVX__) && defined(__AVX2__))
00951 int useavx2;
00952 #endif
00953 #if (VMDQCPUSEVSX && defined(__VEC__))
00954 int usevsx;
00955 #endif
00956 } qcprmsdthreadparms;
00957 
00958 
00959 static void * measure_rmsdmat_qcp_thread(void *voidparms) {
00960 int threadid;
00961 qcprmsdthreadparms *parms = NULL;
00962 #if defined(VMDQCPUSETHRPOOL)
00963 wkf_threadpool_worker_getdata(voidparms, (void **) &parms);
00964 wkf_threadpool_worker_getid(voidparms, &threadid, NULL);
00965 #else
00966 wkf_threadlaunch_getdata(voidparms, (void **) &parms);
00967 wkf_threadlaunch_getid(voidparms, &threadid, NULL);
00968 #endif
00969 
00970 //
00971 // copy in per-thread parameters
00972 //
00973 const AtomSel *sel = parms->sel;
00974 float *rmsdmat = parms->rmsdmat;
00975 
00976 // XXX array padding not universally honored yet...
00977 // int padcnt = parms->padcnt;
00978 
00979 int framecrdsz = parms->framecrdsz;
00980 float *crds = parms->crds;
00981 int first = parms->first;
00982 int last = parms->last;
00983 int step = parms->step;
00984 #if VMDQCPUSEAVX2 && defined(__AVX__) && defined(__AVX2__)
00985 int useavx2 = parms->useavx2;
00986 #endif
00987 #if (VMDQCPUSEAVX512 && defined(__AVX512F__))
00988 int useavx512 = parms->useavx512;
00989 #endif
00990 #if (VMDQCPUSEVSX && defined(__VEC__))
00991 int usevsx = parms->usevsx;
00992 #endif
00993 
00994 #if 0
00995 printf("qcpthread[%d] running... %s\n", threadid, 
00996 #if (VMDQCPUSEAVX512 && defined(__AVX512F__))
00997 (useavx512) ? "(AVX512)" : "(C++)");
00998 #elif (VMDQCPUSESSE && defined(__SSE2__)) || (VMDQCPUSEAVX2 && defined(__AVX__) && defined(__AVX2__))
00999 (useavx2) ? "(AVX2)" : "(C++)");
01000 #elif (VMDQCPUSEVSX && defined(__VEC__))
01001 (useavsx) ? "(VSX)" : "(C++)");
01002 #else 
01003 "(C++)");
01004 #endif
01005 #endif
01006 
01007 int framecount = (last - first + 1) / step;
01008 
01009 wkf_tasktile_t tile;
01010 #if defined(VMDQCPUSETHRPOOL)
01011 while (wkf_threadpool_next_tile(voidparms, 1, &tile) != WKF_SCHED_DONE) {
01012 #else
01013 while (wkf_threadlaunch_next_tile(voidparms, 8, &tile) != WKF_SCHED_DONE) {
01014 #endif
01015 long idx;
01016 
01017 for (idx=tile.start; idx<tile.end; idx++) {
01018 long i, j;
01019 
01020 // compute i,j from idx...
01021 // only compute off-diagonal elements, so we use (framecount-1)
01022 if (idx2sub_tril(framecount-1, idx, &i, &j)) {
01023 printf("qcpthread[%d]: work idx %ld out of triangle!\n", threadid, idx);
01024 break;
01025 }
01026 
01027 // calculate the (weighted) inner product of two structures
01028 double A[9];
01029 double E0 = 0;
01030 
01031 float *xj = crds + (j * 3 * framecrdsz);
01032 float *yj = xj + framecrdsz;
01033 float *zj = xj + framecrdsz*2;
01034 
01035 float *xi = crds + (i * 3 * framecrdsz);
01036 float *yi = xi + framecrdsz;
01037 float *zi = xi + framecrdsz*2;
01038 
01039 #if VMDQCPUSEAVX512 && defined(__AVX512F__)
01040 if (useavx512) {
01041 E0 = InnerProductSOA_avx512(A, xj, yj, zj, xi, yi, zi,
01042 sel->selected, NULL /* weight */);
01043 } else 
01044 #endif
01045 #if VMDQCPUSEAVX2 && defined(__AVX__) && defined(__AVX2__)
01046 if (useavx2) {
01047 E0 = InnerProductSOA_avx2(A, xj, yj, zj, xi, yi, zi,
01048 sel->selected, NULL /* weight */);
01049 } else 
01050 #endif
01051 E0 = InnerProductSOA(A, xj, yj, zj, xi, yi, zi, 
01052 sel->selected, NULL /* weight */);
01053 
01054 // calculate the RMSD & rotational matrix
01055 FastCalcRMSDAndRotation(NULL, A, &rmsdmat[j*framecount + i], 
01056 E0, sel->selected, -1);
01057 
01058 // reflect the outcome of the lower triangle into the upper triangle
01059 rmsdmat[i*framecount + j] = rmsdmat[j*framecount + i];
01060 } 
01061 }
01062 
01063 return NULL;
01064 }
01065 
01066 
01067 
01068 int measure_rmsdmat_qcp(VMDApp *app,
01069 const AtomSel *sel, MoleculeList *mlist,
01070 int num, float *weight, 
01071 int first, int last, int step,
01072 float *rmsdmat) {
01073 if (!sel) return MEASURE_ERR_NOSEL;
01074 if (sel->selected < 1) return MEASURE_ERR_NOSEL;
01075 // if (!weight || !rmsd) return MEASURE_ERR_NOWEIGHT;
01076 
01077 Molecule *mymol = mlist->mol_from_id(sel->molid());
01078 int maxframes = mymol->numframes();
01079 
01080 // accept value of -1 meaning "all" frames
01081 if (last == -1)
01082 last = maxframes-1;
01083 
01084 if (maxframes == 0 || first < 0 || first > last ||
01085 last >= maxframes || step <= 0)
01086 return MEASURE_ERR_BADFRAMERANGE;
01087 
01088 // XXX replace with calls to centralized control system
01089 #if (VMDQCPUSESSE && defined(__SSE2__)) || (VMDQCPUSEAVX2 && defined(__AVX__) && defined(__AVX2__))
01090 // XXX there's no SSE-specific code path 
01091 // int usesse=1;
01092 // if (getenv("VMDNOSSE")) {
01093 // usesse=0;
01094 // }
01095 #endif
01096 #if (VMDQCPUSESSE && defined(__SSE2__)) || (VMDQCPUSEAVX2 && defined(__AVX__) && defined(__AVX2__))
01097 int useavx2=1;
01098 if (getenv("VMDNOAVX2")) {
01099 useavx2=0;
01100 }
01101 #endif
01102 #if (VMDQCPUSEAVX512 && defined(__AVX512F__))
01103 int useavx512=1;
01104 if (getenv("VMDNOAVX512")) {
01105 useavx512=0;
01106 }
01107 #endif
01108 #if (VMDQCPUSEVSX && defined(__VEC__))
01109 int usevsx=1;
01110 if (getenv("VMDNOVSX")) {
01111 usevsx=0;
01112 }
01113 #endif
01114 
01115 #if 0
01116 // need to know how to traverse the list of weights
01117 // there could be 1 weight per atom (sel_flg == 1) or
01118 // 1 weight per selected atom (sel_flg == 0)
01119 int sel_flg;
01120 if (num == sel->num_atoms) {
01121 sel_flg = 1; // using all elements
01122 } else {
01123 sel_flg = 0; // using elements from selection
01124 }
01125 #endif
01126 
01127 // start timers
01128 wkf_timerhandle timer;
01129 timer=wkf_timer_create();
01130 wkf_timer_start(timer);
01131 
01132 
01133 //
01134 // compute CoM for frame/selection while copying them into SOA target bufs 
01135 //
01136 int framecount = (last - first + 1) / step;
01137 
01138 int padcnt = (num + 255) & ~255;
01139 int framecrdsz = padcnt + 256;
01140 float *crds = (float *) calloc(1, (framecount * 3L * framecrdsz + 256) * sizeof(float));
01141 
01142 int frame;
01143 for (frame=first; frame<=last; frame+=step) {
01144 const float *framepos = (mymol->get_frame(frame))->pos;
01145 float *xc = crds + (frame * 3L * framecrdsz);
01146 float *yc = xc + framecrdsz;
01147 float *zc = xc + framecrdsz*2;
01148 
01149 center_convert_single_soa(sel, num, framepos, weight, xc, yc, zc);
01150 }
01151 
01152 double converttime = wkf_timer_timenow(timer);
01153 
01154 #if !(defined(__PGIC__) && defined(_OPENACC))
01155 #if defined(VMDTHREADS)
01156 int numprocs = wkf_thread_numprocessors();
01157 #else
01158 int numprocs = 1;
01159 #endif
01160 
01161 //
01162 // copy in per-thread parameters
01163 //
01164 qcprmsdthreadparms parms;
01165 memset(&parms, 0, sizeof(parms));
01166 parms.sel = sel;
01167 parms.rmsdmat = rmsdmat;
01168 parms.padcnt = padcnt;
01169 parms.framecrdsz = framecrdsz;
01170 parms.crds = crds;
01171 parms.first = first;
01172 parms.last = last;
01173 parms.step = step;
01174 #if (VMDQCPUSESSE && defined(__SSE2__)) || (VMDQCPUSEAVX2 && defined(__AVX__) && defined(__AVX2__))
01175 parms.useavx2 = useavx2;
01176 #endif
01177 #if (VMDQCPUSEAVX512 && defined(__AVX512F__))
01178 parms.useavx512 = useavx512;
01179 #endif
01180 #if (VMDQCPUSEVSX && defined(__VEC__))
01181 parms.usevsx = usevsx;
01182 #endif
01183 
01184 // spawn child threads to do the work
01185 wkf_tasktile_t tile;
01186 tile.start=0;
01187 tile.end=(framecount-1)*(framecount-1)/2; // only compute off-diag elements
01188 
01189 #if defined(VMDORBUSETHRPOOL)
01190 wkf_threadpool_sched_dynamic(app->thrpool, &tile);
01191 rc = wkf_threadpool_launch(app->thrpool, measure_rmsdmat_qcp_thread, &parms, 1);
01192 #else
01193 wkf_threadlaunch(numprocs, &parms, measure_rmsdmat_qcp_thread, &tile);
01194 #endif
01195 #elif defined(__PGIC__) && defined(_OPENACC)
01196 // OpenACC variant
01197 rmsdmat_qcp_acc(sel->selected, padcnt, framecrdsz, framecount, crds, 
01198 // NULL /* weight */, 
01199 rmsdmat);
01200 #else
01201 int i, j;
01202 for (j=0; j<framecount; j++) {
01203 float *xj = crds + (j * 3 * framecrdsz);
01204 float *yj = xj + framecrdsz;
01205 float *zj = xj + framecrdsz*2;
01206 for (i=0; i<j; i++) {
01207 // calculate the (weighted) inner product of two structures
01208 double A[9];
01209 
01210 float *xi = crds + (i * 3 * framecrdsz);
01211 float *yi = xi + framecrdsz;
01212 float *zi = xi + framecrdsz*2;
01213 
01214 double E0 = InnerProductSOA(A, xj, yj, zj, xi, yi, zi, 
01215 sel->selected, NULL /* weight */);
01216 
01217 // calculate the RMSD & rotational matrix
01218 FastCalcRMSDAndRotation(NULL, A, &rmsdmat[j*framecount + i], 
01219 E0, sel->selected, -1);
01220 
01221 // reflect the outcome of the lower triangle into the upper triangle
01222 rmsdmat[i*framecount + j] = rmsdmat[j*framecount + i];
01223 }
01224 }
01225 #endif
01226 
01227 // mark all self-RMSDs with a value of 0.0
01228 for (long l=0; l<framecount; l++) {
01229 rmsdmat[l*framecount + l] = 0.0;
01230 }
01231 
01232 double rmsdtime = wkf_timer_timenow(timer) - converttime;
01233 
01234 // free all temporary buffers
01235 free(crds);
01236 
01237 #if 1
01238 double totaltime = wkf_timer_timenow(timer);
01239 printf("QCP RMSD Matrix calculation time: SOA selection: %.3f RMSD solve: %.3f total: %.3f\n", converttime, rmsdtime, totaltime); 
01240 #endif
01241 
01242 wkf_timer_destroy(timer);
01243 
01244 return MEASURE_NOERR; // and say rmsd is OK
01245 }
01246 
01247 
01248 
01249 int measure_rmsdmat_qcp_ooc(VMDApp *app,
01250 const AtomSel *sel, MoleculeList *mlist,
01251 int nfiles, const char **trjfileset,
01252 int num, float *weight, 
01253 int first, int last, int step,
01254 int &framecount, float *&rmsdmat) {
01255 if (!sel) return MEASURE_ERR_NOSEL;
01256 if (sel->selected < 1) return MEASURE_ERR_NOSEL;
01257 // if (!weight || !rmsd) return MEASURE_ERR_NOWEIGHT;
01258 
01259 
01260 //
01261 // XXX really need to compute per-file frame counts etc
01262 //
01263 framecount = (last - first + 1) / step;
01264 
01265 printf("** measure_rmsdmat_qcp_ooc(): \n");
01266 printf("** first: %d last: %d step: %d nfiles: %d count: %d\n",
01267 first, last, step, nfiles, framecount);
01268 
01269 rmsdmat = (float *) calloc(1, framecount * framecount * sizeof(float));
01270 
01271 // XXX this needs to be properly implemented with fallback functionality
01272 #if defined(VMDCUDA)
01273 qcp_soa_gpu_ooc(app->cuda->get_cuda_devpool(), 
01274 nfiles, trjfileset, sel, first, last, step, rmsdmat);
01275 #endif
01276 
01277 #if 0
01278 Molecule *mymol = mlist->mol_from_id(sel->molid());
01279 
01280 int maxframes = mymol->numframes();
01281 
01282 // accept value of -1 meaning "all" frames
01283 if (last == -1)
01284 last = maxframes-1;
01285 
01286 if (maxframes == 0 || first < 0 || first > last ||
01287 last >= maxframes || step <= 0)
01288 return MEASURE_ERR_BADFRAMERANGE;
01289 
01290 // XXX hacked out lots of stuff we don't need yet...
01291 
01292 
01293 #if (VMDQCPUSESSE && defined(__SSE2__)) || (VMDQCPUSEAVX2 && defined(__AVX__) && defined(__AVX2__))
01294 int useavx2=1;
01295 if (getenv("VMDNOAVX2")) {
01296 useavx2=0;
01297 }
01298 #endif
01299 
01300 // start timers
01301 wkf_timerhandle timer;
01302 timer=wkf_timer_create();
01303 wkf_timer_start(timer);
01304 
01305 //
01306 // compute CoM for frame/selection while copying them into SOA target bufs 
01307 //
01308 int framecount = (last - first + 1) / step;
01309 
01310 int padcnt = (num + 255) & ~255;
01311 int framecrdsz = padcnt + 256;
01312 float *crds = (float *) calloc(1, (framecount * 3L * framecrdsz + 256) * sizeof(float));
01313 
01314 int frame;
01315 for (frame=first; frame<=last; frame+=step) {
01316 const float *framepos = (mymol->get_frame(frame))->pos;
01317 float *xc = crds + (frame * 3L * framecrdsz);
01318 float *yc = xc + framecrdsz;
01319 float *zc = xc + framecrdsz*2;
01320 
01321 center_convert_single_soa(sel, num, framepos, weight, xc, yc, zc);
01322 }
01323 
01324 double converttime = wkf_timer_timenow(timer);
01325 
01326 #if defined(VMDTHREADS)
01327 int numprocs = wkf_thread_numprocessors();
01328 #else
01329 int numprocs = 1;
01330 #endif
01331 
01332 //
01333 // copy in per-thread parameters
01334 //
01335 qcprmsdthreadparms parms;
01336 memset(&parms, 0, sizeof(parms));
01337 parms.sel = sel;
01338 parms.rmsdmat = rmsdmat;
01339 parms.padcnt = padcnt;
01340 parms.framecrdsz = framecrdsz;
01341 parms.crds = crds;
01342 parms.first = first;
01343 parms.last = last;
01344 parms.step = step;
01345 #if (VMDQCPUSESSE && defined(__SSE2__)) || (VMDQCPUSEAVX2 && defined(__AVX__) && defined(__AVX2__))
01346 parms.useavx2 = useavx2;
01347 #endif
01348 
01349 // spawn child threads to do the work
01350 wkf_tasktile_t tile;
01351 tile.start=0;
01352 tile.end=(framecount-1)*(framecount-1)/2; // only compute off-diag elements
01353 
01354 #if defined(VMDORBUSETHRPOOL)
01355 wkf_threadpool_sched_dynamic(app->thrpool, &tile);
01356 rc = wkf_threadpool_launch(app->thrpool, measure_rmsdmat_qcp_thread, &parms, 1);
01357 #else
01358 wkf_threadlaunch(numprocs, &parms, measure_rmsdmat_qcp_thread, &tile);
01359 #endif
01360 
01361 // mark all self-RMSDs with a value of 1.0
01362 for (long l=0; l<framecount; l++) {
01363 rmsdmat[l*framecount + l] = 1.0;
01364 }
01365 
01366 double rmsdtime = wkf_timer_timenow(timer) - converttime;
01367 
01368 // free all temporary buffers
01369 free(crds);
01370 
01371 #if 1
01372 double totaltime = wkf_timer_timenow(timer);
01373 printf("QCP RMSD Matrix calculation time: SOA selection: %.3f RMSD solve: %.3f total: %.3f\n", converttime, rmsdtime, totaltime); 
01374 #endif
01375 
01376 wkf_timer_destroy(timer);
01377 #endif
01378 
01379 return MEASURE_NOERR; // and say rmsd is OK
01380 }
01381 
01382 
01383 
01384 
01385 
01386 
01387 //
01388 // Copyright notice for original QCP FastCalcRMSDAndRotation() routine
01389 //
01390 // If you use this QCP rotation calculation method in a publication, please
01391 // reference:
01392 // Douglas L. Theobald (2005)
01393 // "Rapid calculation of RMSD using a quaternion-based characteristic
01394 // polynomial."
01395 // Acta Crystallographica A 61(4):478-480.
01396 //
01397 // Pu Liu, Dmitris K. Agrafiotis, and Douglas L. Theobald (2009)
01398 // "Fast determination of the optimal rotational matrix for macromolecular
01399 // superpositions."
01400 // Journal of Computational Chemistry 31(7):1561-1563.
01401 //
01402 // Copyright (c) 2009-2013 Pu Liu and Douglas L. Theobald
01403 // All rights reserved.
01404 //
01405 // Redistribution and use in source and binary forms, with or without modification, are permitted
01406 // provided that the following conditions are met:
01407 //
01408 // * Redistributions of source code must retain the above copyright notice, 
01409 // this list of conditions and the following disclaimer.
01410 // * Redistributions in binary form must reproduce the above copyright notice,
01411 // this list of conditions and the following disclaimer in the documentation
01412 // and/or other materials provided with the distribution.
01413 // * Neither the name of the <ORGANIZATION> nor the names of its 
01414 // contributors may be used to endorse or promote products derived from 
01415 // this software without specific prior written permission.
01416 //
01417 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
01418 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
01419 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
01420 // PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
01421 // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
01422 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
01423 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
01424 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
01425 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
01426 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
01427 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
01428 //
01429 #if defined(__PGIC__)
01430 #pragma acc routine seq
01431 #endif
01432 int FastCalcRMSDAndRotation(double *rot, double *A, float *rmsd, 
01433 double E0, int len, double minScore) {
01434 double Sxx, Sxy, Sxz, Syx, Syy, Syz, Szx, Szy, Szz;
01435 double Szz2, Syy2, Sxx2, Sxy2, Syz2, Sxz2, Syx2, Szy2, Szx2,
01436 SyzSzymSyySzz2, Sxx2Syy2Szz2Syz2Szy2, Sxy2Sxz2Syx2Szx2,
01437 SxzpSzx, SyzpSzy, SxypSyx, SyzmSzy,
01438 SxzmSzx, SxymSyx, SxxpSyy, SxxmSyy;
01439 double C[4];
01440 int i;
01441 double mxEigenV; 
01442 double oldg = 0.0;
01443 double b, a, delta, rms, qsqr;
01444 double q1, q2, q3, q4, normq;
01445 double a11, a12, a13, a14, a21, a22, a23, a24;
01446 double a31, a32, a33, a34, a41, a42, a43, a44;
01447 double a2, x2, y2, z2; 
01448 double xy, az, zx, ay, yz, ax; 
01449 double a3344_4334, a3244_4234, a3243_4233, a3143_4133,a3144_4134, a3142_4132; 
01450 double evecprec = 1e-6;
01451 double evalprec = 1e-11;
01452 
01453 Sxx = A[0]; Sxy = A[1]; Sxz = A[2];
01454 Syx = A[3]; Syy = A[4]; Syz = A[5];
01455 Szx = A[6]; Szy = A[7]; Szz = A[8];
01456 
01457 Sxx2 = Sxx * Sxx;
01458 Syy2 = Syy * Syy;
01459 Szz2 = Szz * Szz;
01460 
01461 Sxy2 = Sxy * Sxy;
01462 Syz2 = Syz * Syz;
01463 Sxz2 = Sxz * Sxz;
01464 
01465 Syx2 = Syx * Syx;
01466 Szy2 = Szy * Szy;
01467 Szx2 = Szx * Szx;
01468 
01469 SyzSzymSyySzz2 = 2.0*(Syz*Szy - Syy*Szz);
01470 Sxx2Syy2Szz2Syz2Szy2 = Syy2 + Szz2 - Sxx2 + Syz2 + Szy2;
01471 
01472 C[2] = -2.0 * (Sxx2 + Syy2 + Szz2 + Sxy2 + Syx2 + Sxz2 + Szx2 + Syz2 + Szy2);
01473 C[1] = 8.0 * (Sxx*Syz*Szy + Syy*Szx*Sxz + Szz*Sxy*Syx - Sxx*Syy*Szz - Syz*Szx*Sxy - Szy*Syx*Sxz);
01474 
01475 SxzpSzx = Sxz + Szx;
01476 SyzpSzy = Syz + Szy;
01477 SxypSyx = Sxy + Syx;
01478 SyzmSzy = Syz - Szy;
01479 SxzmSzx = Sxz - Szx;
01480 SxymSyx = Sxy - Syx;
01481 SxxpSyy = Sxx + Syy;
01482 SxxmSyy = Sxx - Syy;
01483 Sxy2Sxz2Syx2Szx2 = Sxy2 + Sxz2 - Syx2 - Szx2;
01484 
01485 C[0] = Sxy2Sxz2Syx2Szx2 * Sxy2Sxz2Syx2Szx2
01486 + (Sxx2Syy2Szz2Syz2Szy2 + SyzSzymSyySzz2) * (Sxx2Syy2Szz2Syz2Szy2 - SyzSzymSyySzz2)
01487 + (-(SxzpSzx)*(SyzmSzy)+(SxymSyx)*(SxxmSyy-Szz)) * (-(SxzmSzx)*(SyzpSzy)+(SxymSyx)*(SxxmSyy+Szz))
01488 + (-(SxzpSzx)*(SyzpSzy)-(SxypSyx)*(SxxpSyy-Szz)) * (-(SxzmSzx)*(SyzmSzy)-(SxypSyx)*(SxxpSyy+Szz))
01489 + (+(SxypSyx)*(SyzpSzy)+(SxzpSzx)*(SxxmSyy+Szz)) * (-(SxymSyx)*(SyzmSzy)+(SxzpSzx)*(SxxpSyy+Szz))
01490 + (+(SxypSyx)*(SyzmSzy)+(SxzmSzx)*(SxxmSyy-Szz)) * (-(SxymSyx)*(SyzpSzy)+(SxzmSzx)*(SxxpSyy-Szz));
01491 
01492 /* Newton-Raphson */
01493 mxEigenV = E0;
01494 for (i = 0; i < 50; ++i) {
01495 oldg = mxEigenV;
01496 x2 = mxEigenV*mxEigenV;
01497 b = (x2 + C[2])*mxEigenV;
01498 a = b + C[1];
01499 delta = ((a*mxEigenV + C[0])/(2.0*x2*mxEigenV + b + a));
01500 mxEigenV -= delta;
01501 #if 0
01502 printf("QCP diff[%3d]: %16g %16g %16g\n", i, mxEigenV - oldg, evalprec*mxEigenV, mxEigenV);
01503 #endif
01504 if (fabs(mxEigenV - oldg) < fabs(evalprec*mxEigenV))
01505 break;
01506 }
01507 
01508 #if !defined(__PGIC__)
01509 if (i == 50) 
01510 printf("MeasureQCP: More than %d iterations needed!\n", i);
01511 #endif
01512 
01513 // the fabs() is to guard against extremely small, 
01514 // but *negative* numbers due to floating point error 
01515 rms = sqrt(fabs(2.0 * (E0 - mxEigenV)/len));
01516 (*rmsd) = float(rms);
01517 /* printf("\n\n %16g %16g %16g \n", rms, E0, 2.0 * (E0 - mxEigenV)/len); */
01518 
01519 if (minScore > 0) 
01520 if (rms < minScore)
01521 return (-1); // Don't bother with rotation. 
01522 
01523 // only perform rotation related calculations if we have a non-NULL
01524 // pointer for the output rotation matrix
01525 if (rot != NULL) {
01526 a11 = SxxpSyy + Szz-mxEigenV; a12 = SyzmSzy; a13 = - SxzmSzx; a14 = SxymSyx;
01527 a21 = SyzmSzy; a22 = SxxmSyy - Szz-mxEigenV; a23 = SxypSyx; a24= SxzpSzx;
01528 a31 = a13; a32 = a23; a33 = Syy-Sxx-Szz - mxEigenV; a34 = SyzpSzy;
01529 a41 = a14; a42 = a24; a43 = a34; a44 = Szz - SxxpSyy - mxEigenV;
01530 a3344_4334 = a33 * a44 - a43 * a34; a3244_4234 = a32 * a44-a42*a34;
01531 a3243_4233 = a32 * a43 - a42 * a33; a3143_4133 = a31 * a43-a41*a33;
01532 a3144_4134 = a31 * a44 - a41 * a34; a3142_4132 = a31 * a42-a41*a32;
01533 q1 = a22*a3344_4334-a23*a3244_4234+a24*a3243_4233;
01534 q2 = -a21*a3344_4334+a23*a3144_4134-a24*a3143_4133;
01535 q3 = a21*a3244_4234-a22*a3144_4134+a24*a3142_4132;
01536 q4 = -a21*a3243_4233+a22*a3143_4133-a23*a3142_4132;
01537 
01538 qsqr = q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4;
01539 
01540 // The following code tries to calculate another column in the 
01541 // adjoint matrix when the norm of the current column is too small.
01542 // Usually this block will never be activated. 
01543 // To be absolutely safe this should be
01544 // uncommented, but it is most likely unnecessary.
01545 if (qsqr < evecprec) {
01546 q1 = a12*a3344_4334 - a13*a3244_4234 + a14*a3243_4233;
01547 q2 = -a11*a3344_4334 + a13*a3144_4134 - a14*a3143_4133;
01548 q3 = a11*a3244_4234 - a12*a3144_4134 + a14*a3142_4132;
01549 q4 = -a11*a3243_4233 + a12*a3143_4133 - a13*a3142_4132;
01550 qsqr = q1*q1 + q2 *q2 + q3*q3+q4*q4;
01551 
01552 if (qsqr < evecprec) {
01553 double a1324_1423 = a13*a24 - a14*a23, a1224_1422 = a12*a24 - a14*a22;
01554 double a1223_1322 = a12*a23 - a13*a22, a1124_1421 = a11*a24 - a14*a21;
01555 double a1123_1321 = a11*a23 - a13*a21, a1122_1221 = a11*a22 - a12*a21;
01556 
01557 q1 = a42 * a1324_1423 - a43 * a1224_1422 + a44 * a1223_1322;
01558 q2 = -a41 * a1324_1423 + a43 * a1124_1421 - a44 * a1123_1321;
01559 q3 = a41 * a1224_1422 - a42 * a1124_1421 + a44 * a1122_1221;
01560 q4 = -a41 * a1223_1322 + a42 * a1123_1321 - a43 * a1122_1221;
01561 qsqr = q1*q1 + q2 *q2 + q3*q3+q4*q4;
01562 
01563 if (qsqr < evecprec) {
01564 q1 = a32 * a1324_1423 - a33 * a1224_1422 + a34 * a1223_1322;
01565 q2 = -a31 * a1324_1423 + a33 * a1124_1421 - a34 * a1123_1321;
01566 q3 = a31 * a1224_1422 - a32 * a1124_1421 + a34 * a1122_1221;
01567 q4 = -a31 * a1223_1322 + a32 * a1123_1321 - a33 * a1122_1221;
01568 qsqr = q1*q1 + q2 *q2 + q3*q3 + q4*q4;
01569 
01570 if (qsqr < evecprec) {
01571 // if qsqr is still too small, return the identity matrix.
01572 rot[0] = rot[4] = rot[8] = 1.0;
01573 rot[1] = rot[2] = rot[3] = rot[5] = rot[6] = rot[7] = 0.0;
01574 
01575 return(0);
01576 }
01577 }
01578 }
01579 }
01580 
01581 normq = sqrt(qsqr);
01582 q1 /= normq;
01583 q2 /= normq;
01584 q3 /= normq;
01585 q4 /= normq;
01586 
01587 a2 = q1 * q1;
01588 x2 = q2 * q2;
01589 y2 = q3 * q3;
01590 z2 = q4 * q4;
01591 
01592 xy = q2 * q3;
01593 az = q1 * q4;
01594 zx = q4 * q2;
01595 ay = q1 * q3;
01596 yz = q3 * q4;
01597 ax = q1 * q2;
01598 
01599 rot[0] = a2 + x2 - y2 - z2;
01600 rot[1] = 2 * (xy + az);
01601 rot[2] = 2 * (zx - ay);
01602 rot[3] = 2 * (xy - az);
01603 rot[4] = a2 - x2 + y2 - z2;
01604 rot[5] = 2 * (yz + ax);
01605 rot[6] = 2 * (zx + ay);
01606 rot[7] = 2 * (yz - ax);
01607 rot[8] = a2 - x2 - y2 + z2;
01608 }
01609 
01610 return 1;
01611 }
01612 

Generated on Mon Nov 17 02:46:24 2025 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002

AltStyle によって変換されたページ (->オリジナル) /