code: def get_bond_angle(atom_positions, bond_angle_index):
      """
      atom_positions: (N, 3)
      bond_angle_index: (3, A)
      bond_angle: (A)
      """
  
      def _get_angle(vec1, vec2):
          norm1 = np.linalg.norm(vec1)
          norm2 = np.linalg.norm(vec2)
          if norm1 == 0 or norm2 == 0:
              return 0
          vec1 = vec1 / (norm1 + 1e-5)  # 1e-5: prevent numerical errors
          vec2 = vec2 / (norm2 + 1e-5)
          angle = np.arccos(np.dot(vec1, vec2))
          return angle
  
      node_i, node_j, node_k = bond_angle_index
      node_i_pos = atom_positions[node_i]
      node_j_pos = atom_positions[node_j]
      node_k_pos = atom_positions[node_k]
  
      v1 = node_i_pos - node_j_pos
      v2 = node_k_pos - node_j_pos
      norm1 = np.linalg.norm(v1, axis=1, keepdims=True) + 1e-5
      norm2 = np.linalg.norm(v2, axis=1, keepdims=True) + 1e-5
      v1 /= norm1
      v2 /= norm2
      bond_angle = np.arccos(np.sum(v1 * v2, 1))
      return np.nan_to_num(bond_angle)
  
imports: import numpy
  import rdkit
  from rdkit import Chem
  from rdkit.Chem import AllChem
  import rdkit.Chem.rdPartialCharges
  from ogb.utils.features import allowable_features, atom_to_feature_vector, atom_feature_vector_to_dict, bond_feature_vector_to_dict
  from local_feature import bond_to_feature_vector, get_bond_feature_dims
  from copy import deepcopy
  import hashlib
label_desc: Use image node_id 23 for calling a global function with example usage: get_bond_angle(atom_positions, bond_angle_index) and returns: np, angle, int