示例#1
0
文件: t_vuf.py 项目: RCand/ttide_py
def t_vuf(ltype, ctime, ju, lat=None):
    """T_VUF Computes nodal modulation corrections.
     [V,U,F]=T_VUF(TYPE,DATE,JU,LAT) returns the astronomical phase V, the
     nodal phase modulation U, and the nodal amplitude correction F at
     a decimal date DATE for the components specified by index JU
     at a latitude LAT.

     TYPE is either 'full' for the 18.6 year set of constitunets, or 'nodal'
     for the 1-year set with satellite modulations.

     If LAT is not specified, then the Greenwich phase V is computed with
     U=0 and F=1.

     Note that V and U are in 'cycles', not degrees or radians (i.e.,
     multiply by 360 to get degrees).

     If LAT is set to NaN, then the nodal corrections are computed for all
     satellites that do *not* have a "latitude-dependent" correction
     factor. This is for compatibility with the ways things are done in
     the xtide package. (The latitude-dependent corrections were zeroed
     out there partly because it was convenient, but this was rationalized
     by saying that since the forcing of tides can occur at latitudes
     other than where they are observed, the idea that observations have
     the equilibrium latitude-dependence is possibly bogus anyway).
     R. Pawlowicz 11/8/99
                   1/5/00 - Changed to allow for no LAT setting.
                  11/8/00 - Added the LAT=NaN option.
                  10/02/03 - Suuport for 18-year (full) constituent set.
     Version 1.2
     Get all the info about constituents.
     Calculate astronomical arguments at mid-point of data time series.
    """

    astro, ader = t_astron(ctime)

    if ltype == 'full':
        const = t_get18consts(ctime)
        # Phase relative to Greenwich (in units of cycles).
        v = rem(np.dot(const.doodson, astro) + const.semi, 1)
        v = v[(ju-1)]
        u = np.zeros(shape=(v.shape, v.shape), dtype='float64')
        f = np.ones(shape=(v.shape, v.shape), dtype='float64')
    else:
        const, sat, shallow = t_getconsts(ctime)
        # Phase relative to Greenwich (in units of cycles).
        # (This only returns values when we have doodson#s,
        # i.e., not for the shallow water components,
        # but these will be computed later.)
        v = np.fmod(np.dot(const['doodson'], astro) + const['semi'], 1)

        if lat is not None:
            # If we have a latitude, get nodal corrections.
            # Apparently the second-order terms in the tidal potential
            # go to zero at the equator, but the third-order terms
            # do not. Hence when trying to infer the third-order terms
            # from the second-order terms, the nodal correction factors
            # blow up. In order to prevent this, it is assumed that the
            # equatorial forcing is due to second-order forcing OFF the
            # equator, from about the 5 degree location. Latitudes are
            # hence (somewhat arbitrarily) forced to be no closer than
            # 5 deg to the equator, as per note in Foreman.
            if ((np.isfinite(lat)) & (abs(lat) < 5)):
                lat = sign(lat) * 5
            slat = np.sin(np.pi * lat / 180)
            # Satellite amplitude ratio adjustment for latitude.
            rr = sat['amprat']
            # no amplitude correction
            if np.isfinite(lat):
                j = np.flatnonzero(sat['ilatfac'] == 1)
                # latitude correction for diurnal constituents
                rr[j] = rr[j] * 0.36309 * (1.0 - 5.0 * slat * slat) / slat
                j = np.flatnonzero(sat['ilatfac'] == 2)
                # latitude correction for semi-diurnal constituents
                rr[j] = rr[j] * 2.59808 * slat
            else:
                rr[sat['ilatfac'] > 0] = 0
            # Calculate nodal amplitude and phase corrections.
            uu = np.fmod(np.dot(sat['deldood'], astro.T[3:6])+sat['phcorr'], 1)
            # uu=uudbl-round(uudbl);  <_ I think this was wrong.
            # The original
            #                         FORTRAN code is:  IUU=UUDBL
            #                                           UU=UUDBL-IUU
            #                         which is truncation.
            # Sum up all of the satellite factors for all satellites.
            nsat = np.max(sat['iconst'].shape)
            nfreq = np.max(const['isat'].shape)

            fsum = np.array(1+sp.sparse.csr_matrix(
                (np.squeeze(rr*np.exp(1j*2*np.pi*uu)),
                 (np.arange(0, nsat), np.squeeze(sat['iconst']-1))),
                shape=(nsat, nfreq)).sum(axis=0)).flatten()

            f = np.absolute(fsum)
            u = np.angle(fsum)/(2*np.pi)

            # Compute amplitude and phase corrections
            # for shallow water constituents.
            for k in np.flatnonzero(np.isfinite(const['ishallow'])):
                ik = ((const['ishallow'][k]-1 +
                       np.array(range(0, const['nshallow'][k]))).astype(int))
                iname = shallow['iname'][ik]-1
                coef = shallow['coef'][ik]
                f[k] = np.prod(np.power(f[iname], coef))
                u[k] = np.dot(u[iname], coef)
                v[k] = np.dot(v[iname], coef)

            f = f[ju]
            u = u[ju]
            v = v[ju]

        else:
            # Astronomical arguments only, no nodal corrections.
            # Compute phases for shallow water constituents.
            for k in np.flatnonzero(np.isfinite(const['ishallow'])):
                ik = ((const['ishallow'][k]-1 +
                       np.array(range(0, const['nshallow'][k]))).astype(int))
                v[k] = np.dot(v[shallow['iname'][ik]-1], shallow['coef'][ik])

            v = v[ju]
            f = np.ones(len(v))
            u = np.zeros(len(v))

    return v, u, f
