code: def load_kernels(radius, num_kpoints, num_kernels, dimension, fixed):
      # Number of tries in the optimization process, to ensure we get the most stable disposition
      num_tries = 100
  
      # Kernel directory
      kernel_dir = join(DIR, "kernels/dispositions")
      if not exists(kernel_dir):
          makedirs(kernel_dir)
  
      # Kernel_file
      if dimension == 3:
          kernel_file = join(
              kernel_dir, "k_{:03d}_{:s}.ply".format(num_kpoints, fixed)
          )
      elif dimension == 2:
          kernel_file = join(
              kernel_dir,
              "k_{:03d}_{:s}_2D.ply".format(num_kpoints, fixed),
          )
      else:
          raise ValueError(
              "Unsupported dimpension of kernel : " + str(dimension)
          )
  
      # Check if already done
      if not exists(kernel_file):
          # Create kernels
          kernel_points, grad_norms = kernel_point_optimization_debug(
              1.0,
              num_kpoints,
              num_kernels=num_tries,
              dimension=dimension,
              fixed=fixed,
              verbose=0,
          )
  
          # Find best candidate
          best_k = np.argmin(grad_norms[-1, :])
  
          # Save points
          original_kernel = kernel_points[best_k, :, :]
          write_ply(kernel_file, original_kernel, ["x", "y", "z"])
  
      else:
          data = read_ply(kernel_file)
          original_kernel = np.vstack(
              (data["x"], data["y"], data["z"])
          ).T
  
      # N.B. 2D kernels are not supported yet
      if dimension == 2:
          return original_kernel
  
      # Random rotations depending of the fixed points
      if fixed == "verticals":
          # Create random rotations
          thetas = np.random.rand(num_kernels) * 2 * np.pi
          c, s = np.cos(thetas), np.sin(thetas)
          R = np.zeros((num_kernels, 3, 3), dtype=np.float32)
          R[:, 0, 0] = c
          R[:, 1, 1] = c
          R[:, 2, 2] = 1
          R[:, 0, 1] = s
          R[:, 1, 0] = -s
  
          # Scale kernels
          original_kernel = radius * np.expand_dims(original_kernel, 0)
  
          # Rotate kernels
          kernels = np.matmul(original_kernel, R)
  
      else:
          # Create random rotations
          u = np.ones((num_kernels, 3))
          v = np.ones((num_kernels, 3))
          wrongs = np.abs(np.sum(u * v, axis=1)) > 0.99
          while np.any(wrongs):
              new_u = np.random.rand(num_kernels, 3) * 2 - 1
              new_u = new_u / np.expand_dims(
                  np.linalg.norm(new_u, axis=1) + 1e-9, -1
              )
              u[wrongs, :] = new_u[wrongs, :]
              new_v = np.random.rand(num_kernels, 3) * 2 - 1
              new_v = new_v / np.expand_dims(
                  np.linalg.norm(new_v, axis=1) + 1e-9, -1
              )
              v[wrongs, :] = new_v[wrongs, :]
              wrongs = np.abs(np.sum(u * v, axis=1)) > 0.99
  
          # Make v perpendicular to u
          v -= np.expand_dims(np.sum(u * v, axis=1), -1) * u
          v = v / np.expand_dims(np.linalg.norm(v, axis=1) + 1e-9, -1)
  
          # Last rotation vector
          w = np.cross(u, v)
          R = np.stack((u, v, w), axis=-1)
  
          # Scale kernels
          original_kernel = radius * np.expand_dims(original_kernel, 0)
  
          # Rotate kernels
          kernels = np.matmul(original_kernel, R)
  
          # Add a small noise
          kernels = kernels
          kernels = kernels + np.random.normal(
              scale=radius * 0.01, size=kernels.shape
          )
  
      return kernels
  
file_path: torch-points3d/torch_points3d/modules/KPConv/kernel_utils.py
imports: import numpy
  import matplotlib.pyplot
  from os import makedirs
  from os.path import join, exists
  import os
  import logging
  from .plyutils import read_ply, write_ply
label_desc: Use image node_id 2 for calling a global function with example usage: load_kernels(radius, num_kpoints, num_kernels, dimension, fixed) and returns: kernels, original_kernel