Example #1
0
def test_norm_int():
    # simps(y, x) == 2.0
    x = np.linspace(0, np.pi, 100)
    y = np.sin(x)

    for scale in [True, False]:
        yy = norm_int(y, x, area=10.0, scale=scale)
        assert np.allclose(simps(yy, x), 10.0)
Example #2
0
def test_norm_int():
    # simps(y, x) == 2.0
    x = np.linspace(0, np.pi, 100)
    y = np.sin(x)
    
    for scale in [True, False]:
        yy = norm_int(y, x, area=10.0, scale=scale)
        assert np.allclose(simps(yy,x), 10.0)
Example #3
0
def cut_norm(full_y, dt, area=1.0):
    """Cut out an FFT spectrum from scipy.fftpack.fft() (or numpy.fft.fft())
    result and normalize the integral `int(f) y(f) df = area`.
    
    full_y : 1d array
        Result of fft(...)
    dt : time step
    area : integral area
    """
    full_faxis = np.fft.fftfreq(full_y.shape[0], dt)
    split_idx = full_faxis.shape[0] / 2
    y_out = full_y[:split_idx]
    faxis = full_faxis[:split_idx]
    return faxis, num.norm_int(y_out, faxis, area=area)
Example #4
0
def cut_norm(full_y, dt, area=1.0):
    """Cut out an FFT spectrum from scipy.fftpack.fft() (or numpy.fft.fft())
    result and normalize the integral `int(f) y(f) df = area`.
    
    full_y : 1d array
        Result of fft(...)
    dt : time step
    area : integral area
    """
    full_faxis = np.fft.fftfreq(full_y.shape[0], dt)
    split_idx = full_faxis.shape[0]/2
    y_out = full_y[:split_idx]
    faxis = full_faxis[:split_idx]
    return faxis, num.norm_int(y_out, faxis, area=area)