示例#2
0
def constituents(minres, constit, shallow, infname, infref, centraltime):
    """[name,freq,kmpr]=constituents(minres,infname) loads tidal constituent
     table (containing 146 constituents), then picks out only the '
     resolvable' frequencies (i.e. those that are MINRES apart), base on 
     the comparisons in the third column of constituents.dat. Only 
     frequencies in the 'standard' set of 69 frequencies are actually used.
     Also return the indices of constituents to be inferred.
     If we have the mat-file, read it in, otherwise create it and read
     it in!
     R Pawlowicz 9/1/01 
     Version 1.0

        19/1/02 - typo fixed (thanks to  Zhigang Xu)
     Compute frequencies from astronomical considerations.
    """
    if minres > 1 / (np.dot(np.dot(18.6, 365.25), 24)):
        # Choose only resolveable pairs for short
        const, sat, cshallow = tgets.t_getconsts(centraltime) # nargout=3
        # Time series  
        ju = np.flatnonzero(const['df'] >= minres)
    else:
        # Choose them all if > 18.6 years.
        const, sat, cshallow = t_get18consts(centraltime) # nargout=3
        ju = np.array([range(2, (max(const['freq'].shape) +1))]).reshape(1, -1).T
        # Skip Z0
        for ff in range(1, 3):
            # loop twice to make sure of neightbouring pairs
            jck = np.flatnonzero(diff(const['freq'][ju]) < minres)
            if (max(jck.shape) > 0):
                jrm = jck
                jrm = jrm + (abs(const['doodsonamp'][ju[(jck + 1 -1)]]) < abs(const['doodsonamp'][ju[(jck -1)]]))
                disp('  Warning! Following constituent pairs violate Rayleigh criterion')
