def obtain_scale_factors(parameter_dict): print("Obtaining correct scaling for epsilon and sigma...") # The scaling factors are 1/largestSigma in the LJ coeffs, and 1 # / largestEpsilon LJFFs = [] for CG_site, directory in parameter_dict["CG_to_template_dirs"].items(): FF_loc = os.path.join( directory, parameter_dict["CG_to_template_force_fields"][CG_site]) FF = hf.load_FF_xml(FF_loc) LJFFs += FF["lj"] largest_sigma = max(list(map(float, np.array(LJFFs)[:, 2]))) largest_epsilon = max(list(map(float, np.array(LJFFs)[:, 1]))) return 1 / float(largest_sigma), 1 / float(largest_epsilon)
def get_new_type_mappings( self, CG_to_template_dirs, CG_to_template_force_fields ): force_field_locations = [] force_field_mappings = [] morphology_atom_types = [] CG_to_template_mappings = {} for CG_site, directory in CG_to_template_dirs.items(): FF_loc = os.path.join( directory, CG_to_template_force_fields[CG_site] ) if FF_loc not in force_field_locations: force_field_locations.append(FF_loc) for FF_loc in force_field_locations: mapping_for_this_FF = {} force_field = hf.load_FF_xml(FF_loc) for lj_interaction in force_field["lj"]: atom_type = lj_interaction[0] while atom_type in morphology_atom_types: # Atom type already exists in morphology, so increment the # atom_type number by one for i in range(1, len(atom_type)): # Work out where the integer start so we can increment # it (should be i = 1 for one-character element names) try: integer = int(atom_type[i:]) break except: continue atom_type = "".join([atom_type[:i], str(integer + 1)]) morphology_atom_types.append(atom_type) mapping_for_this_FF[lj_interaction[0]] = atom_type force_field_mappings.append(mapping_for_this_FF) for CG_site, directory in CG_to_template_dirs.items(): FF_loc = os.path.join( directory, CG_to_template_force_fields[CG_site] ) CG_to_template_mappings[CG_site] = force_field_mappings[ force_field_locations.index(FF_loc) ] return CG_to_template_mappings
def get_FF_coeffs(self): # First find all of the forcefields specified in the par file all_FF_names = {} for CG_site, directory in self.CG_to_template_dirs.items(): FF_loc = os.path.join(directory, self.CG_to_template_force_fields[CG_site]) if FF_loc not in list(all_FF_names.values()): all_FF_names[CG_site] = FF_loc FF_list = [] # Then load in all of the FFs with the appropriate mappings for CG_site in list(all_FF_names.keys()): FF_list.append( hf.load_FF_xml( all_FF_names[CG_site], mapping=self.new_type_mappings[CG_site] ) ) # Combine all of the individual, mapped FFs into one master field master_FF = {} for FF in FF_list: for FF_type in list(FF.keys()): if FF_type not in list(master_FF.keys()): master_FF[FF_type] = FF[FF_type] else: master_FF[FF_type] += FF[FF_type] # Finally, assign the expected variables to each value in the masterFF self.lj_coeffs = master_FF["lj"] self.dpd_coeffs = master_FF["dpd"] self.bond_coeffs = master_FF["bond"] self.angle_coeffs = master_FF["angle"] self.dihedral_coeffs = master_FF["dihedral"] self.improper_coeffs = master_FF["improper"] # Set Pair Coeffs self.pair_class = None if self.pair_type.lower() != "none": # Log the correct pairType energy self.log_quantities.append("".join(["pair_", self.pair_type, "_energy"])) # HOOMD crashes if you don't specify all pair combinations, so need # to make sure we do this. atom_types = sorted( list(set(self.AA_morphology_dict["type"])), key=lambda x: hf.convert_string_to_int(x), ) all_pair_types = [] # Create a list of all of the pairTypes to ensure that the required # coefficients are set for atom_type1 in atom_types: for atom_type2 in atom_types: pair_type = "{0}-{1}".format(atom_type1, atom_type2) reverse_pair_type = "{1}-{0}".format(atom_type1, atom_type2) if (pair_type not in all_pair_types) and ( reverse_pair_type not in all_pair_types ): all_pair_types.append(pair_type) # Read in the pairTypes, parameters and coefficients and set them # for HOOMD if self.pair_type.lower() == "dpd": self.pair_class = pair.dpd( r_cut=self.pair_r_cut * self.s_scale, T=self.temperature ) # Use the geometric mixing rule for all possible combinations of # the specified forcefield coefficients for atom_index1, atom_type1 in enumerate( [coeff[0] for coeff in self.dpd_coeffs] ): for atom_index2, atom_type2 in enumerate( [coeff[0] for coeff in self.dpd_coeffs] ): self.pair_class.pair_coeff.set( atom_type1, atom_type2, A=np.sqrt( (self.dpd_coeffs[atom_index1][1] * self.e_scale) * (self.dpd_coeffs[atom_index2][1] * self.e_scale) ), r_cut=np.sqrt( (self.dpd_coeffs[atom_index1][2] * self.s_scale) * (self.dpd_coeffs[atom_index2][1] * self.s_scale) ), gamma=self.pair_dpd_gamma_val, ) try: all_pair_types.remove( "{0}-{1}".format(atom_type1, atom_type2) ) except: pass # Because we've been removing each pair from allPairTypes, all # that are left are the pair potentials that are unspecified in # the parXX.py (e.g. ghost particle interactions), so set these # interactions to zero for pair_type in all_pair_types: self.pair_class.pair_coeff.set( pair_type.split("-")[0], pair_type.split("-")[1], A=0.0, r_cut=0.0, gamma=0.0, ) elif self.pair_type.lower() == "lj": self.pair_class = pair.lj(r_cut=self.pair_r_cut * self.s_scale) self.pair_class.set_params(mode="xplor") for atom_index1, atom_type1 in enumerate( [coeff[0] for coeff in self.lj_coeffs] ): for atom_index2, atom_type2 in enumerate( [coeff[0] for coeff in self.lj_coeffs] ): self.pair_class.pair_coeff.set( atom_type1, atom_type2, epsilon=np.sqrt( (self.lj_coeffs[atom_index1][1] * self.e_scale) * (self.lj_coeffs[atom_index2][1] * self.e_scale) ), sigma=np.sqrt( (self.lj_coeffs[atom_index1][2] * self.s_scale) * (self.lj_coeffs[atom_index2][2] * self.s_scale) ), ) try: all_pair_types.remove( "{0}-{1}".format(atom_type1, atom_type2) ) except: pass # Because we've been removing each pair from allPairTypes, all # that are left are the pair potentials that are unspecified in # the parXX.py (e.g. ghost particle interactions), so set these # interactions to zero for pair_type in all_pair_types: self.pair_class.pair_coeff.set( pair_type.split("-")[0], pair_type.split("-")[1], epsilon=0.0, sigma=0.0, ) else: raise SystemError( "Non-dpd/lj pair potentials not yet hard-coded!" " Please describe how to interpret them on this line." ) # Set Bond Coeffs # Real bonds if self.bond_type.lower() == "harmonic": if len(self.bond_coeffs) > 0: self.log_quantities.append( "".join(["bond_", self.bond_type, "_energy"]) ) self.bond_class = bond.harmonic() for bond_coeff in self.bond_coeffs: # [k] = kcal mol^{-1} \AA^{-2} * episilon/sigma^{2}, [r0] = # \AA * sigma^{2} self.bond_class.bond_coeff.set( bond_coeff[0], k=bond_coeff[1] * (self.e_scale / (self.s_scale ** 2)), r0=bond_coeff[2] * self.s_scale, ) # Ghost bonds # If there is no anchoring, rather than change the xml, just set the # bond k values to 0. if self.group_anchoring.lower() == "all": group_anchoring_types = [ "".join(["X", CG_type]) for CG_type in list(self.CG_to_template_AAIDs.keys()) ] elif self.group_anchoring.lower() == "none": group_anchoring_types = [] else: group_anchoring_types = [ "".join(["X", CG_type]) for CG_type in self.group_anchoring.split(",") ] anchor_bond_types = [] no_anchor_bond_types = [] for bond_type in self.AA_morphology_dict["bond"]: if "X" in bond_type[0]: atom_type1 = bond_type[0].split("-")[0] atom_type2 = bond_type[0].split("-")[1] if (atom_type1 in group_anchoring_types) or ( atom_type2 in group_anchoring_types ): if bond_type[0] not in anchor_bond_types: anchor_bond_types.append(bond_type[0]) else: if bond_type[0] not in no_anchor_bond_types: no_anchor_bond_types.append(bond_type[0]) for bond_type in anchor_bond_types: self.bond_class.bond_coeff.set(bond_type, k=1E6, r0=0) for bond_type in no_anchor_bond_types: self.bond_class.bond_coeff.set(bond_type, k=0, r0=0) else: raise SystemError( "Non-harmonic bond potentials not yet hard-coded!" " Please describe how to interpret them on this line." ) # Set Angle Coeffs self.angle_class = None if len(self.angle_coeffs) > 0: self.log_quantities.append("".join(["angle_", self.angle_type, "_energy"])) if self.angle_type.lower() == "harmonic": self.angle_class = angle.harmonic() for angle_coeff in self.angle_coeffs: # [k] = kcal mol^{-1} rad^{-2} * epsilon, [t] = rad self.angle_class.set_coeff( angle_coeff[0], k=angle_coeff[1] * self.e_scale, t0=angle_coeff[2], ) else: raise SystemError( "Non-harmonic angle potentials not yet hard-coded!" " please describe how to interpret them on this line." ) else: print("No angles detected!") # Set Dihedral Coeffs self.dihedral_class = None if len(self.dihedral_coeffs) > 0: self.log_quantities.append( "".join(["dihedral_", self.dihedral_type, "_energy"]) ) if self.dihedral_type.lower() == "table": self.dihedral_class = dihedral.table(width=1000) for dihedral_coeff in self.dihedral_coeffs: self.dihedral_class.dihedral_coeff.set( dihedral_coeff[0], func=multi_harmonic_torsion, coeff=dict( v0=dihedral_coeff[1] * self.e_scale, v1=dihedral_coeff[2] * self.e_scale, v2=dihedral_coeff[3] * self.e_scale, v3=dihedral_coeff[4] * self.e_scale, v4=dihedral_coeff[5] * self.e_scale, ), ) elif self.dihedral_type.lower() == "opls": self.dihedral_class = dihedral.opls() for dihedral_coeff in self.dihedral_coeffs: self.dihedral_class.set_coeff( dihedral_coeff[0], k1=dihedral_coeff[1] * self.e_scale, k2=dihedral_coeff[2] * self.e_scale, k3=dihedral_coeff[3] * self.e_scale, k4=dihedral_coeff[4] * self.e_scale, ) else: raise SystemError( "Non-tabulated dihedral potentials not yet hard-coded!" " Please describe how to interpret them on this line." ) else: print("No dihedrals detected") # Set Improper Coeffs self.improper_class = None if len(self.improper_coeffs) > 0: self.improper_class = improper.harmonic() for improper_coeff in self.improper_coeffs: self.improper_class.improper_coeff.set( improper_coeff[0], k=improper_coeff[1] * self.e_scale, chi=improper_coeff[2], )