OSDN Git Service

Experimental implementation of alchemical perturbation (not tested yet)
authortoshinagata1964 <toshinagata1964@a2be9bc6-48de-4e38-9406-05402d4bc13c>
Tue, 27 Sep 2011 23:05:08 +0000 (23:05 +0000)
committertoshinagata1964 <toshinagata1964@a2be9bc6-48de-4e38-9406-05402d4bc13c>
Tue, 27 Sep 2011 23:05:08 +0000 (23:05 +0000)
git-svn-id: svn+ssh://svn.sourceforge.jp/svnroot/molby/trunk@123 a2be9bc6-48de-4e38-9406-05402d4bc13c

MolLib/MD/MDCore.c
MolLib/MD/MDCore.h
MolLib/MD/MDForce.c
MolLib/Ruby_bind/ruby_md.c

index 64ea1a4..878ec12 100644 (file)
@@ -1355,6 +1355,33 @@ md_init_for_positions(MDArena *arena)
        md_center_of_mass(arena, &(arena->initial_center));
 }
 
+/*  Set the alchemical flags  */
+/*  Independent with arena->xmol, mol  */
+int
+md_set_alchemical_flags(MDArena *arena, int nflags, const char *flags)
+{
+       if (arena == NULL)
+               return 0;
+
+       if (nflags == 0 || flags == NULL) {
+               if (arena->alchem_flags != NULL)
+                       free(arena->alchem_flags);
+               arena->alchem_flags = NULL;
+               arena->nalchem_flags = 0;
+               return 0;
+       }
+
+       if (arena->alchem_flags == NULL)
+               arena->alchem_flags = (char *)malloc(nflags);
+       else arena->alchem_flags = (char *)realloc(arena->alchem_flags, nflags);
+       if (arena->alchem_flags == NULL) {
+               arena->nalchem_flags = 0;
+               return -1;
+       }
+       memmove(arena->alchem_flags, flags, nflags);
+       return 0;
+}
+
 const char *
 md_prepare(MDArena *arena, int check_only)
 {
@@ -1500,7 +1527,6 @@ md_prepare(MDArena *arena, int check_only)
        if (t2 > 0)
                md_log(arena, "Number of fixed atoms = %d\n", t2);
 
-
        if (arena->natoms_uniq < mol->natoms) {
                md_log(arena, "Number of symmetry-unique atoms = %d\n", arena->natoms_uniq);
                t2 = 0;
@@ -1976,6 +2002,8 @@ md_output_energies(MDArena *arena)
                md_log(arena, " %11s %11s %11s %11s %11s %11s %11s %11s", "VDW", "ELECT", "AUX", "SURFACE", "KINETIC", "NET", "TEMP", "TEMP_AVG");
                if (periodic)
                        md_log(arena, " %11s", "VOLUME");
+               if (arena->nalchem_flags > 0)
+                       md_log(arena, " %11s %11s", "LAMBDA", "DEDL");
                md_log(arena, "\n");
        }
        md_log(arena, "ENERGY:  %11d %11.5f", arena->step, arena->total_energy * INTERNAL2KCAL);
@@ -1992,6 +2020,9 @@ md_output_energies(MDArena *arena)
                VecCross(v, *av, *bv);
                md_log(arena, " %11.5f", VecDot(v, *cv));
        }
+       if (arena->nalchem_flags > 0) {
+               md_log(arena, " %11.5f %11.5f", arena->alchem_lambda, arena->alchem_energy / arena->alchem_dlambda);
+       }
        md_log(arena, "\n");
        md_log(arena, NULL);
 }
@@ -3037,6 +3068,8 @@ md_arena_release(MDArena *arena)
                free(arena->custom_bond_pars);
        if (arena->custom_pars != NULL)
                free(arena->custom_pars);
+       if (arena->alchem_flags != NULL)
+               free(arena->alchem_flags);
        if (arena->bond_par_i != NULL)
                free(arena->bond_par_i);
        if (arena->angle_par_i != NULL)
