def generate_bnd(cli_file,
                 geo_file,
                 slf_file,
                 bnd_file,
                 varnames,
                 varunits,
                 showbar=True):
    """
    @param cli_file
    @param geo_file
    @param slf_file
    @param bnd_file
    @param varnames
    @param varunits
    @param showbar (boolean) If True display a showbar for the progress
    """

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ cli+slf new mesh ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    if not path.exists(cli_file):
        raise TelemacException(\
             '... the provided cli_file does not seem to exist:'
             ' {}\n\n'.format(cli_file))
    if not path.exists(geo_file):
        raise TelemacException(\
            '... the provided geo_file does not seem to exist: '
            '{}\n\n'.format(geo_file))

    if len(varnames) != len(varunits):
        raise TelemacException(\
          'Not the same number of variables and units\nvarnames: {}\nvarunits: {}'
          '{}\n\n'.format(varnames, varunits))

    # Read the new CLI file to get boundary node numbers
    print('   +> getting hold of the Conlim file and of its liquid boundaries')
    cli = Conlim(cli_file)
    # Keeping only open boundary nodes
    bor = np.extract(cli.bor['lih'] != 2, cli.bor['n'])

    # Find corresponding (x,y) in corresponding new mesh
    print('   +> getting hold of the GEO file and of its bathymetry')
    geo = Selafin(geo_file)
    xys = np.vstack((geo.meshx[bor - 1], geo.meshy[bor - 1])).T
    _ = geo.get_variables_at(0,\
                  subset_variables_slf("BOTTOM: ", geo.varnames)[0])[0]

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ slf existing res ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    if not path.exists(slf_file):
        raise TelemacException(\
               '... the provided slf_file does not seem to exist: '
               '{}\n\n'.format(slf_file))
    slf = Selafin(slf_file)
    slf.set_kd_tree()
    slf.set_mpl_tri()

    print('   +> support extraction')
    # Extract triangles and weigths in 2D
    support2d = []
    if showbar:
        ibar = 0
        pbar = ProgressBar(maxval=len(xys)).start()
    for xyi in xys:
        support2d.append(
            xys_locate_mesh(xyi, slf.ikle2, slf.meshx, slf.meshy, slf.tree,
                            slf.neighbours))
        if showbar:
            ibar += 1
            pbar.update(ibar)
    if showbar:
        pbar.finish()
    # Extract support in 3D
    support3d = list(zip(support2d, len(xys) * [range(slf.nplan)]))

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ writes BND header ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    bnd = Selafin('')
    bnd.fole = {}
    bnd.fole.update({'hook': open(bnd_file, 'wb')})
    bnd.fole.update({'name': bnd_file})
    bnd.fole.update({'endian': ">"})  # big endian
    bnd.fole.update({'float': ('f', 4)})  # single precision

    # Meta data and variable names
    bnd.title = ''
    bnd.nbv1 = len(varnames)
    # /!\ ELEVATION has to be the first variable
    # (for possible vertical re-interpolation within TELEMAC)

    bnd.varnames = []
    bnd.varunits = []
    for var, unit in zip(varnames, varunits):
        new_var = var + (16 - len(var)) * " "
        new_unit = unit + (16 - len(unit)) * " "
        bnd.varnames.append(new_var)
        bnd.varunits.append(new_unit)

    bnd.nvar = bnd.nbv1
    bnd.varindex = range(bnd.nvar)

    # Sizes and mesh connectivity
    bnd.nplan = slf.nplan
    # Number of nodes per boundary element  (ndp2 in 2D and ndp3 in 3D)
    bnd.ndp2 = 2
    bnd.ndp3 = 4
    bnd.npoin2 = len(bor)
    bnd.npoin3 = bnd.npoin2 * slf.nplan
    bnd.iparam = [0, 0, 0, 0, 0, 0, bnd.nplan, 0, 0, 1]
    bnd.ipob2 = bor  # /!\ note that ipobo keeps the original numbering
    print('   +> masking and setting connectivity')
    # Set the array that only includes elements of geo.ikle2
    # with at least two nodes in bor
    array_1d = np.in1d(geo.ikle2, np.sort(bor - 1))
    mask = geo.ikle2[np.where(
        np.sum(array_1d.reshape(geo.nelem2, geo.ndp2), axis=1) == 2)]
    # this ikle2 keeps the original numbering
    ikle2 = np.ravel(mask)[np.in1d(mask,
                                   np.sort(bor - 1))].reshape(len(mask), 2)
    # ~~> re-numbering ikle2 as a local connectivity matrix
    knolg, _ = np.unique(np.ravel(ikle2), return_index=True)
    knogl = dict(zip(knolg, range(len(knolg))))
    bnd.ikle2 = -np.ones_like(ikle2, dtype=np.int)
    for k in range(len(ikle2)):
        # /!\ bnd.ikle2 has a local numbering, fit to the boundary elements
        bnd.ikle2[k] = [knogl[ikle2[k][0]], knogl[ikle2[k][1]]]
    # Last few numbers
    bnd.nelem2 = len(bnd.ikle2)
    if slf.nplan > 1:
        bnd.nelem3 = bnd.nelem2 * (slf.nplan - 1)
    else:
        bnd.nelem3 = bnd.nelem2
        bnd.ndp3 = bnd.ndp2
    # 3D structures
    if slf.nplan > 1:
        bnd.ipob3 = np.ravel(np.add(np.repeat(bnd.ipob2, slf.nplan)\
                                      .reshape((bnd.npoin2, slf.nplan)),
                                    bnd.npoin2*np.arange(slf.nplan)).T)
        bnd.ikle3 = \
            np.repeat(bnd.npoin2*np.arange(slf.nplan-1),
                      bnd.nelem2*bnd.ndp3)\
              .reshape((bnd.nelem2*(slf.nplan-1), bnd.ndp3)) + \
            np.tile(np.add(np.tile(bnd.ikle2, 2),
                           np.repeat(bnd.npoin2*np.arange(2), bnd.ndp2)),
                    (slf.nplan-1, 1))
    else:
        bnd.ipob3 = bnd.ipob2
        bnd.ikle3 = bnd.ikle2
    # Mesh coordinates
    bnd.meshx = geo.meshx[bor - 1]
    bnd.meshy = geo.meshy[bor - 1]

    print('   +> writing header')
    # Write header
    bnd.append_header_slf()

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ writes BND core ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    print('   +> setting variables')
    # TIME and DATE extraction
    bnd.datetime = slf.datetime
    bnd.tags['times'] = slf.tags['times']
    # VARIABLE extraction
    list_var = varnames[0] + ": "
    for var in varnames[1:]:
        list_var += ";" + var + ": "

    vrs = subset_variables_slf(list_var, slf.varnames)

    # Read / Write data, one time step at a time to support large files
    print('   +> reading / writing variables')
    if showbar:
        pbar = ProgressBar(maxval=len(slf.tags['times'])).start()
    zeros = np.zeros((bnd.npoin3, 1), dtype=np.float)
    for itime in range(len(slf.tags['times'])):
        data = get_value_history_slf(slf.file, slf.tags, [itime], support3d,
                                     slf.nvar, slf.npoin3, slf.nplan, vrs)
        data = np.reshape(
            np.transpose(
                np.reshape(np.ravel(data), (bnd.nvar, bnd.npoin2, bnd.nplan)),
                (0, 2, 1)), (bnd.nvar, bnd.npoin3))
        bnd.append_core_time_slf(itime)
        bnd.append_core_vars_slf(data)
        if showbar:
            pbar.update(itime)
    if showbar:
        pbar.finish()

    # Close bnd_file
    bnd.fole['hook'].close()
Beispiel #2
0
def main():
    """ Main function of convertToIni """

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ Dependencies towards other modules ~~~~~~~~~~~~~~~~~~~~~~~~~~
    from argparse import ArgumentParser, RawDescriptionHelpFormatter
    from data_manip.formats.selafin import Selafin
    from data_manip.extraction.parser_selafin import subset_variables_slf, \
        get_value_history_slf
    from pretel.meshes import xys_locate_mesh

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ Reads config file ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    print('\n\nInterpreting command line options\n' + '~' * 72 + '\n')
    parser = ArgumentParser(
        formatter_class=RawDescriptionHelpFormatter,
        description=('''\n
A script to map 2D or 3D outter model results stored in a SELAFIN file,\
 onto the
   one frame of contained SELAFIN file of your choosing (your MESH).
      '''),
        usage=' (--help for help)\n---------\n       =>  '
        '%(prog)s  geo-mesh.slf in-result.slf out-init.slf \n---------')
    parser.add_argument("args", default='', nargs=3)
    options = parser.parse_args()

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ slf new mesh ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    geo_file = options.args[0]
    if not path.exists(geo_file):
        raise TelemacException(
            '... the provided geo_file does not seem to exist: {}'
            '\n\n'.format(geo_file))


# Find corresponding (x,y) in corresponding new mesh
    print('   +> getting hold of the GEO file and of its bathymetry')
    geo = Selafin(geo_file)
    xys = np.vstack((geo.meshx, geo.meshy)).T
    _ = geo.get_variables_at(0,
                             subset_variables_slf("BOTTOM: ",
                                                  geo.varnames)[0])[0]

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ slf existing res ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    slf_file = options.args[1]
    if not path.exists(slf_file):
        raise TelemacException(
            '... the provided geo_file does not seem to exist: {}'
            '\n\n'.format(slf_file))

    slf = Selafin(slf_file)
    slf.set_kd_tree()
    slf.set_mpl_tri()

    print('   +> support extraction')
    # Extract triangles and weights in 2D
    support2d = []
    ibar = 0
    pbar = ProgressBar(maxval=len(xys)).start()
    for xyi in xys:
        support2d.append(
            xys_locate_mesh(xyi, slf.ikle2, slf.meshx, slf.meshy, slf.tree,
                            slf.neighbours))
        ibar += 1
        pbar.update(ibar)
    pbar.finish()
    # Extract support in 3D
    support3d = list(zip(support2d, len(xys) * [range(slf.nplan)]))

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ writes INI header ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    ini_file = options.args[2]
    ini = Selafin('')
    ini.fole = {}
    ini.fole.update({'hook': open(ini_file, 'wb')})
    ini.fole.update({'name': ini_file})
    ini.fole.update({'endian': ">"})  # big endian
    ini.fole.update({'float': ('f', 4)})  # single precision

    # Meta data and variable names
    ini.title = ''
    ini.nbv1 = 5
    # /!\ ELEVATION has to be the first variable
    # (for possible vertical re-interpolation within TELEMAC)
    ini.varnames = [
        'ELEVATION Z     ', 'VELOCITY U      ', 'VELOCITY V      ',
        'SALINITY        ', 'TEMPERATURE     '
    ]
    ini.varunits = [
        'M               ', 'M/S             ', 'M/S             ',
        '                ', '                '
    ]
    ini.nvar = ini.nbv1
    ini.varindex = range(ini.nvar)

    # sizes and mesh connectivity
    ini.nplan = slf.nplan
    ini.ndp2 = 3
    ini.ndp3 = 6
    ini.npoin2 = geo.npoin2
    ini.npoin3 = geo.npoin2 * ini.nplan
    ini.nelem2 = geo.nelem2
    ini.nelem3 = ini.nelem2 * (ini.nplan - 1)

    print('   +> setting connectivity')
    ini.ikle3 = \
        np.repeat(geo.npoin2*np.arange(ini.nplan-1),
                  geo.nelem2*ini.ndp3)\
        .reshape((geo.nelem2*(ini.nplan-1), ini.ndp3)) + \
        np.tile(np.add(np.tile(geo.ikle2, 2),
                np.repeat(geo.npoin2*np.arange(2), geo.ndp2)),
                (ini.nplan-1, 1))
    ini.ipob3 = np.ravel(
        np.add(
            np.repeat(geo.ipob2, ini.nplan).reshape((geo.npoin2, ini.nplan)),
            geo.npoin2 * np.arange(ini.nplan)).T)
    ini.iparam = [0, 0, 0, 0, 0, 0, ini.nplan, 0, 0, 0]

    # Mesh coordinates
    ini.meshx = geo.meshx
    ini.meshy = geo.meshy

    print('   +> writing header')
    # Write header
    ini.append_header_slf()

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ writes INI core ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    print('   +> setting variables')
    ini.tags['times'] = slf.tags['times']
    # VARIABLE extraction
    vrs = subset_variables_slf(
        "ELEVATION Z: ;VELOCITY U: ;VELOCITY V: "
        ";SALINITY: ;TEMPERATURE: ", slf.varnames)

    # Read / Write data for first time step
    zeros = np.zeros((ini.npoin3, 1), dtype=np.float)

    print('   +> extracting variables')
    data = get_value_history_slf(slf.file, slf.tags, [0], support3d, slf.nvar,
                                 slf.npoin3, slf.nplan, vrs)

    # special case for TEMPERATURE and SALINITY
    data[3] = np.maximum(data[3], zeros)
    data[4] = np.maximum(data[4], zeros)
    print('   +> correcting variables')
    # duplicate values below bottom
    data = np.reshape(
        np.transpose(
            np.reshape(np.ravel(data), (ini.nvar, ini.npoin2, ini.nplan)),
            (0, 2, 1)), (ini.nvar, ini.npoin3))
    print('   +> writing variables')
    ini.append_core_time_slf(0)
    ini.append_core_vars_slf(data)

    # Close ini_file
    ini.fole['hook'].close()

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ Jenkins' success message ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    print('\n\nMy work is done\n\n')

    sys.exit(0)
