def initial_coordinates(mof, energy_map, atom_list, energy_limit): """ Determine initial coordinates to start interpenetration simulations from. Points are determined acoording to their energy (accepted if energy < energy_limit) and their position (accepted if applying pbc does not change its coordinates) """ reference_atom = 'C' if reference_atom in atom_list['atom']: ref_atom_index = int(atom_list['atom'].index(reference_atom) + 3) else: ref_atom_index = 3 initial_coors = [] energy_count = 0 pbc_count = 0 for emap_line in energy_map: emap_coor = [emap_line[0], emap_line[1], emap_line[2]] pbc_coor = pbc3(emap_coor, mof.to_frac, mof.to_car) pbc_x = round(pbc_coor[0], 1) pbc_y = round(pbc_coor[1], 1) pbc_z = round(pbc_coor[2], 1) if pbc_x == emap_coor[0] and pbc_y == emap_coor[1] and pbc_z == emap_coor[2]: if emap_line[ref_atom_index] < energy_limit: initial_coors.append([emap_line[0], emap_line[1], emap_line[2]]) else: energy_count += 1 else: pbc_count += 1 # print('Ommited PBC: ', pbc_count, ' Energy: ', energy_count) return initial_coors
def check_extension(sim_par, base_mof, mobile_mof, emap, emap_atom_list, new_structure): """ Checks collision between interpenetrating layer and base layer for a determined distance. Distance is calculated from given ext_cut_off value which determines the packing amount of the interpenetrating layer. Each coordinate in the interpenetrating layer is checked for high energy values by applying perodic boundary conditions to the coordinate according to energy map of the base layer. """ emap_max = [emap[-1][0], emap[-1][1], emap[-1][2]] emap_min = [emap[0][0], emap[0][1], emap[0][2]] side_length = [emap_max[0] - emap_min[0] + 1, emap_max[1] - emap_min[1] + 1, emap_max[2] - emap_min[2] + 1] x_length, y_length = int(side_length[1] * side_length[2]), int(side_length[2]) energy_limit = sim_par['atom_energy_limit'] ext_cut_off = sim_par['ext_cut_off'] rotation_info = new_structure['rotation'] first_point = new_structure['first_point'] translation_vector = new_structure['translation_vector'] packing_factor = Packing.factor(mobile_mof.uc_size, ext_cut_off) uc_vectors = Packing.uc_vectors(mobile_mof.uc_size, mobile_mof.uc_angle) trans_vec = Packing.translation_vectors(packing_factor, uc_vectors) packed_coors = Packing.uc_coors(trans_vec, packing_factor, uc_vectors, mobile_mof.atom_coors) x_angle, y_angle, z_angle = rotation_info collision = False collision_info = {'exist': collision, 'coor': None, 'pbc_coor': None} for unit_cell in packed_coors: if not collision: for coor_index, coor in enumerate(unit_cell): if not collision: rot_coor = coor rot_coor = xyz_rotation(rot_coor, [x_angle, y_angle, z_angle]) new_coor = add3(rot_coor, translation_vector) pbc_coor = pbc3(new_coor, base_mof.to_frac, base_mof.to_car) atom_name = mobile_mof.atom_names[coor_index] atom_index = energy_map_atom_index(atom_name, emap_atom_list) point_energy = tripolate(pbc_coor, atom_index, emap, x_length, y_length) if point_energy < energy_limit: continue else: collision = True collision_info = {'exist': collision, 'coor': [float(round(p, 3)) for p in new_coor], 'pbc_coor': [float(round(p, 3)) for p in pbc_coor]} break else: break else: break return collision_info
def regenerate(s1_name, s2_name, rotation, initial_coordinate, sim_par, sim_dir, export_dir, colorify=True, index=1, format='cif'): """ Reconstruct interpenetrated structure for given MOFs with rotation and initial coordinate. """ s1_mof = MOF(os.path.join(sim_dir['mof_dir'], s1_name + '.cif')) s2_mof = MOF(os.path.join(sim_dir['mof_dir'], s2_name + '.cif')) s2_mof_length = len(s2_mof) first_point = initial_coordinate x_angle, y_angle, z_angle = [math.radians(a) for a in rotation] structure = {'atom_names': [], 'atom_coors': [], 'pbc_coors': []} for idx in range(s2_mof_length): if idx == 0: atom_name = s2_mof.atom_names[idx] rot_coor = s2_mof.atom_coors[idx] rot_coor = xyz_rotation(rot_coor, [x_angle, y_angle, z_angle]) translation_vector = sub3(first_point, rot_coor) else: atom_name = s2_mof.atom_names[idx] rot_coor = s2_mof.atom_coors[idx] rot_coor = xyz_rotation(rot_coor, [x_angle, y_angle, z_angle]) new_coor = add3(rot_coor, translation_vector) pbc_coor = pbc3(new_coor, s1_mof.to_frac, s1_mof.to_car) structure['atom_coors'].append(new_coor) structure['pbc_coors'].append(pbc_coor) structure['atom_names'].append(atom_name) new_structure = {'atom_names': structure['atom_names'], 'name': s2_mof.name} if sim_par['export_pbc']: new_structure['atom_coors'] = structure['pbc_coors'] else: new_structure['atom_coors'] = structure['atom_coors'] # Export structure file new_s2_mof = MOF(new_structure, file_format='dict') joined_mof = s1_mof.join(new_s2_mof, colorify=False) joined_mof.name += '_' + str(index) joined_mof.export(export_dir, file_format=format) if colorify: new_s2_mof = MOF(new_structure, file_format='dict') joined_mof_color = s1_mof.join(new_s2_mof, colorify=True) joined_mof_color.name += '_' + str(index) + 'C' joined_mof_color.export(export_dir, file_format=format)
def check_interpenetration(sim_par, base_mof, mobile_mof, emap, atom_list): """ Run interpenetration algorithm with given simulation parameters and energy map. Returns simulation summary and structural information on the discovered structures. """ # Initialize simulation parameters structure_energy_limit = sim_par['structure_energy_limit'] atom_energy_limit = sim_par['atom_energy_limit'] # atom_energy_limit = sim_par['energy_density_limit'] * mobile_mof.ucv energy_density_limit = sim_par['energy_density_limit'] rotation_freedom = sim_par['rotation_freedom'] summary_percent = sim_par['summary_percent'] try_all_rotations = sim_par['try_all_rotations'] # Get energy map dimensions for trilinear interpolation emap_max = [emap[-1][0], emap[-1][1], emap[-1][2]] emap_min = [emap[0][0], emap[0][1], emap[0][2]] side_length = [emap_max[0] - emap_min[0] + 1, emap_max[1] - emap_min[1] + 1, emap_max[2] - emap_min[2] + 1] x_length, y_length = int(side_length[1] * side_length[2]), int(side_length[2]) if try_all_rotations: all_rot_degrees = possible_rotations(sim_par['rotation_freedom']) rotation_limit = len(all_rot_degrees) sim_par['rotation_limit'] = rotation_limit else: rotation_limit = sim_par['rotation_limit'] initial_coors = initial_coordinates(base_mof, emap, atom_list, atom_energy_limit) trial_limit = len(initial_coors) * rotation_limit div = round(trial_limit / (100 / summary_percent)) rot_freedom = 360 / rotation_freedom # omitted_coordinates = len(emap) - len(initial_coors) summary = {'percent': [], 'structure_count': [], 'trial_count': []} new_structures = [] abort_ip = False mobile_mof_length = len(mobile_mof) structure_count = 0 structure_total_energy = 0 ucv = mobile_mof.ucv energy_density = 0 initial_coor_index = 0 rotation_index = 0 # Interpenetration trial loop for different positions and orientations for t in range(trial_limit): abort_ip = False # Interpenetration trial loop for a specific position and different orientations for idx in range(mobile_mof_length): if not abort_ip: # If the interpenetration is just starting select rotation angles if idx == 0: # Determine random angles for rotation in 3D space # Determine first point for interpenetrating structure if t % rotation_limit == 0: # Start with original orientation for first trial x_angle, y_angle, z_angle = [0, 0, 0] first_point = initial_coors[initial_coor_index] initial_coor_index += 1 rotation_index = 0 elif try_all_rotations: x_angle, y_angle, z_angle = all_rot_degrees[rotation_index] else: x_angle = 2 * math.pi * math.floor(random() * rot_freedom) / rot_freedom y_angle = 2 * math.pi * math.floor(random() * rot_freedom) / rot_freedom z_angle = 2 * math.pi * math.floor(random() * rot_freedom) / rot_freedom rotation_index += 1 # Rotate first atom of the mobile MOF atom_name = mobile_mof.atom_names[idx] rot_coor = mobile_mof.atom_coors[idx] rot_coor = xyz_rotation(rot_coor, [x_angle, y_angle, z_angle]) translation_vector = sub3(first_point, rot_coor) # Initialize new structure dictionary structure = {'atom_names': [], 'atom_coors': [], 'pbc_coors': []} structure['first_point'] = first_point structure['translation_vector'] = translation_vector structure['atom_coors'].append(first_point) structure['pbc_coors'].append(first_point) structure['atom_names'].append(atom_name) structure['rotation'] = [x_angle, y_angle, z_angle] # If interpenetration is still going on elif idx < mobile_mof_length - 1: atom_name = mobile_mof.atom_names[idx] rot_coor = mobile_mof.atom_coors[idx] rot_coor = xyz_rotation(rot_coor, [x_angle, y_angle, z_angle]) new_coor = add3(rot_coor, translation_vector) pbc_coor = pbc3(new_coor, base_mof.to_frac, base_mof.to_car) emap_atom_index = energy_map_atom_index(atom_name, atom_list) point_energy = tripolate(pbc_coor, emap_atom_index, emap, x_length, y_length) structure_total_energy += point_energy energy_density += point_energy / ucv if energy_density > energy_density_limit: structure_total_energy = 0 energy_density = 0 abort_ip = True break # Fix this part (break interpenetration trial loop) else: structure['atom_coors'].append(new_coor) structure['pbc_coors'].append(pbc_coor) structure['atom_names'].append(atom_name) # If interpenetration trial ended with no collision - record structure info else: structure['energy'] = structure_total_energy structure['energy_density'] = energy_density new_structures.append(structure) structure_count += 1 structure_total_energy = 0 energy_density = 0 # Record simulation progress according to division (div) and summary if t % div == 0: percent_complete = round(t / trial_limit * 100) summary['percent'].append(percent_complete) summary['structure_count'].append(structure_count) summary['trial_count'].append(t) return summary, new_structures