Apply re-formatting to C++ in src/ tree.
[alexxy/gromacs.git] / src / gromacs / gmxana / gmx_sans.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
37 #include "gmxpre.h"
38
39 #include "config.h"
40
41 #include <array>
42
43 #include "gromacs/commandline/pargs.h"
44 #include "gromacs/fileio/confio.h"
45 #include "gromacs/fileio/trxio.h"
46 #include "gromacs/fileio/xvgr.h"
47 #include "gromacs/gmxana/gmx_ana.h"
48 #include "gromacs/gmxana/gstat.h"
49 #include "gromacs/gmxana/nsfactor.h"
50 #include "gromacs/math/vec.h"
51 #include "gromacs/pbcutil/pbc.h"
52 #include "gromacs/pbcutil/rmpbc.h"
53 #include "gromacs/topology/index.h"
54 #include "gromacs/topology/topology.h"
55 #include "gromacs/utility/arraysize.h"
56 #include "gromacs/utility/cstringutil.h"
57 #include "gromacs/utility/fatalerror.h"
58 #include "gromacs/utility/futil.h"
59 #include "gromacs/utility/gmxassert.h"
60 #include "gromacs/utility/gmxomp.h"
61 #include "gromacs/utility/pleasecite.h"
62 #include "gromacs/utility/smalloc.h"
63
64 int gmx_sans(int argc, char* argv[])
65 {
66     const char* desc[] = {
67         "[THISMODULE] computes SANS spectra using Debye formula.",
68         "It currently uses topology file (since it need to assigne element for each atom).",
69         "[PAR]",
70         "Parameters:[PAR]",
71         "[TT]-pr[tt] Computes normalized g(r) function averaged over trajectory[PAR]",
72         "[TT]-prframe[tt] Computes normalized g(r) function for each frame[PAR]",
73         "[TT]-sq[tt] Computes SANS intensity curve averaged over trajectory[PAR]",
74         "[TT]-sqframe[tt] Computes SANS intensity curve for each frame[PAR]",
75         "[TT]-startq[tt] Starting q value in nm[PAR]",
76         "[TT]-endq[tt] Ending q value in nm[PAR]",
77         "[TT]-qstep[tt] Stepping in q space[PAR]",
78         "Note: When using Debye direct method computational cost increases as",
79         "1/2 * N * (N - 1) where N is atom number in group of interest.",
80         "[PAR]",
81         "WARNING: If sq or pr specified this tool can produce large number of files! Up to ",
82         "two times larger than number of frames!"
83     };
84     static gmx_bool bPBC     = TRUE;
85     static gmx_bool bNORM    = FALSE;
86     static real     binwidth = 0.2,
87                 grid = 0.05; /* bins shouldn't be smaller then smallest bond (~0.1nm) length */
88     static real         start_q = 0.0, end_q = 2.0, q_step = 0.01;
89     static real         mcover   = -1;
90     static unsigned int seed     = 0;
91     static int          nthreads = -1;
92
93     static const char* emode[]   = { nullptr, "direct", "mc", nullptr };
94     static const char* emethod[] = { nullptr, "debye", "fft", nullptr };
95
96     gmx_neutron_atomic_structurefactors_t* gnsf;
97     gmx_sans_t*                            gsans;
98
99 #define NPA asize(pa)
100
101     t_pargs pa[] = {
102         { "-bin", FALSE, etREAL, { &binwidth }, "[HIDDEN]Binwidth (nm)" },
103         { "-mode", FALSE, etENUM, { emode }, "Mode for sans spectra calculation" },
104         { "-mcover",
105           FALSE,
106           etREAL,
107           { &mcover },
108           "Monte-Carlo coverage should be -1(default) or (0,1]" },
109         { "-method", FALSE, etENUM, { emethod }, "[HIDDEN]Method for sans spectra calculation" },
110         { "-pbc",
111           FALSE,
112           etBOOL,
113           { &bPBC },
114           "Use periodic boundary conditions for computing distances" },
115         { "-grid", FALSE, etREAL, { &grid }, "[HIDDEN]Grid spacing (in nm) for FFTs" },
116         { "-startq", FALSE, etREAL, { &start_q }, "Starting q (1/nm) " },
117         { "-endq", FALSE, etREAL, { &end_q }, "Ending q (1/nm)" },
118         { "-qstep", FALSE, etREAL, { &q_step }, "Stepping in q (1/nm)" },
119         { "-seed", FALSE, etINT, { &seed }, "Random seed for Monte-Carlo" },
120 #if GMX_OPENMP
121         { "-nt", FALSE, etINT, { &nthreads }, "Number of threads to start" },
122 #endif
123     };
124     FILE*                                fp;
125     const char *                         fnTPX, *fnTRX, *fnDAT = nullptr;
126     t_trxstatus*                         status;
127     t_topology*                          top  = nullptr;
128     gmx_rmpbc_t                          gpbc = nullptr;
129     gmx_bool                             bFFT = FALSE, bDEBYE = FALSE;
130     gmx_bool                             bMC     = FALSE;
131     PbcType                              pbcType = PbcType::Unset;
132     matrix                               box;
133     rvec*                                x;
134     int                                  natoms;
135     real                                 t;
136     char**                               grpname = nullptr;
137     int*                                 index   = nullptr;
138     int                                  isize;
139     int                                  i;
140     char*                                hdr            = nullptr;
141     char*                                suffix         = nullptr;
142     gmx_radial_distribution_histogram_t *prframecurrent = nullptr, *pr = nullptr;
143     gmx_static_structurefactor_t *       sqframecurrent = nullptr, *sq = nullptr;
144     gmx_output_env_t*                    oenv;
145
146     std::array<t_filenm, 8> filenames = { { { efTPR, "-s", nullptr, ffREAD },
147                                             { efTRX, "-f", nullptr, ffREAD },
148                                             { efNDX, nullptr, nullptr, ffOPTRD },
149                                             { efDAT, "-d", "nsfactor", ffOPTRD },
150                                             { efXVG, "-pr", "pr", ffWRITE },
151                                             { efXVG, "-sq", "sq", ffWRITE },
152                                             { efXVG, "-prframe", "prframe", ffOPTWR },
153                                             { efXVG, "-sqframe", "sqframe", ffOPTWR } } };
154     t_filenm*               fnm       = filenames.data();
155
156     const auto NFILE = filenames.size();
157
158     nthreads = gmx_omp_get_max_threads();
159
160     if (!parse_common_args(
161                 &argc, argv, PCA_CAN_TIME | PCA_TIME_UNIT, NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, nullptr, &oenv))
162     {
163         return 0;
164     }
165
166     /* check that binwidth not smaller than smallers distance */
167     check_binwidth(binwidth);
168     check_mcover(mcover);
169
170     /* setting number of omp threads globaly */
171     gmx_omp_set_num_threads(nthreads);
172
173     /* Now try to parse opts for modes */
174     GMX_RELEASE_ASSERT(emethod[0] != nullptr, "Options inconsistency; emethod[0] is NULL");
175     switch (emethod[0][0])
176     {
177         case 'd':
178             bDEBYE = TRUE;
179             switch (emode[0][0])
180             {
181                 case 'd': bMC = FALSE; break;
182                 case 'm': bMC = TRUE; break;
183                 default: break;
184             }
185             break;
186         case 'f': bFFT = TRUE; break;
187         default: break;
188     }
189
190     if (bDEBYE)
191     {
192         if (bMC)
193         {
194             fprintf(stderr, "Using Monte Carlo Debye method to calculate spectrum\n");
195         }
196         else
197         {
198             fprintf(stderr, "Using direct Debye method to calculate spectrum\n");
199         }
200     }
201     else if (bFFT)
202     {
203         gmx_fatal(FARGS, "FFT method not implemented!");
204     }
205     else
206     {
207         gmx_fatal(FARGS, "Unknown combination for mode and method!");
208     }
209
210     /* Try to read files */
211     fnDAT = ftp2fn(efDAT, NFILE, fnm);
212     fnTPX = ftp2fn(efTPR, NFILE, fnm);
213     fnTRX = ftp2fn(efTRX, NFILE, fnm);
214
215     gnsf = gmx_neutronstructurefactors_init(fnDAT);
216     fprintf(stderr, "Read %d atom names from %s with neutron scattering parameters\n\n", gnsf->nratoms, fnDAT);
217
218     snew(top, 1);
219     snew(grpname, 1);
220     snew(index, 1);
221
222     read_tps_conf(fnTPX, top, &pbcType, &x, nullptr, box, TRUE);
223
224     printf("\nPlease select group for SANS spectra calculation:\n");
225     get_index(&(top->atoms), ftp2fn_null(efNDX, NFILE, fnm), 1, &isize, &index, grpname);
226
227     gsans = gmx_sans_init(top, gnsf);
228
229     /* Prepare reference frame */
230     if (bPBC)
231     {
232         gpbc = gmx_rmpbc_init(&top->idef, pbcType, top->atoms.nr);
233         gmx_rmpbc(gpbc, top->atoms.nr, box, x);
234     }
235
236     natoms = read_first_x(oenv, &status, fnTRX, &t, &x, box);
237     if (natoms != top->atoms.nr)
238     {
239         fprintf(stderr,
240                 "\nWARNING: number of atoms in tpx (%d) and trajectory (%d) do not match\n",
241                 natoms,
242                 top->atoms.nr);
243     }
244
245     do
246     {
247         if (bPBC)
248         {
249             gmx_rmpbc(gpbc, top->atoms.nr, box, x);
250         }
251         /* allocate memory for pr */
252         if (pr == nullptr)
253         {
254             /* in case its first frame to read */
255             snew(pr, 1);
256         }
257         /*  realy calc p(r) */
258         prframecurrent = calc_radial_distribution_histogram(
259                 gsans, x, box, index, isize, binwidth, bMC, bNORM, mcover, seed);
260         /* copy prframecurrent -> pr and summ up pr->gr[i] */
261         /* allocate and/or resize memory for pr->gr[i] and pr->r[i] */
262         if (pr->gr == nullptr)
263         {
264             /* check if we use pr->gr first time */
265             snew(pr->gr, prframecurrent->grn);
266             snew(pr->r, prframecurrent->grn);
267         }
268         else
269         {
270             /* resize pr->gr and pr->r if needed to preven overruns */
271             if (prframecurrent->grn > pr->grn)
272             {
273                 srenew(pr->gr, prframecurrent->grn);
274                 srenew(pr->r, prframecurrent->grn);
275             }
276         }
277         pr->grn      = prframecurrent->grn;
278         pr->binwidth = prframecurrent->binwidth;
279         /* summ up gr and fill r */
280         for (i = 0; i < prframecurrent->grn; i++)
281         {
282             pr->gr[i] += prframecurrent->gr[i];
283             pr->r[i] = prframecurrent->r[i];
284         }
285         /* normalize histo */
286         normalize_probability(prframecurrent->grn, prframecurrent->gr);
287         /* convert p(r) to sq */
288         sqframecurrent = convert_histogram_to_intensity_curve(prframecurrent, start_q, end_q, q_step);
289         /* print frame data if needed */
290         if (opt2fn_null("-prframe", NFILE, fnm))
291         {
292             snew(hdr, 25);
293             snew(suffix, GMX_PATH_MAX);
294             /* prepare header */
295             sprintf(hdr, "g(r), t = %f", t);
296             /* prepare output filename */
297             auto fnmdup = filenames;
298             sprintf(suffix, "-t%.2f", t);
299             add_suffix_to_output_names(fnmdup.data(), NFILE, suffix);
300             fp = xvgropen(opt2fn_null("-prframe", NFILE, fnmdup.data()),
301                           hdr,
302                           "Distance (nm)",
303                           "Probability",
304                           oenv);
305             for (i = 0; i < prframecurrent->grn; i++)
306             {
307                 fprintf(fp, "%10.6f%10.6f\n", prframecurrent->r[i], prframecurrent->gr[i]);
308             }
309             xvgrclose(fp);
310             sfree(hdr);
311             sfree(suffix);
312         }
313         if (opt2fn_null("-sqframe", NFILE, fnm))
314         {
315             snew(hdr, 25);
316             snew(suffix, GMX_PATH_MAX);
317             /* prepare header */
318             sprintf(hdr, "I(q), t = %f", t);
319             /* prepare output filename */
320             auto fnmdup = filenames;
321             sprintf(suffix, "-t%.2f", t);
322             add_suffix_to_output_names(fnmdup.data(), NFILE, suffix);
323             fp = xvgropen(
324                     opt2fn_null("-sqframe", NFILE, fnmdup.data()), hdr, "q (nm^-1)", "s(q)/s(0)", oenv);
325             for (i = 0; i < sqframecurrent->qn; i++)
326             {
327                 fprintf(fp, "%10.6f%10.6f\n", sqframecurrent->q[i], sqframecurrent->s[i]);
328             }
329             xvgrclose(fp);
330             sfree(hdr);
331             sfree(suffix);
332         }
333         /* free pr structure */
334         sfree(prframecurrent->gr);
335         sfree(prframecurrent->r);
336         sfree(prframecurrent);
337         /* free sq structure */
338         sfree(sqframecurrent->q);
339         sfree(sqframecurrent->s);
340         sfree(sqframecurrent);
341     } while (read_next_x(oenv, status, &t, x, box));
342     close_trx(status);
343
344     /* normalize histo */
345     normalize_probability(pr->grn, pr->gr);
346     sq = convert_histogram_to_intensity_curve(pr, start_q, end_q, q_step);
347     /* prepare pr.xvg */
348     fp = xvgropen(opt2fn_null("-pr", NFILE, fnm), "G(r)", "Distance (nm)", "Probability", oenv);
349     for (i = 0; i < pr->grn; i++)
350     {
351         fprintf(fp, "%10.6f%10.6f\n", pr->r[i], pr->gr[i]);
352     }
353     xvgrclose(fp);
354
355     /* prepare sq.xvg */
356     fp = xvgropen(opt2fn_null("-sq", NFILE, fnm), "I(q)", "q (nm^-1)", "s(q)/s(0)", oenv);
357     for (i = 0; i < sq->qn; i++)
358     {
359         fprintf(fp, "%10.6f%10.6f\n", sq->q[i], sq->s[i]);
360     }
361     xvgrclose(fp);
362     /*
363      * Clean up memory
364      */
365     sfree(pr->gr);
366     sfree(pr->r);
367     sfree(pr);
368     sfree(sq->q);
369     sfree(sq->s);
370     sfree(sq);
371
372     please_cite(stdout, "Garmay2012");
373
374     return 0;
375 }