class Jcope2(object):
    def __init__(self, dates):

        try:
            from pydap.client import open_url
        except Exception as excpt:
            raise TelemacException(
                '... you are in bad luck !\n'
                '  ~>  you need the pydap library unzipped locally\n'
                '{}'.format(excpt))
        # ~~~~ Initialisation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        self.moddates = [datetime(*dates[0]), datetime(*dates[1])]
        jcope2vars = ['el', 'itime', 's', 'u', 'v']
        # /!\ unknown convertion of time records into dates
        jcope2date = [1993, 1, 1]
        jcope2root = 'http://apdrc.soest.hawaii.edu/dods/public_data/FRA-JCOPE2'
        self.slf2d = None
        self.slf3d = None
        self.jcope2ilon = None
        self.jcope2ilat = None
        self.zplan = None
        self.mask2 = None
        self.mask3 = None

        # ~~~~ Time records ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        print('     +> Extract JCOPE2 time records\n')
        self.experiments = []
        experiment = {}  # /!\ only one time period covered at this stage
        for jvar in jcope2vars:
            jcope2url = jcope2root + '/' + jvar
            jcope2data = open_url(jcope2url)
            nit = jcope2data['time'].shape[0]
            # /!\ the following print statement requires __future__
            print('        x ' + str(nit) + ' records from ' + jcope2url,
                  end='')
            its = []
            ats = []
            for itime in range(nit):
                date = datetime(jcope2date[0], jcope2date[1], jcope2date[2])+\
                     timedelta(itime)
                if itime == 0:
                    print(' from: ' + str(date), end='')
                if itime == nit - 1:
                    print(' to: ' + str(date))
                if self.moddates[0] <= date and date <= self.moddates[1]:
                    its.append(itime)
                    ats.append(date)
            if its != []:
                for ivar in list(jcope2data.keys()):
                    if ivar not in ['time', 'lev', 'lat', 'lon']:
                        experiment.update({ivar: jcope2data[ivar]})
            else:
                raise TelemacException(\
                    '... I could not find the time to do your work\n'
                    '  ~>  you may need to select a different time period\n')
        self.experiments.append((experiment, nit, its, ats))
        print('\n')

    def get_header_jcope2(self, bounds):

        # ~~> inheritence
        self.slf3d = Selafin('')  # slf3d
        self.slf2d = Selafin('')  # slf2d surface

        print('     +> Set Selafin Variables')
        self.slf3d.title = ''
        self.slf3d.nbv1 = 6
        self.slf3d.nvar = 6
        self.slf3d.varindex = list(range(self.slf3d.nvar))
        self.slf3d.varnames = ['ELEVATION Z     ', \
            'SALINITY        ', 'TEMPERATURE     ', \
            'VELOCITY U      ', 'VELOCITY V      ', 'VELOCITY W      ']
        self.slf3d.varunits = ['M               ', \
            '                ', '                ', \
            'M/S             ', 'M/S             ', 'M/S             ']
        self.slf2d.title = self.slf3d.title
        self.slf2d.nbv1 = self.slf3d.nbv1 - 1
        self.slf2d.nvar = self.slf2d.nbv1
        self.slf2d.varindex = list(range(self.slf2d.nvar))
        self.slf2d.varnames = self.slf3d.varnames[0:-1]
        self.slf2d.varunits = self.slf3d.varunits[0:-1]

        # ~~~~ Grid coordinates ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        # ~~> the whole of the 2D grid sizes
        print('     +> Extract JCOPE2 sizes')
        # /!\ 'itime' gives me access to nplan in 3D
        jcope2data = self.experiments[0][0]['temp']
        nx1d = jcope2data['lon'].shape[0]
        ny1d = jcope2data['lat'].shape[0]
        print('     +> Extract JCOPE2 mesh')
        lon_x1d = jcope2data['lon'].data[0:nx1d]
        lat_y1d = jcope2data['lat'].data[0:ny1d]
        # ~~> no correction for lat,lon
        # ~~> subset for the Selafin
        print('     +> Set Selafin mesh')
        self.jcope2ilon = \
                np.where((lon_x1d >= bounds[0][1])*(lon_x1d <= bounds[1][1]))[0]
        self.jcope2ilat = \
                np.where((lat_y1d >= bounds[0][0])*(lat_y1d <= bounds[1][0]))[0]
        x = lon_x1d[self.jcope2ilon]
        y = lat_y1d[self.jcope2ilat]
        nx1d = len(x)
        ny1d = len(y)

        # ~~~~ MESH sizes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        print('     +> Set Selafin sizes')
        # ~~> 3D
        self.slf3d.nplan = jcope2data['lev'].shape[0]
        # I do not know any other way
        self.zplan = jcope2data['lev'][0:self.slf3d.nplan][::-1]
        self.slf3d.ndp2 = 3
        self.slf3d.ndp3 = 6
        self.slf3d.npoin2 = nx1d * ny1d
        self.slf3d.npoin3 = self.slf3d.npoin2 * self.slf3d.nplan
        self.slf3d.nelem2 = 2 * (nx1d - 1) * (ny1d - 1)
        self.slf3d.nelem3 = self.slf3d.nelem2 * (self.slf3d.nplan - 1)
        self.slf3d.iparam = [0, 0, 0, 0, 0, 0, self.slf3d.nplan, 0, 0, 0]
        # ~~> 2D
        self.slf2d.nplan = 1
        self.slf2d.ndp2 = self.slf3d.ndp2
        self.slf2d.ndp3 = self.slf2d.ndp2
        self.slf2d.npoin2 = self.slf3d.npoin2
        self.slf2d.npoin3 = self.slf2d.npoin2
        self.slf2d.nelem2 = self.slf3d.nelem2
        self.slf2d.nelem3 = self.slf2d.nelem2
        self.slf2d.iparam = [0, 0, 0, 0, 0, 0, 1, 0, 0, 0]

        # ~~~~ Connectivity ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        print('     +> Set the default Selafin ikle 3D')
        ielem = 0
        pbar = ProgressBar(maxval=self.slf3d.nelem3).start()
        self.slf3d.ikle3 = np.zeros((self.slf3d.nelem3, self.slf3d.ndp3),
                                    dtype=np.int)
        npoin2 = self.slf3d.npoin2
        for k in range(1, self.slf3d.nplan):
            for i in range(1, nx1d):
                for j in range(1, ny1d):
                    ipoin = (i - 1) * ny1d + j - 1 + (k - 1) * npoin2
                    # ~~> first prism
                    self.slf3d.ikle3[ielem][0] = ipoin
                    self.slf3d.ikle3[ielem][1] = ipoin + ny1d
                    self.slf3d.ikle3[ielem][2] = ipoin + 1
                    self.slf3d.ikle3[ielem][3] = ipoin + npoin2
                    self.slf3d.ikle3[ielem][4] = ipoin + ny1d + npoin2
                    self.slf3d.ikle3[ielem][5] = ipoin + 1 + npoin2
                    ielem = ielem + 1
                    pbar.update(ielem)
                    # ~~> second prism
                    self.slf3d.ikle3[ielem][0] = ipoin + ny1d
                    self.slf3d.ikle3[ielem][1] = ipoin + ny1d + 1
                    self.slf3d.ikle3[ielem][2] = ipoin + 1
                    self.slf3d.ikle3[ielem][3] = ipoin + ny1d + npoin2
                    self.slf3d.ikle3[ielem][4] = ipoin + ny1d + 1 + npoin2
                    self.slf3d.ikle3[ielem][5] = ipoin + 1 + npoin2
                    ielem = ielem + 1
                    pbar.update(ielem)
        pbar.finish()
        self.slf2d.ikle3 = np.compress([True, True, True, False, False, False],
                                       self.slf3d.ikle3[0:self.slf3d.nelem2],
                                       axis=1)

        # ~~~~ Boundaries ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        print('     +> Set Selafin ipobO')
        pbar = ProgressBar(maxval=nx1d + ny1d).start()
        ipob2 = np.zeros(self.slf3d.npoin2, dtype=np.int)
        # ~~> along the x-axis (lon)
        for i in range(nx1d):
            ipoin = i * ny1d
            ipob2[ipoin] = i + 1
            ipoin = i * ny1d - 1
            ipob2[ipoin] = 2 * nx1d + (ny1d - 2) - i
            pbar.update(i)
        # ~~> along the y-axis (alt)
        for i in range(1, ny1d):
            ipoin = i
            ipob2[ipoin] = 2 * nx1d + 2 * (ny1d - 2) - i + 1
            ipoin = ny1d * (nx1d - 1) + i
            ipob2[ipoin] = nx1d + i
            pbar.update(i + nx1d)
        pbar.finish()

        # ~~~~ Connectivity ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        # /!\ 'el' gives me access to the real mesh removing
        #       elements with -99 values
        print('     +> Mask the non-values from the Selafin ikle')
        jcope2data = self.experiments[0][0]['el']
        var = np.swapaxes(jcope2data['el']\
                          .data[0, 0, self.jcope2ilat[0]:self.jcope2ilat[-1]+1,
                                self.jcope2ilon[0]:self.jcope2ilon[-1]+1][0],
                          1, 2).ravel()
        # ~> the elements you wish to keep
        array = np.compress(var > -99, np.arange(len(var)))
        array_1d = np.in1d(self.slf2d.ikle3, array)
        array_sum = np.sum(array_1d.reshape(self.slf2d.nelem2,
                                            self.slf2d.ndp2),
                           axis=1)
        mask2 = self.slf2d.ikle3[np.where(array_sum == 3)]

        self.slf2d.nelem2 = len(mask2)
        self.slf2d.nelem3 = self.slf2d.nelem2
        self.slf3d.nelem2 = self.slf2d.nelem2
        self.slf3d.nelem3 = self.slf3d.nelem2 * (self.slf3d.nplan - 1)

        # ~~> re-numbering ikle2 as a local connectivity matrix
        knolg, _ = np.unique(np.ravel(mask2), return_index=True)
        knogl = dict(list(zip(knolg, list(range(len(knolg))))))
        self.mask2 = np.in1d(np.arange(len(var)), knolg)
        self.mask3 = np.tile(self.mask2, self.slf3d.nplan)
        self.slf2d.ikle2 = -np.ones_like(mask2, dtype=np.int)
        for k in range(len(mask2)):
            self.slf2d.ikle2[k] = [
                knogl[mask2[k][0]], knogl[mask2[k][1]], knogl[mask2[k][2]]
            ]

        self.slf3d.npoin2 = len(knolg)
        self.slf3d.npoin3 = self.slf3d.npoin2 * self.slf3d.nplan
        self.slf2d.npoin2 = self.slf3d.npoin2
        self.slf2d.npoin3 = self.slf2d.npoin2

        # ~~> re-connecting the upper floors
        self.slf2d.ikle3 = self.slf2d.ikle2
        self.slf3d.ikle2 = self.slf2d.ikle2

        init = self.slf2d.npoin2 * np.arange(self.slf3d.nplan - 1)
        size = self.slf2d.nelem2 * self.slf3d.ndp3
        self.slf3d.ikle3 = \
          np.repeat(init, size).reshape((self.slf2d.nelem2*(self.slf3d.nplan-1),
                                         self.slf3d.ndp3)) + \
          np.tile(np.add(np.tile(self.slf2d.ikle2, 2),
                         np.repeat(self.slf2d.npoin2*np.arange(2),
                                   self.slf3d.ndp2)),
                  (self.slf3d.nplan-1, 1))

        # ~~~~ Boundaries ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        self.slf2d.ipob2 = ipob2[self.mask2]
        self.slf2d.ipob3 = self.slf2d.ipob2
        self.slf3d.ipob2 = self.slf2d.ipob2
        self.slf3d.ipob3 = np.ravel(np.add(\
                  np.repeat(self.slf2d.ipob2, self.slf3d.nplan)\
                     .reshape((self.slf2d.npoin2, self.slf3d.nplan)),
                  self.slf2d.npoin2*np.arange(self.slf3d.nplan)).T)

        # ~~~~ Mesh ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        print('     +> Set Selafin mesh')
        self.slf3d.meshx = np.tile(x, ny1d).reshape(ny1d, nx1d)\
                                    .T.ravel()[self.mask2] + 0.042
        self.slf3d.meshy = np.tile(y, nx1d)[self.mask2] + 0.042
        self.slf2d.meshx = self.slf3d.meshx
        self.slf2d.meshy = self.slf3d.meshy

    def put_content(self, root_name, only_2d):

        nbar = 0
        for exp in self.experiments:
            nbar += len(exp[2])
        ilat = [self.jcope2ilat[0], self.jcope2ilat[-1] + 1]
        ilon = [self.jcope2ilon[0], self.jcope2ilon[-1] + 1]

        # ~~~~ Time records ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        print('     +> Extract JCOPE2 time records')
        self.slf3d.tags = {'times': []}
        # ~~~~ Start Date and Time ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        self.slf3d.tags['times'] = 86400.0 * np.arange(nbar)
        self.slf2d.tags = {'times': self.slf3d.tags['times']}
        self.slf3d.datetime = self.experiments[-1][3][0].timetuple()[0:6]
        self.slf2d.datetime = self.slf3d.datetime
        self.slf3d.iparam[9] = 1
        self.slf2d.iparam[9] = 1

        print('     +> Write Selafin headers')
        if not only_2d:
            self.slf3d.fole = {}
            self.slf3d.fole.update({'hook': open('t3d_' + root_name, 'wb')})
            self.slf3d.fole.update({'name': 't3d_' + root_name})
            self.slf3d.fole.update({'endian': ">"})  # big endian
            self.slf3d.fole.update({'float': ('f', 4)})  # single precision
            self.slf3d.append_header_slf()
        self.slf2d.fole = {}
        self.slf2d.fole.update({'hook': open('t2d_' + root_name, 'wb')})
        self.slf2d.fole.update({'name': 't2d_' + root_name})
        self.slf2d.fole.update({'endian': ">"})  # big endian
        self.slf2d.fole.update({'float': ('f', 4)})  # single precision
        self.slf2d.append_header_slf()
        # ~~~~ Time loop(s) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        print('     +> Write Selafin cores')
        ibar = 0
        pbar = ProgressBar(maxval=6 * nbar).start()
        for exp in self.experiments:
            jcope2data = exp[0]
            i_1 = min(exp[2])
            i_2 = max(exp[2]) + 1
            for itime in range(i_1, i_2):
                # ~~> time stamp
                pbar.write('        x ' + str(exp[3][itime - i_1]),
                           6 * ibar + 0)
                pbar.update(6 * ibar + 0)
                if not only_2d:
                    self.slf3d.append_core_time_slf(ibar)
                self.slf2d.append_core_time_slf(ibar)

                # ~~> ELEVATION
                data = jcope2data['el']['el']
                var2d = np.swapaxes(
                    data.data[itime, 0, ilat[0]:ilat[1], ilon[0]:ilon[1]][0],
                    1, 2).ravel()[self.mask2]
                self.slf2d.append_core_vars_slf([var2d])
                if not only_2d:
                    var3d = - np.tile(self.zplan, self.slf3d.npoin2)\
                                .reshape(self.slf3d.npoin2, self.slf3d.nplan)\
                                .T.ravel()
                    var3d[self.slf3d.npoin3 - self.slf3d.npoin2:] = var2d
                    self.slf3d.append_core_vars_slf([var3d])
                pbar.write('             - elevation', 6 * ibar + 1)
                pbar.update(6 * ibar + 1)

                # ~~> SALINITY
                data = jcope2data['salt']['salt']
                if only_2d:
                    var = np.swapaxes(
                        data.data[itime, 0:1, ilat[0]:ilat[1],
                                  ilon[0]:ilon[1]][0], 1, 2)
                else:
                    var = np.swapaxes(
                        data.data[itime, 0:self.slf3d.nplan, ilat[0]:ilat[1],
                                  ilon[0]:ilon[1]][0], 1, 2)
                var2d = var[0].ravel()[self.mask2]
                self.slf2d.append_core_vars_slf([var2d])
                if not only_2d:
                    var3d = var[::-1].ravel()[self.mask3]
                    for ipoin in range(self.slf3d.npoin2):
                        for iplan in range(self.slf3d.nplan - 1, 0, -1):
                            if var3d[ipoin +
                                     (iplan - 1) * self.slf3d.npoin2] < -99.0:
                                var3d[ipoin+(iplan-1)*self.slf3d.npoin2] = \
                                          var3d[ipoin+iplan*self.slf3d.npoin2]
                    self.slf3d.append_core_vars_slf([var3d])
                    pbar.write('             - salinity', 6 * ibar + 2)
                pbar.update(6 * ibar + 2)

                # ~~> TEMPERATURE
                data = jcope2data['temp']['temp']
                if only_2d:
                    var = np.swapaxes(
                        data.data[itime, 0:1, ilat[0]:ilat[1],
                                  ilon[0]:ilon[1]][0], 1, 2)
                else:
                    var = np.swapaxes(
                        data.data[itime, 0:self.slf3d.nplan, ilat[0]:ilat[1],
                                  ilon[0]:ilon[1]][0], 1, 2)
                var2d = var[0].ravel()[self.mask2]
                self.slf2d.append_core_vars_slf([var2d])
                if not only_2d:
                    var3d = var[::-1].ravel()[self.mask3]
                    for ipoin in range(self.slf3d.npoin2):
                        for iplan in range(self.slf3d.nplan - 1, 0, -1):
                            if var3d[ipoin +
                                     (iplan - 1) * self.slf3d.npoin2] < -99.0:
                                var3d[ipoin+(iplan-1)*self.slf3d.npoin2] = \
                                          var3d[ipoin+iplan*self.slf3d.npoin2]
                    self.slf3d.append_core_vars_slf([var3d])
                    pbar.write('             - temperature', 6 * ibar + 3)
                pbar.update(6 * ibar + 3)

                # ~~> VELOCITY U
                data = jcope2data['u']['u']
                if only_2d:
                    var = np.swapaxes(
                        data.data[itime, 0:1, ilat[0]:ilat[1],
                                  ilon[0]:ilon[1]][0], 1, 2)
                else:
                    var = np.swapaxes(
                        data.data[itime, 0:self.slf3d.nplan, ilat[0]:ilat[1],
                                  ilon[0]:ilon[1]][0], 1, 2)
                var2d = var[0].ravel()[self.mask2]
                self.slf2d.append_core_vars_slf([var2d])
                if not only_2d:
                    var3d = var[::-1].ravel()[self.mask3]
                    for ipoin in range(self.slf3d.npoin2):
                        for iplan in range(self.slf3d.nplan - 1, 0, -1):
                            if var3d[ipoin +
                                     (iplan - 1) * self.slf3d.npoin2] < -99.0:
                                var3d[ipoin+(iplan-1)*self.slf3d.npoin2] = \
                                          var3d[ipoin+iplan*self.slf3d.npoin2]
                    self.slf3d.append_core_vars_slf([var3d])
                pbar.write('             - u-velocity', 6 * ibar + 4)
                pbar.update(6 * ibar + 4)

                # ~~> VELOCITY V
                data = jcope2data['v']['v']
                if only_2d:
                    var = np.swapaxes(
                        data.data[itime, 0:1, ilat[0]:ilat[1],
                                  ilon[0]:ilon[1]][0], 1, 2)
                else:
                    var = np.swapaxes(
                        data.data[itime, 0:self.slf3d.nplan, ilat[0]:ilat[1],
                                  ilon[0]:ilon[1]][0], 1, 2)
                var2d = var[0].ravel()[self.mask2]
                self.slf2d.append_core_vars_slf([var2d])
                if not only_2d:
                    var3d = var[::-1].ravel()[self.mask3]
                    for ipoin in range(self.slf3d.npoin2):
                        for iplan in range(self.slf3d.nplan - 1, 0, -1):
                            if var3d[ipoin +
                                     (iplan - 1) * self.slf3d.npoin2] < -99.0:
                                var3d[ipoin+(iplan-1)*self.slf3d.npoin2] = \
                                          var3d[ipoin+iplan*self.slf3d.npoin2]
                    self.slf3d.append_core_vars_slf([var3d])
                pbar.write('             - v-velocity', 6 * ibar + 5)
                pbar.update(6 * ibar + 5)

                # ~~> VELOCITY W
                if not only_2d:
                    var3d = 0. * var3d
                    self.slf3d.append_core_vars_slf([var3d])

                ibar += 1

        pbar.finish()
        if not only_2d:
            self.slf3d.fole['hook'].close()
        self.slf2d.fole['hook'].close()

    def __del__(self):
        pass
