Support oneAPI in gitlab CI
[alexxy/gromacs.git] / src / gromacs / gmxana / nsfactor.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
5  * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
6  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7  * and including many others, as listed in the AUTHORS file in the
8  * top-level source directory and at http://www.gromacs.org.
9  *
10  * GROMACS is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public License
12  * as published by the Free Software Foundation; either version 2.1
13  * of the License, or (at your option) any later version.
14  *
15  * GROMACS is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with GROMACS; if not, see
22  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
24  *
25  * If you want to redistribute modifications to GROMACS, please
26  * consider that scientific software is very special. Version
27  * control is crucial - bugs must be traceable. We will be happy to
28  * consider code for inclusion in the official distribution, but
29  * derived work must not be called official GROMACS. Details are found
30  * in the README & COPYING files - if they are missing, get the
31  * official version at http://www.gromacs.org.
32  *
33  * To help us fund GROMACS development, we humbly ask that you cite
34  * the research papers on the package. Check out http://www.gromacs.org.
35  */
36 #include "gmxpre.h"
37
38 #include "nsfactor.h"
39
40 #include "config.h"
41
42 #include <cmath>
43 #include <cstring>
44
45 #include "gromacs/math/vec.h"
46 #include "gromacs/random/threefry.h"
47 #include "gromacs/random/uniformintdistribution.h"
48 #include "gromacs/topology/topology.h"
49 #include "gromacs/utility/basedefinitions.h"
50 #include "gromacs/utility/cstringutil.h"
51 #include "gromacs/utility/exceptions.h"
52 #include "gromacs/utility/fatalerror.h"
53 #include "gromacs/utility/futil.h"
54 #include "gromacs/utility/gmxomp.h"
55 #include "gromacs/utility/smalloc.h"
56 #include "gromacs/utility/strdb.h"
57
58 void check_binwidth(real binwidth)
59 {
60     real smallest_bin = 0.1;
61     if (binwidth < smallest_bin)
62     {
63         gmx_fatal(FARGS,
64                   "Binwidth shouldn't be smaller then smallest bond length (H-H bond ~0.1nm) in a "
65                   "box");
66     }
67 }
68
69 void check_mcover(real mcover)
70 {
71     if (mcover > 1.0)
72     {
73         gmx_fatal(FARGS, "mcover should be -1 or (0,1]");
74     }
75     else if ((mcover < 0) && (mcover != -1))
76     {
77         gmx_fatal(FARGS, "mcover should be -1 or (0,1]");
78     }
79     else
80     {
81         return;
82     }
83 }
84
85 void normalize_probability(int n, double* a)
86 {
87     int    i;
88     double norm = 0.0;
89     for (i = 0; i < n; i++)
90     {
91         norm += a[i];
92     }
93     for (i = 0; i < n; i++)
94     {
95         a[i] /= norm;
96     }
97 }
98
99 gmx_neutron_atomic_structurefactors_t* gmx_neutronstructurefactors_init(const char* datfn)
100 {
101     /* read nsfactor.dat */
102     char                                   line[STRLEN];
103     int                                    nralloc = 10;
104     int                                    n, p;
105     int                                    i, line_no;
106     char                                   atomnm[8];
107     double                                 slength;
108     gmx_neutron_atomic_structurefactors_t* gnsf;
109
110     gmx::FilePtr fp = gmx::openLibraryFile(datfn);
111     line_no         = 0;
112     /* allocate memory for structure */
113     snew(gnsf, nralloc);
114     snew(gnsf->atomnm, nralloc);
115     snew(gnsf->p, nralloc);
116     snew(gnsf->n, nralloc);
117     snew(gnsf->slength, nralloc);
118
119     gnsf->nratoms = line_no;
120
121     while (get_a_line(fp.get(), line, STRLEN))
122     {
123         i = line_no;
124         if (sscanf(line, "%s %d %d %lf", atomnm, &p, &n, &slength) == 4)
125         {
126             gnsf->atomnm[i]  = gmx_strdup(atomnm);
127             gnsf->n[i]       = n;
128             gnsf->p[i]       = p;
129             gnsf->slength[i] = slength;
130             line_no++;
131             gnsf->nratoms = line_no;
132             if (line_no == nralloc)
133             {
134                 nralloc++;
135                 srenew(gnsf->atomnm, nralloc);
136                 srenew(gnsf->p, nralloc);
137                 srenew(gnsf->n, nralloc);
138                 srenew(gnsf->slength, nralloc);
139             }
140         }
141         else
142         {
143             fprintf(stderr, "WARNING: Error in file %s at line %d ignored\n", datfn, line_no);
144         }
145     }
146     srenew(gnsf->atomnm, gnsf->nratoms);
147     srenew(gnsf->p, gnsf->nratoms);
148     srenew(gnsf->n, gnsf->nratoms);
149     srenew(gnsf->slength, gnsf->nratoms);
150
151     return gnsf;
152 }
153
154 gmx_sans_t* gmx_sans_init(const t_topology* top, gmx_neutron_atomic_structurefactors_t* gnsf)
155 {
156     gmx_sans_t* gsans = nullptr;
157     int         i, j;
158     /* Try to assing scattering length from nsfactor.dat */
159     snew(gsans, 1);
160     snew(gsans->slength, top->atoms.nr);
161     /* copy topology data */
162     gsans->top = top;
163     for (i = 0; i < top->atoms.nr; i++)
164     {
165         for (j = 0; j < gnsf->nratoms; j++)
166         {
167             if (top->atoms.atom[i].atomnumber == gnsf->p[j])
168             {
169                 /* we need special case for H and D */
170                 if (top->atoms.atom[i].atomnumber == 1)
171                 {
172                     if (top->atoms.atom[i].m == 1.008000)
173                     {
174                         gsans->slength[i] = gnsf->slength[0];
175                     }
176                     else
177                     {
178                         gsans->slength[i] = gnsf->slength[1];
179                     }
180                 }
181                 else
182                 {
183                     gsans->slength[i] = gnsf->slength[j];
184                 }
185             }
186         }
187     }
188
189     return gsans;
190 }
191
192 gmx_radial_distribution_histogram_t* calc_radial_distribution_histogram(gmx_sans_t*  gsans,
193                                                                         rvec*        x,
194                                                                         matrix       box,
195                                                                         const int*   index,
196                                                                         int          isize,
197                                                                         double       binwidth,
198                                                                         gmx_bool     bMC,
199                                                                         gmx_bool     bNORM,
200                                                                         real         mcover,
201                                                                         unsigned int seed)
202 {
203     gmx_radial_distribution_histogram_t* pr = nullptr;
204     rvec                                 dist;
205     double                               rmax;
206     int                                  i, j;
207 #if GMX_OPENMP
208     double**                  tgr;
209     int                       tid;
210     int                       nthreads;
211     gmx::DefaultRandomEngine* trng = nullptr;
212 #endif
213     int64_t                  mc_max;
214     gmx::DefaultRandomEngine rng(seed);
215
216     /* allocate memory for pr */
217     snew(pr, 1);
218     /* set some fields */
219     pr->binwidth = binwidth;
220
221     /*
222      * create max dist rvec
223      * dist = box[xx] + box[yy] + box[zz]
224      */
225     rvec_add(box[XX], box[YY], dist);
226     rvec_add(box[ZZ], dist, dist);
227
228     rmax = norm(dist);
229
230     pr->grn = static_cast<int>(std::floor(rmax / pr->binwidth) + 1);
231
232     snew(pr->gr, pr->grn);
233
234     if (bMC)
235     {
236         /* Special case for setting automaticaly number of mc iterations to 1% of total number of direct iterations */
237         if (mcover == -1)
238         {
239             mc_max = static_cast<int64_t>(std::floor(0.5 * 0.01 * isize * (isize - 1)));
240         }
241         else
242         {
243             mc_max = static_cast<int64_t>(std::floor(0.5 * mcover * isize * (isize - 1)));
244         }
245 #if GMX_OPENMP
246         nthreads = gmx_omp_get_max_threads();
247         snew(tgr, nthreads);
248         trng = new gmx::DefaultRandomEngine[nthreads];
249         for (i = 0; i < nthreads; i++)
250         {
251             snew(tgr[i], pr->grn);
252             trng[i].seed(rng());
253         }
254 #    pragma omp parallel shared(tgr, trng) private(tid, i, j)
255         {
256             gmx::UniformIntDistribution<int> tdist(0, isize - 1);
257             tid = gmx_omp_get_thread_num();
258 /* now starting parallel threads */
259             INTEL_DIAGNOSTIC_IGNORE(593)
260 #    pragma omp for
261             for (int64_t mc = 0; mc < mc_max; mc++)
262             {
263                 try
264                 {
265                     i = tdist(trng[tid]); // [0,isize-1]
266                     j = tdist(trng[tid]); // [0,isize-1]
267                     if (i != j)
268                     {
269                         tgr[tid][static_cast<int>(std::floor(std::sqrt(distance2(x[index[i]], x[index[j]])) / binwidth))] +=
270                                 gsans->slength[index[i]] * gsans->slength[index[j]];
271                     }
272                 }
273                 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
274             }
275         }
276         INTEL_DIAGNOSTIC_RESET
277         /* collecting data from threads */
278         for (i = 0; i < pr->grn; i++)
279         {
280             for (j = 0; j < nthreads; j++)
281             {
282                 pr->gr[i] += tgr[j][i];
283             }
284         }
285         /* freeing memory for tgr and destroying trng */
286         for (i = 0; i < nthreads; i++)
287         {
288             sfree(tgr[i]);
289         }
290         sfree(tgr);
291         delete[] trng;
292 #else
293         gmx::UniformIntDistribution<int> dist(0, isize - 1);
294         for (int64_t mc = 0; mc < mc_max; mc++)
295         {
296             i = dist(rng); // [0,isize-1]
297             j = dist(rng); // [0,isize-1]
298             if (i != j)
299             {
300                 pr->gr[static_cast<int>(std::floor(std::sqrt(distance2(x[index[i]], x[index[j]])) / binwidth))] +=
301                         gsans->slength[index[i]] * gsans->slength[index[j]];
302             }
303         }
304 #endif
305     }
306     else
307     {
308 #if GMX_OPENMP
309         nthreads = gmx_omp_get_max_threads();
310         /* Allocating memory for tgr arrays */
311         snew(tgr, nthreads);
312         for (i = 0; i < nthreads; i++)
313         {
314             snew(tgr[i], pr->grn);
315         }
316 #    pragma omp parallel shared(tgr) private(tid, i, j)
317         {
318             tid = gmx_omp_get_thread_num();
319 /* starting parallel threads */
320 #    pragma omp for
321             for (i = 0; i < isize; i++)
322             {
323                 try
324                 {
325                     for (j = 0; j < i; j++)
326                     {
327                         tgr[tid][static_cast<int>(std::floor(std::sqrt(distance2(x[index[i]], x[index[j]])) / binwidth))] +=
328                                 gsans->slength[index[i]] * gsans->slength[index[j]];
329                     }
330                 }
331                 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
332             }
333         }
334         /* collecating data for pr->gr */
335         for (i = 0; i < pr->grn; i++)
336         {
337             for (j = 0; j < nthreads; j++)
338             {
339                 pr->gr[i] += tgr[j][i];
340             }
341         }
342         /* freeing memory for tgr */
343         for (i = 0; i < nthreads; i++)
344         {
345             sfree(tgr[i]);
346         }
347         sfree(tgr);
348 #else
349         for (i = 0; i < isize; i++)
350         {
351             for (j = 0; j < i; j++)
352             {
353                 pr->gr[static_cast<int>(std::floor(std::sqrt(distance2(x[index[i]], x[index[j]])) / binwidth))] +=
354                         gsans->slength[index[i]] * gsans->slength[index[j]];
355             }
356         }
357 #endif
358     }
359
360     /* normalize if needed */
361     if (bNORM)
362     {
363         normalize_probability(pr->grn, pr->gr);
364     }
365
366     snew(pr->r, pr->grn);
367     for (i = 0; i < pr->grn; i++)
368     {
369         pr->r[i] = (pr->binwidth * i + pr->binwidth * 0.5);
370     }
371
372     return pr;
373 }
374
375 gmx_static_structurefactor_t* convert_histogram_to_intensity_curve(gmx_radial_distribution_histogram_t* pr,
376                                                                    double start_q,
377                                                                    double end_q,
378                                                                    double q_step)
379 {
380     gmx_static_structurefactor_t* sq = nullptr;
381     int                           i, j;
382     /* init data */
383     snew(sq, 1);
384     sq->qn = static_cast<int>(std::floor((end_q - start_q) / q_step));
385     snew(sq->q, sq->qn);
386     snew(sq->s, sq->qn);
387     for (i = 0; i < sq->qn; i++)
388     {
389         sq->q[i] = start_q + i * q_step;
390     }
391
392     if (start_q == 0.0)
393     {
394         sq->s[0] = 1.0;
395         for (i = 1; i < sq->qn; i++)
396         {
397             for (j = 0; j < pr->grn; j++)
398             {
399                 sq->s[i] += (pr->gr[j] / pr->r[j]) * std::sin(sq->q[i] * pr->r[j]);
400             }
401             sq->s[i] /= sq->q[i];
402         }
403     }
404     else
405     {
406         for (i = 0; i < sq->qn; i++)
407         {
408             for (j = 0; j < pr->grn; j++)
409             {
410                 sq->s[i] += (pr->gr[j] / pr->r[j]) * std::sin(sq->q[i] * pr->r[j]);
411             }
412             sq->s[i] /= sq->q[i];
413         }
414     }
415
416     return sq;
417 }