#               for ick in range(1, (max(jck.shape) +1)):
#                    disp('     ' + const.name(ju[(jck[(ick -1)] -1)], :) + ' vs ' + const.name(ju[(jck[(ick -1)] + 1 -1)], :) + ' - not using ' + const.name(ju[(jrm[(ick -1)] -1)], :))
                ju[(jrm -1)] = np.array([])
    if constit.size !=0:
        # Selected if constituents are specified in input.
        ju = np.array([])
        for k in range(1, (constit.shape[0] +1)):
            j1 = strmatch(constit[(k -1), :], const['name'])
            if (0 in j1.shape):
                disp("Can't recognize name " + constit[(k -1), :] + ' for forced search')
            else:
                if j1 == 1:
                    disp('*************************************************************************')
                    disp("Z0 specification ignored - for non-tidal offsets see 'secular' property")
                    disp('*************************************************************************')
                else:
                    ju = np.array([ju, j1]).reshape(1, -1)
        dum, II = sort(const['freq'][ju]) # nargout=2
        # sort in ascending order of frequency.
        ju = ju[(II -1)]
    #cout
    #disp(['   number of standard constituents used: ',int2str(length(ju))])
    if shallow.size !=0:
        # Add explictly selected shallow water constituents.
        for k in range(1, (shallow.shape[0] +1)):
            j1 = strmatch(shallow[(k -1), :], const['name'])
            if (0 in j1.shape):
                disp("Can't recognize name " + shallow[(k -1), :] + ' for forced search')
            else:
                if np.isnan(const['ishallow'][j1]):
                    disp(shallow[(k -1), :] + ' Not a shallow-water constituent')
                disp('   Forced fit to ' + shallow[(k -1), :])
                ju = np.array([ju, j1]).reshape(1, -1)
    nameu = const['name'][ju]
    fu = const['freq'][ju]
    # Check if neighboring chosen constituents violate Rayleigh criteria.
    jck = np.flatnonzero(np.diff(fu) < minres)
    #cout
    #if (length(jck)>0)
    #disp(['  Warning! Following constituent pairs violate Rayleigh criterion']);
    #for ick=1:length(jck);
    #disp(['     ',nameu(jck(ick),:),'  ',nameu(jck(ick)+1,:)]);
    #end;
    #end
    # For inference, add in list of components to be inferred.
    fi = np.array([])
    namei = np.array([])
    jinf = np.array([])
    jref = np.array([])
    if infname.size !=0:
        fi = np.zeros(shape=(infname.shape[0], 1), dtype='float64')
        namei = np.zeros(shape=(infname.shape[0], 4), dtype='float64')
        jinf = np.zeros(shape=(infname.shape[0], 1), dtype='float64') + NaN
        jref = np.zeros(shape=(infname.shape[0], 1), dtype='float64') + NaN
        for k in range(1, (infname.shape[0] +1)):
            j1 = strmatch(infname[(k -1), :], const.name)
            if (0 in j1.shape):
                disp("Can't recognize name" + infname[(k -1), :] + ' for inference')
            else:
                jinf[(k -1)] = j1
                fi[(k -1)] = const['freq'][j1]
                namei[(k -1), :] = const['name'][j1, :]
                j1 = strmatch(infref[(k -1), :], nameu)
                if (0 in j1.shape):
                    disp("Can't recognize name " + infref[(k -1), :] + ' for as a reference for inference')
                else:
                    jref[(k -1)] = j1
                    print('   Inference of ' + namei[(k -1), :] + ' using ' + nameu[(j1 -1), :] + '\\n')
        jinf[(isnan(jref) -1)] = NaN
    return nameu, fu, ju, namei, fi, jinf, jref