class HYCOM(object):
    def __init__(self, dates):

        try:
            from pydap.client import open_url
        except ImportError:
            print('... you are in bad luck !\n'
                  '  ~>  you need to have the pydap library for '
                  'python 3 installed,\n'
                  '  ~>  along with its dependencies')
            sys.exit(0)
        # ~~~~ Initialisation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        self.moddates = [datetime(*dates[0]), datetime(*dates[1])]
        self.slf2d = None
        self.slf3d = None
        self.hycomdata = None
        self.hycomilon = None
        self.hycomilat = None
        self.zplan = None

        # ~~~~ Time records ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        print('     +> Extract HYCOM time records\n')
        hycomurls = [ \
            'http://tds.hycom.org/thredds/dodsC/GLBa0.08/expt_91.2', \
            'http://tds.hycom.org/thredds/dodsC/GLBa0.08/expt_91.1', \
            'http://tds.hycom.org/thredds/dodsC/GLBa0.08/expt_91.0', \
            'http://tds.hycom.org/thredds/dodsC/GLBa0.08/expt_90.9', \
            'http://tds.hycom.org/thredds/dodsC/GLBa0.08/expt_90.8', \
            'http://tds.hycom.org/thredds/dodsC/GLBa0.08/expt_90.6'
                    ]

        self.experiments = []
        for hycomurl in hycomurls:
            success = False
            attempt = 0
            while not success:
                try:
                    success = True
                    hycomdata = open_url(hycomurl)
                    nit = hycomdata['Date'].shape[0]
                    # /!\ the following print statement requires __future__
                    print('        x ' + str(nit) + ' records from ' +
                          hycomurl,
                          end='')
                    its = []
                    ats = []
                    z = zip(hycomdata['Date'][0:nit], range(nit))
                except Exception:
                    if attempt == 4:
                        raise TelemacException(
                            "Could not access hycom data (Maybe proxy issue ?)"
                        )
                    success = False
                    attempt += 1
                    print(' ... re-attempting {}'.format(attempt))

            for hycomdate, itime in z:
                date = datetime(int(str(hycomdate)[0:4]),
                                int(str(hycomdate)[4:6]),
                                int(str(hycomdate)[6:8]))
                # /!\ the following print statement requires __future__
                if itime == 0:
                    print(' from: ' + str(date), end='')
                if itime == nit - 1:
                    print(' to: ' + str(date))
                if self.moddates[0] <= date and date <= self.moddates[1]:
                    its.append(itime)
                    ats.append(date)
            if its != []:
                self.experiments.append((hycomdata, nit, its, ats, hycomurl))

        print('\n')

    def get_header_hycom(self, bounds):

        # ~~> inheritence
        self.slf3d = Selafin('')  # slf3d
        self.slf2d = Selafin('')  # slf2d surface

        print('     +> Set Selafin Variables')
        self.slf3d.title = ''
        self.slf3d.nbv1 = 6
        self.slf3d.nvar = 6
        self.slf3d.varindex = range(self.slf3d.nvar)
        self.slf3d.varnames = ['ELEVATION Z     ', \
            'SALINITY        ', 'TEMPERATURE     ', \
            'VELOCITY U      ', 'VELOCITY V      ', 'VELOCITY W      ']
        self.slf3d.varunits = ['M               ', \
            'G/L             ', 'DEGREES         ', \
            'M/S             ', 'M/S             ', 'M/S             ']
        self.slf2d.title = self.slf3d.title
        self.slf2d.nbv1 = self.slf3d.nbv1 + 1
        self.slf2d.nvar = self.slf3d.nvar + 1
        self.slf2d.varindex = range(self.slf2d.nvar)
        self.slf2d.varnames = self.slf3d.varnames[0:-1]
        self.slf2d.varnames.append('EMP             ')
        self.slf2d.varnames.append('QTOT            ')
        self.slf2d.varunits = self.slf3d.varunits[0:-1]
        self.slf2d.varunits.append('???             ')
        self.slf2d.varunits.append('???             ')
        # ~~> server access,
        #     get the grid and header from the latest experiment
        self.hycomdata = self.experiments[0][0]

        # ~~~~ Grid coordinates ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        success = False
        while not success:
            try:
                success = True
                # ~~> the whole of the 2D grid sizes
                print('     +> Extract HYCOM sizes')
                nx1d = self.hycomdata['X'].shape[0]
                ny1d = self.hycomdata['Y'].shape[0]
                print('     +> Extract HYCOM mesh')
                lonx1d = self.hycomdata['Longitude']['Longitude']\
                            .data[0, 0:nx1d].ravel()%360
                lat_y1d = self.hycomdata['Latitude']['Latitude']\
                            .data[0:ny1d, 0].ravel()
            except Exception:
                success = False
                print(' ... re-attempting ')
        # ~~> lat,lon correction
        for i in range(nx1d):
            if lonx1d[i] > 180:
                lonx1d[i] = lonx1d[i] - 360.0
        for i in range(2172, ny1d):
            lat_y1d[i] = 47.0 + (i - 2172) / 18.0
        # ~~> subset for the Selafin
        print('     +> Set Selafin mesh')
        self.hycomilon = \
                np.where((lonx1d >= bounds[0][1])*(lonx1d <= bounds[1][1]))[0]
        self.hycomilat = \
                np.where((lat_y1d >= bounds[0][0])*(lat_y1d <= bounds[1][0]))[0]
        x = lonx1d[self.hycomilon]
        y = lat_y1d[self.hycomilat]
        nx1d = len(x)
        ny1d = len(y)

        # ~~~~ MESH sizes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        # ~~> 3D
        success = False
        while not success:
            try:
                success = True
                print('     +> Set Selafin sizes')
                self.slf3d.nplan = self.hycomdata['Depth'].shape[0]
                # I do not know any other way
                self.zplan = self.hycomdata['Depth'][0:self.slf3d.nplan][::-1]
            except Exception:
                success = False
                print(' ... re-attempting ')
        self.slf3d.ndp2 = 3
        self.slf3d.ndp3 = 6
        self.slf3d.npoin2 = nx1d * ny1d
        self.slf3d.npoin3 = self.slf3d.npoin2 * self.slf3d.nplan
        self.slf3d.nelem2 = 2 * (nx1d - 1) * (ny1d - 1)
        self.slf3d.nelem3 = self.slf3d.nelem2 * (self.slf3d.nplan - 1)
        self.slf3d.iparam = [0, 0, 0, 0, 0, 0, self.slf3d.nplan, 0, 0, 0]
        # ~~> 2D
        self.slf2d.nplan = 1
        self.slf2d.ndp2 = self.slf3d.ndp2
        self.slf2d.ndp3 = self.slf2d.ndp2
        self.slf2d.npoin2 = self.slf3d.npoin2
        self.slf2d.npoin3 = self.slf2d.npoin2
        self.slf2d.nelem2 = self.slf3d.nelem2
        self.slf2d.nelem3 = self.slf2d.nelem2
        self.slf2d.iparam = [0, 0, 0, 0, 0, 0, 1, 0, 0, 0]

        print('     +> Set Selafin mesh')
        self.slf3d.meshx = np.tile(x, ny1d).reshape(ny1d, nx1d).itime.ravel()
        self.slf3d.meshy = np.tile(y, nx1d)
        self.slf2d.meshx = self.slf3d.meshx[0:self.slf2d.npoin2]
        self.slf2d.meshy = self.slf3d.meshy[0:self.slf2d.npoin2]

        # ~~~~ Connectivity ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        print('     +> Set Selafin ikle')
        ielem = 0
        pbar = ProgressBar(maxval=self.slf3d.nelem3).start()
        self.slf3d.ikle3 = np.zeros((self.slf3d.nelem3, self.slf3d.ndp3),
                                    dtype=np.int)
        for k in range(1, self.slf3d.nplan):
            for i in range(1, nx1d):
                for j in range(1, ny1d):
                    ipoin = (i - 1) * ny1d + j - 1 + (k -
                                                      1) * self.slf3d.npoin2
                    # ~~> first prism
                    self.slf3d.ikle3[ielem][0] = ipoin
                    self.slf3d.ikle3[ielem][1] = ipoin + ny1d
                    self.slf3d.ikle3[ielem][2] = ipoin + 1
                    self.slf3d.ikle3[ielem][3] = ipoin + self.slf3d.npoin2
                    self.slf3d.ikle3[ielem][4] = ipoin + ny1d + \
                                                 self.slf3d.npoin2
                    self.slf3d.ikle3[ielem][5] = ipoin + 1 + self.slf3d.npoin2
                    ielem = ielem + 1
                    pbar.update(ielem)
                    # ~~> second prism
                    self.slf3d.ikle3[ielem][0] = ipoin + ny1d
                    self.slf3d.ikle3[ielem][1] = ipoin + ny1d + 1
                    self.slf3d.ikle3[ielem][2] = ipoin + 1
                    self.slf3d.ikle3[ielem][3] = ipoin + ny1d + \
                                                 self.slf3d.npoin2
                    self.slf3d.ikle3[ielem][4] = ipoin + ny1d + 1 + \
                                                 self.slf3d.npoin2
                    self.slf3d.ikle3[ielem][5] = ipoin + 1 + self.slf3d.npoin2
                    ielem = ielem + 1
                    pbar.update(ielem)
        pbar.finish()
        self.slf2d.ikle3 = np.compress(np.repeat([True, False],
                                                 self.slf2d.ndp2),
                                       self.slf3d.ikle3[0:self.slf3d.nelem2],
                                       axis=1)

        # ~~~~ Boundaries ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        print('     +> Set Selafin ipobO')
        pbar = ProgressBar(maxval=nx1d + ny1d).start()
        self.slf3d.ipob3 = np.zeros(self.slf3d.npoin3, dtype=np.int)
        # ~~> along the x-axis (lon)
        for i in range(nx1d):
            for k in range(1, self.slf3d.nplan + 1):
                ipoin = i * ny1d + (k - 1) * (2 * nx1d + 2 * ny1d - 4)
                self.slf3d.ipob3[ipoin] = i + 1 + (k - 1) * (2 * nx1d +
                                                             2 * ny1d - 4)
                ipoin = i * ny1d - 1 + (k - 1) * (2 * nx1d + 2 * ny1d - 4)
                self.slf3d.ipob3[ipoin] = 2*nx1d+(ny1d-2) - i  + \
                                                  (k-1)*(2*nx1d+2*ny1d-4)
            pbar.update(i)
        # ~~> along the y-axis (alt)
        for i in range(1, ny1d):
            for k in range(1, self.slf3d.nplan + 1):
                ipoin = i + (k - 1) * (2 * nx1d + 2 * ny1d - 4)
                self.slf3d.ipob3[ipoin] = 2*nx1d + 2*(ny1d-2) -i + 1 + \
                                                  (k-1)*(2*nx1d+2*ny1d-4)
                ipoin = ny1d * (nx1d - 1) + i + (k - 1) * (2 * nx1d +
                                                           2 * ny1d - 4)
                self.slf3d.ipob3[ipoin] = nx1d + i + (k - 1) * (2 * nx1d +
                                                                2 * ny1d - 4)
            pbar.update(i + nx1d)
        pbar.finish()
        self.slf2d.ipob3 = self.slf3d.ipob3[0:self.slf3d.npoin2]

    def put_content(self, root_name, only_2d):

        nbar = 0
        for exp in self.experiments:
            nbar += len(exp[2])
        ilat = [self.hycomilat[0], self.hycomilat[-1] + 1]
        ilon = [self.hycomilon[0], self.hycomilon[-1] + 1]

        # ~~~~ Time records ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        print('     +> Extract HYCOM time records')
        self.slf3d.tags = {'times': []}
        # ~~~~ Start Date and Time ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        self.slf3d.tags['times'] = 86400.0 * np.arange(nbar)
        self.slf2d.tags = {'times': self.slf3d.tags['times']}
        self.slf3d.datetime = self.experiments[-1][3][0].timetuple()[0:6]
        self.slf2d.datetime = self.slf3d.datetime
        self.slf3d.iparam[9] = 1
        self.slf2d.iparam[9] = 1

        print('     +> Write Selafin headers')
        if not only_2d:
            self.slf3d.fole = {}
            self.slf3d.fole.update({'hook': open('t3d_' + root_name, 'wb')})
            self.slf3d.fole.update({'name': 't3d_' + root_name})
            self.slf3d.fole.update({'endian': ">"})  # big endian
            self.slf3d.fole.update({'float': ('f', 4)})  # single precision
            self.slf3d.append_header_slf()
        self.slf2d.fole = {}
        self.slf2d.fole.update({'hook': open('t2d_' + root_name, 'wb')})
        self.slf2d.fole.update({'name': 't2d_' + root_name})
        self.slf2d.fole.update({'endian': ">"})  # big endian
        self.slf2d.fole.update({'float': ('f', 4)})  # single precision
        self.slf2d.append_header_slf()
        # ~~~~ Time loop(s) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        var3d = np.zeros(self.slf3d.npoin3, dtype=np.float)
        var2d = np.zeros(self.slf2d.npoin3, dtype=np.float)
        print('     +> Write Selafin cores')
        ibar = 0
        pbar = ProgressBar(maxval=10 * nbar).start()
        for exp in self.experiments[::-1]:
            hycomdata = exp[0]
            i_1 = min(exp[2])
            i_2 = max(exp[2]) + 1
            for itime in range(i_1, i_2):
                # ~~> time stamp
                pbar.write('        x ' + str(exp[3][itime - i_1]),
                           10 * ibar + 0)
                pbar.update(10 * ibar + 0)
                if not only_2d:
                    self.slf3d.append_core_time_slf(ibar)
                self.slf2d.append_core_time_slf(ibar)

                # ~~> HYCOM variable extraction
                #     ( 1L:times, 33L:layers, yyL:ny1d, xxL:nx1d )

                # ~~> ELEVATION
                success = False
                while not success:
                    try:
                        success = True
                        pbar.write('             - ssh', 10 * ibar + 1)
                        data = hycomdata['ssh']['ssh']
                        v2d = np.swapaxes(
                            data.data[itime, ilat[0]:ilat[1],
                                      ilon[0]:ilon[1]][0], 0, 1).ravel()
                    except Exception:
                        success = False
                        pbar.write(
                            '    ... re-attempting because I failed ...',
                            10 * ibar)
                var2d = np.where(v2d < 10000, v2d, 0.0)
                self.slf2d.append_core_vars_slf([var2d])
                if not only_2d:
                    var3d = - np.tile(self.zplan, self.slf3d.npoin2)\
                                .reshape(self.slf3d.npoin2, self.slf3d.nplan)\
                                .itime.ravel()
                    var3d[self.slf3d.npoin3 - self.slf3d.npoin2:] = var2d
                    self.slf3d.append_core_vars_slf([var3d])
                pbar.update(10 * ibar + 1)

                # ~~> SALINITY
                success = False
                while not success:
                    try:
                        success = True
                        pbar.write('             - surface_salinity_trend',
                                   10 * ibar + 2)
                        data = hycomdata['surface_salinity_trend']\
                                             ['surface_salinity_trend']
                        v2d = np.swapaxes(\
                                  data.data[itime, ilat[0]:ilat[1],
                                            ilon[0]:ilon[1]][0],
                                  0, 1).ravel()
                    except Exception:
                        success = False
                        pbar.write(
                            '    ... re-attempting because I failed ...',
                            10 * ibar)
                var2d = np.where(v2d < 10000, v2d, 0.0)
                self.slf2d.append_core_vars_slf([var2d])
                pbar.update(10 * ibar + 2)
                if not only_2d:
                    success = False
                    while not success:
                        try:
                            success = True
                            pbar.write('             - salinity',
                                       10 * ibar + 3)
                            data = hycomdata['salinity']['salinity']
                            var = np.swapaxes(\
                                    data.data[itime, 0:self.slf3d.nplan,
                                              ilat[0]:ilat[1],
                                              ilon[0]:ilon[1]][0],
                                    1, 2)
                        except Exception:
                            success = False
                            pbar.write(\
                                  '    ... re-attempting because I failed ...',
                                  10*ibar)
                    v3d = var[::-1].ravel()
                    var3d = np.where(v3d < 10000, v3d, 0.0)
                    self.slf3d.append_core_vars_slf([var3d])
                pbar.update(10 * ibar + 3)

                # ~~> TEMPERATURE
                success = False
                while not success:
                    try:
                        success = True
                        pbar.write('             - surface_temperature_trend',
                                   10 * ibar + 4)
                        data = hycomdata['surface_temperature_trend']\
                                        ['surface_temperature_trend']
                        v2d = np.swapaxes(\
                                  data.data[itime, ilat[0]:ilat[1],
                                            ilon[0]:ilon[1]][0],
                                  0, 1).ravel()
                    except Exception:
                        success = False
                        pbar.write(
                            '    ... re-attempting because I failed ...',
                            10 * ibar)
                var2d = np.where(v2d < 10000, v2d, 0.0)
                self.slf2d.append_core_vars_slf([var2d])
                pbar.update(10 * ibar + 4)
                if not only_2d:
                    success = False
                    while not success:
                        try:
                            success = True
                            pbar.write('             - temperature',
                                       10 * ibar + 5)
                            data = hycomdata['temperature']['temperature']
                            var = np.swapaxes(\
                                      data.data[itime, 0:self.slf3d.nplan,
                                                ilat[0]:ilat[1],
                                                ilon[0]:ilon[1]][0],
                                      1, 2)
                        except Exception:
                            success = False
                            pbar.write(\
                                 '    ... re-attempting because I failed ...',
                                 10*ibar)
                    v3d = var[::-1].ravel()
                    var3d = np.where(v3d < 10000, v3d, 0.0)
                    self.slf3d.append_core_vars_slf([var3d])
                pbar.update(10 * ibar + 5)

                # ~~> VELOCITY U
                success = False
                while not success:
                    try:
                        success = True
                        pbar.write('             - u-velocity', 10 * ibar + 6)
                        data = hycomdata['u']['u']
                        if only_2d:
                            var = np.swapaxes(
                                data.data[itime, 0:1, ilat[0]:ilat[1],
                                          ilon[0]:ilon[1]][0], 1, 2)
                        else:
                            var = np.swapaxes(
                                data.data[itime, 0:self.slf3d.nplan,
                                          ilat[0]:ilat[1], ilon[0]:ilon[1]][0],
                                1, 2)
                    except Exception:
                        success = False
                        pbar.write(
                            '    ... re-attempting because I failed ...',
                            10 * ibar)
                v2d = var[0].ravel()
                var2d = np.where(v2d < 10000, v2d, 0.0)
                self.slf2d.append_core_vars_slf([var2d])
                if not only_2d:
                    v3d = var[::-1].ravel()
                    var3d = np.where(v3d < 10000, v3d, 0.0)
                    self.slf3d.append_core_vars_slf([var3d])
                pbar.update(10 * ibar + 6)

                # ~~> VELOCITY V
                success = False
                while not success:
                    try:
                        success = True
                        pbar.write('             - v-velocity', 10 * ibar + 7)
                        data = hycomdata['v']['v']
                        if only_2d:
                            var = np.swapaxes(
                                data.data[itime, 0:1, ilat[0]:ilat[1],
                                          ilon[0]:ilon[1]][0], 1, 2)
                        else:
                            var = np.swapaxes(
                                data.data[itime, 0:self.slf3d.nplan,
                                          ilat[0]:ilat[1], ilon[0]:ilon[1]][0],
                                1, 2)
                    except Exception:
                        success = False
                        pbar.write(
                            '    ... re-attempting because I failed ...',
                            10 * ibar)
                v2d = var[0].ravel()
                var2d = np.where(v2d < 10000, v2d, 0.0)
                self.slf2d.append_core_vars_slf([var2d])
                if not only_2d:
                    v3d = var[::-1].ravel()
                    var3d = np.where(v3d < 10000, v3d, 0.0)
                    self.slf3d.append_core_vars_slf([var3d])
                pbar.update(10 * ibar + 7)

                # ~~> VELOCITY W
                if not only_2d:
                    var3d = 0. * var3d
                    self.slf3d.append_core_vars_slf([var3d])

                # ~~> EMP ???
                success = False
                while not success:
                    try:
                        success = True
                        pbar.write('             - emp', 10 * ibar + 8)
                        data = hycomdata['emp']['emp']
                        v2d = np.swapaxes(
                            data.data[itime, ilat[0]:ilat[1],
                                      ilon[0]:ilon[1]][0], 0, 1).ravel()
                    except Exception:
                        success = False
                        pbar.write(
                            '    ... re-attempting because I failed ...',
                            10 * ibar)
                var2d = np.where(v2d < 10000, v2d, 0.0)
                self.slf2d.append_core_vars_slf([var2d])
                pbar.update(10 * ibar + 8)

                # ~~> TEMPERATURE
                success = False
                while not success:
                    try:
                        success = True
                        pbar.write('             - qtot', 10 * ibar + 9)
                        data = hycomdata['qtot']['qtot']
                        v2d = np.swapaxes(
                            data.data[itime, ilat[0]:ilat[1],
                                      ilon[0]:ilon[1]][0], 0, 1).ravel()
                    except Exception:
                        success = False
                        pbar.write(
                            '    ... re-attempting because I failed ...',
                            10 * ibar)
                var2d = np.where(v2d < 10000, v2d, 0.0)
                self.slf2d.append_core_vars_slf([var2d])
                pbar.update(10 * ibar + 9)

                ibar += 1

        pbar.finish()
        if not only_2d:
            self.slf3d.fole['hook'].close()
        self.slf2d.fole['hook'].close()

    def __del__(self):
        pass
