Пример #1
0
def check_bonds(morphology, bond_dict):
    for bond in morphology["bond"]:
        posn1 = np.array(morphology["position"][bond[1]])
        +(
            np.array(morphology["image"][bond[1]])
            * np.array([morphology["lx"], morphology["ly"], morphology["lz"]])
        )
        posn2 = np.array(morphology["position"][bond[2]])
        +(
            np.array(morphology["image"][bond[2]])
            * np.array([morphology["lx"], morphology["ly"], morphology["lz"]])
        )
        separation = hf.calculate_separation(posn1, posn2)
        if separation >= morphology["lx"] / 2.0:
            print(
                "Periodic bond found:",
                bond,
                "because separation =",
                separation,
                ">=",
                morphology["lx"] / 2.0,
            )
            morphology = move_bonded_atoms(bond[1], morphology, bond_dict)
    return morphology
 def calculate_hop(self, chromophore_list):
     # Terminate if the next hop would be more than the termination limit
     if self.hop_limit is not None:
         if self.no_hops + 1 > self.hop_limit:
             return 1
     # Determine the hop times to all possible neighbours
     hop_times = []
     if self.use_average_hop_rates is True:
         # Use the average hop values given in the parameter dict to pick a
         # hop
         for neighbour_details in self.current_chromophore.neighbours:
             neighbour = chromophore_list[neighbour_details[0]]
             assert neighbour.ID == neighbour_details[0]
             if (self.mol_ID_dict[self.current_chromophore.ID] ==
                     self.mol_ID_dict[neighbour.ID]):
                 hop_rate = self.average_intra_hop_rate
             else:
                 hop_rate = self.average_inter_hop_rate
             hop_time = hf.determine_event_tau(hop_rate)
             # Keep track of the chromophoreID and the corresponding tau
             hop_times.append([neighbour.ID, hop_time])
     else:
         # Obtain the reorganisation energy in J (from eV in the parameter
         # file)
         for neighbour_index, transfer_integral in enumerate(
                 self.current_chromophore.neighbours_TI):
             # Ignore any hops with a NoneType transfer integral (usually
             # due to an orca error)
             if transfer_integral is None:
                 continue
             delta_E_ij = self.current_chromophore.neighbours_delta_E[
                 neighbour_index]
             # Load the specified hopping prefactor
             prefactor = self.hopping_prefactor
             # Get the relative image so we can update the carrier image
             # after the hop
             relative_image = self.current_chromophore.neighbours[
                 neighbour_index][1]
             # All of the energies are in eV currently, so convert them to J
             if self.use_VRH is True:
                 neighbour_chromo = chromophore_list[
                     self.current_chromophore.neighbours[neighbour_index]
                     [0]]
                 neighbour_chromo_posn = neighbour_chromo.posn + (
                     np.array(relative_image) *
                     np.array([axis[1] - axis[0]
                               for axis in self.sim_dims]))
                 # Chromophore separation needs converting to m
                 chromophore_separation = (hf.calculate_separation(
                     self.current_chromophore.posn, neighbour_chromo_posn) *
                                           1E-10)
                 hop_rate = hf.calculate_carrier_hop_rate(
                     self.lambda_ij * elementary_charge,
                     transfer_integral * elementary_charge,
                     delta_E_ij * elementary_charge,
                     prefactor,
                     self.T,
                     use_VRH=True,
                     rij=chromophore_separation,
                     VRH_delocalisation=self.VRH_delocalisation,
                     boltz_pen=self.use_simple_energetic_penalty,
                 )
             else:
                 hop_rate = hf.calculate_carrier_hop_rate(
                     self.lambda_ij * elementary_charge,
                     transfer_integral * elementary_charge,
                     delta_E_ij * elementary_charge,
                     prefactor,
                     self.T,
                     boltz_pen=self.use_simple_energetic_penalty,
                 )
             hop_time = hf.determine_event_tau(hop_rate)
             # Keep track of the chromophoreID and the corresponding tau
             hop_times.append([
                 self.current_chromophore.neighbours[neighbour_index][0],
                 hop_time,
                 relative_image,
             ])
     # Sort by ascending hop time
     hop_times.sort(key=lambda x: x[1])
     if len(hop_times) == 0:
         # We are trapped here, so create a dummy hop with time 1E99
         hop_times = [[self.current_chromophore.ID, 1E99, [0, 0, 0]]]
     # As long as we're not limiting by the number of hops:
     if self.hop_limit is None:
         # Ensure that the next hop does not put the carrier over its
         # lifetime
         if (self.current_time + hop_times[0][1]) > self.lifetime:
             # Send the termination signal to singleCoreRunKMC.py
             return 1
     # Move the carrier and send the contiuation signal to
     # singleCoreRunKMC.py
     # Take the quickest hop
     self.perform_hop(chromophore_list[hop_times[0][0]], hop_times[0][1],
                      hop_times[0][2])
     return 0
Пример #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