Ejemplo n.º 1
0
 def get_AA_template_position(self, CG_to_template_AAIDs):
     CG_COMs = {}
     # For each CG site, determine the types and positions so we can
     # calculate the COM
     for site_name in list(CG_to_template_AAIDs.keys()):
         atom_IDs = CG_to_template_AAIDs[site_name]
         AA_template = self.AA_templates_dictionary[site_name]
         # If the key's length is zero, then add all the atoms from the
         # template
         if len(atom_IDs) == 0:
             atom_IDs = list(np.arange(len(AA_template["type"])))
             # Update self.CGToTemplateAAIDs with these for later on
             CG_to_template_AAIDs[site_name] = atom_IDs
         site_positions = []
         site_types = []
         for atom_ID in atom_IDs:
             site_types.append(AA_template["type"][atom_ID])
             site_positions.append(
                 AA_template["unwrapped_position"][atom_ID]
             )
         # These output as numpy arrays because we can't do maths with lists
         CG_COMs[site_name] = hf.calc_COM(
             site_positions, list_of_atom_types=site_types
         )
     return CG_COMs, CG_to_template_AAIDs
Ejemplo n.º 2
0
 def obtain_chromophore_COM(
     self,
     electronically_active_unwrapped_posns,
     electronically_active_types,
     sim_dims,
 ):
     # Calculate the chromophore's position in the morphology (CoM of all
     # atoms in self.AAIDs from AA_morphology_dict)
     chromo_unwrapped_posn = hf.calc_COM(
         electronically_active_unwrapped_posns,
         list_of_atom_types=electronically_active_types,
     )
     chromo_wrapped_posn = copy.deepcopy(chromo_unwrapped_posn)
     chromo_wrapped_image = [0, 0, 0]
     # Now calculate the wrapped position of the chromophore and its image
     for axis in range(3):
         sim_extent = sim_dims[axis][1] - sim_dims[axis][0]
         while chromo_wrapped_posn[axis] < sim_dims[axis][0]:
             chromo_wrapped_posn[axis] += sim_extent
             chromo_wrapped_image[axis] -= 1
         while chromo_wrapped_posn[axis] > sim_dims[axis][1]:
             chromo_wrapped_posn[axis] -= sim_extent
             chromo_wrapped_image[axis] += 1
     return chromo_unwrapped_posn, chromo_wrapped_posn, chromo_wrapped_image