Beispiel #5
0
class Ecmwf(object):
    """
    Ecmwf class
    """
    def __init__(self, dates, request):

        # ~> Initialisation
        self.moddates = dates
        self.request = request
        # ~~> inheritence
        self.slf2d = Selafin('')
        self.slf2d.title = ''
        self.slf2d.fole = {}
        self.connection = None
        self.ecmwfdata = None
        self.nx1d = None
        self.ny1d = None
        self.maskx = None
        self.masky = None
        self.nb_freq = None
        self.nb_direct = None
        self.freq = None
        self.dirc = None
        self.typ = None

    def connect_to_ecmwf(self, dataset):

        status = ''
        # ~> Establish connection
        self.connection = Connection(CONFIG['email'],
                                     CONFIG['key'],
                                     quiet=True,
                                     verbose=False)
        # ~> Verify connection
        user = self.connection.call("%s/%s" % (CONFIG['url'], "who-am-i"))
        print('   ~> access through username: %s\n' %
              (user["full_name"] or "user '%s'" % user["uid"], ))
        # ~> Request dataset
        self.connection.submit("%s/%s/requests" % (CONFIG['url'], dataset),
                               self.request)
        status = self.connection.status
        print('   ~> request has been ' + status)
        # ~> Wait for remote processing
        while not self.connection.ready():
            if status != self.connection.status:
                status = self.connection.status
                print('   ~> request remains ' + status + '...')
            self.connection.wait()
        # ~> Request completed
        print('   ~> request is now ' + self.connection.status)
        self.connection.cleanup()

    def download_ecmwf(self):

        result = self.connection.result()
        file_name = self.request.get("target")

        # ~> tries connecting 3 times before stopping
        tries = 0
        while True:

            # ~> downloading file by blocks
            http = urlopen(result["href"])
            f = open(file_name, "wb")
            ibar = 0
            pbar = ProgressBar(maxval=result["size"]).start()
            while True:
                chunk = http.read(1024 * 1024)
                if not chunk:
                    break
                f.write(chunk)
                ibar += len(chunk)
                pbar.update(ibar)
            f.flush()
            f.close()
            pbar.finish()
            # ~> have I got everything ?
            if ibar == result["size"]:
                break
            if tries == 3:
                raise TelemacException("    ... exhausted the number "
                                       "of download trials.\nYou may wish "
                                       "to attempt this again later.")
            print("    ... trying to download the data once more ...")
            tries += 1

    def open_ecmwf(self):

        self.ecmwfdata = netcdf.netcdf_file(self.request.get("target"), 'r')

        # ~~~~ Time records ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        ats = self.ecmwfdata.variables['time'][:] - 70 * 365.25 * 24
        at0 = datetime.fromtimestamp(ats[0] * 3600.)
        self.slf2d.datetime = [d for d in at0.timetuple()[0:6]]
        # time record in hours
        self.slf2d.tags = {'times': 3600 * (ats - ats[0])}

    def close_ecmwf(self):
        self.slf2d.fole['hook'].close()

    def set_geometry(self):

        # ~~> 2D grid
        print('   +> set the mesh and connectivity')
        x = self.ecmwfdata.variables['longitude'][:]
        self.nx1d = len(x)
        y = self.ecmwfdata.variables['latitude'][:]
        self.ny1d = len(y)
        # ~~> TODO: a more complicated check should be done here
        #           with a mix of entries
        y_1, x_1, y_2, x_2 = self.request['area'].split('/')

        if float(x_1) < 0.:
            x_1 = float(x_1) + 360.0
        if float(x_2) < 0.:
            x_2 = float(x_2) + 360.0
        self.maskx = np.logical_and(float(x_1) <= x[0], x[0] <= float(x_2))
        if not np.any(self.maskx):
            raise TelemacException(
                '... your spatial range seems out of bound:\n       '
                'you asked for [ {}-{}], while x is:\n       {}'
                '\n\n'.format(x_1, x_2, repr(x)))

        self.masky = np.logical_and(float(y_1) <= y.T[0], y.T[0] <= float(y_2))
        if not np.any(self.masky):
            raise TelemacException(
                '... your spatial range seems out of bound:\n       '
                'you asked for [ {}-{}], while x is:\n       {}'
                '\n\n'.format(y_1, y_2, repr(y)))

        self.slf2d.meshx = np.tile(x, self.ny1d)\
            .reshape(self.ny1d, self.nx1d).T.ravel()
        self.slf2d.meshy = np.tile(y, self.nx1d)

        self.slf2d.nplan = 1
        self.slf2d.ndp2 = 3
        self.slf2d.ndp3 = self.slf2d.ndp2
        self.slf2d.npoin2 = self.nx1d * self.ny1d
        self.slf2d.npoin3 = self.slf2d.npoin2
        self.slf2d.nelem2 = 2 * (self.nx1d - 1) * (self.ny1d - 1)
        self.slf2d.nelem3 = self.slf2d.nelem2
        # ~~> lat,lon correction
        for i in range(self.slf2d.npoin2):
            if self.slf2d.meshx[i] > 180:
                self.slf2d.meshx[i] = self.slf2d.meshx[i] - 360.0
        # for i in range(2172,self.ny1d):
        #   self.slf2d.meshy[i] = 47.0 + ( i-2172 )/18.0

        # ~~> Connectivity
        ielem = 0
        pbar = ProgressBar(maxval=self.slf2d.nelem3).start()
        self.slf2d.ikle3 = np.zeros((self.slf2d.nelem3, self.slf2d.ndp3),
                                    dtype=np.int)
        for i in range(1, self.nx1d):
            for j in range(1, self.ny1d):
                ipoin = (i - 1) * self.ny1d + j - 1
                # ~~> first triangle
                self.slf2d.ikle3[ielem][0] = ipoin
                self.slf2d.ikle3[ielem][1] = ipoin + self.ny1d
                self.slf2d.ikle3[ielem][2] = ipoin + 1
                ielem = ielem + 1
                pbar.update(ielem)
                # ~~> second triangle
                self.slf2d.ikle3[ielem][0] = ipoin + self.ny1d
                self.slf2d.ikle3[ielem][1] = ipoin + self.ny1d + 1
                self.slf2d.ikle3[ielem][2] = ipoin + 1
                ielem = ielem + 1
                pbar.update(ielem)
        pbar.finish()

        # ~~> Boundaries
        pbar = ProgressBar(maxval=self.nx1d + self.ny1d).start()
        self.slf2d.ipob3 = np.zeros(self.slf2d.npoin3, dtype=np.int)
        # ~~> along the x-axis (lon)
        for i in range(self.nx1d):
            ipoin = i * self.ny1d
            self.slf2d.ipob3[ipoin] = i + 1
            ipoin = i * self.ny1d - 1
            self.slf2d.ipob3[ipoin] = 2 * self.nx1d + (self.ny1d - 2) - i
            pbar.update(i)
        # ~~> along the y-axis (alt)
        for i in range(1, self.ny1d):
            ipoin = i
            self.slf2d.ipob3[ipoin] = 2 * self.nx1d + 2 * (self.ny1d -
                                                           2) - i + 1
            ipoin = self.ny1d * (self.nx1d - 1) + i
            self.slf2d.ipob3[ipoin] = self.nx1d + i
            pbar.update(i + self.nx1d)
        pbar.finish()

        # ~~> Boundary points
        self.slf2d.iparam = [
            0, 0, 0, 0, 0, 0, 0, 2 * self.nx1d + 2 * (self.ny1d - 2), 0, 1
        ]

    def put_geometry(self, file_name):

        print('   +> writing up the geometry file')

        self.slf2d.fole = {}
        self.slf2d.fole.update({'hook': open(file_name, 'wb')})
        self.slf2d.fole.update({'name': file_name})
        self.slf2d.fole.update({'endian': ">"})  # big endian
        self.slf2d.fole.update({'float': ('f', 4)})  # single precision

        self.slf2d.varnames = ['RANGE          ']
        self.slf2d.varunits = ['UI             ']
        self.slf2d.nbv1 = len(self.slf2d.varnames)
        self.slf2d.nvar = self.slf2d.nbv1
        self.slf2d.varindex = range(self.slf2d.nvar)

        print('       - Write Selafin geometry')
        self.slf2d.append_header_slf()

        print('       - Write Selafin core')
        varof = self.ecmwfdata.variables['d2fd'].add_offset
        varsf = self.ecmwfdata.variables['d2fd'].scale_factor
        var2d = np.zeros((self.nx1d, self.ny1d), dtype=np.float)
        ibar = 0
        pbar = ProgressBar(maxval=len(self.slf2d.tags['times'])).start()
        for itime in range(len(self.slf2d.tags['times'])):
            self.slf2d.append_core_time_slf(itime)
            var = self.ecmwfdata.variables['d2fd'][itime]
            z = 10**(varsf * np.swapaxes(np.swapaxes(var, 1, 3), 0, 2) + varof)
            for j in range(self.ny1d):
                for i in range(self.nx1d):
                    var2d[i, j] = max(z[j][i].ravel())
            self.slf2d.append_core_vars_slf([var2d.ravel()])
            ibar += 1
            pbar.update(ibar)
        pbar.finish()

        self.slf2d.fole['hook'].close()

    def set_spectral(self):

        print('   +> reseting the header of the spectral file')

        print('      - read the spectra definition')
        self.nb_freq = len(self.ecmwfdata.variables['frequency'][:])
        self.nb_direct = len(self.ecmwfdata.variables['direction'][:])
        self.freq = [
            0.035, 0.038, 0.042, 0.046, 0.051, 0.056, 0.061, 0.067, 0.074,
            0.081, 0.09, 0.098, 0.108, 0.119, 0.131, 0.144, 0.159, 0.174,
            0.192, 0.211, 0.232, 0.255, 0.281, 0.309, 0.34, 0.374, 0.411,
            0.453, 0.498, 0.548
        ]
        #  /!? only for TOMAWC to work
        self.dirc = 7.5 + 15. * np.arange(self.nb_direct) - 7.5

        # ~~> sizes (spectral numbers)
        self.slf2d.nplan = 1
        self.slf2d.ndp2 = 4
        self.slf2d.ndp3 = self.slf2d.ndp2
        self.slf2d.npoin2 = self.nb_direct * self.nb_freq
        self.slf2d.npoin3 = self.slf2d.npoin2
        self.slf2d.nelem2 = self.nb_direct * (self.nb_freq - 1)
        self.slf2d.nelem3 = self.slf2d.nelem2
        self.slf2d.nptfr = 2 * self.nb_direct
        self.slf2d.iparam = [0, 0, 0, 0, 0, 0, 0, 2 * self.nb_direct, 0, 1]

        # ~~> 2D grid (spectral grid) - TODO: use numpy here !
        self.slf2d.meshx = np.zeros(self.slf2d.npoin2, dtype=np.float)
        self.slf2d.meshy = np.zeros(self.slf2d.npoin2, dtype=np.float)
        print('      - set the mesh')
        ipoin = 0
        pbar = ProgressBar(maxval=self.slf2d.npoin2).start()
        for j_f in range(self.nb_freq):
            for j_d in range(self.nb_direct):
                self.slf2d.meshx[j_d+self.nb_direct*j_f] = \
                          self.freq[j_f]*math.sin(math.pi*self.dirc[j_d]/180.)
                self.slf2d.meshy[j_d+self.nb_direct*j_f] = \
                    self.freq[j_f]*math.cos(math.pi*self.dirc[j_d]/180.)
                ipoin += 1
                pbar.update(ipoin)
        pbar.finish()

        # ~~> Connectivity - TODO: use numpy here !
        print('      - set the connectivity')
        ielem = 0
        pbar = ProgressBar(maxval=self.slf2d.nelem3).start()
        self.slf2d.ikle3 = np.zeros((self.slf2d.nelem3, self.slf2d.ndp3),
                                    dtype=np.int)
        for j_f in range(self.nb_freq - 1):
            for j_d in range(self.nb_direct):
                self.slf2d.ikle3[ielem][0] = (j_d+1) % self.nb_direct + \
                                                      j_f*self.nb_direct
                ielem += 1
        for ielem in range(self.slf2d.nelem3):
            self.slf2d.ikle3[ielem][1] = ielem
            self.slf2d.ikle3[ielem][2] = ielem + self.nb_direct
            self.slf2d.ikle3[ielem][3] = self.slf2d.ikle3[ielem][0] + \
                self.nb_direct
            pbar.update(ielem)
        pbar.finish()

        # ~~> Boundaries - TODO: use numpy here !
        self.slf2d.ipob3 = np.zeros(self.slf2d.npoin3, dtype=np.int)
        # ~~> along the ?-axis
        for j_d in range(self.nb_direct):
            self.slf2d.ipob3[j_d] = j_d
        for j_d in range(self.nb_direct, 2 * self.nb_direct):
            self.slf2d.ipob3[j_d] = self.nb_direct * (self.nb_freq + 1) - j_d

    def append_header_ecmwf(self):

        self.slf2d.varnames = []
        self.slf2d.varunits = []
        # ~~> variables
        self.slf2d.title = ''
        if self.typ == 'wave':
            self.slf2d.varnames = [
                'WAVE HEIGHT     ', 'WAVE PERIOD     ', 'WAVE DIRECTION  '
            ]
            self.slf2d.varunits = [
                'M               ', 'S               ', 'DEGREES         '
            ]
        elif self.typ == 'spec':
            for i in range(self.nx1d * self.ny1d):
                self.slf2d.varnames.append(
                    ('F PT ' + str(i + 1) + '                ')[:16])
                self.slf2d.varunits.append('UI              ')
            print('    - from ', self.slf2d.varnames[0], ' to ',
                  self.slf2d.varnames[-1])
        else:
            self.slf2d.varnames = [
                'SURFACE PRESSURE', 'WIND VELOCITY U ', 'WIND VELOCITY V ',
                'AIR TEMPERATURE '
            ]
            self.slf2d.varunits = [
                'UI              ', 'M/S             ', 'M/S             ',
                'DEGREES         '
            ]
        self.slf2d.nbv1 = len(self.slf2d.varnames)
        self.slf2d.nvar = self.slf2d.nbv1
        self.slf2d.varindex = range(self.slf2d.nvar)

        self.slf2d.append_header_slf()

    def append_core_time_ecmwf(self, itime):
        self.slf2d.append_core_time_slf(itime)

    def append_core_vars_ecmwf(self, itime):
        # Note: this is how you get to the attributes ...
        # ecmwfdata.variables['sp'].ncattrs()
        # in particular ...
        # ecmwfdata.variables['sp'].units
        # ecmwfdata.variables['sp'].missing_value

        if self.typ == 'wave':
            # ~~> WAVE HEIGHT == 'swh'
            var2d = np.swapaxes(self.ecmwfdata.variables['swh'][itime][:], 0,
                                1).ravel()
            varof = self.ecmwfdata.variables['swh'].add_offset
            varsf = self.ecmwfdata.variables['swh'].scale_factor
            self.slf2d.append_core_vars_slf([varsf * var2d + varof])

            # ~~> SIGNIFICANT WAVE PERIOD == 'mwp'
            var2d = np.swapaxes(self.ecmwfdata.variables['mwp'][itime][:], 0,
                                1).ravel()
            varof = self.ecmwfdata.variables['mwp'].add_offset
            varsf = self.ecmwfdata.variables['mwp'].scale_factor
            self.slf2d.append_core_vars_slf([varsf * var2d + varof])

            # ~~> MEAN WAVE DIRECTION == 'mwd'
            var2d = np.swapaxes(self.ecmwfdata.variables['mwd'][itime][:], 0,
                                1).ravel()
            varof = self.ecmwfdata.variables['mwd'].add_offset
            varsf = self.ecmwfdata.variables['mwd'].scale_factor
            self.slf2d.append_core_vars_slf([varsf * var2d + varof])

        elif self.typ == 'spec':
            varof = self.ecmwfdata.variables['d2fd'].add_offset
            varsf = self.ecmwfdata.variables['d2fd'].scale_factor
            var = self.ecmwfdata.variables['d2fd'][itime]
            z = 10**(varsf * np.swapaxes(np.swapaxes(var, 1, 3), 0, 2) + varof)
            for j in range(self.ny1d):
                for i in range(self.nx1d):
                    self.slf2d.append_core_vars_slf([z[j][i].ravel()])

        else:
            # ~~> SURFACE PRESSURE == 'sp'
            var2d = np.swapaxes(self.ecmwfdata.variables['sp'][itime][:], 0,
                                1).ravel()
            varof = self.ecmwfdata.variables['sp'].add_offset
            varsf = self.ecmwfdata.variables['sp'].scale_factor
            # print( self.ecmwfdata.variables['sp'].units )
            self.slf2d.append_core_vars_slf([varsf * var2d + varof])

            # ~~> WIND VELOCITY U == 'u10'
            var2d = np.swapaxes(self.ecmwfdata.variables['u10'][itime][:], 0,
                                1).ravel()
            varof = self.ecmwfdata.variables['u10'].add_offset
            varsf = self.ecmwfdata.variables['u10'].scale_factor
            # print( self.ecmwfdata.variables['u10'].units )
            self.slf2d.append_core_vars_slf([varsf * var2d + varof])

            # ~~> WIND VELOCITY V == 'v10'
            var2d = np.swapaxes(self.ecmwfdata.variables['v10'][itime][:], 0,
                                1).ravel()
            varof = self.ecmwfdata.variables['v10'].add_offset
            varsf = self.ecmwfdata.variables['v10'].scale_factor
            # print( self.ecmwfdata.variables['v10'].units )
            self.slf2d.append_core_vars_slf([varsf * var2d + varof])

            # ~~> AIR TEMPERATURE == 't2m'
            var2d = np.swapaxes(self.ecmwfdata.variables['t2m'][itime][:], 0,
                                1).ravel()
            varof = self.ecmwfdata.variables['t2m'].add_offset
            varsf = self.ecmwfdata.variables['t2m'].scale_factor
            # Kelvin to Celsius
            self.slf2d.append_core_vars_slf([varsf * var2d + varof - 273.15])

    def put_content(self, file_name, stream, showbar=True):

        # ~~> netcdf reader
        self.typ = stream

        if self.typ == 'spec':
            # ~~> new Selafin writer
            self.slf2d.fole = {}
            self.slf2d.fole.update({'hook': open(file_name, 'wb')})
            self.slf2d.fole.update({'name': file_name})
            self.slf2d.fole.update({'endian': ">"})  # big endian
            self.slf2d.fole.update({'float': ('f', 4)})  # single precision

            print('     +> Write Selafin header')
            self.append_header_ecmwf()

            print('     +> Write Selafin core')
            ibar = 0
            if showbar:
                pbar = ProgressBar(maxval=len(self.slf2d.tags['times']))\
                    .start()
            for itime in range(len(self.slf2d.tags['times'])):
                self.append_core_time_ecmwf(itime)
                self.append_core_vars_ecmwf(ibar)
                ibar += 1
                if showbar:
                    pbar.update(ibar)
            self.slf2d.fole['hook'].close()
            if showbar:
                pbar.finish()

        else:
            # ~~> new Selafin writer
            self.slf2d.fole = {}
            self.slf2d.fole.update({'hook': open(file_name, 'wb')})
            self.slf2d.fole.update({'name': file_name})
            self.slf2d.fole.update({'endian': ">"})  # big endian
            self.slf2d.fole.update({'float': ('f', 4)})  # single precision

            print('     +> Write Selafin header')
            self.append_header_ecmwf()

            print('     +> Write Selafin core')
            ibar = 0
            if showbar:
                pbar = ProgressBar(maxval=len(self.slf2d.tags['times']))\
                    .start()
            for itime in range(len(self.slf2d.tags['times'])):
                self.append_core_time_ecmwf(itime)
                self.append_core_vars_ecmwf(ibar)
                ibar += 1
                if showbar:
                    pbar.update(ibar)
            self.slf2d.fole['hook'].close()
            if showbar:
                pbar.finish()
