int test_kinetic_energy() { const unsigned long ndim=100000; double * mtm; unsigned long i; double kin_eng,kin_eng_test; mtm=(double*)malloc(ndim*sizeof(double)); kin_eng=0; for(i=0;i<ndim;++i) { mtm[i]=1.; kin_eng+=mtm[i]*mtm[i]; } kin_eng*=0.5; kinetic_energy(ndim,mtm,&kin_eng_test); if(kin_eng != kin_eng_test) { free(mtm); return EXIT_FAILURE; } free(mtm); return EXIT_SUCCESS; }
double* get_statistics() { //kinetic energy, LJ potential energy, springiness potential energy, pressure, instantaneous tempretature // double* stats = (double*) malloc( (INST_TEMP+1) * sizeof(double) ); // to nice extending for another constants double* stats = (double*) malloc( (5) * sizeof(double) ); // to nice extending for another constants stats[0] = kinetic_energy(); #ifdef VERBOSE printf("kinetic: %lf\n",stats[0]); #endif stats[1] = LJ_potential(); #ifdef VERBOSE printf("Lj : %lf\n",stats[1]); #endif stats[2] = springiness_potential(); #ifdef VERBOSE printf("spring : %lf\n",stats[2]); #endif stats[3] = pressure(); #ifdef VERBOSE printf("press. : %lf\n",stats[3]); #endif //stats[4] = temperature(); stats[4] = temperature_from_kinetic(stats[0]); #ifdef VERBOSE printf("temp. : %lf\n",stats[4]); #endif return stats; }
int main(int argc, char *argv[]) { //============= set up MPI ================= MPI_Status status; const int root_process = 0; int ierr, my_id, an_id, num_procs; // tag for MPI message: energy minimization convergence const int tag_99 = 99; // initialize MPI, get my_id for this process and total number of processes ierr = MPI_Init(&argc, &argv); ierr = MPI_Comm_rank(MPI_COMM_WORLD, &my_id); ierr = MPI_Comm_size(MPI_COMM_WORLD, &num_procs); //============ get input files ============= // default filenames char input_gro[MAX_STR_LEN], input_param[MAX_STR_LEN], input_mdset[MAX_STR_LEN]; strcpy(input_gro, "init.gro"); strcpy(input_param, "param.txt"); strcpy(input_mdset, "mdset.txt"); // parse arguments (input files) // -gro for gro file // -par for parameter file // -mds for MD settings int iarg = 1; while (1) { if (iarg >= argc) { break; } if (0 == strcmp(argv[iarg], "-gro") && iarg < argc) { strcpy(input_gro, argv[iarg + 1]); iarg += 2; } else if (0 == strcmp(argv[iarg], "-par") && iarg < argc) { strcpy(input_param, argv[iarg + 1]); iarg += 2; } else if (0 == strcmp(argv[iarg], "-mds") && iarg < argc) { strcpy(input_mdset, argv[iarg + 1]); iarg += 2; } else { ++ iarg; } } //========= define variables and read MD settings =========== // varialbles to read from input_mdset RunSet *p_runset = my_malloc(sizeof(RunSet)); Metal *p_metal = my_malloc(sizeof(Metal)); // read md settings from input_mdset read_settings(input_mdset, p_runset, p_metal); // initialize timer time_t start_t = time(NULL); struct timeval tv; gettimeofday(&tv, NULL); double start_time = (tv.tv_sec) + (tv.tv_usec) * 1.0e-6; // time_used[0] = total // time_used[1] = QSC density // time_used[2] = QSC force // time_used[3] = Bonded // time_used[4] = Nonbonded // time_used[5] = CPIM matrix // time_used[6] = CPIM vector // time_used[7] = CPIM solve // time_used[8] = CPIM force // time_used[10] = QSC density communication double **time_used = my_malloc(sizeof(double *) * num_procs); for(an_id = 0; an_id < num_procs; ++ an_id) { time_used[an_id] = my_malloc(sizeof(double) * 15); int it; for(it = 0; it < 15; ++ it) { time_used[an_id][it] = 0.0; } } // Coulomb type: cut_off, wolf_sum if (0 == strcmp(p_runset->coulomb_type, "cut_off" )) { p_runset->use_coulomb = 0; } else if (0 == strcmp(p_runset->coulomb_type, "wolf_sum")) { p_runset->use_coulomb = 1; } else { printf("Error: unknown coulomb_type %s!\n", p_runset->coulomb_type); exit(1); } // Damped shifted force (DSF) approach for electrostatics // Ref.: Fennell and Gezelter, J. Chem. Phys. 2006, 124, 234104 // dx.doi.org/10.1063/1.2206581 // Based on: Wolf et al., J. Chem. Phys. 1999, 110, 8254 // dx.doi.org/10.1063/1.478738 // Zahn et al., J. Phys. Chem. B 2002, 106, 10725-10732 // dx.doi.org/10.1021/jp025949h // Benchmark: McCann and Acevedo, J. Chem. Theory Comput. 2013, 9, 944-950 // dx.doi.org/10.1021/ct300961e // note: p_runset->rCut and p_runset->w_alpha were read from mdset.txt p_runset->rCut2 = p_runset->rCut * p_runset->rCut; double rCut = p_runset->rCut; double rCut2 = p_runset->rCut2; double w_alpha = p_runset->w_alpha; p_runset->w_a2_sqrtPI = w_alpha * 2.0 * INV_SQRT_PI; p_runset->w_Const = erfc(w_alpha * rCut) / rCut2 + p_runset->w_a2_sqrtPI * exp(-w_alpha * w_alpha * rCut2) / rCut; p_runset->w_erfc_arCut = erfc(w_alpha * rCut)/ rCut; // van der Waals type: cut_off or shifted if (0 == strcmp(p_runset->vdw_type, "cut_off")) { p_runset->use_vdw = 0; } else if (0 == strcmp(p_runset->vdw_type, "shifted")) { p_runset->use_vdw = 1; } else { printf("Error: unknown vdw_type %s!\n", p_runset->vdw_type); exit(1); } // Shifted force method for Lennard-Jones potential // Ref: Toxvaerd and Dyre, J. Chem. Phys. 2011, 134, 081102 // dx.doi.org/10.1063/1.3558787 p_runset->inv_rc12 = 1.0 / pow(rCut, 12.0); p_runset->inv_rc6 = 1.0 / pow(rCut, 6.0); //============== read force field parameters from param.txt ================= Topol *p_topol = my_malloc(sizeof(Topol)); int mol, atom; int number_VSites, number_Cstrs; // variables for bonded and nonbonded interaction // CPIM: capacitance-polarizability interaction model // Ref.: a) Jensen and Jensen, J. Phys. Chem. C, 2008, 112, 15697-15703 // dx.doi.org/10.1021/jp804116z // b) Morton and Jensen, J. Chem. Phys., 2010, 133, 074103 // dx.doi.org/10.1063/1.3457365 // // p_metal->cpff_polar: polarizability // p_metal->cpff_capac: capacitance // p_metal->n_NPs: number of nanoparticles // p_metal->cpff_chg: total charge of a nanoparticle // p_metal->start_NP: first atom of a nanoparticle // p_metal->end_NP: last atom of a nanoparticle // read parameters from input_param, step 1 read_param_1(input_param, p_topol, p_metal); // allocate memory for arrays // CPIM charge and indices p_metal->cpff_chg = my_malloc(p_metal->n_NPs * sizeof(double)); p_metal->start_NP = my_malloc(p_metal->n_NPs * sizeof(int)); p_metal->end_NP = my_malloc(p_metal->n_NPs * sizeof(int)); // molecules and atom parameters // For a given molecule type (mol from 0 to p_topol->mol_types-1): // p_topol->atom_num[mol]: its number of atoms // p_topol->mol_num[mol]: the number of this type of molecule in the system p_topol->atom_num = my_malloc(p_topol->mol_types * sizeof(int)); p_topol->mol_num = my_malloc(p_topol->mol_types * sizeof(int)); p_topol->atom_param = my_malloc(p_topol->mol_types * sizeof(AtomParam *)); // van der Waals interaction parameters NonbondedParam *data_nonbonded = my_malloc_2(p_topol->n_types * p_topol->n_types * sizeof(NonbondedParam), "data_nonbonded"); p_topol->nonbonded_param = my_malloc(p_topol->n_types * sizeof(NonbondedParam *)); int i_type; for(i_type = 0; i_type < p_topol->n_types; ++ i_type) { p_topol->nonbonded_param[i_type] = &(data_nonbonded[p_topol->n_types * i_type]); } // bonded potentials: bond, pair, angle, dihedral int *data_bonded = my_malloc(sizeof(int) * p_topol->mol_types * 6); p_topol->n_bonds = &(data_bonded[0]); p_topol->n_pairs = &(data_bonded[p_topol->mol_types]); p_topol->n_angles = &(data_bonded[p_topol->mol_types * 2]); p_topol->n_dihedrals = &(data_bonded[p_topol->mol_types * 3]); p_topol->n_vsites = &(data_bonded[p_topol->mol_types * 4]); p_topol->n_constraints = &(data_bonded[p_topol->mol_types * 5]); p_topol->vsite_funct = my_malloc(p_topol->mol_types * sizeof(int *)) ; p_topol->bond_param = my_malloc(p_topol->mol_types * sizeof(BondParam *)); p_topol->pair_param = my_malloc(p_topol->mol_types * sizeof(PairParam *)) ; p_topol->angle_param = my_malloc(p_topol->mol_types * sizeof(AngleParam *)); p_topol->dihedral_param = my_malloc(p_topol->mol_types * sizeof(DihedralParam *)) ; p_topol->vsite_4 = my_malloc(p_topol->mol_types * sizeof(VSite_4 *)) ; p_topol->constraint = my_malloc(p_topol->mol_types * sizeof(Constraint *)) ; p_topol->exclude = my_malloc(p_topol->mol_types * sizeof(int **)) ; // Quantum Sutton-Chen densities for metal if (p_metal->min >=0 && p_metal->max >= p_metal->min) { p_metal->inv_sqrt_dens = my_malloc(sizeof(double) * p_metal->num); } // read parameters from input_param, step 2 read_param_2(input_param, p_topol, p_metal); int nAtoms = p_topol->n_atoms; int nMols = p_topol->n_mols; // count number of virtual sites and constraints number_VSites = 0; number_Cstrs = 0; for(mol = 0; mol < p_topol->mol_types; ++ mol) { number_VSites += p_topol->n_vsites[mol] * p_topol->mol_num[mol]; number_Cstrs += p_topol->n_constraints[mol] * p_topol->mol_num[mol]; } // Gaussian distribution width for capacitance-polarizability model // see Mayer, Phys. Rev. B 2007, 75, 045407 // and Jensen, J. Phys. Chem. C 2008, 112, 15697 p_metal->inv_polar = 1.0 / p_metal->cpff_polar; p_metal->inv_capac = 1.0 / p_metal->cpff_capac; double R_q = sqrt(2.0 / M_PI) * p_metal->cpff_capac; double R_p = pow(sqrt(2.0 / M_PI) * p_metal->cpff_polar / 3.0, 1.0 / 3.0); p_metal->inv_R_qq = 1.0 / sqrt(R_q * R_q + R_q * R_q); p_metal->inv_R_pq = 1.0 / sqrt(R_p * R_p + R_q * R_q); p_metal->inv_R_pp = 1.0 / sqrt(R_p * R_p + R_p * R_p); // print info if (root_process == my_id) { printf("\n"); printf(" +-----------------------------------------------------+\n"); printf(" | CapacMD program version 1.0 |\n"); printf(" | Xin Li, TheoChemBio, KTH, Stockholm |\n"); printf(" +-----------------------------------------------------+\n"); printf("\n"); printf(" .------------------ reference paper ------------------.\n"); printf("\n"); printf(" Molecular Dynamics Simulations using a Capacitance-Polarizability Force Field,\n"); printf(" Xin Li and Hans Agren, J. Phys. Chem. C, 2015, DOI: 10.1021/acs.jpcc.5b04347\n"); printf("\n"); printf("\n"); printf(" Calculation started at %s", ctime(&start_t)); printf(" Parallelized via MPI, number of processors = %d\n", num_procs); printf("\n"); printf("\n"); printf(" .------------------ run parameters -------------------.\n"); printf("\n"); printf(" run_type = %s, ensemble = %s\n", p_runset->run_type, p_runset->ensemble); printf(" vdw_type = %s, coulomb_type = %s\n", p_runset->vdw_type, p_runset->coulomb_type); printf(" rCut = %.3f nm, ", rCut); if (1 == p_runset->use_coulomb) { printf("alpha = %.3f nm^-1", p_runset->w_alpha); } printf("\n"); printf(" ref_T = %.1f K\n", p_runset->ref_temp); printf("\n"); printf("\n"); printf(" .------------------- molecule info -------------------.\n"); printf("\n"); printf(" There are %d types of molecules.\n", p_topol->mol_types); printf("\n"); for(mol = 0; mol < p_topol->mol_types; ++ mol) { printf(" Molecule[%5d], num= %5d\n", mol, p_topol->mol_num[mol]); for(atom = 0; atom < p_topol->atom_num[mol]; ++ atom) { printf(" Atom[%5d], charge= %8.3f, mass= %8.3f, atomtype= %5d\n", atom, p_topol->atom_param[mol][atom].charge, p_topol->atom_param[mol][atom].mass, p_topol->atom_param[mol][atom].atomtype); } printf("\n"); } printf("\n"); } //=========== distribute molecules/atoms/metals among the procs ============== Task *p_task = my_malloc(sizeof(Task)); int *data_start_end = my_malloc(sizeof(int) * num_procs * 6); p_task->start_mol = &(data_start_end[0]); p_task->end_mol = &(data_start_end[num_procs]); p_task->start_atom = &(data_start_end[num_procs * 2]); p_task->end_atom = &(data_start_end[num_procs * 3]); p_task->start_metal = &(data_start_end[num_procs * 4]); p_task->end_metal = &(data_start_end[num_procs * 5]); find_start_end(p_task->start_mol, p_task->end_mol, nMols, num_procs); find_start_end(p_task->start_atom, p_task->end_atom, nAtoms, num_procs); find_start_end(p_task->start_metal, p_task->end_metal, p_metal->num, num_procs); long int *data_long_start_end = my_malloc(sizeof(long int) * num_procs * 2); p_task->start_pair = &(data_long_start_end[0]); p_task->end_pair = &(data_long_start_end[num_procs]); long int n_pairs = nAtoms * (nAtoms - 1) / 2; find_start_end_long(p_task->start_pair, p_task->end_pair, n_pairs, num_procs); //============= assign indices, masses and charges ======================= // For each atom in the system (i from 0 to p_topol->n_atoms-1) // atom_info[i].iAtom: the index of this atom in its molecule type // atom_info[i].iMol: the index of its molecule type // atom_info[i].molID: the index of its molecule in the system // For each molecule in the system (im from 0 to p_topol->n_mols-1) // mol_info[im].mini: the index of its first atom in the system // mol_info[im].maxi: the index of its last atom in the system Atom_Info *atom_info = my_malloc(nAtoms * sizeof(Atom_Info)); Mol_Info *mol_info = my_malloc(nMols * sizeof(Mol_Info)); assign_indices(p_topol, p_metal, atom_info, mol_info); //======= allocate memory for coordinates, velocities and forces ========== System *p_system = my_malloc(sizeof(System)); // potential energy // p_system->potential[0] = total energy // p_system->potential[1] = metal quantum Sutton-Chen energy // p_system->potential[2] = non-metal bond stretching energy // p_system->potential[3] = non-metal angle bending energy // p_system->potential[4] = non-metal torsional energy // p_system->potential[5] = // p_system->potential[6] = Long range Coulomb // p_system->potential[7] = Coulomb energy (including 1-4) // p_system->potential[8] = vdW energy (including 1-4) // p_system->potential[9] = // p_system->potential[10] = CPIM metal charge - non-metal charge // p_system->potential[11] = CPIM metal dipole - non-metal charge // p_system->potential[12] = CPIM metal charge - metal charge // p_system->potential[13] = CPIM metal charge - metal dipole // p_system->potential[14] = CPIM metal dipole - metal dipole double *data_potential = my_malloc(sizeof(double) * 15 * 2); p_system->potential = &(data_potential[0]); p_system->partial_pot = &(data_potential[15]); p_system->old_potential = 0.0; // virial tensor p_system->virial = my_malloc(DIM * sizeof(double*)); p_system->partial_vir = my_malloc(DIM * sizeof(double*)); double *data_vir = my_malloc(DIM*2 * DIM * sizeof(double)); int i; for (i = 0; i < DIM; ++ i) { p_system->virial[i] = &(data_vir[DIM * i]); p_system->partial_vir[i] = &(data_vir[DIM * (DIM + i)]); } // box size // Note: for now we treat rectangular box only. // "p_system->box" has six elements // the first three are length in x, y, z // the second three are half of the length in x, y, z p_system->box = my_malloc(sizeof(double) * DIM*2); // coordinates, velocities and forces double *data_rvf = my_malloc_2(nAtoms*7 * DIM * sizeof(double), "data_rvf"); p_system->rx = &(data_rvf[0]); p_system->ry = &(data_rvf[nAtoms]); p_system->rz = &(data_rvf[nAtoms*2]); p_system->vx = &(data_rvf[nAtoms*3]); p_system->vy = &(data_rvf[nAtoms*4]); p_system->vz = &(data_rvf[nAtoms*5]); p_system->fx = &(data_rvf[nAtoms*6]); p_system->fy = &(data_rvf[nAtoms*7]); p_system->fz = &(data_rvf[nAtoms*8]); // forces from slave processors p_system->partial_fx = &(data_rvf[nAtoms*9]); p_system->partial_fy = &(data_rvf[nAtoms*10]); p_system->partial_fz = &(data_rvf[nAtoms*11]); // old position for RATTLE constraints p_system->old_rx = &(data_rvf[nAtoms*12]); p_system->old_ry = &(data_rvf[nAtoms*13]); p_system->old_rz = &(data_rvf[nAtoms*14]); // old force for CG optimization p_system->old_fx = &(data_rvf[nAtoms*15]); p_system->old_fy = &(data_rvf[nAtoms*16]); p_system->old_fz = &(data_rvf[nAtoms*17]); // direction for CG optimization p_system->sx = &(data_rvf[nAtoms*18]); p_system->sy = &(data_rvf[nAtoms*19]); p_system->sz = &(data_rvf[nAtoms*20]); //================ read input gro file ================== // vQ and vP are "velocities" of the thermostat/barostat particles p_system->vQ = 0.0; p_system->vP = 0.0; int groNAtoms; read_gro(input_gro, p_system, &groNAtoms, atom_info); if (groNAtoms != nAtoms) { printf("Error: groNAtoms(%d) not equal to nAtoms(%d)!\n", groNAtoms, nAtoms); exit(1); } // half of box length for PBC p_system->box[3] = p_system->box[0] * 0.5; p_system->box[4] = p_system->box[1] * 0.5; p_system->box[5] = p_system->box[2] * 0.5; p_system->volume = p_system->box[0] * p_system->box[1] * p_system->box[2]; p_system->inv_volume = 1.0 / p_system->volume; //================== set MD variables ========================== // degree of freedom p_system->ndf = 3 * (nAtoms - number_VSites) - 3 - number_Cstrs; // temperature coupling; kT = kB*T, in kJ mol^-1 p_runset->kT = K_BOLTZ * p_runset->ref_temp; p_system->qMass = (double)p_system->ndf * p_runset->kT * p_runset->tau_temp * p_runset->tau_temp; //p_system->pMass = (double)p_system->ndf * p_runset->kT * p_runset->tau_pres * p_runset->tau_pres; // temperature control p_system->first_temp = 0.0; p_system->ext_temp = 0.0; // time step p_runset->dt_2 = 0.5 * p_runset->dt; //=================== CPIM matrix and arrays ========================= // Relay matrix // external electric field and potential // induced dipoles and charges double *data_relay = NULL; // dimension of matrix: 4M + n_NPs int n_mat = p_metal->num * 4 + p_metal->n_NPs; // initialize mat_relay, vec_ext and vec_pq if (p_metal->min >=0 && p_metal->max >= p_metal->min) { data_relay = my_malloc_2(sizeof(double) * n_mat * 3, "data_relay"); p_metal->vec_pq = &(data_relay[0]); p_metal->vec_ext = &(data_relay[n_mat]); p_metal->diag_relay = &(data_relay[n_mat * 2]); int i_mat; for(i_mat = 0; i_mat < n_mat; ++ i_mat) { p_metal->vec_pq[i_mat] = 0.0; p_metal->vec_ext[i_mat] = 0.0; p_metal->diag_relay[i_mat] = 1.0; } } //================== compute forces ========================== mpi_force(p_task, p_topol, atom_info, mol_info, p_runset, p_metal, p_system, my_id, num_procs, time_used); //========== file handles: gro, vec_pq, binary dat, parameters ======== FILE *file_gro, *file_pq, *file_dat; file_gro = NULL; file_pq = NULL; file_dat = NULL; //========== adjust velocities and write trajectories ================= if (root_process == my_id) { // sum potential energies sum_potential(p_system->potential); // update temperature remove_comm(nAtoms, atom_info, p_system); kinetic_energy(p_system, nAtoms, atom_info); // save the starting temperature p_system->first_temp = p_system->inst_temp; p_system->ext_temp = p_runset->ref_temp; // creat traj.gro for writing file_gro = fopen("traj.gro","w") ; if (NULL == file_gro) { printf( "Cannot write to traj.gro!\n" ) ; exit(1); } // creat vec_pq.txt for writing file_pq = fopen("vec_pq.txt","w") ; if (NULL == file_pq) { printf( "Cannot write to vec_pq.txt!\n" ) ; exit(1); } // creat traj.dat for writing file_dat = fopen("traj.dat","w") ; if (NULL == file_dat) { printf( "Cannot write to traj.dat!\n" ) ; exit(1); } // get maximal force get_fmax_rms(nAtoms, p_system); // write the starting geometry to gro and dat file write_gro(file_gro, p_system, nAtoms, atom_info, 0); write_binary(file_dat, p_system, nAtoms, 0); if (p_metal->min >=0 && p_metal->max >= p_metal->min) { write_vec_pq(file_pq, p_metal, 0); } printf(" .---------------- start MD calculation ---------------.\n"); printf("\n"); // check p_runset->run_type if (0 == strcmp(p_runset->run_type, "em") || 0 == strcmp(p_runset->run_type, "cg")) { printf(" Step %-5d Fmax=%10.3e E=%15.8e\n", 0, p_system->f_max, p_system->potential[0]); } else if (0 == strcmp(p_runset->run_type, "md")) { printf(" %10.3f Fmax=%.2e E=%.6e T=%.3e\n", 0.0, p_system->f_max, p_system->potential[0], p_system->inst_temp); } } //========= Now start MD steps ============================ int step = 0; //=================================================== // Energy minimization using steepest descent or CG //=================================================== if (0 == strcmp(p_runset->run_type, "em") || 0 == strcmp(p_runset->run_type, "cg")) { p_system->vQ = 0.0; p_system->vP = 0.0; int converged = 0; double gamma = 0.0; double delta_pot; // initialize direction sx,sy,sz for(i = 0; i < nAtoms; ++ i) { p_system->old_fx[i] = p_system->fx[i]; p_system->old_fy[i] = p_system->fy[i]; p_system->old_fz[i] = p_system->fz[i]; p_system->sx[i] = p_system->fx[i]; p_system->sy[i] = p_system->fy[i]; p_system->sz[i] = p_system->fz[i]; } for(step = 1; step <= p_runset->em_steps && 0 == converged; ++ step) { // update coordinates on root processor if (root_process == my_id) { p_system->old_potential = p_system->potential[0]; for(i = 0; i < nAtoms; ++ i) { p_system->old_rx[i] = p_system->rx[i]; p_system->old_ry[i] = p_system->ry[i]; p_system->old_rz[i] = p_system->rz[i]; // fix metal coordinates? if (1 == p_metal->fix_pos && 1 == atom_info[i].is_metal) { continue; } p_system->rx[i] += p_system->sx[i] / p_system->f_max * p_runset->em_length; p_system->ry[i] += p_system->sy[i] / p_system->f_max * p_runset->em_length; p_system->rz[i] += p_system->sz[i] / p_system->f_max * p_runset->em_length; } // apply constraints rattle_1st(p_runset->dt, mol_info, atom_info, p_topol, p_system); // zero velocities for(i = 0; i < nAtoms; ++ i) { p_system->vx[i] = 0.0; p_system->vy[i] = 0.0; p_system->vz[i] = 0.0; } } // update forces mpi_force(p_task, p_topol, atom_info, mol_info, p_runset, p_metal, p_system, my_id, num_procs, time_used); if (root_process == my_id) { // check potential and fmax on root processor sum_potential(p_system->potential); get_fmax_rms(nAtoms, p_system); delta_pot = p_system->potential[0] - p_system->old_potential; if (delta_pot <= 0.0) { p_runset->em_length *= 1.2; } else { p_runset->em_length *= 0.2; } // print info and write to the gro file printf(" Step %-5d Fmax=%10.3e E=%15.8e\n", step, p_system->f_max, p_system->potential[0]); // write trajectories write_gro(file_gro, p_system, nAtoms, atom_info, step); write_binary(file_dat, p_system, nAtoms, step); if (p_metal->min >=0 && p_metal->max >= p_metal->min) { write_vec_pq(file_pq, p_metal, step); } // check convergence if (p_system->f_max < p_runset->em_tol && p_system->f_rms < p_runset->em_tol * 0.5 && fabs(delta_pot) < p_runset->em_tol * 0.1) { printf("\n"); printf(" F_max (%13.6e) smaller than %e\n", p_system->f_max, p_runset->em_tol); printf(" F_rms (%13.6e) smaller than %e\n", p_system->f_rms, p_runset->em_tol * 0.5); printf(" delta_E (%13.6e) smaller than %e\n", delta_pot, p_runset->em_tol * 0.1); printf("\n"); printf(" =========== Optimization converged ============\n"); converged = 1; } else { // update gamma for CG optimization // for steep descent, gamma = 0.0 if (0 == strcmp(p_runset->run_type, "cg")) { double g22 = 0.0; double g12 = 0.0; double g11 = 0.0; for(i = 0; i < nAtoms; ++ i) { g22 += p_system->fx[i] * p_system->fx[i] + p_system->fy[i] * p_system->fy[i] + p_system->fz[i] * p_system->fz[i]; g12 += p_system->old_fx[i] * p_system->fx[i] + p_system->old_fy[i] * p_system->fy[i] + p_system->old_fz[i] * p_system->fz[i]; g11 += p_system->old_fx[i] * p_system->old_fx[i] + p_system->old_fy[i] * p_system->old_fy[i] + p_system->old_fz[i] * p_system->old_fz[i]; } gamma = (g22 - g12) / g11; } for(i = 0; i < nAtoms; ++ i) { p_system->sx[i] = p_system->fx[i] + gamma * p_system->sx[i]; p_system->sy[i] = p_system->fy[i] + gamma * p_system->sy[i]; p_system->sz[i] = p_system->fz[i] + gamma * p_system->sz[i]; p_system->old_fx[i] = p_system->fx[i]; p_system->old_fy[i] = p_system->fy[i]; p_system->old_fz[i] = p_system->fz[i]; } } // communicate convergence for(an_id = 1; an_id < num_procs; ++ an_id) { ierr = MPI_Send(&converged, 1, MPI_INT, an_id, tag_99, MPI_COMM_WORLD); } } else { ierr = MPI_Recv(&converged, 1, MPI_INT, root_process, tag_99, MPI_COMM_WORLD, &status); } // exit loop if converged if (1 == converged) { break; } } } //=============================== // MD with nvt ensemble //=============================== else if (0 == strcmp(p_runset->run_type, "md")) { for (step = 1; step <= p_runset->nSteps; ++ step) { if (root_process == my_id) { // gradually increase the reference temperature if (step < p_runset->nHeating) { p_runset->ref_temp = p_system->first_temp + (p_system->ext_temp - p_system->first_temp) * step / p_runset->nHeating; } else { p_runset->ref_temp = p_system->ext_temp; } // update kT accordingly p_runset->kT = K_BOLTZ * p_runset->ref_temp; // thermostat for 1st half step if (0 == strcmp(p_runset->ensemble, "nvt")) { nose_hoover(p_runset, p_system, nAtoms); } // update velocity for 1st half step for(i = 0; i < nAtoms; ++ i) { // fix metal coordinates? if (1 == p_metal->fix_pos && 1 == atom_info[i].is_metal) { continue; } // for virtual sites, inv_mass == 0.0; double inv_mass = atom_info[i].inv_mass; p_system->vx[i] += p_runset->dt_2 * p_system->fx[i] * inv_mass; p_system->vy[i] += p_runset->dt_2 * p_system->fy[i] * inv_mass; p_system->vz[i] += p_runset->dt_2 * p_system->fz[i] * inv_mass; } // update position for the whole time step // using velocity at half time step for(i = 0; i < nAtoms; ++ i) { // fix metal coordinates? if (1 == p_metal->fix_pos && 1 == atom_info[i].is_metal) { continue; } p_system->old_rx[i] = p_system->rx[i]; p_system->old_ry[i] = p_system->ry[i]; p_system->old_rz[i] = p_system->rz[i]; p_system->rx[i] += p_runset->dt * p_system->vx[i]; p_system->ry[i] += p_runset->dt * p_system->vy[i]; p_system->rz[i] += p_runset->dt * p_system->vz[i]; } // apply constraints for the 1st half rattle_1st(p_runset->dt, mol_info, atom_info, p_topol, p_system); } // compute forces mpi_force(p_task, p_topol, atom_info, mol_info, p_runset, p_metal, p_system, my_id, num_procs, time_used); // update velocities if (root_process == my_id) { // sum potential energies sum_potential(p_system->potential); // update velocity for 2nd half step for(i = 0; i < nAtoms; ++ i) { // fix metal coordinates? if (1 == p_metal->fix_pos && 1 == atom_info[i].is_metal) { continue; } // for virtual sites, inv_mass == 0.0; double inv_mass = atom_info[i].inv_mass; p_system->vx[i] += p_runset->dt_2 * p_system->fx[i] * inv_mass; p_system->vy[i] += p_runset->dt_2 * p_system->fy[i] * inv_mass; p_system->vz[i] += p_runset->dt_2 * p_system->fz[i] * inv_mass; } // apply constraints for the 2nd half rattle_2nd(p_runset->dt, mol_info, atom_info, p_topol, p_system); // update temperature kinetic_energy(p_system, nAtoms, atom_info); // thermostat for 2nd half step if (0 == strcmp(p_runset->ensemble, "nvt")) { nose_hoover(p_runset, p_system, nAtoms); } // remove center of mass motion and update temperature remove_comm(nAtoms, atom_info, p_system); kinetic_energy(p_system, nAtoms, atom_info); // print information for this step if (0 == step % p_runset->nSave) { // apply PBC apply_pbc(nMols, mol_info, p_system->rx, p_system->ry, p_system->rz, p_system->box); get_fmax_rms(nAtoms, p_system); printf(" %10.3f Fmax=%.2e E=%.6e T=%.3e (%.1f)\n", p_runset->dt * step, p_system->f_max, p_system->potential[0], p_system->inst_temp, p_runset->ref_temp); // write to dat file write_binary(file_dat, p_system, nAtoms, step); } // regularly write to gro file if (0 == step % (p_runset->nSave*10)) { write_gro(file_gro, p_system, nAtoms, atom_info, step); if (p_metal->min >=0 && p_metal->max >= p_metal->min) { write_vec_pq(file_pq, p_metal, step); } } } } } //=========== Finalize MD ==================== sum_time_used(time_used, my_id, num_procs); if (root_process == my_id) { fclose(file_dat); fclose(file_pq); fclose(file_gro); #ifdef DEBUG int i; for(i = 0; i < nAtoms; ++ i) { printf(" f[%5d]= %12.5e, %12.5e, %12.5e\n", i, p_system->fx[i], p_system->fy[i], p_system->fz[i]); } #endif printf("\n"); printf("\n"); printf(" .------------- final potential energies --------------.\n"); printf("\n"); print_potential(p_system->potential); time_t end_t = time(NULL); gettimeofday(&tv, NULL); double end_time = (tv.tv_sec) + (tv.tv_usec) * 1.0e-6; printf(" Calculation ended normally at %s", ctime(&end_t)); printf(" %.3f seconds were used\n", end_time - start_time ); printf("\n"); printf("\n"); printf(" .------------------- time usage ----------------------.\n"); printf("\n"); analyze_time_used(time_used, num_procs); printf("\n"); } // free arrays for(an_id = 0; an_id < num_procs; ++ an_id) { free(time_used[an_id]); } free(time_used); free(p_metal->cpff_chg); free(p_metal->start_NP); free(p_metal->end_NP); free(data_bonded); data_bonded = NULL; for(mol = 0; mol < p_topol->mol_types; ++ mol) { int atom_i; for(atom_i = 0; atom_i < p_topol->atom_num[mol]; ++ atom_i) { free(p_topol->exclude[mol][atom_i]); } free(p_topol->exclude[mol]); free(p_topol->bond_param[mol]); free(p_topol->pair_param[mol]); free(p_topol->angle_param[mol]); free(p_topol->dihedral_param[mol]); free(p_topol->vsite_4[mol]); free(p_topol->vsite_funct[mol]); free(p_topol->constraint[mol]); } free(p_topol->exclude); free(p_topol->bond_param); free(p_topol->pair_param); free(p_topol->angle_param); free(p_topol->dihedral_param); p_topol->bond_param = NULL; p_topol->pair_param = NULL; p_topol->angle_param = NULL; p_topol->dihedral_param = NULL; free(p_topol->vsite_4); free(p_topol->vsite_funct); p_topol->vsite_4 = NULL; p_topol->vsite_funct = NULL; free(p_topol->constraint); p_topol->constraint = NULL; free(p_topol->mol_num); free(p_topol->atom_num); for(mol = 0; mol < p_topol->mol_types; ++ mol) { free(p_topol->atom_param[mol]); } free(p_topol->atom_param); if (p_metal->min >=0 && p_metal->max >= p_metal->min) { free(p_metal->inv_sqrt_dens); p_metal->inv_sqrt_dens = NULL; free(data_relay); //free(p_metal->mat_relay); data_relay = NULL; //p_metal->mat_relay = NULL; p_metal->vec_pq = NULL; p_metal->vec_ext = NULL; p_metal->diag_relay = NULL; } free(data_start_end); free(data_long_start_end); data_start_end = NULL; data_long_start_end = NULL; p_task->start_mol = NULL; p_task->end_mol = NULL; p_task->start_atom = NULL; p_task->end_atom = NULL; p_task->start_metal = NULL; p_task->end_metal = NULL; p_task->start_pair = NULL; p_task->end_pair = NULL; free(atom_info); free(mol_info); atom_info = NULL; mol_info = NULL; free(data_potential); data_potential = NULL; p_system->potential = NULL; p_system->partial_pot = NULL; free(p_system->virial); free(p_system->partial_vir); free(data_vir); free(p_system->box); p_system->box = NULL; free(data_rvf); data_rvf = NULL; p_system->rx = NULL; p_system->ry = NULL; p_system->rz = NULL; p_system->vx = NULL; p_system->vy = NULL; p_system->vz = NULL; p_system->fx = NULL; p_system->fy = NULL; p_system->fz = NULL; p_system->partial_fx = NULL; p_system->partial_fy = NULL; p_system->partial_fz = NULL; p_system->old_rx = NULL; p_system->old_ry = NULL; p_system->old_rz = NULL; p_system->old_fx = NULL; p_system->old_fy = NULL; p_system->old_fz = NULL; p_system->sx = NULL; p_system->sy = NULL; p_system->sz = NULL; free(p_topol->nonbonded_param); free(data_nonbonded); p_topol->nonbonded_param = NULL; data_nonbonded = NULL; free(p_task); free(p_system); free(p_topol); free(p_metal); free(p_runset); p_task = NULL; p_system = NULL; p_topol = NULL; p_metal = NULL; p_runset = NULL; ierr = MPI_Finalize(); if (ierr) {} return 0; }
EXPORT bool output_spectral_in_time( char *basename, Wave *wave, Front *front) { COMPONENT comp; Locstate state; float *coords; float *L = wave->rect_grid->L; float *U = wave->rect_grid->U; float *h = wave->rect_grid->h; float kk,kx,ky,dk,Ek,Vk; int icoords[MAXD]; int dim = wave->rect_grid->dim; int status; char eng_name[100],vor_name[100]; static FILE *eng_file,*vor_file; static int first = YES; int i, ix, iy; int xmax, ymax, kmax, mx, my, dummy; COMPLEX **mesh_eng,**mesh_vor; Locstate lstate,rstate,bstate,tstate; debug_print("fft","Entered output_spectral_in_time()\n"); if (first) { first = NO; (void) sprintf(eng_name,"%s.energy-time.dat",basename); (void) sprintf(vor_name,"%s.vorticity-time.dat",basename); eng_file = fopen(eng_name,"w"); vor_file = fopen(vor_name,"w"); fprintf(eng_file,"VARIABLES=k,E(k)\n",xmax,ymax); fprintf(vor_file,"VARIABLES=k,V(k)\n",xmax,ymax); } xmax = wave->rect_grid->gmax[0]; ymax = wave->rect_grid->gmax[1]; if (!Powerof2(xmax,&mx,&dummy) || !Powerof2(ymax,&my,&dummy)) { screen("output_spectral_in_time() cannot analyze " "mesh not power of 2\n"); screen("xmax = %d ymax = %d\n",xmax,ymax); return FUNCTION_FAILED; } bi_array(&mesh_eng,xmax,ymax,sizeof(COMPLEX)); bi_array(&mesh_vor,xmax,ymax,sizeof(COMPLEX)); for (iy = 0; iy < ymax; ++iy) { for (ix = 0; ix < xmax; ++ix) { icoords[0] = ix; icoords[1] = iy; coords = Rect_coords(icoords,wave); comp = Rect_comp(icoords,wave); state = Rect_state(icoords,wave); if (ix != 0) icoords[0] = ix - 1; else icoords[0] = ix; lstate = Rect_state(icoords,wave); if (ix != xmax-1) icoords[0] = ix + 1; else icoords[0] = ix; rstate = Rect_state(icoords,wave); icoords[0] = ix; if (iy != 0) icoords[1] = iy - 1; else icoords[1] = iy; bstate = Rect_state(icoords,wave); if (iy != ymax-1) icoords[1] = iy + 1; else icoords[1] = iy; tstate = Rect_state(icoords,wave); mesh_eng[ix][iy].real = kinetic_energy(state); mesh_eng[ix][iy].imag = 0.0; mesh_vor[ix][iy].real = (Mom(rstate)[1]/Dens(rstate) - Mom(lstate)[1]/Dens(lstate))/h[0] - (Mom(tstate)[0]/Dens(tstate) - Mom(bstate)[0]/Dens(bstate))/h[1]; mesh_vor[ix][iy].imag = 0.0; } } fft2d(mesh_eng,xmax,ymax,1); fft2d(mesh_vor,xmax,ymax,1); kmax = xmax/2; dk = 2.0*PI/(U[0] - L[0]); fprintf(eng_file,"ZONE\n",kk,Ek); fprintf(vor_file,"ZONE\n",kk,Vk); for (i = 0; i < kmax; ++i) { Ek = 0.0; Vk = 0.0; kk = 2.0*i*PI/(U[0] - L[0]); for (iy = 0; iy < ymax/2; ++iy) { for (ix = 0; ix < xmax/2; ++ix) { kx = 2.0*ix*PI/(U[0] - L[0]); ky = 2.0*iy*PI/(U[1] - L[1]); if (sqrt(sqr(kx)+sqr(ky)) >= kk-0.5*dk && sqrt(sqr(kx)+sqr(ky)) < kk+0.5*dk) { Ek += 2.0*sqrt(sqr(mesh_eng[ix][iy].real) + sqr(mesh_eng[ix][iy].imag)); Vk += 2.0*sqrt(sqr(mesh_vor[ix][iy].real) + sqr(mesh_vor[ix][iy].imag)); } } } fprintf(eng_file,"%lf\t%lf\n",kk,Ek); fprintf(vor_file,"%lf\t%lf\n",kk,Vk); } fflush(eng_file); fflush(vor_file); free_these(2,mesh_eng,mesh_vor); debug_print("fft","Left output_spectral_in_time()\n"); return FUNCTION_SUCCEEDED; } /*end output_spectral_in_time*/
EXPORT bool output_spectral_analysis( char *basename, Wave *wave, Front *front) { COMPONENT comp; Locstate state; float *coords; float *L = wave->rect_grid->L; float *U = wave->rect_grid->U; float *h = wave->rect_grid->h; int icoords[MAXD]; int dim = wave->rect_grid->dim; int status; int step = front->step; char energy_name[100],vorticity_name[100], enstrophy_name[100],dens_name[100],pres_name[100]; FILE *energy_file,*vorticity_file, *enstrophy_file,*dens_file,*pres_file; debug_print("fft","Entered fft_energy_spectral()\n"); (void) sprintf(energy_name,"%s.energy%s.dat",basename, right_flush(step,TSTEP_FIELD_WIDTH)); (void) sprintf(vorticity_name,"%s.vorticity%s.dat",basename, right_flush(step,TSTEP_FIELD_WIDTH)); (void) sprintf(enstrophy_name,"%s.enstrophy%s.dat",basename, right_flush(step,TSTEP_FIELD_WIDTH)); (void) sprintf(dens_name,"%s.density%s.dat",basename, right_flush(step,TSTEP_FIELD_WIDTH)); (void) sprintf(pres_name,"%s.pressure%s.dat",basename, right_flush(step,TSTEP_FIELD_WIDTH)); energy_file = fopen(energy_name,"w"); vorticity_file = fopen(vorticity_name,"w"); enstrophy_file = fopen(enstrophy_name,"w"); dens_file = fopen(dens_name,"w"); pres_file = fopen(pres_name,"w"); if (wave->sizest == 0) { debug_print("fft","Left fft_energy_spectral()\n"); return FUNCTION_FAILED; } switch (dim) { #if defined(ONED) case 1: { int ix; int xmax; COMPLEX *mesh_energy; xmax = wave->rect_grid->gmax[0]; uni_array(&mesh_energy,xmax,sizeof(COMPLEX)); for (ix = 0; ix < xmax; ++ix) { icoords[0] = ix; coords = Rect_coords(icoords,wave); comp = Rect_comp(icoords,wave); state = Rect_state(icoords,wave); mesh_energy[ix].real = Energy(state); mesh_energy[ix].imag = 0.0; } break; } #endif /* defined(ONED) */ #if defined(TWOD) case 2: { int ix, iy; int xmax, ymax, mx, my, dummy; COMPLEX **mesh_energy,**mesh_vorticity,**mesh_enstrophy; Locstate lstate,rstate,bstate,tstate; float kk,kx,ky,dk; xmax = wave->rect_grid->gmax[0]; ymax = wave->rect_grid->gmax[1]; if (!Powerof2(xmax,&mx,&dummy) || !Powerof2(ymax,&my,&dummy)) { screen("fft_energy_spectral() cannot analyze " "mesh not power of 2\n"); screen("xmax = %d ymax = %d\n",xmax,ymax); return FUNCTION_FAILED; } bi_array(&mesh_energy,xmax,ymax,sizeof(COMPLEX)); bi_array(&mesh_vorticity,xmax,ymax,sizeof(COMPLEX)); fprintf(energy_file,"zone i=%d, j=%d\n",xmax,ymax); fprintf(vorticity_file,"zone i=%d, j=%d\n",xmax,ymax); fprintf(dens_file,"zone i=%d, j=%d\n",xmax,ymax); fprintf(pres_file,"zone i=%d, j=%d\n",xmax,ymax); fprintf(enstrophy_file,"zone i=%d, j=%d\n",xmax,ymax); for (iy = 0; iy < ymax; ++iy) { for (ix = 0; ix < xmax; ++ix) { icoords[0] = ix; icoords[1] = iy; coords = Rect_coords(icoords,wave); comp = Rect_comp(icoords,wave); state = Rect_state(icoords,wave); mesh_energy[ix][iy].real = kinetic_energy(state); mesh_energy[ix][iy].imag = 0.0; if (ix != 0) icoords[0] = ix - 1; else icoords[0] = ix; lstate = Rect_state(icoords,wave); if (ix != xmax-1) icoords[0] = ix + 1; else icoords[0] = ix; rstate = Rect_state(icoords,wave); icoords[0] = ix; if (iy != 0) icoords[1] = iy - 1; else icoords[1] = iy; bstate = Rect_state(icoords,wave); if (iy != ymax-1) icoords[1] = iy + 1; else icoords[1] = iy; tstate = Rect_state(icoords,wave); mesh_vorticity[ix][iy].real = (Mom(rstate)[1]/Dens(rstate) - Mom(lstate)[1]/Dens(lstate))/h[0] - (Mom(tstate)[0]/Dens(tstate) - Mom(bstate)[0]/Dens(bstate))/h[1]; mesh_vorticity[ix][iy].imag = 0.0; fprintf(energy_file,"%lf\n",kinetic_energy(state)); fprintf(vorticity_file,"%lf\n",mesh_vorticity[ix][iy].real); fprintf(enstrophy_file,"%lf\n", sqr(mesh_vorticity[ix][iy].real)); fprintf(dens_file,"%lf\n",Dens(state)); fprintf(pres_file,"%lf\n",pressure(state)); } } fft_output2d(basename,"energy",step,wave->rect_grid, mesh_energy); fft_output2d(basename,"vorticity",step,wave->rect_grid, mesh_vorticity); free_these(2,mesh_energy,mesh_vorticity); break; } #endif /* defined(TWOD) */ #if defined(THREED) case 3: { int ix, iy, iz; int xmax, ymax, zmax; COMPLEX ***mesh_energy; xmax = wave->rect_grid->gmax[0]; ymax = wave->rect_grid->gmax[1]; zmax = wave->rect_grid->gmax[2]; tri_array(&mesh_energy,xmax,ymax,zmax,sizeof(COMPLEX)); for (iz = 0; iz < zmax; ++iz) { icoords[2] = iz; for (iy = 0; iy < ymax; ++iy) { icoords[1] = iy; for (ix = 0; ix < xmax; ++ix) { icoords[0] = ix; coords = Rect_coords(icoords,wave); comp = Rect_comp(icoords,wave); state = Rect_state(icoords,wave); mesh_energy[ix][iy][iz].real = Energy(state); mesh_energy[ix][iy][iz].imag = 0.0; } } } break; } #endif /* defined(THREED) */ } fclose(energy_file); fclose(vorticity_file); fclose(enstrophy_file); fclose(dens_file); fclose(pres_file); debug_print("fft","Left fft_energy_spectral()\n"); return FUNCTION_SUCCEEDED; } /*end fft_energy_spectral*/
/** * @brief Determine the total energy of the ion cloud. * * The function sums the Coulomb and kinetic energy contribution of each ion * by calling the IonCloud::coulomb_energy() and IonCloud::kinetic_energy() * functions. * * @return The total energy. */ double IonCloud::total_energy() const { return kinetic_energy() + coulomb_energy(); }
int main(int argc, char *argv[]) { simparams X; X.m = 1; X.lambda = 1./6.; X.n = 4; X.gamma = 1./X.n; X.rho = 100; X.N = 10; // default X.tmax = 100; X.steps = 1e6; X.dt = X.tmax*1.0/X.steps; double te = 14; // ? if ( argc != 4 ) { printf("Too few arguments. 3 required, %d given\n", argc-1); printf("Usage: %s {N_min} {N_max} {N_step}\n", argv[0]); return 1; } int N_min = abs(atoi(argv[1])); int N_max = abs(atoi(argv[2])); int N_step = abs(atoi(argv[3])); if (N_min == 0) { printf("You can't run the simulation for 0 particles\n"); printf("I'm going to ignore this and start at %d\n",N_step); N_min = N_step; } double **q = (double**) malloc(N_max * sizeof(double)); for (int i = 0; i < N_max; i++) q[i] = (double*) malloc(X.steps * sizeof(double)); double **p = (double**) malloc(N_max * sizeof(double)); for (int i = 0; i < N_max; i++) p[i] = (double*) malloc(X.steps * sizeof(double)); double *Q = (double*) malloc(X.steps * sizeof(double)); double *EKin = (double*) malloc(X.steps * sizeof(double)); double *var = (double*) malloc(N_max * sizeof(double)); time_t integration_time; double *times = (double*) malloc(N_max * sizeof(double)); FILE* fp; if ((fp = fopen("Var.dat","w")) == NULL) return 1; for (int n = N_min; n <= N_max; n += N_step) { X.N = n; printf("### N = %d ###\n", X.N); printf("Setting up the simulation\n"); setup_simulation(q, p, X.N); printf("Integrating differential equation\n"); integration_time = time(NULL); for (int t = 0; t < X.steps-1; t++) { velocity_verlet_step(q, p, t, &X); } times[X.N-1] = time(NULL) - integration_time; printf("Total time taken: %.2f seconds\n", times[X.N-1]); printf("Calculating COM motion and kinetic energy\n"); centre_of_mass_motion(Q, q, &X); kinetic_energy(EKin, p, &X); var[X.N-1] = mean_kinetic_energy(EKin, te, &X); fprintf(fp, "%d\t%e\t%e\n", X.N, var[X.N-1],times[X.N-1]); fflush(fp); //if ( n+N_step > N_max ) { printf("Writing results to disk\n"); write_single_pointer("Q.dat", Q, X.steps, X.dt); write_single_pointer("EKin.dat", EKin, X.steps, X.dt); //} } fclose(fp); free(q); free(p); free(Q); free(EKin); free(var); free(times); return 0; }
int main(){ /////////////////// //Initialize data// /////////////////// time_t t; int i,j,k; int amount = 1000; // Amount of particles int timesteps = 1000; // Amount of timesteps int refresh = 10; // Amount of timesteps without updating neighbour list double rc = 2.5; // Cutoff radius double rv; // Neighborlist radius double rho = 0.5; // Density double m = 1; // Mass of particles double region = pow((amount*m)/rho,1./3.); // Sidelength of region double dt = 0.005; // Timestep double kB = 1; // Boltzmannconstant double g = 0; // Guiding factor double T = 1; // Temperature double sigma = sqrt((2*kB*T*g)/m); // Whitenoise factor double sig = sqrt((kB*T)/m); // Initial velocity factor double vmax; // Maximum velocity of particles double kinetic; // Kinetic energy double positions[amount][3]; // Particle positions double velocities[amount][3]; // Particle velocities double forces[amount][3]; // Particle forces double amount_neighbors[amount]; // Amount of neighbors per particle double ****neighbor; // Neighbor List double *vel_sum; // For checking momentum conservation XiEta rnd_XiEta[amount][3]; // Array of linear independent normally distributed numbers srand((unsigned) time(&t)); // Set seed for random number generator /////////////////////////// //Setup initial positions// /////////////////////////// create_particles(positions, amount, region); initial_velocities(velocities, sig, m, amount); /////////////////////////////// //Allocate memory on the heap// /////////////////////////////// neighbor = malloc(amount*sizeof(double ***)); if (neighbor){ for (i=0; i<amount; ++i){ neighbor[i] = malloc(amount*sizeof(double **)); if (neighbor[i]){ for (j=0; j<amount; ++j){ neighbor[i][j] = malloc(3*sizeof(double *)); if (!neighbor[i][j]){ printf("\nMemory allocation error!\n"); } } } } } ////////////////// //Main algorithm// ////////////////// printf("timestep\tsum velocity\tkinetic energy per particle\n"); for (i=0; i<timesteps; i++){ if (i%refresh==0){ vmax = sqrt(max_abs(velocities, amount)); rv = rc + refresh*vmax*dt; update_neighborlist(neighbor, amount_neighbors, positions, rv, amount, region); } for (j=0; j<amount; j++){ for (k=0; k<3; k++){ rnd_XiEta[j][k] = normal_value(0,1); } } calculate_force(forces, neighbor, positions, region, amount); update_velocity(velocities, forces, dt, g, sigma, m, rnd_XiEta, amount); update_position(positions, velocities, dt, sigma, rnd_XiEta, amount); calculate_force(forces, neighbor, positions, region, amount); update_velocity(velocities, forces, dt, g, sigma, m, rnd_XiEta, amount); if (i%100==0){ vel_sum = sum_vel(velocities, amount); kinetic = kinetic_energy(velocities, m, amount); printf("%d\t\t%f\t%f\n", i, vel_sum[0]+vel_sum[1]+vel_sum[2], kinetic/amount); } } //////////////////////////// //Clear memory on the heap// //////////////////////////// for (i=0; i<amount; i++){ for (j=0; j<amount; j++){ free(neighbor[i][j]); } free(neighbor[i]); } free(neighbor); return 0; }