def get_normal(building_block): """ Get a normal to a plane crossing the building block. The normal is defined by the plane of best fit to the placer atoms of the building block. It is defined such that the it always forms an acute angle with the vector running from the centroid of the placer atoms to the centroid of the building block. Parameters ---------- building_block : :.BuildingBlock` The building block whose normal should be calculated. Returns ------- :class:`numpy.ndarray` The normal of `building_block`. """ placer_centroid = building_block.get_centroid( atom_ids=building_block.get_placer_ids(), ) normal = stk.get_acute_vector( reference=building_block.get_centroid() - placer_centroid, vector=building_block.get_plane_normal( atom_ids=building_block.get_placer_ids(), ), ) if np.allclose(normal, [0, 0, 1], atol=1e-13): return np.array([0, 0, 1]) if np.allclose(normal, [0, 0, -1], atol=1e-13): return np.array([0, 0, -1]) return normal
def functional_group_angle(building_block): fg0 = next(building_block.get_functional_groups(0)) fg0_position = building_block.get_centroid( atom_ids=fg0.get_placer_ids(), ) placer_centroid = building_block.get_centroid( atom_ids=building_block.get_placer_ids(), ) fg0_direction = fg0_position - placer_centroid centroid = building_block.get_centroid( atom_ids=building_block.get_placer_ids(), ) normal = stk.get_acute_vector( reference=building_block.get_centroid() - placer_centroid, vector=building_block.get_plane_normal( atom_ids=building_block.get_placer_ids(), ), ) axis = -np.cross(normal, fg0_direction) def inner(functional_group): position = building_block.get_centroid( atom_ids=functional_group.get_placer_ids(), ) fg_direction = position - centroid theta = stk.vector_angle(fg0_direction, fg_direction) projection = fg_direction @ axis if theta > 0 and projection < 0: return 2*np.pi - theta return theta return inner
def get_plane_normal(building_block): macrocycle = max( rdkit.GetSymmSSSR(building_block.to_rdkit_mol()), key=len, ) centroid = building_block.get_centroid() atom1_position, = building_block.get_atomic_positions(0) normal = -stk.get_acute_vector( reference=atom1_position - centroid, vector=building_block.get_plane_normal(macrocycle), ) if np.allclose(normal, [1, 0, 0], atol=1e-13): return np.array([1, 0, 0], dtype=np.float64) elif np.allclose(normal, [-1, 0, 0], atol=1e-13): return np.array([-1, 0, 0], dtype=np.float64) return normal