Beispiel #6
0
def generate_atm(geo_file, slf_file, atm_file, ll2utm):

# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
# ~~~~ slf new mesh ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    if not path.exists(geo_file):
        raise TelemacException(\
            '... the provided geo_file does not '
            'seem to exist: {}\n\n'.format(geo_file))

# Find corresponding (x,y) in corresponding new mesh
    print('   +> getting hold of the GEO file')
    geo = Selafin(geo_file)
    if ll2utm is not None:
        zone = int(ll2utm[:-1])
        zone_letter = ll2utm[-1]
        x, y = to_latlon(geo.meshx, geo.meshy, zone, zone_letter)
    else:
        x = geo.meshx
        y = geo.meshy
    xys = np.vstack((x, y)).T

# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
# ~~~~ slf existing res ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    if not path.exists(slf_file):
        raise TelemacException(\
                '... the provided slf_file does not seem to exist: '
                '{}\n\n'.format(slf_file))
    slf = Selafin(slf_file)
    slf.set_kd_tree()
    slf.set_mpl_tri()

    print('   +> support extraction')
    # Extract triangles and weights in 2D
    support2d = []
    ibar = 0
    pbar = ProgressBar(maxval=len(xys)).start()
    for xyi in xys:
        support2d.append(xys_locate_mesh(xyi, slf.ikle2, slf.meshx, slf.meshy,
                                         slf.tree, slf.neighbours))
        ibar += 1
        pbar.update(ibar)
    pbar.finish()
    # Extract support in 3D
    support3d = list(zip(support2d, len(xys)*[range(slf.nplan)]))

# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
# ~~~~ writes ATM header ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    atm = Selafin('')
    atm.fole = {}
    atm.fole.update({'hook':open(atm_file, 'wb')})
    atm.fole.update({'name':atm_file})
    atm.fole.update({'endian':">"})     # big endian
    atm.fole.update({'float':('f', 4)})  # single precision

    # Meta data and variable names
    atm.title = ''
    atm.varnames = []
    atm.varunits = []
    if 'WIND VELOCITY U ' in slf.varnames:
        atm.varnames.append('WIND VELOCITY U ')
        atm.varunits.append('M/S             ')
    if 'WIND VELOCITY V ' in slf.varnames:
        atm.varnames.append('WIND VELOCITY V ')
        atm.varunits.append('M/S             ')
    if 'SURFACE PRESSURE' in slf.varnames:
        atm.varnames.append('SURFACE PRESSURE')
        atm.varunits.append('UI              ')
    if 'AIR TEMPERATURE ' in slf.varnames:
        atm.varnames.append('AIR TEMPERATURE ')
        atm.varunits.append('DEGREES         ')
    if not atm.varnames:
        raise TelemacException(
            'There are no meteorological variables to convert!')
    atm.nbv1 = len(atm.varnames)
    atm.nvar = atm.nbv1
    atm.varindex = range(atm.nvar)

    # Sizes and mesh connectivity
    atm.nplan = slf.nplan          # it should be 2d but why the heack not ...
    atm.ndp2 = slf.ndp2
    atm.ndp3 = slf.ndp3
    atm.npoin2 = geo.npoin2
    atm.npoin3 = geo.npoin2*atm.nplan
    atm.nelem2 = geo.nelem2

    print('   +> setting connectivity')
    if atm.nplan > 1:
        atm.nelem3 = geo.nelem2*(atm.nplan-1)
        atm.ikle2 = geo.ikle2
        atm.ikle3 = \
            np.repeat(geo.npoin2*np.arange(atm.nplan-1),
                      geo.nelem2*atm.ndp3)\
              .reshape((geo.nelem2*(atm.nplan-1), atm.ndp3)) + \
            np.tile(np.add(np.tile(geo.ikle2, 2),
                           np.repeat(geo.npoin2*np.arange(2),
                                     geo.ndp2)),
                    (atm.nplan-1, 1))
        atm.ipob2 = geo.ipob2
        atm.ipob3 = np.ravel(np.add(np.repeat(geo.ipob2, atm.nplan)\
                                      .reshape((geo.npoin2, atm.nplan)),
                                    geo.npoin2*np.arange(atm.nplan)).T)
    else:
        atm.nelem3 = geo.nelem2
        atm.ikle2 = geo.ikle2
        atm.ikle3 = geo.ikle3
        atm.ipob2 = geo.ipob2
        atm.ipob3 = geo.ipob3
    atm.iparam = [0, 0, 0, 0, 0, 0, 0, np.count_nonzero(atm.ipob2), 0, 1]

    # Mesh coordinates
    atm.meshx = geo.meshx
    atm.meshy = geo.meshy

    print('   +> writing header')
    # Write header
    atm.datetime = slf.datetime
    atm.append_header_slf()

# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
# ~~~~ writes ATM core ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    print('   +> setting variables')
    # TIME and DATE extraction
    atm.tags['times'] = slf.tags['times']
    # VARIABLE extraction
    vrs = subset_variables_slf(';'.join([var+': ' for var in atm.varnames]),
                               slf.varnames)

    # Read / Write data, one time step at a time to support large files
    pbar = ProgressBar(maxval=len(slf.tags['times'])).start()
    for time in range(len(slf.tags['times'])):

        data = get_value_history_slf(slf.file, slf.tags, [time], support3d,
                                     slf.nvar, slf.npoin3, slf.nplan, vrs)
        # special cases ?
        atm.append_core_time_slf(time)
        atm.append_core_vars_slf(np.reshape(np.transpose(\
                  np.reshape(np.ravel(data),
                             (atm.nvar, atm.npoin2, atm.nplan)),
                  (0, 2, 1)),
                                            (atm.nvar, atm.npoin3)))
        pbar.update(time)
    pbar.finish()

    # Close atm_file
    atm.fole['hook'].close()
