code: def real_sph_harm(L, spherical_coordinates, zero_m_only=True):
      """
      Computes formula strings of the the real part of the spherical harmonics up to degree L (excluded).
      Variables are either spherical coordinates phi and theta (or cartesian coordinates x,y,z) on the UNIT SPHERE.
      Parameters
      ----------
          L: int
              Degree up to which to calculate the spherical harmonics (degree L is excluded).
          spherical_coordinates: bool
              - True: Expects the input of the formula strings to be phi and theta.
              - False: Expects the input of the formula strings to be x, y and z.
          zero_m_only: bool
              If True only calculate the harmonics where m=0.
      Returns
      -------
          Y_lm_real: list
              Computes formula strings of the the real part of the spherical harmonics up
              to degree L (where degree L is not excluded).
              In total L^2 many sph harm exist up to degree L (excluded). However, if zero_m_only only is True then
              the total count is reduced to be only L many.
      """
      z = sym.symbols("z")
      P_l_m = associated_legendre_polynomials(L, zero_m_only)
      if zero_m_only:
          # for all m != 0: Y_lm = 0
          Y_l_m = [[0] for l in range(L)]
      else:
          Y_l_m = [
              [0] * (2 * l + 1) for l in range(L)
          ]  # for order l: -l <= m <= l
  
      # convert expressions to spherical coordiantes
      if spherical_coordinates:
          # replace z by cos(theta)
          theta = sym.symbols("theta")
          for l in range(L):
              for m in range(len(P_l_m[l])):
                  if not isinstance(P_l_m[l][m], int):
                      P_l_m[l][m] = P_l_m[l][m].subs(z, sym.cos(theta))
  
      ## calculate Y_lm
      # Y_lm = N * P_lm(cos(theta)) * exp(i*m*phi)
      #             { sqrt(2) * (-1)^m * N * P_l|m| * sin(|m|*phi)   if m < 0
      # Y_lm_real = { Y_lm                                           if m = 0
      #             { sqrt(2) * (-1)^m * N * P_lm * cos(m*phi)       if m > 0
  
      for l in range(L):
          Y_l_m[l][0] = sym.simplify(
              sph_harm_prefactor(l, 0) * P_l_m[l][0]
          )  # Y_l0
  
      if not zero_m_only:
          phi = sym.symbols("phi")
          for l in range(1, L):
              # m > 0
              for m in range(1, l + 1):
                  Y_l_m[l][m] = sym.simplify(
                      2**0.5
                      * (-1) ** m
                      * sph_harm_prefactor(l, m)
                      * P_l_m[l][m]
                      * sym.cos(m * phi)
                  )
              # m < 0
              for m in range(1, l + 1):
                  Y_l_m[l][-m] = sym.simplify(
                      2**0.5
                      * (-1) ** m
                      * sph_harm_prefactor(l, -m)
                      * P_l_m[l][m]
                      * sym.sin(m * phi)
                  )
  
          # convert expressions to cartesian coordinates
          if not spherical_coordinates:
              # replace phi by atan2(y,x)
              x = sym.symbols("x")
              y = sym.symbols("y")
              for l in range(L):
                  for m in range(len(Y_l_m[l])):
                      Y_l_m[l][m] = sym.simplify(
                          Y_l_m[l][m].subs(phi, sym.atan2(y, x))
                      )
      return Y_l_m
  
file_path: DIG/dig/threedgraph/method/comenet/ocp/utils.py
imports: import torch
  import sympy
  import numpy
  from scipy.optimize import brentq
  from scipy import special
label_desc: Use image node_id 7 for calling a global function with example usage: real_sph_harm(L, spherical_coordinates, zero_m_only) and returns: Y_l_m