def remove_ghost_particles(last_phase_xml, output_file_name, sigma=1.0): # Remove all the ghost particles from the morphology for the final output final_morphology = hf.load_morphology_xml(last_phase_xml) # Determine the atomIDs for each particle beginning with the letters 'X' # or 'R' - these are the ghost particles atom_IDs_to_remove = [] for atom_ID, atom_type in enumerate(final_morphology["type"]): if (atom_type[0] == "X") or (atom_type[0] == "R"): # This is a ghost particle atom_IDs_to_remove.append(atom_ID) # Reverse sorting trick so that the location indices don't change as we # delete particles from the system atom_IDs_to_remove.sort(reverse=True) # Now delete the atoms from the morphology atom_attribs = ["position", "image", "type", "mass", "diameter", "body", "charge"] for atom_ID in atom_IDs_to_remove: for key in atom_attribs: final_morphology[key].pop(atom_ID) final_morphology["natoms"] -= len(atom_IDs_to_remove) # Delete any constraints associated with those atoms that have been removed atom_constraints = ["bond", "angle", "dihedral", "improper"] for key in atom_constraints: constraints_to_remove = [] for constraint_no, constraint in enumerate(final_morphology[key]): for atom_ID in constraint[1:]: if (atom_ID in atom_IDs_to_remove) and ( constraint_no not in constraints_to_remove ): constraints_to_remove.append(constraint_no) constraints_to_remove.sort(reverse=True) for constraint_no in constraints_to_remove: final_morphology[key].pop(constraint_no) # Output the final morphology hf.write_morphology_xml(final_morphology, output_file_name, sigma)
def scale_morphology(initial_morphology, parameter_dict, s_scale, e_scale): # If sScale != 1.0, then scale the morphology and rewrite the phase0 xml print("Scaling morphology by sigma = {:f}...".format(1 / s_scale)) if s_scale != 1.0: hf.scale(initial_morphology, s_scale) hf.write_morphology_xml( initial_morphology, os.path.join( parameter_dict["output_morph_dir"], os.path.splitext(parameter_dict["morphology"])[0], "morphology", "".join(["phase0_", parameter_dict["morphology"]]), ), )
def main(): list_of_files = sys.argv[1:] if len(list_of_files) < 1: print("No files requested to convert!") exit() for file_name in list_of_files: print("Fixing the images for {:s}...".format(file_name)) morphology = hf.load_morphology_xml(file_name) morphology = zero_out_images(morphology) bond_dict = get_bond_dict(morphology) morphology = check_bonds(morphology, bond_dict) file_directory, split_file_name = os.path.split(file_name) hf.write_morphology_xml( morphology, os.path.join(file_directory, "".join(["image_fix_", split_file_name])), )
def main(): parser = argparse.ArgumentParser() parser.add_argument( "-m", "--molecule_source", required=False, help="""Specify the source of the add_hydrogens dictionary and sigma value. This can be in the form of a python script which returns a function of the dictionary and sigma or the name of the molecule.""", ) args, input_files = parser.parse_known_args() hydrogens_to_add, sigma_val = find_information(args) for input_file in input_files: # This dictionary has keys of the atom type, and values where the first # element is the number of bonds required for us to add a hydrogen to # the atom and the second element of the value defines how many # hydrogens to add to said atom. print( "THIS FUNCTION IS SET UP TO USE A DICTIONARY TO DEFINE HOW MANY HYDROGENS " " TO ADD TO BONDS OF A SPECIFIC TYPE WITH A CERTAIN NUMBER OF BONDS" ) print(hydrogens_to_add) print( "IF THE ABOVE DICTIONARY DOESN'T LOOK RIGHT, PLEASE TERMINATE NOW AND " " IGNORE ANY OUTPUTS UNTIL THE DICTIONARY HAS BEEN RECTIFIED" ) print("Additionally, we're using a sigma value of", sigma_val) morphology_dict = hf.load_morphology_xml(input_file, sigma=sigma_val) # We MUST fix the images first, otherwise the hydrogens will be put in # incorrectly. morphology_dict = fi.zero_out_images(morphology_dict) bond_dict = fi.get_bond_dict(morphology_dict) morphology_dict = fi.check_bonds(morphology_dict, bond_dict) # Now we can safely obtain the unwrapped positions morphology_dict = hf.add_unwrapped_positions(morphology_dict) hydrogen_positions = calculate_hydrogen_positions( morphology_dict, hydrogens_to_add ) morphology_dict = add_hydrogens_to_morph( morphology_dict, hydrogen_positions ) morphology_dict = hf.add_wrapped_positions(morphology_dict) hf.write_morphology_xml( morphology_dict, input_file.replace(".xml", "_AA.xml"), check_wrapped_posns=False, )
def main( AA_morphology_dict, CG_morphology_dict, CG_to_AAID_master, parameter_dict, chromophore_list, ): # Get the random seed now for all the child processes if parameter_dict["random_seed_override"] is not None: np.random.seed(parameter_dict["random_seed_override"]) # Main execution function for run_HOOMD that performs the required MD phases # First, scale the input morphology based on the pair potentials such that # the distances and energies are normalised to the strongest pair # interaction and the diameter of the largest atom (makes it easier on # HOOMDs calculations and ensures that T = 1.0 is an interesting temperature # threshold) current_files = os.listdir( os.path.join( parameter_dict["output_morph_dir"], os.path.splitext(parameter_dict["morphology"])[0], "morphology", ) ) # sScale, eScale = obtainScaleFactors(parameterDict) print("Under the hood eScaling and sScaling has been disabled.") s_scale = 1.0 e_scale = 1.0 # Only scale the morphology if it hasn't been already if (parameter_dict["overwrite_current_data"] is False) and ( "".join(["phase0_", parameter_dict["morphology"]]) in current_files ): pass else: scale_morphology(AA_morphology_dict, parameter_dict, s_scale, e_scale) # Reset logfile try: os.remove( os.path.join( parameter_dict["output_morph_dir"], os.path.splitext(parameter_dict["morphology"])[0], "morphology", "".join( [ "energies_", os.path.splitext(parameter_dict["morphology"])[0], ".log", ] ), ) ) except OSError: pass # Perform each molecular dynamics phase as specified in the parXX.py for phase_no in range(parameter_dict["number_of_phases"]): input_file = "phase{0:d}_{1:s}".format(phase_no, parameter_dict["morphology"]) output_file = "phase{0:d}_{1:s}".format( phase_no + 1, parameter_dict["morphology"] ) if output_file in current_files: if parameter_dict["overwrite_current_data"] is False: print(output_file, "already exists. Skipping...") continue md_phase( AA_morphology_dict, CG_morphology_dict, CG_to_AAID_master, parameter_dict, phase_no, os.path.join( parameter_dict["output_morph_dir"], os.path.splitext(parameter_dict["morphology"])[0], "morphology", input_file, ), os.path.join( parameter_dict["output_morph_dir"], os.path.splitext(parameter_dict["morphology"])[0], "morphology", output_file, ), s_scale, e_scale, ).optimise_structure() final_xml_name = os.path.join( parameter_dict["output_morph_dir"], os.path.splitext(parameter_dict["morphology"])[0], "morphology", "".join(["final_", parameter_dict["morphology"]]), ) if "".join(["final_", parameter_dict["morphology"]]) not in current_files: # Now all phases are complete, remove the ghost particles from the # system print("Removing ghost particles to create final output...") remove_ghost_particles( os.path.join( parameter_dict["output_morph_dir"], os.path.splitext(parameter_dict["morphology"])[0], "morphology", output_file, ), final_xml_name, sigma=s_scale, ) # Finally, update the pickle file with the most recent and realistic # AAMorphologyDict so that we can load it again further along the pipeline AA_morphology_dict = hf.load_morphology_xml(final_xml_name) # Now that we've obtained the final fine-grained morphology, we need to fix # the images to prevent issues with obtaining the chromophores and running # them through the ZINDO/S calculations later... AA_morphology_dict = hf.fix_images(AA_morphology_dict) # ...add in the unwrapped positions... AA_morphology_dict = hf.add_unwrapped_positions(AA_morphology_dict) # ...rewrite the final morphology xml... hf.write_morphology_xml(AA_morphology_dict, final_xml_name) # ...and write the pickle file. hf.write_pickle( ( AA_morphology_dict, CG_morphology_dict, CG_to_AAID_master, parameter_dict, chromophore_list, ), os.path.join( parameter_dict["output_morph_dir"], os.path.splitext(parameter_dict["morphology"])[0], "code", "".join([os.path.splitext(parameter_dict["morphology"])[0], ".pickle"]), ), ) return ( AA_morphology_dict, CG_morphology_dict, CG_to_AAID_master, parameter_dict, chromophore_list, )
def analyse_morphology(self): # Split the morphology into individual molecules print("Finding molecules...") molecule_IDs, molecule_lengths = self.split_molecules() rolling_AA_index = 0 CG_morphology_dict = {} AA_morphology_dict = {} # Set the AAMorphology and CGMorphology system sizes to the same as the # input file system size for box_dimension in ["lx", "ly", "lz", "xy", "xz", "yz"]: CG_morphology_dict[box_dimension] = self.CG_dictionary[ box_dimension ] AA_morphology_dict[box_dimension] = self.CG_dictionary[ box_dimension ] CG_to_AAID_master = [] # This is a list of dictionaries. Elements in # the list correspond to molecules (useful for splitting out individual # molecules for the xyz conversion) within the element, the dictionary # key is the CG site, the value is a list containing the CG type (e.g. # 'thio') as the first element and then another list of all the AAIDs # corresponding to that CG site as the second element. # If no CG_to_template info is present in the parameter dict, then we # can assume that the morphology is already fine-grained and so we can # just return the important information and skip this module if len(self.CG_to_template_dirs) == 0: print( "No CG to AA data found in parameter file - the morphology is already" " fine-grained! Skipping this module..." ) # Write the xml file and create the pickle print("Writing xml file...") AA_file_name = os.path.join( self.output_morph_dir, self.morphology_name, "morphology", "".join([self.morphology_name, ".xml"]), ) atomistic_morphology = hf.add_unwrapped_positions( self.CG_dictionary ) # Now write the morphology xml hf.write_morphology_xml(atomistic_morphology, AA_file_name) # And finally write the pickle with the CGDictionary as None (to # indicate to MorphCT that no fine-graining has taken place), but # the other parameters assigned as required. pickle_location = os.path.join( self.output_morph_dir, self.morphology_name, "code", "".join([self.morphology_name, ".pickle"]), ) hf.write_pickle( ( atomistic_morphology, None, None, self.parameter_dict, self.chromophore_list, ), pickle_location, ) return ( atomistic_morphology, None, None, self.parameter_dict, self.chromophore_list, ) # Create a ghost particle dictionary to be added at the end of the # morphology. This way, we don't mess up the order of atoms later on # when trying to split back up into individual molecules and monomers. # The ghost dictionary contains all of the type T and type X particles # that will anchor the thiophene rings to the CG COM positions. ghost_dictionary = { "position": [], "image": [], "unwrapped_position": [], "mass": [], "diameter": [], "type": [], "body": [], "bond": [], "angle": [], "dihedral": [], "improper": [], "charge": [], } # Need to check for atom-type conflicts and suitably increment the # type indices if more than one molecule type is being fine-grained new_type_mappings = self.get_new_type_mappings( self.CG_to_template_dirs, self.CG_to_template_force_fields ) # Need to update the self.parameterDict, which will be rewritten at the # end of this module self.parameter_dict["new_type_mappings"] = new_type_mappings molecule = [] unique_mappings = [] CG_sites, mappings = hf.parallel_sort( list(new_type_mappings.keys()), list(new_type_mappings.values()) ) for index, mapping in enumerate(mappings): if mapping not in unique_mappings: molecule.append([]) unique_mappings.append(mapping) molecule[-1].append(CG_sites[index]) printExplanation = True for index, CG_sites in enumerate(molecule): printMol = True initial_atoms, final_atoms = hf.parallel_sort( list(unique_mappings[index].keys()), list(unique_mappings[index].values()), ) for index, initial_atom in enumerate(initial_atoms): if initial_atom == final_atoms[index]: continue if printExplanation is True: print( "The following atom types have been remapped due to conflicting" " typenames in the atomistic templates:" ) printExplanation = False if printMol is True: print( "Atom types belonging the molecule described by", "".join([repr(CG_sites), ":"]), ) printMol = False print(initialAtom, "--->", finalAtoms[index]) print("Adding", len(molecule_IDs), "molecules to the system...") for molecule_number in range(len(molecule_IDs)): print("Adding molecule number", molecule_number, "\r", end=" ") if sys.stdout is not None: sys.stdout.flush() # Obtain the AA dictionary for each molecule using the # fine-graining procedure AA_molecule_dict, C_gto_AAIDs, ghost_dictionary = atomistic( molecule_number, molecule_IDs[molecule_number], self.CG_dictionary, molecule_lengths, rolling_AA_index, ghost_dictionary, self.parameter_dict, ).return_data() CG_to_AAID_master.append(C_gto_AAIDs) # Update the morphology dictionaries with this new molecule for key in list(self.CG_dictionary.keys()): if key not in [ "lx", "ly", "lz", "xy", "xz", "yz", "time_step", "dimensions", ]: if key not in list(AA_morphology_dict.keys()): AA_morphology_dict[key] = AA_molecule_dict[key] else: AA_morphology_dict[key] += AA_molecule_dict[key] rolling_AA_index += len(AA_molecule_dict["type"]) # Now add the ghost dictionary to the end of the morphology file # total_number_of_atoms should be == rolling_AA_index, but don't want # to take any chances. total_number_of_atoms = len(AA_morphology_dict["type"]) # Add in the wrapped positions of the ghosts. Need to know sim dims for # this for key in ["lx", "ly", "lz", "xy", "xz", "yz"]: ghost_dictionary[key] = AA_morphology_dict[key] ghost_dictionary = hf.add_wrapped_positions(ghost_dictionary) for key in ["lx", "ly", "lz", "xy", "xz", "yz"]: ghost_dictionary.pop(key) # The real atoms that the ghost particles are bonded to are already # correct and no longer need to be changed. # However, the ghost particles themselves will have the wrong indices # if we were to add them to the system directly. # Therefore, increment all of the ghost bond indices that begin with a # * (ghost particle) by the total number of atoms already in the # system. for bond_no, bond in enumerate(ghost_dictionary["bond"]): if str(bond[1])[0] == "*": ghost_dictionary["bond"][bond_no][1] = ( int(bond[1][1:]) + total_number_of_atoms ) if str(bond[2])[0] == "*": ghost_dictionary["bond"][bond_no][2] = ( int(bond[2][1:]) + total_number_of_atoms ) # Now append all ghosts to morphology for key in list(ghost_dictionary.keys()): AA_morphology_dict[key] += ghost_dictionary[key] # Finally, update the number of atoms AA_morphology_dict["natoms"] += len(ghost_dictionary["type"]) print("\n") # Now write the xml file and create the pickle print("Writing xml file...") AA_file_name = os.path.join( self.output_morph_dir, self.morphology_name, "morphology", "".join([self.morphology_name, ".xml"]), ) # Replace the `positions' with the `unwrapped_positions' ready for # writing AA_morphology_dict = hf.replace_wrapped_positions(AA_morphology_dict) # Update the additional_constraints that we put in by checking all of # the constraints have the correct names before writing AA_morphology_dict = hf.check_constraint_names(AA_morphology_dict) # Now write the morphology xml hf.write_morphology_xml(AA_morphology_dict, AA_file_name) # And finally write the pickle pickle_location = os.path.join( self.output_morph_dir, self.morphology_name, "code", "".join([self.morphology_name, ".pickle"]), ) hf.write_pickle( ( AA_morphology_dict, self.CG_dictionary, CG_to_AAID_master, self.parameter_dict, self.chromophore_list, ), pickle_location, ) return ( AA_morphology_dict, self.CG_dictionary, CG_to_AAID_master, self.parameter_dict, self.chromophore_list, )