def main():
    """ Main function of convertToSPE """

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ Dependencies towards other modules ~~~~~~~~~~~~~~~~~~~~~~~~~~
    from argparse import ArgumentParser, RawDescriptionHelpFormatter
    from data_manip.formats.selafin import Selafin
    from data_manip.formats.conlim import Conlim
    from pretel.meshes import xys_locate_mesh

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ Reads config file ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    print('\n\nInterpreting command line options\n' + '~' * 72 + '\n')
    parser = ArgumentParser(\
        formatter_class=RawDescriptionHelpFormatter,
        description=('''\n
A script to map spectral outter model results, stored as SELAFIN files, onto the
    spatially and time varying boundary of a spatially contained SELAFIN file
    of your choosing (your MESH).
        '''),
        usage=' (--help for help)\n---------\n       => '\
                ' %(prog)s  open-bound.cli open-bound.slf in-outer-geo.slf '\
                'in-outer-spec.slf out-bound.slf \n---------')
    parser.add_argument(\
        "--ll2utm", dest="ll2utm", default=None,
        help="assume outer file is in lat-long and open-bound file in UTM")
    parser.add_argument("args", default='', nargs=5)
    options = parser.parse_args()

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ cli+slf new mesh ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    cli_file = options.args[0]
    if not path.exists(cli_file):
        raise TelemacException(\
                '... the provided cli_file does not seem '
                'to exist: {}\n\n'.format(cli_file))
    geo_file = options.args[1]
    if not path.exists(geo_file):
        raise TelemacException(\
                '... the provided geo_file does not seem to exist: '
                '{}\n\n'.format(geo_file))

    # Read the new CLI file to get boundary node numbers
    print('   +> getting hold of the CONLIM file and of its liquid boundaries')
    cli = Conlim(cli_file)
    # Keeping only open boundary nodes
    bor = np.extract(cli.bor['lih'] != 2, cli.bor['n'])

    # Find corresponding (x,y) in corresponding new mesh
    print('   +> getting hold of the GEO file and of its bathymetry')
    geo = Selafin(geo_file)
    if options.ll2utm != None:
        zone = int(options.ll2utm)
        x, y = to_lat_long(geo.meshx[bor - 1], geo.meshy[bor - 1], zone)
    else:
        x = geo.meshx[bor - 1]
        y = geo.meshy[bor - 1]
    xys = np.vstack((x, y)).T

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ slf+spe existing res ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    slf_file = options.args[2]
    if not path.exists(slf_file):
        raise TelemacException(\
                '... the provided slf_file does not seem to exist: '
                '{}\n\n'.format(slf_file))
    slf = Selafin(slf_file)
    slf.set_kd_tree()
    slf.set_mpl_tri()
    spe_file = options.args[3]
    if not path.exists(spe_file):
        raise TelemacException(\
                '... the provided slf_file does not seem to exist: '
                '{}\n\n'.format(spe_file))
    spe = Selafin(spe_file)

    print('   +> support extraction')
    # Extract triangles and weigths in 2D
    support2d = []
    ibar = 0
    pbar = ProgressBar(maxval=len(xys)).start()
    for xyi in xys:
        support2d.append(
            xys_locate_mesh(xyi, slf.ikle2, slf.meshx, slf.meshy, slf.tree,
                            slf.neighbours))
        ibar += 1
        pbar.update(ibar)
    pbar.finish()

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ writes BND header ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    bnd_file = options.args[4]
    bnd = Selafin('')
    bnd.fole = {}
    bnd.fole.update({'hook': open(bnd_file, 'wb')})
    bnd.fole.update({'name': bnd_file})
    bnd.fole.update({'endian': ">"})  # big endian
    bnd.fole.update({'float': ('f', 4)})  # single precision

    # Meta data and variable names
    bnd.title = spe.title
    # spectrum for new locations / nodes
    for i in range(len(bor)):
        bnd.varnames.append(('F'+('00'+str(i))[-2:]+' PT2D'+('000000'+\
                                  str(bor[i]))[-6:]+'                ')[:16])
        bnd.varunits.append('UI              ')
    bnd.nbv1 = len(bnd.varnames)
    bnd.nvar = bnd.nbv1
    bnd.varindex = range(bnd.nvar)

    # sizes and mesh connectivity / spectrum
    bnd.nplan = spe.nplan
    bnd.ndp2 = spe.ndp2
    bnd.ndp3 = bnd.ndp2
    bnd.npoin2 = spe.npoin2
    bnd.npoin3 = spe.npoin3
    bnd.iparam = spe.iparam
    bnd.ipob2 = spe.ipob2
    bnd.ikle2 = spe.ikle2
    # Last few numbers
    bnd.nelem2 = len(bnd.ikle2)
    bnd.nelem3 = bnd.nelem2
    bnd.ipob3 = bnd.ipob2
    bnd.ikle3 = bnd.ikle2
    # Mesh coordinates
    bnd.meshx = spe.meshx
    bnd.meshy = spe.meshy

    print('   +> writing header')
    # Write header
    bnd.append_header_slf()

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ writes BND core ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    print('   +> setting variables')
    # TIME and DATE extraction
    bnd.datetime = spe.datetime
    bnd.tags['times'] = spe.tags['times']

    # pointer initialisation
    f = spe.file['hook']
    endian = spe.file['endian']
    ftype, fsize = spe.file['float']

    # Identofy variables (required for support2d geo-locations)
    specloc = []
    for n, _ in support2d:
        specloc.extend(n)
    vars_indexes = np.unique(specloc)
    if fsize == 4:
        z = np.zeros((len(vars_indexes), spe.npoin2), dtype=np.float32)
        data = np.zeros(spe.npoin2, dtype=np.float32)
    else:
        z = np.zeros((len(vars_indexes), spe.npoin2), dtype=np.float64)
        data = np.zeros(spe.npoin2, dtype=np.float64)

    # Read / Write data, one time step at a time to support large files
    print('   +> reading / writing variables')
    pbar = ProgressBar(maxval=len(spe.tags['times'])).start()
    for itime in range(len(spe.tags['times'])):
        f.seek(
            spe.tags['cores'][itime])  # [itime] is the frame to be extracted
        f.seek(4 + fsize + 4, 1)  # the file pointer is initialised
        bnd.append_core_time_slf(itime)

        # Extract relevant spectrum, where
        #  vars_indexes only contains the relevant nodes
        #  jvar varies from 0 to len(vars_indexes)
        jvar = 0
        for ivar in range(spe.nvar):
            # the file pointer advances through all records to keep on track
            f.seek(4, 1)
            if ivar in vars_indexes:
                z[jvar, :] = unpack(endian+str(spe.npoin2)+\
                                         ftype, f.read(fsize*spe.npoin2))
                jvar += 1
            else:
                # the file pointer advances through all records to keep on track
                f.seek(fsize * spe.npoin2, 1)
            f.seek(4, 1)

        # linear interpolation
        ivar = 0
        for b_n, l_n in support2d:
            data[:] = 0.
            for inod in range(len(b_n)):
                jvar = np.where(vars_indexes == b_n[inod])[0][0]
                data += l_n[inod] * z[jvar, :]
            bnd.append_core_vars_slf([data])
            ivar += 1

        pbar.update(itime)
    pbar.finish()

    # Close bnd_file
    bnd.fole['hook'].close()

    print('   +> writing out the file with coordinate to impose')
    dat = [str(len(bor)) + ' 0']
    for i in np.sort(bor):
        dat.append(str(i) + ' ' + repr(geo.meshx[i-1]) + ' ' + \
                      repr(geo.meshy[i-1]) + ' 0.0')
    put_file_content(bnd_file + '.dat', dat)

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ Jenkins' success message ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    print('\n\nMy work is done\n\n')

    sys.exit(0)
class Parafins(Selafins):
    """
    Class to handle partitionned serafin files
    """

    def __init__(self, file_name, root=None):
        """
        Initialise the class

        @param file_name (string) Name of the global selafin file
        @param root (string) Name of the non partitionned file

        """
        Selafins.__init__(self)
        # ~~> The main slf is based on the header of the GEO file
        self.slf = Selafin(file_name)
        self.file = self.slf.file
        self.fole = self.slf.fole
        if root != None:
            # ~~> Loading the individual headers
            self.add_root(root)
            # ~~> Making sure there are all inter-compatible
            if self.suite and self.merge:
                self.slf.tags = self.slfs[0].tags
                self.slf.nbv1 = self.slfs[0].nbv1
                self.slf.varnames = self.slfs[0].varnames
                self.slf.varunits = self.slfs[0].varunits
                self.slf.nbv2 = self.slfs[0].nbv2
                self.slf.cldnames = self.slfs[0].cldnames
                self.slf.cldunits = self.slfs[0].cldunits
                self.slf.nvar = self.slf.nbv1 + self.slf.nbv2
                self.slf.varindex = range(self.slf.nvar)
            else:
                raise TelemacException(\
                        "... Incompatibilities between files "
                        "for {}".format(path.basename(root)))

            # ~~> Create the corresponding map
            self.map_poin = np.zeros(self.slf.npoin3, dtype=np.int)
            for i, slf in zip(range(len(self.slfs)), self.slfs):
                self.map_poin[slf.ipob3-1] = i

    def alter_endian(self):
        """
        Alter Endianess
        """
        self.slf.alter_endian()

    def alter_float(self):
        """
        Alter float precision
        """
        self.slf.alter_float()

    def add_root(self, root):
        """
        Add a list of partitioned files

        @param root (string) Name of the non partitionned file
        """
        # ~~> list all entries
        diroot = path.dirname(root)
        if path.dirname(root).strip() == '':
            diroot = getcwd()
        root = path.join(diroot, path.basename(root))
        slfnames = glob.glob(root+'?????-?????')
        # ~~> match expression
        if slfnames == []:
            print("... Could not find any sub-files to the root: "+root)
            return []
        npsize = len(slfnames)
        for nptime in range(npsize):
            fle = root+'{0:05d}-{1:05d}'.format(npsize-1, nptime)
            if not fle in slfnames:
                print("... Could not find the following sub-file in "\
                      "the list: "+fle)
                return []
        print('      +> Reading the header from the following partitions:')
        for fle in sorted(slfnames):
            print('         ~> '+path.basename(fle))
            slf = Selafin(fle)
            self.slfs.append(slf)
        return

    def get_palues(self, time):
        """
        get values for a given time step on the global mesh

        @param time (int) Time step

        @return (np.array) Containing value for each variable
        """
        _, fsize = self.file['float']
        if len(self.slfs) > 0:
            if fsize == 4:
                varsor = np.zeros((self.slf.nbv1+self.slf.nbv2,
                                   self.slf.npoin3),
                                  dtype=np.float32)
            else:
                varsor = np.zeros((self.slf.nbv1+self.slf.nbv2,
                                   self.slf.npoin3),
                                  dtype=np.float64)
            for slf in self.slfs:
                varsub = slf.get_values(time)
                for ivar in range(self.slf.nvar):
                    varsor[ivar][slf.ipob3-1] = varsub[ivar]
        else:
            varsor = self.slf.get_values(time)
        return varsor

    def get_series(self, nodes, vars_indexes=None):
        """
        get series (value on a point for all time step) for the global mesh

        @param nodes (list) List of nodes to extract series for
        @param vars_indexes (list) List of variables for which to extract data

        @return (np.array) containing the series
        """
        _, fsize = self.file['float']
        if vars_indexes is None:
            vars_indexes = self.slf.varindex
        if len(self.slfs) > 0:
            if fsize == 4:
                z = np.zeros((len(vars_indexes), len(nodes),
                              len(self.slf.tags['cores'])),
                             dtype=np.float32)
            else:
                z = np.zeros((len(vars_indexes), len(nodes),
                              len(self.slf.tags['cores'])),
                             dtype=np.float64)
            mproc = self.map_poin[nodes]
            print('      +> Extracting time series from the following'\
                  ' partitions:')
            for islf, slf in zip(range(len(self.slfs)), self.slfs):
                if not islf in mproc:
                    continue
                # ~~> Filter the list of nodes according to sub ipobo
                sub_g_nodes = np.compress(mproc == islf,
                                          np.array(list(zip(range(len(nodes)),
                                                            nodes)),
                                                   dtype=[('r', int),
                                                          ('n', int)]))
                # /!\ why does this work in a sorted way ?
                sub_l_nodes = np.searchsorted(np.sort(slf.ipob2),
                                              sub_g_nodes['n']) + 1
                print('         ~> '+str(len(sub_l_nodes))+\
                        ' nodes from partition '+str(islf))
                # ~~> Get the series from individual sub-files
                subz = slf.get_series(sub_l_nodes, vars_indexes)
                # ~~> Reorder according to original list of nodes
                for ivar in range(len(vars_indexes)):
                    z[ivar][sub_g_nodes['r']] = subz[ivar]
            return z
        else:
            return self.slf.get_series(nodes)  # ,vars_indexes not necessary

    # TODO: files also have to have the same header
    def put_content(self, file_name, showbar=True):
        """
        Write global content into file_name

        @param file_name (string) Name of the output file
        @param showbar (boolean) display a showbar (default=True)
        """
        self.slf.fole.update({'hook':open(file_name, 'wb')})
        ibar = 0
        if showbar:
            pbar = ProgressBar(maxval=len(self.slf.tags['times'])).start()
        self.slf.append_header_slf()
        # ~~> Time stepping
        for itime in range(len(self.slf.tags['times'])):
            ibar += 1
            self.slf.append_core_time_slf(itime) # Time stamps
            self.slf.append_core_vars_slf(self.get_palues(itime))
            if showbar:
                pbar.update(ibar)
        self.slf.fole['hook'].close()
        if showbar:
            pbar.finish()

    def cut_content(self, root):
        """
        Apply existing partition to other files

        @param root (string) Name of the global geometry file
        @param showbar (bolean) to display the showbar
        """
        islf = Selafin(root)
        print('      +> Writing the core of the following partitions:')
        #TODO: do this loop in python parallel with a
        # bottle neck at islf.get_values(t)
        for slf in self.slfs:
            sub = deepcopy(slf)
            sub.fole.update({'endian':islf.fole['endian']})
            sub.fole.update({'float':islf.fole['float']})
            _, fsize = islf.fole['float']
            # ~~ Conversion to local islf ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            # you know the geom is 2D
            # keep the ipobo2, X2D, Y2D and ikle2 and replace the rest
            sub.nplan = islf.nplan
            sub.iparam = islf.iparam

            # ~~> matching partitions
            if self.slf.npoin2 == islf.npoin2:
                sub.ndp2 = islf.ndp2
                sub.ndp3 = islf.ndp3
                sub.npoin3 = sub.npoin2*islf.nplan
                if islf.nplan > 1:
                    sub.nelem3 = sub.nelem2*(islf.nplan-1)
                    tmp = np.repeat(sub.ipob2, islf.nplan)\
                            .reshape((sub.npoin2, islf.nplan))
                    sub.ipob3 = np.ravel(np.add(tmp, \
                            sub.npoin2*np.arange(islf.nplan)).t)
                    sub.ikle3 = \
                        np.repeat(sub.npoin2*np.arange(islf.nplan-1),
                                  sub.nelem2*islf.ndp3)\
                          .reshape((sub.nelem2*(islf.nplan-1), islf.ndp3)) + \
                        np.tile(np.add(np.tile(sub.ikle2, 2),
                                       np.repeat(sub.npoin2*np.arange(2),
                                                 sub.ndp2)),
                                (islf.nplan-1, 1))
                else:
                    sub.nelem3 = sub.nelem2
                    sub.ipob3 = sub.ipob2
                    sub.ikle3 = sub.ikle2
                indices = sub.ipob2-1

            # ~~> filtered partitions / non reversable !
            else:
                # Intersection with the local domain (sub of slfs):
                # the intersection could be empty
                intsect = np.in1d(islf.ipob2, np.sort(sub.ipob2))
                # The new compressed ipobo
                sub.ipob2 = np.compress(intsect, islf.ipob2)
                # Those node numbers of the islf you keep ~> converting
                # True/False into indices
                indices = np.arange(len(islf.ipob2), dtype=np.int)[intsect]
                # Set the array that only includes elements of islf.ikle2
                # with at least two nodes in the subdomain
                ikles2_1d = np.in1d(islf.ikle2, np.sort(indices))\
                              .reshape(islf.nelem2, islf.ndp2)
                gkle2 = islf.ikle2[np.where(np.sum(ikles2_1d, axis=1) == \
                                              islf.ndp2)]
                # re-numbering ikle2 as a local connectivity matrix
                knolg = np.unique(np.ravel(gkle2))
                knogl = dict(zip(knolg, range(len(knolg))))
                sub.ikle2 = - np.ones_like(gkle2, dtype=np.int)
                for k in range(len(gkle2)):
                    for k_i in range(islf.ndp2):
                        # /!\ sub.ikle2 has a local numbering,
                        # fit to the boundary elements
                        sub.ikle2[k][k_i] = knogl[gkle2[k][k_i]]
                # Set the remaining integers
                sub.npoin2 = len(sub.ipob2)
                sub.nelem2 = len(sub.ikle2)
                sub.ndp2 = islf.ndp2
                sub.ndp3 = islf.ndp3
                sub.npoin3 = sub.npoin2*islf.nplan
                if islf.nplan > 1:
                    sub.nelem3 = sub.nelem2*(islf.nplan-1)
                    tmp = np.repeat(sub.ipob2, islf.nplan)\
                            .reshape((sub.npoin2, islf.nplan))
                    sub.ipob3 = np.ravel(np.add(tmp,\
                                   sub.npoin2*np.arange(islf.nplan)).T)
                    sub.ikle3 = \
                        np.repeat(sub.npoin2*np.arange(islf.nplan-1),
                                  sub.nelem2*islf.ndp3)\
                          .reshape((sub.nelem2*(islf.nplan-1), islf.ndp3)) + \
                        np.tile(np.add(np.tile(sub.ikle2, 2),
                                       np.repeat(sub.npoin2*np.arange(2),
                                                 sub.ndp2)),
                                (islf.nplan-1, 1))
                else:
                    sub.nelem3 = sub.nelem2
                    sub.ipob3 = sub.ipob2
                    sub.ikle3 = sub.ikle2
                # Set the remaining floats
                sub.meshx = islf.meshx[indices]
                sub.meshy = islf.meshy[indices]

            # ~~ Addition of variables from islf ~~~~~~~~~~~~~~~~~~~~~~
            sub.title = islf.title
            sub.datetime = islf.datetime
            sub.varnames = islf.varnames
            sub.varunits = islf.varunits
            sub.cldnames = islf.cldnames
            sub.cldunits = islf.cldunits
            sub.nbv1 = islf.nbv1
            sub.nbv2 = islf.nbv2
            sub.nvar = sub.nbv1+sub.nbv2
            # ~~ put_content ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            file_name = path.basename(sub.file['name'])\
                           .replace(self.slf.file['name'], root)
            sub.fole.update({'hook':open(file_name, 'wb')})
            sub.append_header_slf()
            print('         ~> '+path.basename(file_name))
            # ~~> Time stepping
            sub.tags['times'] = islf.tags['times']
            if fsize == 4:
                varsors = np.zeros((sub.nvar, sub.npoin3), dtype=np.float32)
            else:
                varsors = np.zeros((sub.nvar, sub.npoin3), dtype=np.float64)
            for itime in range(len(islf.tags['times'])):
                sub.append_core_time_slf(itime) # Time stamps
                for ivar, var in zip(range(sub.nvar), islf.get_values(itime)):
                    for n in range(sub.nplan):
                        varsors[ivar][n*sub.npoin2:(n+1)*sub.npoin2] = \
                              var[n*islf.npoin2:(n+1)*islf.npoin2][indices]
                sub.append_core_vars_slf(varsors)
            sub.fole['hook'].close()