Example #5
0
def pdos(vel,
         dt=1.0,
         m=None,
         full_out=False,
         area=1.0,
         window=True,
         npad=None,
         tonext=False,
         mirr=False,
         method='direct'):
    """Phonon DOS by FFT of the VACF or direct FFT of atomic velocities.
    
    Integral area is normalized to `area`. It is possible (and recommended) to
    zero-padd the velocities (see `npad`). 
    
    Parameters
    ----------
    vel : 3d array (nstep, natoms, 3)
        atomic velocities
    dt : time step
    m : 1d array (natoms,), 
        atomic mass array, if None then mass=1.0 for all atoms is used  
    full_out : bool
    area : float
        normalize area under frequency-PDOS curve to this value
    window : bool 
        use Welch windowing on data before FFT (reduces leaking effect,
        recommended)
    npad : {None, int}
        method='direct' only: Length of zero padding along `axis`. `npad=None`
        = no padding, `npad > 0` = pad by a length of ``(nstep-1)*npad``. `npad
        > 5` usually results in sufficient interpolation.
    tonext : bool
        method='direct' only: Pad `vel` with zeros along `axis` up to the next
        power of two after the array length determined by `npad`. This gives
        you speed, but variable (better) frequency resolution.
    mirr : bool 
        method='vacf' only: mirror one-sided VACF at t=0 before fft

    Returns
    -------
    if full_out = False
        | ``(faxis, pdos)``
        | faxis : 1d array [1/unit(dt)]
        | pdos : 1d array, the phonon DOS, normalized to `area`
    if full_out = True
        | if method == 'direct':
        |     ``(faxis, pdos, (full_faxis, full_pdos, split_idx))``
        | if method == 'vavcf':
        |     ``(faxis, pdos, (full_faxis, full_pdos, split_idx, vacf, fft_vacf))``
        |     fft_vacf : 1d complex array, result of fft(vacf) or fft(mirror(vacf))
        |     vacf : 1d array, the VACF
    
    Examples
    --------
    >>> from pwtools.constants import fs,rcm_to_Hz
    >>> tr = Trajectory(...)
    >>> # freq in [Hz] if timestep in [s]
    >>> freq,dos = pdos(tr.velocity, m=tr.mass, dt=tr.timestep*fs, 
    >>>                 method='direct', npad=1)
    >>> # frequency in [1/cm]
    >>> plot(freq/rcm_to_Hz, dos)
    
    Notes
    -----
    padding (only method='direct'): With `npad` we pad the velocities `vel`
    with ``npad*(nstep-1)`` zeros along `axis` (the time axis) before FFT
    b/c the signal is not periodic. For `npad=1`, this gives us the exact
    same spectrum and frequency resolution as with ``pdos(...,
    method='vacf',mirr=True)`` b/c the array to be fft'ed has length
    ``2*nstep-1`` along the time axis in both cases (remember that the
    array length = length of the time axis influences the freq.
    resolution). FFT is only fast for arrays with length = a power of two.
    Therefore, you may get very different fft speeds depending on whether
    ``2*nstep-1`` is a power of two or not (in most cases it won't). Try
    using `tonext` but remember that you get another (better) frequency
    resolution.

    References
    ----------
    [1] Phys Rev B 47(9) 4863, 1993

    See Also
    --------
    :func:`pwtools.signal.fftsample`
    :func:`pwtools.signal.acorr`
    :func:`direct_pdos`
    :func:`vacf_pdos`

    """
    mass = m
    # assume vel.shape = (nstep,natoms,3)
    axis = 0
    assert vel.shape[-1] == 3
    if mass is not None:
        assert len(mass) == vel.shape[1], "len(mass) != vel.shape[1]"
        # define here b/c may be used twice below
        mass_bc = mass[None, :, None]
    if window:
        sl = [None] * vel.ndim
        sl[axis] = slice(None)  # ':'
        vel2 = vel * (welch(vel.shape[axis])[sl])
    else:
        vel2 = vel
    # handle options which are mutually exclusive
    if method == 'vacf':
        assert npad in [0, None], "use npad={0,None} for method='vacf'"
    # padding
    if npad is not None:
        nadd = (vel2.shape[axis] - 1) * npad
        if tonext:
            vel2 = pad_zeros(vel2,
                             tonext=True,
                             tonext_min=vel2.shape[axis] + nadd,
                             axis=axis)
        else:
            vel2 = pad_zeros(vel2, tonext=False, nadd=nadd, axis=axis)
    if method == 'direct':
        full_fft_vel = np.abs(fft(vel2, axis=axis))**2.0
        full_faxis = np.fft.fftfreq(vel2.shape[axis], dt)
        split_idx = len(full_faxis) // 2
        faxis = full_faxis[:split_idx]
        # First split the array, then multiply by `mass` and average. If
        # full_out, then we need full_fft_vel below, so copy before slicing.
        arr = full_fft_vel.copy() if full_out else full_fft_vel
        fft_vel = num.slicetake(arr,
                                slice(0, split_idx),
                                axis=axis,
                                copy=False)
        if mass is not None:
            fft_vel *= mass_bc
        # average remaining axes, summing is enough b/c normalization is done below
        # sums: (nstep, natoms, 3) -> (nstep, natoms) -> (nstep,)
        pdos = num.sum(fft_vel, axis=axis, keepdims=True)
        default_out = (faxis, num.norm_int(pdos, faxis, area=area))
        if full_out:
            # have to re-calculate this here b/c we never calculate the full_pdos
            # normally
            if mass is not None:
                full_fft_vel *= mass_bc
            full_pdos = num.sum(full_fft_vel, axis=axis, keepdims=True)
            extra_out = (full_faxis, full_pdos, split_idx)
            return default_out + extra_out
        else:
            return default_out
    elif method == 'vacf':
        vacf = fvacf(vel2, m=mass)
        if mirr:
            fft_vacf = fft(mirror(vacf))
        else:
            fft_vacf = fft(vacf)
        full_faxis = np.fft.fftfreq(fft_vacf.shape[axis], dt)
        full_pdos = np.abs(fft_vacf)
        split_idx = len(full_faxis) // 2
        faxis = full_faxis[:split_idx]
        pdos = full_pdos[:split_idx]
        default_out = (faxis, num.norm_int(pdos, faxis, area=area))
        extra_out = (full_faxis, full_pdos, split_idx, vacf, fft_vacf)
        if full_out:
            return default_out + extra_out
        else:
            return default_out
