Пример #1
0
def dipole(src,
           rec,
           depth,
           res,
           freqtime,
           aniso=None,
           eperm=None,
           mperm=None,
           verb=2):
    r"""Return the electromagnetic field due to a dipole source.

    This is a modified version of ``empymod.model.dipole()``. It returns the
    separated contributions of TM--, TM-+, TM+-, TM++, TMdirect, TE--, TE-+,
    TE+-, TE++, and TEdirect.

    Parameters
    ----------
    src, rec : list of floats or arrays
        Source and receiver coordinates (m): [x, y, z].
        The x- and y-coordinates can be arrays, z is a single value.
        The x- and y-coordinates must have the same dimension.

        Sources or receivers placed on a layer interface are considered in the
        upper layer.

        Sources and receivers must be in the same layer.

    depth : list
        Absolute layer interfaces z (m); #depth = #res - 1
        (excluding +/- infinity).

    res : array_like
        Horizontal resistivities rho_h (Ohm.m); #res = #depth + 1.

    freqtime : float
        Frequency f (Hz). (The name ``freqtime`` is kept for consistency with
        ``empymod.model.dipole()``. Only one frequency at once.

    aniso : array_like, optional
        Anisotropies lambda = sqrt(rho_v/rho_h) (-); #aniso = #res.
        Defaults to ones.

    eperm : array_like, optional
        Relative electric permittivities epsilon (-);
        #eperm = #res. Default is ones.

    mperm : array_like, optional
        Relative magnetic permeabilities mu (-);
        #mperm = #res. Default is ones.

    verb : {0, 1, 2, 3, 4}, optional
        Level of verbosity, default is 2:
            - 0: Print nothing.
            - 1: Print warnings.
            - 2: Print additional runtime and kernel calls
            - 3: Print additional start/stop, condensed parameter information.
            - 4: Print additional full parameter information


    Returns
    -------
    TM, TE : list of ndarrays, (nfreq, nrec, nsrc)
        Frequency-domain EM field [V/m], separated into
        TM = [TM--, TM-+, TM+-, TM++, TMdirect]
        and
        TE = [TE--, TE-+, TE+-, TE++, TEdirect].

        However, source and receiver are normalised. So the source strength is
        1 A and its length is 1 m. Therefore the electric field could also be
        written as [V/(A.m2)].

        The shape of EM is (nfreq, nrec, nsrc). However, single dimensions
        are removed.

    """

    # === 1. LET'S START ============
    t0 = printstartfinish(verb)

    # === 2. CHECK INPUT ============
    # Check layer parameters
    model = check_model(depth, res, aniso, eperm, eperm, mperm, mperm, False,
                        verb)
    depth, res, aniso, epermH, epermV, mpermH, mpermV, _ = model

    # Check frequency => get etaH, etaV, zetaH, and zetaV
    frequency = check_frequency(freqtime, res, aniso, epermH, epermV, mpermH,
                                mpermV, verb)
    freq, etaH, etaV, zetaH, zetaV = frequency

    # Check src and rec
    src, nsrc = check_dipole(src, 'src', verb)
    rec, nrec = check_dipole(rec, 'rec', verb)

    # Get offsets
    off, ang = get_off_ang(src, rec, nsrc, nrec, verb)

    # Get layer number in which src and rec reside (lsrc/lrec)
    lsrc, zsrc = get_layer_nr(src, depth)
    lrec, zrec = get_layer_nr(rec, depth)

    # Check limitations of this routine compared to the standard ``dipole``
    if lsrc != lrec:  # src and rec in same layer
        print("* ERROR   :: src and rec must be in the same layer; " +
              "<lsrc>/<lrec> provided: " + str(lsrc) + "/" + str(lrec))
        raise ValueError('src-z/rec-z')

    if depth.size < 2:  # at least two layers
        print("* ERROR   :: model must have more than one layer; " +
              "<depth> provided: " + _strvar(depth[1:]))
        raise ValueError('depth')

    if freq.size > 1:  # only 1 frequency
        print("* ERROR   :: only one frequency permitted; " +
              "<freqtime> provided: " + _strvar(freqtime))
        raise ValueError('frequency')

    # === 3. EM-FIELD CALCULATION ============
    # This part is a simplification of:
    # - model.fem()
    # - transform.dlf()
    # - kernel.wavenumber()

    # DLF filter we use
    filt = key_201_2012()

    # 3.1. COMPUTE REQUIRED LAMBDAS for given hankel-filter-base
    lambd = filt.base / off[:, None]

    # 3.2. CALL THE KERNEL
    PTM, PTE = greenfct(zsrc, zrec, lsrc, lrec, depth, etaH, etaV, zetaH,
                        zetaV, lambd)

    # 3.3. CARRY OUT THE HANKEL TRANSFORM WITH DLF
    factAng = angle_factor(ang, 11, False, False)
    zmfactAng = (factAng[:, np.newaxis] - 1) / 2
    zpfactAng = (factAng[:, np.newaxis] + 1) / 2
    fact = 4 * np.pi * off

    # TE [uu, ud, du, dd, df]
    for i, val in enumerate(PTE):
        PTE[i] = (factAng * np.dot(-val, filt.j1) / off +
                  np.dot(zmfactAng * val * lambd, filt.j0)) / fact

    # TM [uu, ud, du, dd, df]
    for i, val in enumerate(PTM):
        PTM[i] = (factAng * np.dot(-val, filt.j1) / off +
                  np.dot(zpfactAng * val * lambd, filt.j0)) / fact

    # 3.4. Remove non-physical contributions

    # (Note: The T*dd corrections differ slightly from the equations given in
    # the accompanying pdf, due to the way the direct field is accounted for
    # in the book.)

    # General parameters
    Gam = np.sqrt((zetaH * etaH)[:, None, :, None])  # Gam for lambd=0
    iGam = Gam[:, :, lsrc, 0]
    lgam = np.sqrt(zetaH[:, lsrc] * etaH[:, lsrc])
    ddepth = np.r_[depth, np.inf]
    ds = ddepth[lsrc + 1] - ddepth[lsrc]

    def get_rp_rm(z_eta):
        r"""Return Rp, Rm."""

        # Get Rp/Rm for lambd=0
        Rp, Rm = reflections(depth, z_eta, Gam, lrec, lsrc, False)

        # Depending on model Rp/Rm have 3 or 4 dimensions. Last two are
        # wavenumbers and layers btw src and rec, which both are 1.
        if Rp.ndim == 4:
            Rp = np.squeeze(Rp, axis=3)
        if Rm.ndim == 4:
            Rm = np.squeeze(Rm, axis=3)
        Rp = np.squeeze(Rp, axis=2)
        Rm = np.squeeze(Rm, axis=2)

        # Calculate reverberation M and general factor npfct
        Ms = 1 - Rp * Rm * np.exp(-2 * iGam * ds)
        npfct = factAng * zetaH[:, lsrc] / (fact * off * lgam * Ms)

        return Rp, Rm, npfct

    # TE modes TE[uu, ud, du, dd]
    Rp, Rm, npfct = get_rp_rm(zetaH)

    PTE[0] += npfct * Rp * Rm * np.exp(-lgam * (2 * ds - zrec + zsrc))
    PTE[1] += npfct * Rp * np.exp(-lgam * (2 * ddepth[lrec + 1] - zrec - zsrc))
    PTE[2] += npfct * Rm * np.exp(-lgam * (zrec + zsrc))
    PTE[3] += npfct * Rp * Rm * np.exp(-lgam * (2 * ds + zrec - zsrc))

    # TM modes TM[uu, ud, du, dd]
    Rp, Rm, npfct = get_rp_rm(etaH)

    PTM[0] -= npfct * Rp * Rm * np.exp(-lgam * (2 * ds - zrec + zsrc))
    PTM[1] += npfct * Rp * np.exp(-lgam * (2 * ddepth[lrec + 1] - zrec - zsrc))
    PTM[2] += npfct * Rm * np.exp(-lgam * (zrec + zsrc))
    PTM[3] -= npfct * Rp * Rm * np.exp(-lgam * (2 * ds + zrec - zsrc))

    # 3.5 Reshape for number of sources
    for i, val in enumerate(PTE):
        PTE[i] = np.squeeze(val.reshape((-1, nrec, nsrc), order='F'))

    for i, val in enumerate(PTM):
        PTM[i] = np.squeeze(val.reshape((-1, nrec, nsrc), order='F'))

    # === 4. FINISHED ============
    printstartfinish(verb, t0)

    # return [TMuu, TMud, TMdu, TMdd, TMdf], [TEuu, TEud, TEdu, TEdd, TEdf]
    return PTM, PTE
Пример #2
0
def test_strvar():
    out = utils._strvar(np.arange(3) * np.pi)
    assert out == "0 3.14159 6.28319"

    out = utils._strvar(np.pi, '{:20.10e}')
    assert out == "    3.1415926536e+00"