Beispiel #9
0
def tesselate(options):
    """
    Generate a mesh from a polygon
    """
    if not options.freplace:
        if len(options.args) != 2:
            raise TelemacException(\
                    '\nThe code "tessellate" here '
                    'requires one i2s/i3s file and '
                    'one output slf file\n')
        i3s_file = options.args[0]
        out_file = options.args[1]
    else:
        if len(options.args) != 1:
            raise TelemacException(\
                    '\nThe code "tessellate" here '
                    'requires one i2s/i3s file\n')
        i3s_file = options.args[0]
        head, _ = path.splitext(i3s_file)
        out_file = head+'.slf'

    i3s_file = path.realpath(i3s_file)
    if not path.exists(i3s_file):
        raise TelemacException(\
                '\nCould not find '
                'the file named: {}'.format(i3s_file))

    print('\n\nTessellating ' + path.basename(i3s_file) + ' within ' + \
            path.dirname(i3s_file) + '\n'+'~'*72+'\n')
    i2s = InS(i3s_file)
    ikle2, ipob2, meshx, meshy = tessellate_poly(i2s, debug=True)

    print('\n\nWriting down the Selafin file ' + \
            path.basename(out_file) + '\n'+'~'*72+'\n')
    slf = Selafin('')
    slf.title = ''
    slf.nplan = 1
    slf.ndp2 = 3
    slf.ndp3 = 3
    slf.nbv1 = 1
    slf.nvar = 1
    slf.varindex = 1
    slf.varnames = ['BOTTOM          ']
    slf.varunits = ['M               ']
    slf.ikle2 = ikle2
    slf.ikle3 = slf.ikle2
    slf.meshx = meshx
    slf.meshy = meshy
    slf.npoin2 = i2s.npoin
    slf.npoin3 = slf.npoin2
    slf.nelem2 = len(slf.ikle2)/slf.ndp3
    slf.nelem3 = slf.nelem2
    slf.iparam = [0, 0, 0, 0, 0, 0, 1, 0, 0, 0]
    slf.ipob2 = ipob2
    slf.ipob3 = slf.ipob2
    slf.fole = {'hook':open(out_file, 'wb'), 'endian':">",
                'float':('f', 4), 'name':out_file}
    slf.tags['times'] = [1]
    if options.sph2ll != None:
        radius = 6371000.
        long0, lat0 = options.sph2ll.split(":")
        long0 = np.deg2rad(float(long0))
        lat0 = np.deg2rad(float(lat0))
        const = np.tan(lat0/2. + np.pi/4.)
        slf.meshx = np.rad2deg(slf.meshx/radius + long0)
        slf.meshy = np.rad2deg(2.*np.arctan(const*np.exp(slf.meshy/radius)) \
                                      - np.pi/2.)
    if options.ll2sph != None:
        radius = 6371000.
        long0, lat0 = options.ll2sph.split(":")
        long0 = np.deg2rad(float(long0))
        lat0 = np.deg2rad(float(lat0))
        slf.meshx = radius * (np.deg2rad(slf.meshx) - long0)
        slf.meshy = radius * \
                  (np.log(np.tan(np.deg2rad(slf.meshy)/2. + np.pi/4.)) \
                                      - np.log(np.tan(lat0/2. + np.pi/4.)))
    if options.ll2utm != None:
        zone = int(options.ll2utm)
        slf.meshx, slf.meshy, zone = utm.from_lat_long(slf.meshx, slf.meshy,
                                                       zone)
    if options.utm2ll != None:
        zone = int(options.utm2ll)
        slf.meshx, slf.meshy = utm.to_lat_long(slf.meshx, slf.meshy, zone)
    slf.append_header_slf()
    slf.append_core_time_slf(0)
    slf.append_core_vars_slf([np.zeros(slf.npoin2)])
    slf.fole['hook'].close()
Beispiel #10
0
class Grib(object):
    def __init__(self, dataset, request, stream):

        self.request = request
        # ~~> inheritence
        self.slf2d = Selafin('')
        self.slf2d.title = ''
        self.slf2d.fole = {}
        self.dataset = []
        self.variables = []
        self.byrowdown = False
        self.maskx = None
        self.masky = None
        self.nx1d = None
        self.ny1d = None
        self.nb_direct = None
        self.nb_freq = None
        self.freq = None
        self.dirc = None

        print('   +> identifying relevant files, by variables')
        # ~~> filter requested variables
        self.variables = []
        found_dataset = []
        ibar = 0
        pbar = ProgressBar(maxval=len(dataset)).start()
        for data in dataset:
            grbs = pygrib.open(data)
            for grb in grbs:
                if str(grb.indicatorOfParameter) in request['param'].split(
                        '/'):
                    if data not in found_dataset:
                        found_dataset.append(data)
                    if grb.indicatorOfParameter not in self.variables:
                        self.variables.append(grb.indicatorOfParameter)
                    else:
                        break
            grbs.close()
            ibar += 1
            pbar.update(ibar)
        pbar.finish()
        if self.variables == []:
            raise TelemacException(
                '... could not find the requested valiables.\n\n')

        print('   +> sorting out timeline')
        # ~~>  checking consistency of origin of date and time
        for data in found_dataset:
            grbs = pygrib.open(data)
            for grb in grbs:
                at0 = pygrib.julian_to_datetime(grb.julianDay)
                break
            break
        print('      - start date and time', at0)
        self.slf2d.datetime = [d for d in at0.timetuple()[0:6]]
        # ~~>  recording times from origin of date and time
        ats = []
        dts = []
        ibar = 0
        pbar = ProgressBar(maxval=len(found_dataset)).start()
        for data in found_dataset:
            grbs = pygrib.open(data)
            for grb in grbs:
                date = str(grb.validityDate)
                ats.append(datetime(int(date[:4]), int(date[4:6]),
                                    int(date[6:])) + \
                              timedelta(seconds=int(grb.validityTime*36)))
                dts.append((ats[-1] - at0).total_seconds())
                break
            ibar += 1
            pbar.update(ibar)
        pbar.finish()
        print('      - finish date and time', ats[-1])
        # ~~>  checking if the list is sorted
        if not all(ats[i] < ats[i + 1] for i in range(len(ats) - 1)):
            raise TelemacException(\
                    '... your dataset is not sorted. '
                    'Here is the time profile in seconds:\n'
                    '{}\n\n'.format(repr(dts)))
        # ~~> filter requested times
        udates = [datetime(*[int(a) for a in d.split('-')]) \
                      for d in request['date'].split('/to/')]
        self.slf2d.tags = {'times': []}
        udates[1] = udates[1] + timedelta(hours=24.)
        for i in range(len(ats)):
            if udates[0] <= ats[i] and ats[i] <= udates[1]:
                self.slf2d.tags['times'].append(dts[i])
                self.dataset.append(found_dataset[i])
        times = self.slf2d.tags['times']
        print('   +> actual timeline')
        print('      - start date and time  ',
              at0 + timedelta(seconds=times[0]))
        print('      - finish date and time ',
              at0 + timedelta(seconds=times[-1]))

        # ~> Other initialisations
        self.typ = stream

        # ~~> spatial sizes
        print('   +> checking out sizes')
        grbs = pygrib.open(self.dataset[0])
        for grb in grbs:
            self.missing_value = grb.missingValue
            self.scale_values_by = grb.scaleValuesBy
            self.offset = grb.offset
            break
        grbs.close()

    def open_grib(self, file_name):

        self.slf2d.fole.update({'hook': open(file_name, 'wb')})
        self.slf2d.fole.update({'name': file_name})
        self.slf2d.fole.update({'endian': ">"})  # big endian
        self.slf2d.fole.update({'float': ('f', 4)})  # single precision

    def close_grib(self):
        self.slf2d.fole['hook'].close()

    def set_geometry(self):

        # ~~> header
        self.byrowdown = False

        # ~~> 2D grid
        print('   +> set the mesh and connectivity')
        x_1, y_1, x_2, y_2 = self.request['area'].split('/')
        grbs = pygrib.open(self.dataset[0])
        for grb in grbs:
            y, x = grb.latlons()
            self.maskx = np.logical_and(float(x_1) <= x[0], x[0] <= float(x_2))
            l_x = x[0][self.maskx]
            if not np.any(self.maskx):
                self.maskx = np.logical_and(
                    float(x_1) <= x[0] - 360., x[0] - 360. <= float(x_2))
                l_x = x[0][self.maskx] - 360.
            if not np.any(self.maskx):
                raise TelemacException(\
                    '... your spatial range seems out of bound:\n       '
                    'you asked for [ {} - {}], while x is:\n       '
                    '{}\n\n'.format(x_1, x_2, repr(x)))
            self.nx1d = len(l_x)
            self.masky = np.logical_and(
                float(y_1) <= y.T[0], y.T[0] <= float(y_2))
            l_y = y.T[0][self.masky]
            if not np.any(self.masky):
                raise TelemacException(\
                    '... your spatial range seems out of bound:\n       '
                    'you asked for [ {} - {}], while x is:\n       '
                    '{}\n\n'.format(y_1, y_2, repr(y)))
            self.ny1d = len(l_y)
            if self.byrowdown:
                self.slf2d.meshx = np.ravel(np.tile(l_x, self.ny1d)\
                                            .reshape(self.ny1d, self.nx1d))
                self.slf2d.meshy = np.ravel(np.tile(l_y, self.nx1d)\
                                            .reshape(self.nx1d, self.ny1d).T)
            else:
                self.slf2d.meshx = np.ravel(np.tile(l_x, self.ny1d)\
                                            .reshape(self.ny1d, self.nx1d).T)
                self.slf2d.meshy = np.ravel(np.tile(l_y, self.nx1d)\
                                            .reshape(self.nx1d, self.ny1d))
            break
        grbs.close()

        self.slf2d.nplan = 1
        self.slf2d.ndp2 = 3
        self.slf2d.ndp3 = self.slf2d.ndp2
        self.slf2d.npoin2 = self.nx1d * self.ny1d
        self.slf2d.npoin3 = self.slf2d.npoin2
        self.slf2d.nelem2 = 2 * (self.nx1d - 1) * (self.ny1d - 1)
        self.slf2d.nelem3 = self.slf2d.nelem2

        # ~~> Connectivity - numbered by rows
        ielem = 0
        pbar = ProgressBar(maxval=self.slf2d.nelem3).start()
        self.slf2d.ikle3 = np.zeros((self.slf2d.nelem3, self.slf2d.ndp3),
                                    dtype=np.int)
        if self.byrowdown:
            for j in range(1, self.ny1d):
                for i in range(1, self.nx1d):
                    ipoin = (j - 1) * self.nx1d + i - 1
                    # ~~> first triangle
                    self.slf2d.ikle3[ielem][0] = ipoin
                    self.slf2d.ikle3[ielem][1] = ipoin + self.nx1d
                    self.slf2d.ikle3[ielem][2] = ipoin + 1
                    ielem = ielem + 1
                    pbar.update(ielem)
                    # ~~> second triangle
                    self.slf2d.ikle3[ielem][0] = ipoin + self.nx1d
                    self.slf2d.ikle3[ielem][1] = ipoin + self.nx1d + 1
                    self.slf2d.ikle3[ielem][2] = ipoin + 1
                    ielem = ielem + 1
                    pbar.update(ielem)
        else:
            for j in range(1, self.ny1d):
                for i in range(1, self.nx1d):
                    ipoin = j - 1 + (i - 1) * self.ny1d
                    # ~~> first triangle
                    self.slf2d.ikle3[ielem][0] = ipoin
                    self.slf2d.ikle3[ielem][1] = ipoin + 1
                    self.slf2d.ikle3[ielem][2] = ipoin + self.ny1d
                    ielem = ielem + 1
                    pbar.update(ielem)
                    # ~~> second triangle
                    self.slf2d.ikle3[ielem][0] = ipoin + self.ny1d
                    self.slf2d.ikle3[ielem][1] = ipoin + 1
                    self.slf2d.ikle3[ielem][2] = ipoin + self.ny1d + 1
                    ielem = ielem + 1
                    pbar.update(ielem)
        pbar.finish()

        # ~~> Boundaries
        self.slf2d.ipob3 = np.zeros(self.slf2d.npoin3, dtype=np.int)

        if self.byrowdown:
            # ~~> around the box
            for i in range(self.nx1d):
                ipoin = i
                self.slf2d.ipob3[i] = ipoin
            for i in range(self.nx1d):
                ipoin = self.nx1d + self.ny1d - 2 + i
                self.slf2d.ipob3[self.nx1d * self.ny1d - i - 1] = ipoin
            for j in range(1, self.ny1d - 1):
                ipoin = j + self.nx1d - 1
                self.slf2d.ipob3[(j + 1) * self.nx1d - 1] = ipoin
            for j in range(1, self.ny1d - 1):
                ipoin = self.ny1d + 2 * self.nx1d + j - 3
                self.slf2d.ipob3[self.nx1d*self.ny1d-j*self.nx1d-self.nx1d] = \
                        ipoin
        else:
            # ~~> around the box
            for j in range(self.ny1d):
                ipoin = j
                self.slf2d.ipob3[j] = ipoin
            for j in range(self.ny1d):
                ipoin = self.ny1d + self.nx1d - 2 + j
                self.slf2d.ipob3[self.ny1d * self.nx1d - j - 1] = ipoin
            for i in range(1, self.nx1d - 1):
                ipoin = i + self.ny1d - 1
                self.slf2d.ipob3[(i + 1) * self.ny1d - 1] = ipoin
            for i in range(1, self.nx1d - 1):
                ipoin = self.nx1d + 2 * self.ny1d + i - 3
                self.slf2d.ipob3[self.ny1d*self.nx1d-i*self.ny1d-self.ny1d] = \
                        ipoin

        # ~~> Boundary points
        self.slf2d.iparam = [
            0, 0, 0, 0, 0, 0, 0, 2 * self.nx1d + 2 * (self.ny1d - 2), 0, 1
        ]

    def put_geometry(self, file_name):

        print('   +> writing up the geometry file')

        self.slf2d.fole = {}
        self.slf2d.fole.update({'hook': open(file_name, 'wb')})
        self.slf2d.fole.update({'name': file_name})
        self.slf2d.fole.update({'endian': ">"})  # big endian
        self.slf2d.fole.update({'float': ('f', 4)})  # single precision

        self.slf2d.varnames = ['RANGE          ']
        self.slf2d.varunits = ['UI             ']
        self.slf2d.nbv1 = len(self.slf2d.varnames)
        self.slf2d.nvar = self.slf2d.nbv1
        self.slf2d.varindex = range(self.slf2d.nvar)

        print('       - Write Selafin header')
        self.slf2d.append_header_slf()

        # ~~> A few more number and a spectral template for input/output
        grbs = pygrib.open(self.dataset[0])
        for grb in grbs:
            nb_direct = grb.numberOfDirections
            nb_freq = grb.numberOfFrequencies
            break
        grbs.close()
        spec = np.zeros((nb_direct, nb_freq, self.nx1d, self.ny1d),
                        dtype=np.float)
        var = np.zeros((self.nx1d, self.ny1d), dtype=np.float)

        print('       - Write Selafin core')
        ibar = 0
        pbar = ProgressBar(maxval=len(self.slf2d.tags['times'])).start()
        for itime in range(len(self.slf2d.tags['times'])):

            self.slf2d.append_core_time_slf(self.slf2d.tags['times'][itime])
            grbs = pygrib.open(self.dataset[itime])
            for grb in grbs:
                i_i = 0
                data = grb.values.data
                data[np.where(np.absolute(data) <= 0.001)] = np.nan
                data[np.where(data == self.missing_value)] = np.nan
                data = 10.**data
                data[np.isnan(data)] = 0.
                for i_y in range(len(self.masky)):
                    if self.masky[i_y]:
                        spec[grb.directionNumber-1,
                             grb.frequencyNumber-1, :, i_i] = \
                              data[i_y][self.maskx]
                        i_i += 1
            grbs.close()

            ibar += 1
            pbar.update(ibar)
            for i_x in range(self.nx1d):
                for i_y in range(self.ny1d):
                    var[i_x, i_y] = max(spec[:, :, i_x, i_y].ravel()) -\
                                      min(spec[:, :, i_x, i_y].ravel())
            self.slf2d.append_core_vars_slf([var.ravel()])

        pbar.finish()
        self.slf2d.fole['hook'].close()

    def set_spectral(self):

        print('   +> reseting the header of the spectral file')

        print('      - read the spectra definition')
        grbs = pygrib.open(self.dataset[0])
        for grb in grbs:
            self.nb_direct = grb.numberOfDirections
            self.nb_freq = grb.nb_freq
            self.freq = np.asarray(grb.scaledFrequencies, dtype=np.float) / \
                            grb.frequencyScalingFactor
            #  /!? only so that TOMAWAC works
            self.dirc = np.asarray(grb.scaledDirections, dtype=np.float) / \
                            grb.directionScalingFactor - 7.5
            break
        grbs.close()

        # ~~> sizes (spectral numbers)
        self.slf2d.nplan = 1
        self.slf2d.ndp2 = 4
        self.slf2d.ndp3 = self.slf2d.ndp2
        self.slf2d.npoin2 = self.nb_direct * self.nb_freq
        self.slf2d.npoin3 = self.slf2d.npoin2
        self.slf2d.nelem2 = self.nb_direct * (self.nb_freq - 1)
        self.slf2d.nelem3 = self.slf2d.nelem2
        self.slf2d.nptfr = 2 * self.nb_direct
        self.slf2d.iparam = [0, 0, 0, 0, 0, 0, 0, 2 * self.nb_direct, 0, 1]

        # ~~> 2D grid (spectral grid) - TODO: use numpy here !
        self.slf2d.meshx = np.zeros(self.slf2d.npoin2, dtype=np.float)
        self.slf2d.meshy = np.zeros(self.slf2d.npoin2, dtype=np.float)
        print('      - set the mesh')
        ipoin = 0
        pbar = ProgressBar(maxval=self.slf2d.npoin2).start()
        for j_f in range(self.nb_freq):
            for i_i in range(self.nb_direct):
                self.slf2d.meshx[i_i+self.nb_direct*j_f] = \
                          self.freq[j_f]*math.sin(math.pi*self.dirc[i_i]/180.)
                self.slf2d.meshy[i_i+self.nb_direct*j_f] = \
                          self.freq[j_f]*math.cos(math.pi*self.dirc[i_i]/180.)
                ipoin += 1
                pbar.update(ipoin)
        pbar.finish()

        # ~~> Connectivity - TODO: use numpy here !
        print('      - set the connectivity')
        ielem = 0
        pbar = ProgressBar(maxval=self.slf2d.nelem3).start()
        self.slf2d.ikle3 = np.zeros((self.slf2d.nelem3, self.slf2d.ndp3),
                                    dtype=np.int)
        for j_f in range(self.nb_freq - 1):
            for i_i in range(self.nb_direct):
                self.slf2d.ikle3[ielem][0] = (i_i+1) % self.nb_direct + \
                                                      j_f*self.nb_direct
                ielem += 1
        for ielem in range(self.slf2d.nelem3):
            self.slf2d.ikle3[ielem][1] = ielem
            self.slf2d.ikle3[ielem][2] = ielem + self.nb_direct
            self.slf2d.ikle3[ielem][3] = self.slf2d.ikle3[ielem][0] + \
                                                  self.nb_direct
            pbar.update(ielem)
        pbar.finish()

        # ~~> Boundaries - TODO: use numpy here !
        pbar = ProgressBar(maxval=self.nx1d + self.ny1d).start()
        self.slf2d.ipob3 = np.zeros(self.slf2d.npoin3, dtype=np.int)
        # ~~> along the ?-axis
        for i_i in range(self.nb_direct):
            self.slf2d.ipob3[i_i] = i_i
        for i_i in range(self.nb_direct, 2 * self.nb_direct):
            self.slf2d.ipob3[i_i] = self.nb_direct * \
                                          (self.nb_freq+1) - i_i
        pbar.finish()

    def append_header_grib(self):

        self.slf2d.varnames = []
        self.slf2d.varunits = []
        if self.typ == 'wave':
            # TODO: codes for waves
            raise TelemacException('... waves, not coded yet')
        elif self.typ == 'oper':
            for i in self.variables:
                if 151 == i:
                    self.slf2d.varnames.append('SURFACE PRESSURE')
                    self.slf2d.varunits.append('UI              ')
                if 165 == i:
                    self.slf2d.varnames.append('WIND VELOCITY U ')
                    self.slf2d.varunits.append('M/S             ')
                if 166 == i:
                    self.slf2d.varnames.append('WIND VELOCITY V ')
                    self.slf2d.varunits.append('M/S             ')
                if 167 == i:
                    self.slf2d.varnames.append('AIR TEMPERATURE ')
                    self.slf2d.varunits.append('DEGREES         ')
            for var in self.slf2d.varnames:
                print('    - ', var)
        elif self.typ == 'spec':
            if 251 in self.variables:
                for i in range(self.nx1d * self.ny1d):
                    self.slf2d.varnames.append\
                              (('F PT '+str(i+1)+'                ')[:16])
                    self.slf2d.varunits.append('UI              ')
            print('    - from ', self.slf2d.varnames[0], ' to ',
                  self.slf2d.varnames[-1])
        if self.slf2d.varnames == []:
            raise TelemacException(\
               '... could not match requested valiable with type of file.\n\n')
        self.slf2d.nbv1 = len(self.slf2d.varnames)
        self.slf2d.nvar = self.slf2d.nbv1
        self.slf2d.varindex = range(self.slf2d.nvar)

        self.slf2d.append_header_slf()

    def append_core_time_grib(self, itime):

        self.slf2d.append_core_time_slf(self.slf2d.tags['times'][itime])

    def append_core_vars_grib(self, itime):

        if self.typ == 'wave':
            pass
            # ~~> WAVE HEIGHT == 'swh'
            # ~~> SIGNIFICANT WAVE PERIOD == 'mwp'
            # ~~> MEAN WAVE DIRECTION == 'mwd'

        elif self.typ == 'oper':
            var2d = np.zeros((self.slf2d.nvar, self.slf2d.npoin2),
                             dtype=np.float)
            grbs = pygrib.open(self.dataset[itime])
            for grb in grbs:
                if grb.indicatorOfParameter in self.variables:
                    jvar = self.variables.index(grb.indicatorOfParameter)
                    var2d[jvar, :] = np.ravel(grb.values.T)
            grbs.close()
            for jvar in range(self.slf2d.nvar):
                self.slf2d.append_core_vars_slf([var2d[jvar, :]])

        elif self.typ == 'spec':

            spec = np.zeros(
                (self.nb_direct, self.nb_freq, self.nx1d, self.ny1d),
                dtype=np.float)
            grbs = pygrib.open(self.dataset[itime])
            ibar = 0
            maxval = self.nb_direct * self.nb_freq
            pbar = ProgressBar(maxval=maxval).start()
            for grb in grbs:
                i_i = 0
                data = grb.values.data
                data[np.where(np.absolute(data) <= 0.001)] = np.nan
                data[np.where(data == self.missing_value)] = np.nan
                data = 10.**data
                data[np.isnan(data)] = 0.
                for i_y in range(len(self.masky)):
                    if self.masky[i_y]:
                        spec[grb.directionNumber-1, grb.frequencyNumber-1, :,
                             i_i] = \
                                  data[i_y][self.maskx]
                        i_i += 1
                ibar += 1
                pbar.update(ibar)
            pbar.finish()
            grbs.close()

            for i_x in range(self.nx1d):
                for i_y in range(self.ny1d):
                    self.slf2d.append_core_vars_slf([np.ravel\
                            (spec[:, :, i_x, i_y].T)])

    def put_content(self, file_name, showbar=True):

        self.open_grib(file_name)

        print('     +> Write Selafin header')
        self.append_header_grib()

        print('     +> Write Selafin core')
        if showbar:
            pbar = ProgressBar(maxval=len(self.dataset)).start()
        for itime in range(len(self.dataset)):
            seconds = int(self.slf2d.tags['times'][itime])
            date = (datetime(*self.slf2d.datetime) +
                    timedelta(seconds=seconds)).timetuple()[0:6]
            print("        - {}-{}-{} {}:{}:{}".format(date[2], date[1], \
                               date[0], date[3], date[4], date[5]))
            self.append_core_time_grib(itime)
            self.append_core_vars_grib(itime)
            if showbar:
                pbar.update(itime)
        if showbar:
            pbar.finish()

        self.close_grib()
