/* The function used for spawning threads. Extracts the mdrunner() arguments from its one argument and calls mdrunner(), after making a commrec. */ static void mdrunner_start_fn(void *arg) { struct mdrunner_arglist *mda = (struct mdrunner_arglist*)arg; struct mdrunner_arglist mc = *mda; /* copy the arg list to make sure that it's thread-local. This doesn't copy pointed-to items, of course, but those are all const. */ t_commrec *cr; /* we need a local version of this */ FILE *fplog = NULL; t_filenm *fnm; fnm = dup_tfn(mc.nfile, mc.fnm); cr = reinitialize_commrec_for_this_thread(mc.cr); if (MASTER(cr)) { fplog = mc.fplog; } mdrunner(&mc.hw_opt, fplog, cr, mc.nfile, fnm, mc.oenv, mc.bVerbose, mc.bCompact, mc.nstglobalcomm, mc.ddxyz, mc.dd_node_order, mc.rdd, mc.rconstr, mc.dddlb_opt, mc.dlb_scale, mc.ddcsx, mc.ddcsy, mc.ddcsz, mc.nbpu_opt, mc.nstlist_cmdline, mc.nsteps_cmdline, mc.nstepout, mc.resetstep, mc.nmultisim, mc.repl_ex_nst, mc.repl_ex_nex, mc.repl_ex_seed, mc.pforce, mc.cpt_period, mc.max_hours, mc.imdport, mc.Flags); }
int gmx_sans(int argc, char *argv[]) { const char *desc[] = { "[THISMODULE] computes SANS spectra using Debye formula.", "It currently uses topology file (since it need to assigne element for each atom).", "[PAR]", "Parameters:[PAR]" "[TT]-pr[tt] Computes normalized g(r) function averaged over trajectory[PAR]", "[TT]-prframe[tt] Computes normalized g(r) function for each frame[PAR]", "[TT]-sq[tt] Computes SANS intensity curve averaged over trajectory[PAR]", "[TT]-sqframe[tt] Computes SANS intensity curve for each frame[PAR]", "[TT]-startq[tt] Starting q value in nm[PAR]", "[TT]-endq[tt] Ending q value in nm[PAR]", "[TT]-qstep[tt] Stepping in q space[PAR]", "Note: When using Debye direct method computational cost increases as", "1/2 * N * (N - 1) where N is atom number in group of interest.", "[PAR]", "WARNING: If sq or pr specified this tool can produce large number of files! Up to two times larger than number of frames!" }; static gmx_bool bPBC = TRUE; static gmx_bool bNORM = FALSE; static real binwidth = 0.2, grid = 0.05; /* bins shouldnt be smaller then smallest bond (~0.1nm) length */ static real start_q = 0.0, end_q = 2.0, q_step = 0.01; static real mcover = -1; static unsigned int seed = 0; static int nthreads = -1; static const char *emode[] = { NULL, "direct", "mc", NULL }; static const char *emethod[] = { NULL, "debye", "fft", NULL }; gmx_neutron_atomic_structurefactors_t *gnsf; gmx_sans_t *gsans; #define NPA asize(pa) t_pargs pa[] = { { "-bin", FALSE, etREAL, {&binwidth}, "[HIDDEN]Binwidth (nm)" }, { "-mode", FALSE, etENUM, {emode}, "Mode for sans spectra calculation" }, { "-mcover", FALSE, etREAL, {&mcover}, "Monte-Carlo coverage should be -1(default) or (0,1]"}, { "-method", FALSE, etENUM, {emethod}, "[HIDDEN]Method for sans spectra calculation" }, { "-pbc", FALSE, etBOOL, {&bPBC}, "Use periodic boundary conditions for computing distances" }, { "-grid", FALSE, etREAL, {&grid}, "[HIDDEN]Grid spacing (in nm) for FFTs" }, {"-startq", FALSE, etREAL, {&start_q}, "Starting q (1/nm) "}, {"-endq", FALSE, etREAL, {&end_q}, "Ending q (1/nm)"}, { "-qstep", FALSE, etREAL, {&q_step}, "Stepping in q (1/nm)"}, { "-seed", FALSE, etINT, {&seed}, "Random seed for Monte-Carlo"}, #ifdef GMX_OPENMP { "-nt", FALSE, etINT, {&nthreads}, "Number of threads to start"}, #endif }; FILE *fp; const char *fnTPX, *fnNDX, *fnTRX, *fnDAT = NULL; t_trxstatus *status; t_topology *top = NULL; t_atom *atom = NULL; gmx_rmpbc_t gpbc = NULL; gmx_bool bTPX; gmx_bool bFFT = FALSE, bDEBYE = FALSE; gmx_bool bMC = FALSE; int ePBC = -1; matrix box; char title[STRLEN]; rvec *x; int natoms; real t; char **grpname = NULL; atom_id *index = NULL; int isize; int i, j; char *hdr = NULL; char *suffix = NULL; t_filenm *fnmdup = NULL; gmx_radial_distribution_histogram_t *prframecurrent = NULL, *pr = NULL; gmx_static_structurefactor_t *sqframecurrent = NULL, *sq = NULL; output_env_t oenv; #define NFILE asize(fnm) t_filenm fnm[] = { { efTPX, "-s", NULL, ffREAD }, { efTRX, "-f", NULL, ffREAD }, { efNDX, NULL, NULL, ffOPTRD }, { efDAT, "-d", "nsfactor", ffOPTRD }, { efXVG, "-pr", "pr", ffWRITE }, { efXVG, "-sq", "sq", ffWRITE }, { efXVG, "-prframe", "prframe", ffOPTWR }, { efXVG, "-sqframe", "sqframe", ffOPTWR } }; nthreads = gmx_omp_get_max_threads(); if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_TIME_UNIT | PCA_BE_NICE, NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv)) { return 0; } /* check that binwidth not smaller than smallers distance */ check_binwidth(binwidth); check_mcover(mcover); /* setting number of omp threads globaly */ gmx_omp_set_num_threads(nthreads); /* Now try to parse opts for modes */ switch (emethod[0][0]) { case 'd': bDEBYE = TRUE; switch (emode[0][0]) { case 'd': bMC = FALSE; break; case 'm': bMC = TRUE; break; default: break; } break; case 'f': bFFT = TRUE; break; default: break; } if (bDEBYE) { if (bMC) { fprintf(stderr, "Using Monte Carlo Debye method to calculate spectrum\n"); } else { fprintf(stderr, "Using direct Debye method to calculate spectrum\n"); } } else if (bFFT) { gmx_fatal(FARGS, "FFT method not implemented!"); } else { gmx_fatal(FARGS, "Unknown combination for mode and method!"); } /* Try to read files */ fnDAT = ftp2fn(efDAT, NFILE, fnm); fnTPX = ftp2fn(efTPX, NFILE, fnm); fnTRX = ftp2fn(efTRX, NFILE, fnm); gnsf = gmx_neutronstructurefactors_init(fnDAT); fprintf(stderr, "Read %d atom names from %s with neutron scattering parameters\n\n", gnsf->nratoms, fnDAT); snew(top, 1); snew(grpname, 1); snew(index, 1); bTPX = read_tps_conf(fnTPX, title, top, &ePBC, &x, NULL, box, TRUE); printf("\nPlease select group for SANS spectra calculation:\n"); get_index(&(top->atoms), ftp2fn_null(efNDX, NFILE, fnm), 1, &isize, &index, grpname); gsans = gmx_sans_init(top, gnsf); /* Prepare reference frame */ if (bPBC) { gpbc = gmx_rmpbc_init(&top->idef, ePBC, top->atoms.nr); gmx_rmpbc(gpbc, top->atoms.nr, box, x); } natoms = read_first_x(oenv, &status, fnTRX, &t, &x, box); if (natoms != top->atoms.nr) { fprintf(stderr, "\nWARNING: number of atoms in tpx (%d) and trajectory (%d) do not match\n", natoms, top->atoms.nr); } do { if (bPBC) { gmx_rmpbc(gpbc, top->atoms.nr, box, x); } /* allocate memory for pr */ if (pr == NULL) { /* in case its first frame to read */ snew(pr, 1); } /* realy calc p(r) */ prframecurrent = calc_radial_distribution_histogram(gsans, x, box, index, isize, binwidth, bMC, bNORM, mcover, seed); /* copy prframecurrent -> pr and summ up pr->gr[i] */ /* allocate and/or resize memory for pr->gr[i] and pr->r[i] */ if (pr->gr == NULL) { /* check if we use pr->gr first time */ snew(pr->gr, prframecurrent->grn); snew(pr->r, prframecurrent->grn); } else { /* resize pr->gr and pr->r if needed to preven overruns */ if (prframecurrent->grn > pr->grn) { srenew(pr->gr, prframecurrent->grn); srenew(pr->r, prframecurrent->grn); } } pr->grn = prframecurrent->grn; pr->binwidth = prframecurrent->binwidth; /* summ up gr and fill r */ for (i = 0; i < prframecurrent->grn; i++) { pr->gr[i] += prframecurrent->gr[i]; pr->r[i] = prframecurrent->r[i]; } /* normalize histo */ normalize_probability(prframecurrent->grn, prframecurrent->gr); /* convert p(r) to sq */ sqframecurrent = convert_histogram_to_intensity_curve(prframecurrent, start_q, end_q, q_step); /* print frame data if needed */ if (opt2fn_null("-prframe", NFILE, fnm)) { snew(hdr, 25); snew(suffix, GMX_PATH_MAX); /* prepare header */ sprintf(hdr, "g(r), t = %f", t); /* prepare output filename */ fnmdup = dup_tfn(NFILE, fnm); sprintf(suffix, "-t%.2f", t); add_suffix_to_output_names(fnmdup, NFILE, suffix); fp = xvgropen(opt2fn_null("-prframe", NFILE, fnmdup), hdr, "Distance (nm)", "Probability", oenv); for (i = 0; i < prframecurrent->grn; i++) { fprintf(fp, "%10.6f%10.6f\n", prframecurrent->r[i], prframecurrent->gr[i]); } done_filenms(NFILE, fnmdup); fclose(fp); sfree(hdr); sfree(suffix); sfree(fnmdup); } if (opt2fn_null("-sqframe", NFILE, fnm)) { snew(hdr, 25); snew(suffix, GMX_PATH_MAX); /* prepare header */ sprintf(hdr, "I(q), t = %f", t); /* prepare output filename */ fnmdup = dup_tfn(NFILE, fnm); sprintf(suffix, "-t%.2f", t); add_suffix_to_output_names(fnmdup, NFILE, suffix); fp = xvgropen(opt2fn_null("-sqframe", NFILE, fnmdup), hdr, "q (nm^-1)", "s(q)/s(0)", oenv); for (i = 0; i < sqframecurrent->qn; i++) { fprintf(fp, "%10.6f%10.6f\n", sqframecurrent->q[i], sqframecurrent->s[i]); } done_filenms(NFILE, fnmdup); fclose(fp); sfree(hdr); sfree(suffix); sfree(fnmdup); } /* free pr structure */ sfree(prframecurrent->gr); sfree(prframecurrent->r); sfree(prframecurrent); /* free sq structure */ sfree(sqframecurrent->q); sfree(sqframecurrent->s); sfree(sqframecurrent); } while (read_next_x(oenv, status, &t, x, box)); close_trj(status); /* normalize histo */ normalize_probability(pr->grn, pr->gr); sq = convert_histogram_to_intensity_curve(pr, start_q, end_q, q_step); /* prepare pr.xvg */ fp = xvgropen(opt2fn_null("-pr", NFILE, fnm), "G(r)", "Distance (nm)", "Probability", oenv); for (i = 0; i < pr->grn; i++) { fprintf(fp, "%10.6f%10.6f\n", pr->r[i], pr->gr[i]); } xvgrclose(fp); /* prepare sq.xvg */ fp = xvgropen(opt2fn_null("-sq", NFILE, fnm), "I(q)", "q (nm^-1)", "s(q)/s(0)", oenv); for (i = 0; i < sq->qn; i++) { fprintf(fp, "%10.6f%10.6f\n", sq->q[i], sq->s[i]); } xvgrclose(fp); /* * Clean up memory */ sfree(pr->gr); sfree(pr->r); sfree(pr); sfree(sq->q); sfree(sq->s); sfree(sq); please_cite(stdout, "Garmay2012"); return 0; }
/* called by mdrunner() to start a specific number of threads (including the main thread) for thread-parallel runs. This in turn calls mdrunner() for each thread. All options besides nthreads are the same as for mdrunner(). */ static t_commrec *mdrunner_start_threads(gmx_hw_opt_t *hw_opt, FILE *fplog, t_commrec *cr, int nfile, const t_filenm fnm[], const output_env_t oenv, gmx_bool bVerbose, gmx_bool bCompact, int nstglobalcomm, ivec ddxyz, int dd_node_order, real rdd, real rconstr, const char *dddlb_opt, real dlb_scale, const char *ddcsx, const char *ddcsy, const char *ddcsz, const char *nbpu_opt, int nstlist_cmdline, gmx_int64_t nsteps_cmdline, int nstepout, int resetstep, int nmultisim, int repl_ex_nst, int repl_ex_nex, int repl_ex_seed, real pforce, real cpt_period, real max_hours, unsigned long Flags) { int ret; struct mdrunner_arglist *mda; t_commrec *crn; /* the new commrec */ t_filenm *fnmn; /* first check whether we even need to start tMPI */ if (hw_opt->nthreads_tmpi < 2) { return cr; } /* a few small, one-time, almost unavoidable memory leaks: */ snew(mda, 1); fnmn = dup_tfn(nfile, fnm); /* fill the data structure to pass as void pointer to thread start fn */ /* hw_opt contains pointers, which should all be NULL at this stage */ mda->hw_opt = *hw_opt; mda->fplog = fplog; mda->cr = cr; mda->nfile = nfile; mda->fnm = fnmn; mda->oenv = oenv; mda->bVerbose = bVerbose; mda->bCompact = bCompact; mda->nstglobalcomm = nstglobalcomm; mda->ddxyz[XX] = ddxyz[XX]; mda->ddxyz[YY] = ddxyz[YY]; mda->ddxyz[ZZ] = ddxyz[ZZ]; mda->dd_node_order = dd_node_order; mda->rdd = rdd; mda->rconstr = rconstr; mda->dddlb_opt = dddlb_opt; mda->dlb_scale = dlb_scale; mda->ddcsx = ddcsx; mda->ddcsy = ddcsy; mda->ddcsz = ddcsz; mda->nbpu_opt = nbpu_opt; mda->nstlist_cmdline = nstlist_cmdline; mda->nsteps_cmdline = nsteps_cmdline; mda->nstepout = nstepout; mda->resetstep = resetstep; mda->nmultisim = nmultisim; mda->repl_ex_nst = repl_ex_nst; mda->repl_ex_nex = repl_ex_nex; mda->repl_ex_seed = repl_ex_seed; mda->pforce = pforce; mda->cpt_period = cpt_period; mda->max_hours = max_hours; mda->Flags = Flags; /* now spawn new threads that start mdrunner_start_fn(), while the main thread returns, we set thread affinity later */ ret = tMPI_Init_fn(TRUE, hw_opt->nthreads_tmpi, TMPI_AFFINITY_NONE, mdrunner_start_fn, (void*)(mda) ); if (ret != TMPI_SUCCESS) { return NULL; } crn = reinitialize_commrec_for_this_thread(cr); return crn; }