Rework -Weverything
[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,2021, 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     t_pargs pa[] = {
100         { "-bin", FALSE, etREAL, { &binwidth }, "[HIDDEN]Binwidth (nm)" },
101         { "-mode", FALSE, etENUM, { emode }, "Mode for sans spectra calculation" },
102         { "-mcover",
103           FALSE,
104           etREAL,
105           { &mcover },
106           "Monte-Carlo coverage should be -1(default) or (0,1]" },
107         { "-method", FALSE, etENUM, { emethod }, "[HIDDEN]Method for sans spectra calculation" },
108         { "-pbc",
109           FALSE,
110           etBOOL,
111           { &bPBC },
112           "Use periodic boundary conditions for computing distances" },
113         { "-grid", FALSE, etREAL, { &grid }, "[HIDDEN]Grid spacing (in nm) for FFTs" },
114         { "-startq", FALSE, etREAL, { &start_q }, "Starting q (1/nm) " },
115         { "-endq", FALSE, etREAL, { &end_q }, "Ending q (1/nm)" },
116         { "-qstep", FALSE, etREAL, { &q_step }, "Stepping in q (1/nm)" },
117         { "-seed", FALSE, etINT, { &seed }, "Random seed for Monte-Carlo" },
118 #if GMX_OPENMP
119         { "-nt", FALSE, etINT, { &nthreads }, "Number of threads to start" },
120 #endif
121     };
122     FILE*                                fp;
123     const char *                         fnTPX, *fnTRX, *fnDAT = nullptr;
124     t_trxstatus*                         status;
125     t_topology*                          top  = nullptr;
126     gmx_rmpbc_t                          gpbc = nullptr;
127     gmx_bool                             bFFT = FALSE, bDEBYE = FALSE;
128     gmx_bool                             bMC     = FALSE;
129     PbcType                              pbcType = PbcType::Unset;
130     matrix                               box;
131     rvec*                                x;
132     int                                  natoms;
133     real                                 t;
134     char**                               grpname = nullptr;
135     int*                                 index   = nullptr;
136     int                                  isize;
137     int                                  i;
138     char*                                hdr            = nullptr;
139     char*                                suffix         = nullptr;
140     gmx_radial_distribution_histogram_t *prframecurrent = nullptr, *pr = nullptr;
141     gmx_static_structurefactor_t *       sqframecurrent = nullptr, *sq = nullptr;
142     gmx_output_env_t*                    oenv;
143
144     std::array<t_filenm, 8> filenames = { { { efTPR, "-s", nullptr, ffREAD },
145                                             { efTRX, "-f", nullptr, ffREAD },
146                                             { efNDX, nullptr, nullptr, ffOPTRD },
147                                             { efDAT, "-d", "nsfactor", ffOPTRD },
148                                             { efXVG, "-pr", "pr", ffWRITE },
149                                             { efXVG, "-sq", "sq", ffWRITE },
150                                             { efXVG, "-prframe", "prframe", ffOPTWR },
151                                             { efXVG, "-sqframe", "sqframe", ffOPTWR } } };
152     t_filenm*               fnm       = filenames.data();
153
154     const auto NFILE = filenames.size();
155
156     nthreads = gmx_omp_get_max_threads();
157
158     if (!parse_common_args(
159                 &argc, argv, PCA_CAN_TIME | PCA_TIME_UNIT, NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, nullptr, &oenv))
160     {
161         return 0;
162     }
163
164     /* check that binwidth not smaller than smallers distance */
165     check_binwidth(binwidth);
166     check_mcover(mcover);
167
168     /* setting number of omp threads globaly */
169     gmx_omp_set_num_threads(nthreads);
170
171     /* Now try to parse opts for modes */
172     GMX_RELEASE_ASSERT(emethod[0] != nullptr, "Options inconsistency; emethod[0] is NULL");
173     switch (emethod[0][0])
174     {
175         case 'd':
176             bDEBYE = TRUE;
177             switch (emode[0][0])
178             {
179                 case 'd': bMC = FALSE; break;
180                 case 'm': bMC = TRUE; break;
181                 default: break;
182             }
183             break;
184         case 'f': bFFT = TRUE; break;
185         default: break;
186     }
187
188     if (bDEBYE)
189     {
190         if (bMC)
191         {
192             fprintf(stderr, "Using Monte Carlo Debye method to calculate spectrum\n");
193         }
194         else
195         {
196             fprintf(stderr, "Using direct Debye method to calculate spectrum\n");
197         }
198     }
199     else if (bFFT)
200     {
201         gmx_fatal(FARGS, "FFT method not implemented!");
202     }
203     else
204     {
205         gmx_fatal(FARGS, "Unknown combination for mode and method!");
206     }
207
208     /* Try to read files */
209     fnDAT = ftp2fn(efDAT, NFILE, fnm);
210     fnTPX = ftp2fn(efTPR, NFILE, fnm);
211     fnTRX = ftp2fn(efTRX, NFILE, fnm);
212
213     gnsf = gmx_neutronstructurefactors_init(fnDAT);
214     fprintf(stderr, "Read %d atom names from %s with neutron scattering parameters\n\n", gnsf->nratoms, fnDAT);
215
216     snew(top, 1);
217     snew(grpname, 1);
218     snew(index, 1);
219
220     read_tps_conf(fnTPX, top, &pbcType, &x, nullptr, box, TRUE);
221
222     printf("\nPlease select group for SANS spectra calculation:\n");
223     get_index(&(top->atoms), ftp2fn_null(efNDX, NFILE, fnm), 1, &isize, &index, grpname);
224
225     gsans = gmx_sans_init(top, gnsf);
226
227     /* Prepare reference frame */
228     if (bPBC)
229     {
230         gpbc = gmx_rmpbc_init(&top->idef, pbcType, top->atoms.nr);
231         gmx_rmpbc(gpbc, top->atoms.nr, box, x);
232     }
233
234     natoms = read_first_x(oenv, &status, fnTRX, &t, &x, box);
235     if (natoms != top->atoms.nr)
236     {
237         fprintf(stderr,
238                 "\nWARNING: number of atoms in tpx (%d) and trajectory (%d) do not match\n",
239                 natoms,
240                 top->atoms.nr);
241     }
242
243     do
244     {
245         if (bPBC)
246         {
247             gmx_rmpbc(gpbc, top->atoms.nr, box, x);
248         }
249         /* allocate memory for pr */
250         if (pr == nullptr)
251         {
252             /* in case its first frame to read */
253             snew(pr, 1);
254         }
255         /*  realy calc p(r) */
256         prframecurrent = calc_radial_distribution_histogram(
257                 gsans, x, box, index, isize, binwidth, bMC, bNORM, mcover, seed);
258         /* copy prframecurrent -> pr and summ up pr->gr[i] */
259         /* allocate and/or resize memory for pr->gr[i] and pr->r[i] */
260         if (pr->gr == nullptr)
261         {
262             /* check if we use pr->gr first time */
263             snew(pr->gr, prframecurrent->grn);
264             snew(pr->r, prframecurrent->grn);
265         }
266         else
267         {
268             /* resize pr->gr and pr->r if needed to preven overruns */
269             if (prframecurrent->grn > pr->grn)
270             {
271                 srenew(pr->gr, prframecurrent->grn);
272                 srenew(pr->r, prframecurrent->grn);
273             }
274         }
275         pr->grn      = prframecurrent->grn;
276         pr->binwidth = prframecurrent->binwidth;
277         /* summ up gr and fill r */
278         for (i = 0; i < prframecurrent->grn; i++)
279         {
280             pr->gr[i] += prframecurrent->gr[i];
281             pr->r[i] = prframecurrent->r[i];
282         }
283         /* normalize histo */
284         normalize_probability(prframecurrent->grn, prframecurrent->gr);
285         /* convert p(r) to sq */
286         sqframecurrent = convert_histogram_to_intensity_curve(prframecurrent, start_q, end_q, q_step);
287         /* print frame data if needed */
288         if (opt2fn_null("-prframe", NFILE, fnm))
289         {
290             snew(hdr, 25);
291             snew(suffix, GMX_PATH_MAX);
292             /* prepare header */
293             sprintf(hdr, "g(r), t = %f", t);
294             /* prepare output filename */
295             auto fnmdup = filenames;
296             sprintf(suffix, "-t%.2f", t);
297             add_suffix_to_output_names(fnmdup.data(), NFILE, suffix);
298             fp = xvgropen(opt2fn_null("-prframe", NFILE, fnmdup.data()),
299                           hdr,
300                           "Distance (nm)",
301                           "Probability",
302                           oenv);
303             for (i = 0; i < prframecurrent->grn; i++)
304             {
305                 fprintf(fp, "%10.6f%10.6f\n", prframecurrent->r[i], prframecurrent->gr[i]);
306             }
307             xvgrclose(fp);
308             sfree(hdr);
309             sfree(suffix);
310         }
311         if (opt2fn_null("-sqframe", NFILE, fnm))
312         {
313             snew(hdr, 25);
314             snew(suffix, GMX_PATH_MAX);
315             /* prepare header */
316             sprintf(hdr, "I(q), t = %f", t);
317             /* prepare output filename */
318             auto fnmdup = filenames;
319             sprintf(suffix, "-t%.2f", t);
320             add_suffix_to_output_names(fnmdup.data(), NFILE, suffix);
321             fp = xvgropen(
322                     opt2fn_null("-sqframe", NFILE, fnmdup.data()), hdr, "q (nm^-1)", "s(q)/s(0)", oenv);
323             for (i = 0; i < sqframecurrent->qn; i++)
324             {
325                 fprintf(fp, "%10.6f%10.6f\n", sqframecurrent->q[i], sqframecurrent->s[i]);
326             }
327             xvgrclose(fp);
328             sfree(hdr);
329             sfree(suffix);
330         }
331         /* free pr structure */
332         sfree(prframecurrent->gr);
333         sfree(prframecurrent->r);
334         sfree(prframecurrent);
335         /* free sq structure */
336         sfree(sqframecurrent->q);
337         sfree(sqframecurrent->s);
338         sfree(sqframecurrent);
339     } while (read_next_x(oenv, status, &t, x, box));
340     close_trx(status);
341
342     /* normalize histo */
343     normalize_probability(pr->grn, pr->gr);
344     sq = convert_histogram_to_intensity_curve(pr, start_q, end_q, q_step);
345     /* prepare pr.xvg */
346     fp = xvgropen(opt2fn_null("-pr", NFILE, fnm), "G(r)", "Distance (nm)", "Probability", oenv);
347     for (i = 0; i < pr->grn; i++)
348     {
349         fprintf(fp, "%10.6f%10.6f\n", pr->r[i], pr->gr[i]);
350     }
351     xvgrclose(fp);
352
353     /* prepare sq.xvg */
354     fp = xvgropen(opt2fn_null("-sq", NFILE, fnm), "I(q)", "q (nm^-1)", "s(q)/s(0)", oenv);
355     for (i = 0; i < sq->qn; i++)
356     {
357         fprintf(fp, "%10.6f%10.6f\n", sq->q[i], sq->s[i]);
358     }
359     xvgrclose(fp);
360     /*
361      * Clean up memory
362      */
363     sfree(pr->gr);
364     sfree(pr->r);
365     sfree(pr);
366     sfree(sq->q);
367     sfree(sq->s);
368     sfree(sq);
369
370     please_cite(stdout, "Garmay2012");
371
372     return 0;
373 }