/* Copyright (C) 2010,2011 The ESPResSo project Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010 Max-Planck-Institute for Polymer Research, Theory Group This file is part of ESPResSo. ESPResSo is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. ESPResSo is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /** \file energy.h Implementation of the energy calculation. */ #ifndef _ENERGY_H #define _ENERGY_H #include "utils.h" #include "integrate.h" #include "statistics.h" #include "thermostat.h" #include "communication.h" /* include the energy files */ #include "p3m.h" #include "p3m-dipolar.h" #include "lj.h" #include "ljgen.h" #include "steppot.h" #include "hertzian.h" #include "bmhtf-nacl.h" #include "buckingham.h" #include "soft_sphere.h" #include "ljcos.h" #include "ljcos2.h" #include "ljangle.h" #include "tab.h" #include "overlap.h" #include "gb.h" #include "fene.h" #include "harmonic.h" #include "subt_lj.h" #include "angle.h" #include "angledist.h" #include "dihedral.h" #include "debye_hueckel.h" #include "endangledist.h" #include "reaction_field.h" #include "mmm1d.h" #include "mmm2d.h" #include "maggs.h" #include "morse.h" #include "ewald.h" #include "elc.h" #include "mdlc_correction.h" /** \name Exported Variables */ /************************************************************/ /address@hidden/ /// extern Observable_stat energy, total_energy; /address@hidden/ /** \name Exported Functions */ /************************************************************/ /address@hidden/ /** parallel energy calculation. @param result non-zero only on master node; will contain the cumulative over all nodes. */ void energy_calc(double *result); /** Calculate non bonded energies between a pair of particles. @param p1 pointer to particle 1. @param p2 pointer to particle 2. @param ia_params the interaction parameters between the two particles @param d vector between p1 and p2. @param dist distance between p1 and p2. @param dist2 distance squared between p1 and p2. @return the short ranged interaction energy between the two particles */ MDINLINE double calc_non_bonded_pair_energy(Particle *p1, Particle *p2, IA_parameters *ia_params, double d[3], double dist, double dist2) { double ret = 0; #ifdef NO_INTRA_NB if (p1->p.mol_id==p2->p.mol_id) return 0; #endif #ifdef MOL_CUT //You may want to put a correction factor for smoothing function else then theta if (checkIfParticlesInteractViaMolCut(p1,p2,ia_params)==0) return 0; #endif #ifdef LENNARD_JONES /* lennard jones */ ret += lj_pair_energy(p1,p2,ia_params,d,dist); #endif #ifdef LENNARD_JONES_GENERIC /* Generic lennard jones */ ret += ljgen_pair_energy(p1,p2,ia_params,d,dist); #endif #ifdef LJ_ANGLE /* Directional LJ */ ret += ljangle_pair_energy(p1,p2,ia_params,d,dist); #endif #ifdef SMOOTH_STEP /* smooth step */ ret += SmSt_pair_energy(p1,p2,ia_params,d,dist,dist2); #endif #ifdef HERTZIAN /* Hertzian potential */ ret += hertzian_pair_energy(p1,p2,ia_params,d,dist,dist2); #endif #ifdef BMHTF_NACL /* BMHTF NaCl */ ret += BMHTF_pair_energy(p1,p2,ia_params,d,dist,dist2); #endif #ifdef MORSE /* morse */ ret +=morse_pair_energy(p1,p2,ia_params,d,dist); #endif #ifdef BUCKINGHAM /* lennard jones */ ret += buck_pair_energy(p1,p2,ia_params,d,dist); #endif #ifdef SOFT_SPHERE /* soft-sphere */ ret += soft_pair_energy(p1,p2,ia_params,d,dist); #endif #ifdef LJCOS2 /* lennard jones */ ret += ljcos2_pair_energy(p1,p2,ia_params,d,dist); #endif #ifdef TABULATED /* tabulated */ ret += tabulated_pair_energy(p1,p2,ia_params,d,dist); #endif #ifdef LJCOS /* lennard jones cosine */ ret += ljcos_pair_energy(p1,p2,ia_params,d,dist); #endif #ifdef GAY_BERNE /* Gay-Berne */ ret += gb_pair_energy(p1,p2,ia_params,d,dist); #endif #ifdef INTER_RF ret += interrf_pair_energy(p1,p2,ia_params,dist); #endif return ret; } /** Add non bonded energies and short range coulomb between a pair of particles. @param p1 pointer to particle 1. @param p2 pointer to particle 2. @param d vector between p1 and p2. @param dist distance between p1 and p2. @param dist2 distance squared between p1 and p2. */ MDINLINE void add_non_bonded_pair_energy(Particle *p1, Particle *p2, double d[3], double dist, double dist2) { IA_parameters *ia_params = get_ia_param(p1->p.type,p2->p.type); #if defined(ELECTROSTATICS) || defined(DIPOLES) double ret = 0; #endif *obsstat_nonbonded(&energy, p1->p.type, p2->p.type) += calc_non_bonded_pair_energy(p1, p2, ia_params, d, dist, dist2); #ifdef ELECTROSTATICS if (coulomb.method != COULOMB_NONE) { /* real space coulomb */ switch (coulomb.method) { #ifdef P3M case COULOMB_P3M: ret = p3m_pair_energy(p1->p.q*p2->p.q,d,dist2,dist); break; case COULOMB_ELC_P3M: ret = p3m_pair_energy(p1->p.q*p2->p.q,d,dist2,dist); if (elc_params.dielectric_contrast_on) ret += 0.5*ELC_P3M_dielectric_layers_energy_contribution(p1,p2); break; #endif case COULOMB_EWALD: ret = ewald_coulomb_pair_energy(p1,p2,d,dist2,dist); break; case COULOMB_DH: ret = dh_coulomb_pair_energy(p1,p2,dist); break; case COULOMB_RF: ret = rf_coulomb_pair_energy(p1,p2,dist); break; case COULOMB_INTER_RF: //this is done above as interaction ret = 0; break; case COULOMB_MMM1D: ret = mmm1d_coulomb_pair_energy(p1,p2,d,dist2,dist); break; case COULOMB_MMM2D: ret = mmm2d_coulomb_pair_energy(p1->p.q*p2->p.q,d,dist2,dist); break; default : ret = 0.; } energy.coulomb[0] += ret; } #endif #ifdef DIPOLES if (coulomb.Dmethod != DIPOLAR_NONE) { ret=0; switch (coulomb.Dmethod) { #ifdef DP3M case DIPOLAR_MDLC_P3M: //fall trough case DIPOLAR_P3M: ret = dp3m_pair_energy(p1,p2,d,dist2,dist); break; #endif } energy.dipolar[0] += ret; } #endif } /** Calculate bonded energies for one particle. @param p1 particle for which to calculate energies */ MDINLINE void add_bonded_energy(Particle *p1) { char *errtxt; Particle *p2, *p3 = NULL, *p4 = NULL; Bonded_ia_parameters *iaparams; int i, type_num, type, n_partners, bond_broken; double ret=0, dx[3] = {0, 0, 0}; i = 0; while(ibl.n) { type_num = p1->bl.e[i++]; iaparams = &bonded_ia_params[type_num]; type = iaparams->type; n_partners = iaparams->num; /* fetch particle 2, which is always needed */ p2 = local_particles[p1->bl.e[i++]]; if (!p2) { errtxt = runtime_error(128 + 2*TCL_INTEGER_SPACE); ERROR_SPRINTF(errtxt,"{069 bond broken between particles %d and %d (particles not stored on the same node)} ", p1->p.identity, p1->bl.e[i-1]); return; } /* fetch particle 3 eventually */ if (n_partners >= 2) { p3 = local_particles[p1->bl.e[i++]]; if (!p3) { errtxt = runtime_error(128 + 3*TCL_INTEGER_SPACE); ERROR_SPRINTF(errtxt,"{070 bond broken between particles %d, %d and %d (particles not stored on the same node)} ", p1->p.identity, p1->bl.e[i-2], p1->bl.e[i-1]); return; } } /* fetch particle 4 eventually */ if (n_partners >= 3) { p4 = local_particles[p1->bl.e[i++]]; if (!p4) { errtxt = runtime_error(128 + 4*TCL_INTEGER_SPACE); ERROR_SPRINTF(errtxt,"{071 bond broken between particles %d, %d, %d and %d (particles not stored on the same node)} ", p1->p.identity, p1->bl.e[i-3], p1->bl.e[i-2], p1->bl.e[i-1]); return; } } /* similar to the force, we prepare the center-center vector */ if (n_partners == 1) get_mi_vector(dx, p1->r.p, p2->r.p); switch(type) { case BONDED_IA_FENE: bond_broken = fene_pair_energy(p1, p2, iaparams, dx, &ret); break; case BONDED_IA_HARMONIC: bond_broken = harmonic_pair_energy(p1, p2, iaparams, dx, &ret); break; #ifdef LENNARD_JONES case BONDED_IA_SUBT_LJ: bond_broken = subt_lj_pair_energy(p1, p2, iaparams, dx, &ret); break; #endif #ifdef BOND_ANGLE case BONDED_IA_ANGLE: bond_broken = angle_energy(p1, p2, p3, iaparams, &ret); break; #endif #ifdef BOND_ANGLEDIST case BONDED_IA_ANGLEDIST: bond_broken = angledist_energy(p1, p2, p3, iaparams, &ret); break; #endif #ifdef BOND_ENDANGLEDIST case BONDED_IA_ENDANGLEDIST: bond_broken = endangledist_pair_energy(p1, p2, iaparams, dx, &ret); break; #endif case BONDED_IA_DIHEDRAL: bond_broken = dihedral_energy(p2, p1, p3, p4, iaparams, &ret); break; #ifdef BOND_CONSTRAINT case BONDED_IA_RIGID_BOND: bond_broken = 0; ret = 0; break; #endif #ifdef TABULATED case BONDED_IA_TABULATED: switch(iaparams->p.tab.type) { case TAB_BOND_LENGTH: bond_broken = tab_bond_energy(p1, p2, iaparams, dx, &ret); break; case TAB_BOND_ANGLE: bond_broken = tab_angle_energy(p1, p2, p3, iaparams, &ret); break; case TAB_BOND_DIHEDRAL: bond_broken = tab_dihedral_energy(p2, p1, p3, p4, iaparams, &ret); break; default : errtxt = runtime_error(128 + TCL_INTEGER_SPACE); ERROR_SPRINTF(errtxt,"{072 add_bonded_energy: tabulated bond type of atom %d unknown\n", p1->p.identity); return; } break; #endif #ifdef OVERLAPPED case BONDED_IA_OVERLAPPED: switch(iaparams->p.overlap.type) { case OVERLAP_BOND_LENGTH: bond_broken = overlap_bond_energy(p1, p2, iaparams, dx, &ret); break; case OVERLAP_BOND_ANGLE: bond_broken = overlap_angle_energy(p1, p2, p3, iaparams, &ret); break; case OVERLAP_BOND_DIHEDRAL: bond_broken = overlap_dihedral_energy(p2, p1, p3, p4, iaparams, &ret); break; default : errtxt = runtime_error(128 + TCL_INTEGER_SPACE); ERROR_SPRINTF(errtxt,"{072 add_bonded_energy: overlapped bond type of atom %d unknown\n", p1->p.identity); return; } break; #endif #ifdef BOND_VIRTUAL case BONDED_IA_VIRTUAL_BOND: bond_broken = 0; ret = 0; break; #endif default : errtxt = runtime_error(128 + TCL_INTEGER_SPACE); ERROR_SPRINTF(errtxt,"{073 add_bonded_energy: bond type of atom %d unknown\n", p1->p.identity); return; } switch (n_partners) { case 1: if (bond_broken) { char *errtext = runtime_error(128 + 2*TCL_INTEGER_SPACE); ERROR_SPRINTF(errtext,"{074 bond broken between particles %d and %d} ", p1->p.identity, p2->p.identity); continue; } break; case 2: if (bond_broken) { char *errtext = runtime_error(128 + 3*TCL_INTEGER_SPACE); ERROR_SPRINTF(errtext,"{075 bond broken between particles %d, %d and %d} ", p1->p.identity, p2->p.identity, p3->p.identity); continue; } break; case 3: if (bond_broken) { char *errtext = runtime_error(128 + 4*TCL_INTEGER_SPACE); ERROR_SPRINTF(errtext,"{076 bond broken between particles %d, %d, %d and %d} ", p1->p.identity, p2->p.identity, p3->p.identity, p4->p.identity); continue; } } *obsstat_bonded(&energy, type_num) += ret; } } /** Calculate kinetic energies for one particle. @param p1 particle for which to calculate energies */ MDINLINE void add_kinetic_energy(Particle *p1) { #ifdef VIRTUAL_SITES if (ifParticleIsVirtual(p1)) return; #endif /* kinetic energy */ energy.data.e[0] += (SQR(p1->m.v[0]) + SQR(p1->m.v[1]) + SQR(p1->m.v[2]))*PMASS(*p1); #ifdef ROTATION #ifdef ROTATIONAL_INERTIA /* the rotational part is added to the total kinetic energy; Here we use the rotational inertia */ energy.data.e[0] += (SQR(p1->m.omega[0])*p1->p.rinertia[0] + SQR(p1->m.omega[1])*p1->p.rinertia[1] + SQR(p1->m.omega[2])*p1->p.rinertia[2])*time_step*time_step; #else /* the rotational part is added to the total kinetic energy; at the moment, we assume unit inertia tensor I=(1,1,1) */ energy.data.e[0] += (SQR(p1->m.omega[0]) + SQR(p1->m.omega[1]) + SQR(p1->m.omega[2]))*time_step*time_step; #endif #endif } /** implementation of analyze energy */ int tclcommand_analyze_parse_and_print_energy(Tcl_Interp *interp, int argc, char **argv); /address@hidden/ #endif