code: def xyztodat(pos, edge_index, num_nodes, batch):
      j, i = edge_index  # j->i
  
      # Calculate distances. # number of edges
      dist = (pos[i] - pos[j]).pow(2).sum(dim=-1).sqrt()
  
      value = torch.arange(j.size(0), device=j.device)
      adj_t = SparseTensor(
          row=i, col=j, value=value, sparse_sizes=(num_nodes, num_nodes)
      )
      adj_t_row = adj_t[j]
      num_triplets = adj_t_row.set_value(None).sum(dim=1).to(torch.long)
  
      # Node indices (k->j->i) for triplets.
      idx_i = i.repeat_interleave(num_triplets)
      idx_j = j.repeat_interleave(num_triplets)
      idx_k = adj_t_row.storage.col()
      mask = idx_i != idx_k
      idx_i, idx_j, idx_k = idx_i[mask], idx_j[mask], idx_k[mask]
  
      # Edge indices (k-j, j->i) for triplets.
      idx_kj = adj_t_row.storage.value()[mask]
      idx_ji = adj_t_row.storage.row()[mask]
  
      # Calculate angles. 0 to pi
      pos_ji = pos[idx_i] - pos[idx_j]
      pos_jk = pos[idx_k] - pos[idx_j]
      a = (pos_ji * pos_jk).sum(
          dim=-1
      )  # cos_angle * |pos_ji| * |pos_jk|
      b = torch.cross(pos_ji, pos_jk).norm(
          dim=-1
      )  # |sin_angle| * |pos_ji| * |pos_jk|
      angle = torch.atan2(b, a)
  
      adj_nearest, adj_nearest2 = get_nearst_node(pos, batch)
  
      adj_nearest_row = adj_nearest[idx_j]
      adj_nearest2_row = adj_nearest2[idx_j]
      idx_k_n = adj_nearest_row.storage.col()
      idx_k_n2 = adj_nearest2_row.storage.col()
      mask = idx_k_n == idx_i
      idx_k_n[mask] = idx_k_n2[mask]
  
      # Calculate torsions.
      pos_j0 = pos[idx_k] - pos[idx_j]
      pos_ji = pos[idx_i] - pos[idx_j]
      pos_jk = pos[idx_k_n] - pos[idx_j]
      dist_ji = pos_ji.pow(2).sum(dim=-1).sqrt()
      plane1 = torch.cross(pos_ji, pos_j0)
      plane2 = torch.cross(pos_ji, pos_jk)
      a = (plane1 * plane2).sum(
          dim=-1
      )  # cos_angle * |plane1| * |plane2|
      b = (torch.cross(plane1, plane2) * pos_ji).sum(dim=-1) / dist_ji
      torsion = torch.atan2(b, a)  # -pi to pi
      torsion[torsion <= 0] += 2 * PI  # 0 to 2pi
  
      return dist, angle, torsion, i, j, idx_kj, idx_ji
  
file_path: DIG/dig/ggraph3D/method/G_SphereNet/model/geometric_computing.py
imports: import torch
  from torch_scatter import scatter
  from torch_sparse import SparseTensor
  from math import sqrt, pi
  from torch_geometric.nn import knn_graph
label_desc: Use image node_id 3 for calling a global function with example usage: xyztodat(pos, edge_index, num_nodes, batch) and returns: dist,  angle,  torsion,  i,  j,  idx_kj,  idx_ji