code: def perlin3(size, units=(1.,)*3, repeat=(10.,)*3, shift=0, scale=None, n_volumes=1):
      
      if np.isscalar(shift):
          shift = (shift,)*len(size)
  
      if scale:
          if np.isscalar(scale):
              scale = (scale,)*3
          repeat = scale
          units = (1.,)*3
  
      if n_volumes==1:
          return _perlin3_single(size, units, repeat, shift=shift)
      else:
          Nx, Ny, Nz = size
          Nz2 = Nz//n_volumes+1
          res = np.empty((Nz, Ny, Nx), np.float32)
          res_part = np.empty((Nz2, Ny, Nx), np.float32)
          for i in range(n_volumes):
              i1, i2 = i*Nz2, np.clip((i+1)*Nz2, 0, Nz)
              if i<n_volumes-1:
                  res_part = _perlin3_single((Nx, Ny, i2-i1+1),
                                             units,
                                             shift=shift,
                                             repeat=repeat,
                                             offz=Nz2*i,
                                             Nz0=Nz)
  
                  res[i1:i2, ...] = res_part[:-1, ...]
              else:
                  res_part = _perlin3_single((Nx, Ny, i2-i1),
                                             units,
                                             shift=shift,
                                             repeat=repeat,
                                             offz=Nz2*i,
                                             Nz0=Nz)
  
                  res[i1:i2, ...] = res_part
          return res
docs: returns a 3d perlin noise array of given size (Nx,Ny,Nz)
      and units (dx,dy,dz) with given repeats (in units)
      by doing the noise calculations on the gpu
  
      The volume can be splitted into n_volumes pieces if gou memory is not enough
  
      either scale or units have to be given
  
      shift = (.1,.1,.2)  or 1. can be used to slide the pattern in each dim (offset = 0 is the original)