index 0ec76c3..333989a 100644 (file)
@@ -229,6 +229,13 @@ typedef struct MDArena {
        Int    ncustom_pars;
        MDCustomPar *custom_pars;
        
+       /*  Alchemical perturbation  */
+       Int    nalchem_flags;
+       char   *alchem_flags;  /*  0: normal, 1: vanishing, 2: appearing  */
+       Double alchem_lambda;
+       Double alchem_dlambda;
+       Double alchem_energy;
+       
        /*  Fix atoms  */
        /*      Int nfix_atoms;
         fix_atom_record *fix_atoms; */
@@ -433,6 +440,8 @@ int md_check_abnormal_angle(MDArena *arena, Molecule *mol, int idx);
 int md_check_abnormal_dihedral(MDArena *arena, Molecule *mol, int idx);
 int md_check_abnormal_improper(MDArena *arena, Molecule *mol, int idx);
 
+int md_set_alchemical_flags(MDArena *arena, int nflags, const char *flags);
+
 void md_amend_by_symmetry(MDArena *arena);
 void md_cell_recalculate(MDArena *arena);
 void md_scale_cell(MDArena *arena, const Transform tf, int scale_atoms);
index 19ac456..40a4516 100644 (file)
@@ -637,6 +637,8 @@ s_calc_nonbonded_force_sub(MDArena *arena, Double *energies, Double *eenergies,
        int i;
        Vector *vdr;
        Double limit, elimit, dielec_r;
+       Double lambda, dlambda;
+
        MDVerlet *vl;
 /*     MDVdwCache *vdw_cache = arena->vdw_cache; */
        Atom *atoms = arena->mol->atoms;
@@ -674,6 +676,32 @@ s_calc_nonbonded_force_sub(MDArena *arena, Double *energies, Double *eenergies,
                        && !(get_group_flag(group_flags_1, vl->n2) && get_group_flag(group_flags_2, vl->n1)))
                                continue;
                }
+
+               if (arena->nalchem_flags > 0) {
+                       char c1, c2;
+                       if (vl->n1 < arena->nalchem_flags)
+                               c1 = arena->alchem_flags[vl->n1];
+                       else c1 = 0;
+                       if (vl->n2 < arena->nalchem_flags)
+                               c2 = arena->alchem_flags[vl->n2];
+                       else c2 = 0;
+                       if ((c1 == 1 && c2 == 2) || (c1 == 2 && c2 == 1))
+                               continue;
+                       if (c1 == 1 || c2 == 1) {
+                               lambda = (1.0 - arena->alchem_lambda);
+                               dlambda = -arena->alchem_dlambda;
+                       } else if (c1 == 2 || c2 == 2) {
+                               lambda = arena->alchem_lambda;
+                               dlambda = arena->alchem_dlambda;
+                       } else {
+                               lambda = 1.0;
+                               dlambda = 0.0;
+                       }
+               } else {
+                       lambda = 1.0;
+                       dlambda = 0.0;
+               }
+               
                if (vl->vdw_type == 1) {
                        A = vp->par.A14;
                        B = vp->par.B14;
@@ -749,12 +777,17 @@ s_calc_nonbonded_force_sub(MDArena *arena, Double *energies, Double *eenergies,
                        k0 *= arena->scale14_vdw;
                        k1 *= arena->scale14_vdw;
                }
+               k0 /= vl->mult;
+               k1 *= lambda;
                VecScale(fij, rij, k1);
-               *energies += k0 / vl->mult;
+               *energies += k0 * lambda;
                if (forces != NULL) {
                        VecDec(forces[vl->n1], fij);
                        VecInc(forces[vl->n2], fij);
                }
+               if (dlambda != 0.0)
+                       arena->alchem_energy += k0 * dlambda;
+
                if (arena->debug_result && arena->debug_output_level > 1) {
                        fprintf(arena->debug_result, "nonbonded(vdw) force %d-%d: r=%f, k0=%f, k1=%f, {%f %f %f}\n", vl->n1+1, vl->n2+1, sqrt(r2), k0/KCAL2INTERNAL, k1*sqrt(r2)/KCAL2INTERNAL, fij.x/KCAL2INTERNAL, fij.y/KCAL2INTERNAL, fij.z/KCAL2INTERNAL);
                }
@@ -932,7 +965,8 @@ calc_force(MDArena *arena)
        memset(arena->energies, 0, sizeof(Double) * kSlowIndex);
        memset(arena->forces, 0, sizeof(Vector) * kSlowIndex * natoms);
        arena->total_energy = 0.0;
-       
+       arena->alchem_energy = 0.0;
+
        if (arena->step == arena->start_step || arena->step % arena->surface_potential_freq == 0) {
                doSurface = 1;
                arena->energies[kSurfaceIndex] = 0.0;
index 6eb55c4..be6c44e 100644 (file)
@@ -318,7 +318,8 @@ s_ElectroCutoffSym, s_PairlistDistanceSym, s_TemperatureSym, s_TransientTempSym,
 s_AverageTempSym, s_AndersenFreqSym, s_AndersenCouplingSym, s_RandomSeedSym, 
 s_DielectricSym, s_GradientConvergenceSym, s_CoordinateConvergenceSym, s_UseXplorShiftSym, 
 s_Scale14VdwSym, s_Scale14ElectSym, s_RelocateCenterSym, s_SurfaceProbeRadiusSym, 
-s_SurfaceTensionSym, s_SurfacePotentialFreqSym, s_UseGraphiteSym;
+s_SurfaceTensionSym, s_SurfacePotentialFreqSym, s_UseGraphiteSym,
+s_AlchemicalLambdaSym, s_AlchemicalDeltaLambdaSym;
 
 struct s_MDArenaAttrDef {
        char *name;
@@ -360,6 +361,8 @@ static struct s_MDArenaAttrDef s_MDArenaAttrDefTable[] = {
        {"surface_tension",   &s_SurfaceTensionSym,   0, 0, 'f', offsetof(MDArena, surface_tension)},
        {"surface_potential_freq", &s_SurfacePotentialFreqSym, 0, 0, 'i', offsetof(MDArena, surface_potential_freq)},
        {"use_graphite",      &s_UseGraphiteSym,      0, 0, 'i', offsetof(MDArena, use_graphite)},
+       {"alchemical_lambda", &s_AlchemicalLambdaSym, 0, 0, 'f', offsetof(MDArena, alchem_lambda)},
+       {"alchemical_delta_lambda", &s_AlchemicalDeltaLambdaSym, 0, 0, 'f', offsetof(MDArena, alchem_dlambda)},
        {NULL} /* Sentinel */
 };
 
@@ -594,6 +597,83 @@ s_MDArena_ToHash(VALUE self)
 
 /*
  *  call-seq:
+ *     set_alchemical_perturbation(group1, group2) -> [group1, group2]
+ *
+ *  Set vanishing and appearing atom groups for alchemical perturbation.
+ */
+static VALUE
+s_MDArena_SetAlchemicalPerturbation(VALUE self, VALUE gval1, VALUE gval2)
+{
+       IntGroup *ig1, *ig2;
+       MDArena *arena;
+       char *flags;
+       int i, n;
+       Data_Get_Struct(self, MDArena, arena);
+       if (gval1 == Qnil && gval2 == Qnil) {
+               md_set_alchemical_flags(arena, 0, NULL);
+               return self;
+       }
+       if (arena->mol == NULL)
+               rb_raise(rb_eMolbyError, "Molecule is not set");
+       n = arena->mol->natoms;
+       flags = (char *)calloc(1, n);
+       ig1 = IntGroupFromValue(gval1);  /*  nil argument is taken as an empty IntGroup  */
+       ig2 = IntGroupFromValue(gval2);
+       for (i = 0; i < n; i++) {
+               if (IntGroupLookupPoint(ig1, i) >= 0)
+                       flags[i] = 1;
+               if (IntGroupLookupPoint(ig2, i) >= 0) {
+                       if (flags[i] == 1)
+                               rb_raise(rb_eMolbyError, "duplicate atom (%d) in vanishing and appearing groups", i);
+                       flags[i] = 2;
+               }
+       }
+       if (md_set_alchemical_flags(arena, n, flags) != 0)
+               rb_raise(rb_eMolbyError, "cannot set alchemical flags");
+       free(flags);
+       gval1 = ValueFromIntGroup(ig1);
+       gval2 = ValueFromIntGroup(ig2);
+       IntGroupRelease(ig1);
+       IntGroupRelease(ig2);
+       return rb_ary_new3(2, gval1, gval2);
+}
+
+/*
+ *  call-seq:
+ *     get_alchemical_perturbation -> [group1, group2] or nil
+ *
+ *  If alchemical perturbation is enabled, get the current vanishing and appearing atom groups.
+ *  Otherwise, return nil.
+ */
+static VALUE
+s_MDArena_GetAlchemicalPerturbation(VALUE self)
+{
+       IntGroup *ig1, *ig2;
+       VALUE gval1, gval2;
+       MDArena *arena;
+       int i;
+       Data_Get_Struct(self, MDArena, arena);
+       if (arena->nalchem_flags == 0)
+               return Qnil;
+       if (arena->mol == NULL)
+               rb_raise(rb_eMolbyError, "Molecule is not set");        
+       ig1 = IntGroupNew();
+       ig2 = IntGroupNew();
+       for (i = 0; i < arena->nalchem_flags; i++) {
+               if (arena->alchem_flags[i] == 1)
+                       IntGroupAdd(ig1, i, 1);
+               else if (arena->alchem_flags[i] == 2)
+                       IntGroupAdd(ig2, i, 1);
+       }
+       gval1 = ValueFromIntGroup(ig1);
+       gval2 = ValueFromIntGroup(ig2);
+       IntGroupRelease(ig1);
+       IntGroupRelease(ig2);
+       return rb_ary_new3(2, gval1, gval2);    
+}
+
+/*
+ *  call-seq:
  *     keys -> Array
  *
  *  Returns an array of valid attributes.
@@ -664,6 +744,8 @@ Init_MolbyMDTypes(void)
        rb_define_method(rb_cMDArena, "[]=", s_MDArena_Set, 2);
        rb_define_method(rb_cMDArena, "to_hash", s_MDArena_ToHash, 0);
        rb_define_method(rb_cMDArena, "keys", s_MDArena_Keys, 0);
+       rb_define_method(rb_cMDArena, "set_alchemical_perturbation", s_MDArena_SetAlchemicalPerturbation, 2);
+       rb_define_method(rb_cMDArena, "get_alchemical_perturbation", s_MDArena_GetAlchemicalPerturbation, 0);
        rb_define_method(rb_cMDArena, "print_surface_area", s_MDArena_PrintSurfaceArea, 0);
 
        /*  All setter and getter are handled with the same C function (attribute name is taken