示例#3
0
def t_predic(tim, names, freq, tidecon, **kwargs):
    """T_PREDIC Tidal prediction
     YOUT=T_PREDIC(TIM,NAMES,FREQ,TIDECON) makes a tidal prediction
     using the output of T_TIDE at the specified times TIM in decimal
     days (from DATENUM). Optional arguments can be specified using
     property/value pairs:

           YOUT=T_PREDIC(...,TIDECON,property,value,...)

     Available properties are:

        In the simplest case, the tidal analysis was done without nodal
        corrections, and thus neither will the prediction. If nodal
        corrections were used in the analysis, then it is likely we will
        want to use them in the prediction too and these are computed
        using the latitude, if given.

         'latitude'        decimal degrees (+north) (default: none)

        If the original analysis was >18.6 years satellites are
        not included and we force that here:

         'anallength'      'nodal' (default)
                           'full'  For >18.6 years.

        The tidal prediction may be restricted to only some of the
        available constituents:

         'synthesis'    0 - Use all selected constituents.  (default)
                        scalar>0 - Use only those constituents with a SNR
                                   greater than that given (1 or 2 are
                                   good choices).


      It is possible to call t_predic without using property names, in
      which case the assumed calling sequence is

        YOUT=T_PREDIC(TIM,NAMES,FREQ,TIDECON,LATITUDE,SYNTHESIS);

      T_PREDIC can be called using the tidal structure available as an
      optional output from T_TIDE

        YOUT=T_PREDIC(TIM,TIDESTRUC,...)

      This is in fact the recommended calling procedure (and required
      when the analysis results are from series>18.6 years in length)
     R. Pawlowicz 11/8/99
     Version 1.0
     8/2/03 - Added block processing to generate prediction (to
              avoid memory overflows for long time series).
     29/9/04 - small bug with undefined ltype fixed
    """

    longseries = 0
    ltype = 'nodal'
    lat = np.array([])
    synth = 0
    k = 1
    tim = tim.reshape(-1, 1)

    # Use kwargs to set values other then the defaults
    if kwargs is not None:
        for key, value in kwargs.items():
            if (key == 'ltype'):
                ltype = value
            if (key == 'synth'):
                synth = value

    # Do the synthesis.
    snr = (tidecon[:, 0] / tidecon[:, 1]) ** 2
    # signal to noise ratio
    if synth > 0:
        I = snr > synth
        if not any(I):
            print('No predictions with this SNR')
            yout = np.nan + np.zeros(shape=(tim.shape, tim.shape),
                                     dtype='float64')
            return yout
        tidecon = tidecon[I, :]
        names = names[I]
        freq = freq[I]
    if tidecon.shape[1] == 4:
        # Real time series
        ap = np.multiply(tidecon[:, 0]/2.0,
                         np.exp(-1j*tidecon[:, 2]*np.pi/180))
        am = np.conj(ap)
    else:
        ap = np.multiply((tidecon[:, 0] + tidecon[:, 2]) / 2.0,
                         np.exp(np.dot(np.dot(1j, np.pi) / 180,
                                       (tidecon[:, 4] - tidecon[:, 6]))))

        am = np.multiply((tidecon[:, 0] - tidecon[:, 2]) / 2.0,
                         np.exp(np.dot(np.dot(1j, np.pi) / 180,
                                       (tidecon[:, 4] + tidecon[:, 6]))))

    # Mean at central point (get rid of one point at end to
    # take mean of odd number of points if necessary).
    jdmid = np.mean(tim[0:np.dot(2, np.fix((max(tim.shape) - 1) / 2)) + 1])
    if longseries:
        const = t_get18consts
        ju = np.zeros(shape=(freq.shape, freq.shape), dtype='float64')
        for k in range(1, (names.shape[0]+1)):
            inam = strmatch(names[(k-1), :], const.name)
            if max(inam.shape) == 1:
                ju[(k-1)] = inam
            else:
                if max(inam.shape) > 1:
                    minf, iminf = np.min(abs(freq[(k-1)] - const.freq(inam)))
                    ju[(k-1)] = inam[(iminf-1)]
    else:
        const, sat, cshallow = t_getconsts(np.array([]))
        ju = np.zeros((len(freq),), dtype='int32')
        # Check to make sure names and frequencies match expected values.
        for k in range(0, (names.shape[0])):
            ju[k] = np.argwhere(const['name'] == names[(k)])
        # if any(freq~=const.freq(ju)),
        # error('Frequencies do not match names in input');
        # end;
    # Get the astronical argument with or without nodal corrections.
    if ((lat.size != 0) & (np.absolute(jdmid) > 1)):
        v, u, f = t_vuf(ltype, jdmid, ju, lat)
    else:
        if np.fabs(jdmid) > 1:
            # a real date
            v, u, f = t_vuf(ltype, jdmid, ju)
        else:
            v = np.zeros((len(ju),), dtype='float64')
            u = v
            f = np.ones((len(ju),), dtype='float64')

    ap = ap * f * np.exp(+1j*2*np.pi*(u + v))
    am = am * f * np.exp(-1j*2*np.pi*(u + v))
    tim = tim - jdmid

    n, m = tim.shape
    ntim = max(tim.shape)
    nsub = 10000
    yout = np.zeros([n*m, ], dtype='complex128')

    # longer than one year hourly.
    for j1 in np.arange(0, ntim, nsub):
        j1 = j1.astype(int)
        j2 = np.min([j1 + nsub, ntim]).astype(int)
        tap = np.repeat(ap, j2-j1).reshape(len(ap), j2-j1)
        tam = np.repeat(am, j2-j1).reshape(len(am), j2-j1)

        touter = np.outer(24*1j*2*np.pi*freq, tim[j1:j2])
        yout[j1:j2] = np.sum(np.multiply(np.exp(touter), tap), axis=0) +\
            np.sum(np.multiply(np.exp(-touter), tam), axis=0)

    if (tidecon.shape[1] == 4):
        return np.real(yout)
    else:
        return yout