Ejemplo n.º 3
0
 def run_fine_grainer(self, ghost_dictionary):
     AA_dictionary = {
         "position": [],
         "image": [],
         "unwrapped_position": [],
         "mass": [],
         "diameter": [],
         "type": [],
         "body": [],
         "bond": [],
         "angle": [],
         "dihedral": [],
         "improper": [],
         "charge": [],
         "lx": 0,
         "ly": 0,
         "lz": 0,
         "xy": 0,
         "xz": 0,
         "yz": 0,
     }
     # Find the COMs of each CG site in the system, so that we know where to
     # move the template to
     CG_co_ms, self.CG_to_template_AAIDs = self.get_AA_template_position(
         self.CG_to_template_AAIDs
     )
     # Need to keep track of the atom ID numbers globally - run_fine_grainer
     # sees individual monomers, atomistic sees molecules and the xml needs
     # to contain the entire morphology.
     no_atoms_in_molecule = 0
     CG_type_list = {}
     for site_ID in self.site_IDs:
         CG_type_list[site_ID] = self.CG_dictionary["type"][site_ID]
     # Sort the CG sites into monomers so we can iterate over each monomer
     # in order to perform the fine-graining
     monomer_list = self.sort_into_monomers(CG_type_list)
     current_monomer_index = sum(
         self.molecule_lengths[: self.molecule_index]
     )
     atom_ID_lookup_table = {}
     # Calculate the total number of permitted atoms
     total_permitted_atoms = self.get_total_permitted_atoms(monomer_list)
     # Set the initial and final atom indices to None initially, so that we
     # don't add terminating units for small molecules
     start_atom_index = None
     end_atom_index = None
     for monomer_no, monomer in enumerate(monomer_list):
         # This monomer should have the same template file for all CG sites
         # in the monomer, if not we've made a mistake in splitting the
         # monomers. So check this:
         template_files = []
         monomer_CG_types = []
         for CG_site in monomer:
             template_files.append(
                 self.CG_to_template_files[
                     self.CG_dictionary["type"][CG_site]
                 ]
             )
             monomer_CG_types.append(self.CG_dictionary["type"][CG_site])
         if len(set(template_files)) != 1:
             print(monomer)
             print(monomer_CG_types)
             print(template_files)
             raise SystemError("Not all monomer sites are the same template")
         # Copy the template dictionary for editing for this monomer
         this_monomer_dictionary = copy.deepcopy(
             self.AA_templates_dictionary[
                 self.CG_dictionary["type"][monomer[0]]
             ]
         )
         for key in ["lx", "ly", "lz"]:  # TODO: Tilts
             this_monomer_dictionary[key] = self.CG_dictionary[key]
         # Include the image tag in case it's not present in the template
         if len(this_monomer_dictionary["image"]) == 0:
             this_monomer_dictionary["image"] = [[0, 0, 0]] * len(
                 this_monomer_dictionary["position"]
             )
         for site_ID in monomer:
             site_posn = np.array(
                 self.CG_dictionary["unwrapped_position"][site_ID]
             )
             site_translation = site_posn - CG_co_ms[CG_type_list[site_ID]]
             atom_ID_lookup_table[site_ID] = [
                 CG_type_list[site_ID],
                 [
                     x + no_atoms_in_molecule + self.no_atoms_in_morphology
                     for x in self.CG_to_template_AAIDs[
                         CG_type_list[site_ID]
                     ]
                 ],
             ]
             # Add the atoms in based on the CG site position
             for AAID in self.CG_to_template_AAIDs[CG_type_list[site_ID]]:
                 this_monomer_dictionary["unwrapped_position"][AAID] = list(
                     np.array(
                         this_monomer_dictionary["unwrapped_position"][AAID]
                     )
                     + site_translation
                 )
             # Next sort out the rigid bodies
             if CG_type_list[site_ID] in self.rigid_body_sites:
                 # Every rigid body needs a ghost particle that describes
                 # its CoM
                 AAID_positions = []
                 AAID_atom_types = []
                 # If the key is specified with no values, assume that all
                 # the AAIDs in the template constitute the rigid body
                 if len(self.rigid_body_sites[CG_type_list[site_ID]]) == 0:
                     self.rigid_body_sites[CG_type_list[site_ID]] = list(
                         np.arange(
                             len(
                                 self.CG_to_template_AAIDs[
                                     CG_type_list[site_ID]
                                 ]
                             )
                         )
                     )
                 for AAID in self.rigid_body_sites[CG_type_list[site_ID]]:
                     this_monomer_dictionary["body"][
                         AAID
                     ] = current_monomer_index
                     AAID_positions.append(
                         this_monomer_dictionary["unwrapped_position"][AAID]
                     )
                     AAID_atom_types.append(
                         this_monomer_dictionary["type"][AAID]
                     )
                 # Now create the ghost particle describing the rigid body
                 ghost_COM = hf.calc_COM(
                     AAID_positions, list_of_atom_types=AAID_atom_types
                 )
                 ghost_dictionary["unwrapped_position"].append(ghost_COM)
                 ghost_dictionary["mass"].append(1.0)
                 ghost_dictionary["diameter"].append(1.0)
                 ghost_dictionary["type"].append(
                     "R{:s}".format(CG_type_list[site_ID])
                 )
                 ghost_dictionary["body"].append(current_monomer_index)
                 ghost_dictionary["charge"].append(0.0)
                 # Then create the corresponding CG anchorpoint
                 ghost_dictionary["unwrapped_position"].append(ghost_COM)
                 ghost_dictionary["mass"].append(1.0)
                 ghost_dictionary["diameter"].append(1.0)
                 ghost_dictionary["type"].append(
                     "X{:s}".format(CG_type_list[site_ID])
                 )
                 ghost_dictionary["body"].append(-1)
                 ghost_dictionary["charge"].append(0.0)
                 # Now create a bond between them
                 # We want to bond together the previous two ghost particles,
                 # so this should work as it requires no knowledge of the
                 # number of ghost particles already in the system.
                 ghost_dictionary["bond"].append(
                     [
                         "{0:s}-{1:s}".format(
                             ghost_dictionary["type"][-2],
                             ghost_dictionary["type"][-1],
                         ),
                         "".join(
                             ["*" + str(len(ghost_dictionary["type"]) - 2)]
                         ),
                         "".join(
                             ["*" + str(len(ghost_dictionary["type"]) - 1)]
                         ),
                     ]
                 )
             else:
                 # Create a ghost particle that describe the CG anchorpoint
                 # for the non-rigid body
                 ghost_dictionary["unwrapped_position"].append(
                     self.CG_dictionary["unwrapped_position"][site_ID]
                 )
                 ghost_dictionary["mass"].append(1.0)
                 ghost_dictionary["diameter"].append(1.0)
                 ghost_dictionary["type"].append(
                     "X{:s}".format(CG_type_list[site_ID])
                 )
                 ghost_dictionary["body"].append(-1)
                 ghost_dictionary["charge"].append(0.0)
                 # Add in bonds between the CG anchorpoints and the atom
                 # closest to the CG site.
                 # Find the atom closest to the CG site
                 closest_atom_ID = None
                 closest_atom_posn = 1e99
                 for AAID, AA_position in enumerate(
                     this_monomer_dictionary["unwrapped_position"]
                 ):
                     separation = hf.calculate_separation(
                         self.CG_dictionary["unwrapped_position"][site_ID],
                         AA_position,
                     )
                     if separation < closest_atom_posn:
                         closest_atom_posn = separation
                         closest_atom_ID = AAID
                 # Add in the bond:
                 # Note that, in order to distinguish between the
                 # ghost_atom_IDs and the real_atomIDs, I've put an
                 # underscore in front of the closest_atom_ID, and a * in
                 # front of the ghost_atom_ID. When incrementing the
                 # atom_IDs for this monomer or this molecule, the
                 # real_atom_IDs will be incremented correctly.
                 # Later, when the ghost atoms are added to the main system,
                 # the ghost_atom_IDs will be incremented according to the
                 # number of atoms in the whole system (i.e. the ghosts
                 # appear at the end of the real atoms).
                 # At this time, the real_atom_IDs will be left unchanged
                 # because they are already correct for the whole system.
                 ghost_dictionary["bond"].append(
                     [
                         "{0:s}-{1:s}".format(
                             ghost_dictionary["type"][-1],
                             this_monomer_dictionary["type"][
                                 closest_atom_ID
                             ],
                         ),
                         "".join(
                             ["*" + str(len(ghost_dictionary["type"]) - 1)]
                         ),
                         "".join(
                             [
                                 "_"
                                 + str(
                                     closest_atom_ID + no_atoms_in_molecule
                                 )
                             ]
                         ),
                     ]
                 )
         # Now add in the bonds between CG_sites in this monomer
         for bond in self.CG_dictionary["bond"]:
             if (bond[1] in monomer) and (bond[2] in monomer):
                 CG_bond_type = bond[0]
                 this_monomer_dictionary["bond"].append(
                     self.CG_to_template_bonds[CG_bond_type]
                 )
         # Now need to add in the additional_constraints for this monomer
         # (which include the bond, angle and dihedral for the inter-monomer
         # connections). However, we need a check to make sure that we don't
         # add stuff for the final monomer (because those atoms +25 don't
         # exist in this molecule!)
         for constraint in self.additional_constraints:
             # Firstly, skip this constraint if the current monomer doesn't
             # have the relevant atom types
             if (
                 all(
                     [
                         atom_type in set(this_monomer_dictionary["type"])
                         for atom_type in constraint[0].split("-")
                     ]
                 )
                 is False
             ):
                 continue
             # Also check that we're not at the final monomer
             at_final_monomer = False
             for atom_ID in constraint[2:]:
                 if (
                     no_atoms_in_molecule + atom_ID + 1
                 ) > total_permitted_atoms:
                     at_final_monomer = True
                     break
             if at_final_monomer is True:
                 break
             # Work out which key to write the constraint to based on its
             # length:
             # 3 = Bond, 4 = Angle, 5 = Dihedral, 6 = Improper
             constraint_type = ["bond", "angle", "dihedral", "improper"]
             this_monomer_dictionary[
                 constraint_type[len(constraint) - 3]
             ].append(constraint)
         # Finally, increment the atom IDs to take into account previous
         # monomers in this molecule and then update the AADictionary.
         # Note that the ghost dictionary bond was already updated to have
         # the correct realAtom AAID for this molecule when the bond was
         # created. Therefore, leave the ghost dictionary unchanged.
         this_monomer_dictionary, ghost_dictionary = hf.increment_atom_IDs(
             this_monomer_dictionary,
             ghost_dictionary,
             no_atoms_in_molecule,
             modify_ghost_dictionary=False,
         )
         no_atoms_in_molecule += len(this_monomer_dictionary["type"])
         current_monomer_index += 1
         # Update the current AA dictionary with this monomer
         AA_dictionary = self.update_molecule_dictionary(
             this_monomer_dictionary, AA_dictionary
         )
     # All Monomers sorted, now for the final bits
     AA_dictionary["natoms"] = no_atoms_in_molecule
     for key in ["lx", "ly", "lz"]:
         AA_dictionary[key] = this_monomer_dictionary[key]
     # Now we terminate the molecules using the technique in the
     # add_hydrogens_to_UA analysis script
     new_hydrogen_data = []
     for atom_index, atom_type in enumerate(AA_dictionary["type"]):
         if atom_type not in self.molecule_terminating_connections.keys():
             continue
         bonded_AAIDs = []
         # Iterate over all termination connections defined for this
         # atom_type (in case we are trying to do something mega
         # complicated)
         for connection_info in self.molecule_terminating_connections[
             atom_type
         ]:
             for [bond_name, AAID1, AAID2] in AA_dictionary["bond"]:
                 if AAID1 == atom_index:
                     if AAID2 not in bonded_AAIDs:
                         bonded_AAIDs.append(AAID2)
                 elif AAID2 == atom_index:
                     if AAID1 not in bonded_AAIDs:
                         bonded_AAIDs.append(AAID1)
             if len(bonded_AAIDs) != connection_info[0]:
                 continue
             new_hydrogen_positions = hf.get_terminating_positions(
                 AA_dictionary["unwrapped_position"][atom_index],
                 [
                     AA_dictionary["unwrapped_position"][bonded_AAID]
                     for bonded_AAID in bonded_AAIDs
                 ],
                 1,
             )
             for hydrogen_position in new_hydrogen_positions:
                 new_hydrogen_data.append(
                     [atom_index, list(hydrogen_position)]
                 )
     AA_dictionary = self.add_terminating_to_molecule(
         AA_dictionary, new_hydrogen_data
     )
     AA_dictionary = hf.add_wrapped_positions(AA_dictionary)
     AA_dictionary = hf.add_masses(AA_dictionary)
     AA_dictionary = hf.add_diameters(AA_dictionary)
     # Now the molecule is done, we need to add on the correct identifying
     # numbers for all the bonds, angles and dihedrals (just as we did
     # between monomers) for the other molecules in the system, so that they
     # all connect to the right atoms.
     # Note that here we need to increment the '_'+ATOMIDs in the ghost
     # dictionary to take into account the number of molecules.
     AA_dictionary, ghost_dictionary = hf.increment_atom_IDs(
         AA_dictionary,
         ghost_dictionary,
         self.no_atoms_in_morphology,
         modify_ghost_dictionary=True,
     )
     return AA_dictionary, atom_ID_lookup_table, ghost_dictionary