def main():
    """ Main function of convertToSPG """
    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ Dependencies towards other modules ~~~~~~~~~~~~~~~~~~~~~~~~~~
    from argparse import ArgumentParser, RawDescriptionHelpFormatter
    from data_manip.formats.selafin import Selafin
    from data_manip.extraction.parser_selafin import subset_variables_slf, \
                                       get_value_history_slf
    from pretel.meshes import xys_locate_mesh

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ Reads config file ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    print('\n\nInterpreting command line options\n' + '~' * 72 + '\n')
    parser = ArgumentParser(\
        formatter_class=RawDescriptionHelpFormatter,
        description=('''\n
Creates a binary sponge file from a global model (SLF form)
    by interpolating on a given GEO model domain. The time series in
    the SPG file are extracted only at the nodes where the SPONGE mask
    value is more than 0.5.
        '''),
        usage=' (--help for help)\n---------\n       =>  '\
              '%(prog)s  geo-mask.slf in-result.slf out-sponge.slf \n---------')
    parser.add_argument("args", default='', nargs=3)
    options = parser.parse_args()

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ slf new mesh ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    geo_file = options.args[0]
    if not path.exists(geo_file):
        raise TelemacException(\
                'Could not find geo_file: {}\n\n'.format(geo_file))

    # Read the new GEO file and its SPONGE mask variable
    print('   +> getting hold of the GEO file and of its SPONGE mask')
    geo = Selafin(geo_file)
    _, spg = geo.get_variables_at(0, subset_variables_slf(\
              "BOTTOM: ;SPONGE mask: ", geo.varnames)[0])[0:2]
    print('   +> extracting the masked elements')
    # Keeping only masked nodes
    array_1d = np.in1d(geo.ikle2, np.sort(np.where(spg > 0.)[0]))
    mask = geo.ikle2[np.where(
        np.sum(array_1d.reshape(geo.nelem2, geo.ndp2), axis=1) > 0)]
    bor = np.unique(mask) + 1

    # Find corresponding (x,y) for the mask
    print('   +> getting hold of the GEO file and of its bathymetry')
    xys = np.vstack((geo.meshx[bor - 1], geo.meshy[bor - 1])).T

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ slf existing res ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    slf_file = options.args[1]
    if not path.exists(geo_file):
        raise TelemacException(\
                'Could not find slf_file: {}\n\n'.format(slf_file))
    slf = Selafin(slf_file)
    slf.set_kd_tree()
    slf.set_mpl_tri()

    print('   +> support extraction')
    # Extract triangles and weigths in 2D
    support2d = []
    ibar = 0
    pbar = ProgressBar(maxval=len(xys)).start()
    for xyi in xys:
        support2d.append(
            xys_locate_mesh(xyi, slf.ikle2, slf.meshx, slf.meshy, slf.tree,
                            slf.neighbours))
        ibar += 1
        pbar.update(ibar)
    pbar.finish()
    # Extract support in 3D
    support3d = zip(support2d, len(xys) * [range(slf.nplan)])

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ writes BND header ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    bnd_file = options.args[2]
    bnd = Selafin('')
    bnd.fole = {}
    bnd.fole.update({'hook': open(bnd_file, 'wb')})
    bnd.fole.update({'name': bnd_file})
    bnd.fole.update({'endian': ">"})  # big endian
    bnd.fole.update({'float': ('f', 4)})  # single precision

    # Meta data and variable names
    bnd.title = ''
    bnd.nbv1 = 5
    # /!\ ELEVATION has to be the first variable
    # (for possible vertical re-interpolation within TELEMAC)
    bnd.varnames = ['ELEVATION Z     ', \
                    'VELOCITY U      ', 'VELOCITY V      ', \
                    'SALINITY        ', 'TEMPERATURE     ']
    bnd.varunits = ['M               ', \
                    'M/S             ', 'M/S             ', \
                    '                ', '                ']
    bnd.nvar = bnd.nbv1
    bnd.varindex = range(bnd.nvar)

    # Sizes and mesh connectivity
    bnd.nplan = slf.nplan
    bnd.ndp2 = 3
    bnd.ndp3 = 6
    bnd.npoin2 = len(bor)
    bnd.npoin3 = bnd.npoin2 * slf.nplan
    bnd.iparam = [0, 0, 0, 0, 0, 0, bnd.nplan, 0, 0, 0]
    bnd.ipob2 = bor  # /!\ Note that ipobO keeps the original numbering
    print('   +> masking and setting connectivity')
    # Set the array that only includes elements of geo.ikle2
    # with at least two nodes in bor
    ikle2 = mask
    # ~~> re-numbering ikle2 as a local connectivity matrix
    knolg, _ = np.unique(np.ravel(ikle2), return_index=True)
    knogl = dict(zip(knolg, range(len(knolg))))
    bnd.ikle2 = -np.ones_like(ikle2, dtype=np.int)
    for k in range(len(ikle2)):
        # /!\ bnd.ikle2 has a local numbering, fit to the boundary elements
        bnd.ikle2[k] = [
            knogl[ikle2[k][0]], knogl[ikle2[k][1]], knogl[ikle2[k][2]]
        ]
    # Last few numbers
    bnd.nelem2 = len(bnd.ikle2)
    if slf.nplan > 1:
        bnd.nelem3 = bnd.nelem2 * (slf.nplan - 1)
    else:
        bnd.nelem3 = bnd.nelem2
    # 3D structures
    if slf.nplan > 1:
        bnd.ipob3 = np.ravel(np.add(np.repeat(bnd.ipob2, slf.nplan)\
                                              .reshape((bnd.npoin2, slf.nplan)),
                                    bnd.npoin2*np.arange(slf.nplan)).T)
        bnd.ikle3 = \
            np.repeat(bnd.npoin2*np.arange(slf.nplan-1),
                      bnd.nelem2*bnd.ndp3)\
              .reshape((bnd.nelem2*(slf.nplan-1), bnd.ndp3)) + \
            np.tile(np.add(np.tile(bnd.ikle2, 2),
                           np.repeat(bnd.npoin2*np.arange(2), bnd.ndp2)),
                    (slf.nplan-1, 1))
    else:
        bnd.ipob3 = bnd.ipob2
        bnd.ikle3 = bnd.ikle2
    # Mesh coordinates
    bnd.meshx = geo.meshx[bor - 1]
    bnd.meshy = geo.meshy[bor - 1]

    print('   +> writing header')
    # Write header
    bnd.append_header_slf()

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ writes BND core ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    print('   +> setting variables')
    # TIME and DATE extraction
    bnd.tags['times'] = slf.tags['times']
    # VARIABLE extraction
    vrs = subset_variables_slf("ELEVATION Z: ;VELOCITY U: ;VELOCITY V: "\
                                      ";SALINITY: ;TEMPERATURE: ", slf.varnames)

    # Read / Write data, one time step at a time to support large files
    print('   +> reading / writing variables')
    pbar = ProgressBar(maxval=len(slf.tags['times'])).start()
    zeros = np.zeros((bnd.npoin3, 1), dtype=np.float)
    for time in range(len(slf.tags['times'])):
        data = get_value_history_slf(slf.file, slf.tags, [time], support3d,
                                     slf.nvar, slf.npoin3, slf.nplan, vrs)
        # special case for TEMPERATURE and SALINITY
        data[3] = np.maximum(data[3], zeros)
        data[4] = np.maximum(data[4], zeros)
        data = np.reshape(
            np.transpose(
                np.reshape(np.ravel(data), (bnd.nvar, bnd.npoin2, bnd.nplan)),
                (0, 2, 1)), (bnd.nvar, bnd.npoin3))
        bnd.append_core_time_slf(time)
        bnd.append_core_vars_slf(data)
        pbar.update(time)
    pbar.finish()

    # Close bnd_file
    bnd.fole['hook'].close()

    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    # ~~~~ Jenkins' success message ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    print('\n\nMy work is done\n\n')

    sys.exit(0)