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