Example #6
0
    fourier_out_data_fn = pj(fourier_dir, 'fourier_out_data_1d.txt')
    fourier_in_fn = pj(fourier_dir, 'fourier_1d.in')
    fourier_out_fn = pj(fourier_dir, 'fourier_1d.log')
    fourier_in_txt = '%s\n%s\n%e\n%e\n%e\n%i' % (
        fourier_in_data_fn, fourier_out_data_fn, dt / constants.th, 0,
        fmax * fmax_extend_fac / (constants.c0 * 100), 1)
    common.file_write(fourier_in_fn, fourier_in_txt)
    # In order to make picky gfortrans happy, we need to use savetxt(...,
    # fmt="%g") such that the first column is an integer (1 instead of
    # 1.0000e+00).
    np.savetxt(fourier_in_data_fn, fourier_in_data, fmt='%g')
    common.system("%s < %s > %s" %
                  (fourier_exe, fourier_in_fn, fourier_out_fn))
    fourier_out_data = np.loadtxt(fourier_out_data_fn)
    f3 = fourier_out_data[:, 0] * (constants.c0 * 100)  # 1/cm -> Hz
    y3n = num.norm_int(fourier_out_data[:, 1], f3)

f1, y1n = cut_norm(y1, dt)
f2, y2n = cut_norm(y2, dt)

figs = []
axs = []

figs.append(plt.figure())
axs.append(figs[-1].add_subplot(111))
axs[-1].set_title('1d arr')
axs[-1].plot(f1, y1n, label='1d |fft(arr)|^2, direct')
axs[-1].plot(f2, y2n, label='1d |fft(acorr(arr))|, vacf')
if use_fourier:
    axs[-1].plot(f3, y3n, label='1d fourier.x')
axs[-1].legend()
Example #7
0
    fourier_out_fn = pj(fourier_dir, 'fourier_1d.log')
    fourier_in_txt = '%s\n%s\n%e\n%e\n%e\n%i' %(fourier_in_data_fn,
                                                fourier_out_data_fn,
                                                dt/constants.th,
                                                0,
                                                fmax*fmax_extend_fac/(constants.c0*100),
                                                1)
    common.file_write(fourier_in_fn, fourier_in_txt)
    # In order to make picky gfortrans happy, we need to use savetxt(...,
    # fmt="%g") such that the first column is an integer (1 instead of
    # 1.0000e+00). 
    np.savetxt(fourier_in_data_fn, fourier_in_data, fmt='%g')
    common.system("%s < %s > %s" %(fourier_exe, fourier_in_fn, fourier_out_fn))
    fourier_out_data = np.loadtxt(fourier_out_data_fn)
    f3 = fourier_out_data[:,0]*(constants.c0*100) # 1/cm -> Hz
    y3n = num.norm_int(fourier_out_data[:,1], f3)

f1, y1n = cut_norm(y1, dt)
f2, y2n = cut_norm(y2, dt)

figs = []
axs = []

figs.append(plt.figure())
axs.append(figs[-1].add_subplot(111))
axs[-1].set_title('1d arr')
axs[-1].plot(f1, y1n, label='1d |fft(arr)|^2, direct')
axs[-1].plot(f2, y2n, label='1d |fft(acorr(arr))|, vacf')
if use_fourier:
    axs[-1].plot(f3, y3n, label='1d fourier.x')
axs[-1].legend()