def _obliquity3D( nt, nr, dt, dr, rho, vel, nffts, critical=100.0, ntaper=10, composition=True, backend="numpy", dtype="complex128", ): r"""3D Obliquity operator and FFT operator Parameters ---------- nt : :obj:`int` Number of samples along the time axis nr : :obj:`tuple` Number of samples along the receiver axes dt : :obj:`float` Sampling along the time axis dr : :obj:`tuple` Samplings along the receiver array rho : :obj:`float` Density along the receiver array (must be constant) vel : :obj:`float` Velocity along the receiver array (must be constant) nffts : :obj:`tuple`, optional Number of samples along the wavenumber and frequency axes critical : :obj:`float`, optional Percentage of angles to retain in obliquity factor. For example, if ``critical=100`` only angles below the critical angle :math:`\sqrt{k_y^2 + k_x^2} < \frac{\omega}{vel}` will be retained ntaper : :obj:`float`, optional Number of samples of taper applied to obliquity factor around critical angle composition : :obj:`bool`, optional Create obliquity factor for composition (``True``) or decomposition (``False``) backend : :obj:`str`, optional Backend used for creation of obliquity factor operator (``numpy`` or ``cupy``) dtype : :obj:`str`, optional Type of elements in input array. Returns ------- FFTop : :obj:`pylops.LinearOperator` FFT operator OBLop : :obj:`pylops.LinearOperator` Obliquity factor operator """ # create Fourier operator FFTop = FFTND(dims=[nr[0], nr[1], nt], nffts=nffts, sampling=[dr[0], dr[1], dt], dtype=dtype) # create obliquity operator [Ky, Kx, F] = np.meshgrid(FFTop.fs[0], FFTop.fs[1], FFTop.fs[2], indexing="ij") k = F / vel Kz = np.sqrt((k**2 - Ky**2 - Kx**2).astype(dtype)) Kz[np.isnan(Kz)] = 0 if composition: OBL = Kz / (rho * np.abs(F)) OBL[F == 0] = 0 else: OBL = rho * (np.abs(F) / Kz) OBL[Kz == 0] = 0 # cut off and taper OBL = _filter_obliquity(OBL, F, Kx, vel, critical, ntaper, Ky=Ky) OBL = get_module(backend).asarray(OBL) OBLop = Diagonal(OBL.ravel(), dtype=dtype) return FFTop, OBLop