Exemple #1
0
    def export_html_diags(self, htmlfile, **kwargs):
        """Make and export diagnostics as an html file

        Parameters
        ----------
        htmlfile: string
            Output html file name
        **kwargs
            All other params are passed to :meth:`plot_diags`

        Return
        ------
        string
            Html file name
        """
        # Get figures
        figs = self.plot_diags(**kwargs)
        figs = {'Ensemble diagnostics': figs}

        # Figure paths
        figs = dicttree_relpath(figs, os.path.dirname(htmlfile))

        # Render with template
        checkdir(htmlfile)
        render_and_export_html_template('dict2tree.html',
                                        htmlfile,
                                        title='Ensemble diagnostics',
                                        content=figs)
        self.created(htmlfile)
        return htmlfile
Exemple #2
0
def write_usages(app):
    for name in status_iterator(
            app.config.sonathelps_commands,
            bold("generating helps... "),
            length=len(app.config.sonathelps_commands),
    ):

        # Get help string
        opts = app.config.sonathelps_commands[name]
        with capture() as out:
            try:
                sonat_main(opts)
            except SystemExit:
                pass

        # Write help as rst
        help_file = app.config.sonathelps_help_pat.format(name)
        if help_file:
            help_file = os.path.join(app.builder.srcdir, help_file)
            checkdir(help_file, asfile=True)
            prog = ' '.join(['sonat'] + opts[:-1])
            f = open(help_file, 'w')
            f.write(opt2rst(out[0], prog=prog))
            f.close()

        # Write usage
        usage_file = app.config.sonathelps_usage_pat.format(name)
        if usage_file:
            usage_file = os.path.join(app.builder.srcdir, usage_file)
            checkdir(usage_file, asfile=True)
            f = open(usage_file, 'w')
            f.write(out[0])
            f.close()
Exemple #3
0
def write_options(app):
    app.info(bold('writing config options to rst... '), nonl=True)
    config_directives = os.path.join(app.env.srcdir,
                                     app.config.sonatconfig_options_file)
    checkdir(config_directives)
    f = open(config_directives, 'w')
    f.write(SONAT_CFGM.get_rst(mode='specs'))
    f.close()
    app.info('done into ' + os.path.basename(config_directives))
Exemple #4
0
def write_ncdumps(app):
    for name in app.builder.status_iterator(
            app.config.sonatncdumph_ncfiles,
            bold("generating list of ncdump -h... "),
            length=len(app.config.sonatncdumph_ncfiles),
    ):
        txtfile = app.config.sonatncdumph_filepat.format(name)
        txtfile = os.path.join(app.builder.srcdir, txtfile)
        checkdir(txtfile, asfile=True)
        ncfile = app.config.sonatncdumph_ncfiles[name]
        if not os.path.isabs(ncfile):
            ncfile = os.path.join(app.builder.srcdir, ncfile)
        os.system('ncdump -h {ncfile} > {txtfile}'.format(**locals()))
Exemple #5
0
def write_content(app):
    app.info(bold('writing default config to rst... '), nonl=True)
    config_rstfile = os.path.join(app.env.srcdir,
                                  app.config.sonatconfig_content_file)
    checkdir(config_rstfile)
    s = StringIO()
    SONAT_CFGM.defaults().write(s)
    s = s.getvalue()
    s = s.replace(os.environ['USER'], '${USER}')
    f = open(config_rstfile, 'w')
    f.write(s)
    f.close()
    app.info('done into ' + os.path.basename(config_rstfile))
Exemple #6
0
def arm_sa_from_cfg(cfg, sa_names=None):
    """Perform ARM sensitivity analyses

    Parameters
    ----------
    cfg: configobj/dict
    sa_names: strings, None
        Force the list of registered sensitivity analyserrs

    Return
    ------
    string
        Path to HTML file
    """

    # Config
    cfga = cfg['arm']
    cfgas = cfga['sa']
    cfge = cfg['ens']
    cfgo = cfg['obs']
    cfgop = cfgo['plots']
    lon = get_cfg_xminmax(cfg)
    lat = get_cfg_yminmax(cfg)
    ncensfile = get_cfg_path(cfg, 'ens', 'ncensfile')
    varnames = cfge['varnames'] or None
    norms = get_cfg_norms(cfg)
    htmlfile = get_cfg_path(cfg, ['arm', 'sa'], 'htmlfile')

    # Init
    logger = init_from_cfg(cfg)

    # Load ensemble
    ens = Ensemble.from_file(ncensfile,
                             varnames=varnames,
                             logger=logger,
                             lon=lon,
                             lat=lat)

    # Load obs manager
    obs = load_obs_from_cfg(cfg)

    # Init ARM
    arm = ARM(ens, obs, norms=norms, logger=logger)

    # Bathy
    bathy = read_bathy_from_cfg(cfg, logger)
    if bathy is not None:
        arm.set_bathy(bathy)

    # SA names
    if sa_names is None:
        sa_names = list_arm_sensitivity_analysers()

    # Loop on analysers
    res = OrderedDict()
    for sa_name in sa_names:

        # Format paths
        rebase_cfg_paths(cfg, ['arm', 'sa', sa_name])

        # Config
        kwargs = cfgas[sa_name].dict()
        activate = kwargs.pop('activate')
        if not activate:
            continue

        # Setup analyser
        sa = get_arm_sensitivity_analyser(sa_name, arm, logger=logger)

        # Run and export
        res.update(
            sa.plot(lon=lon,
                    lat=lat,
                    obs_color=cfgop['colorcycle'],
                    obs_marker=cfgop['markercycle'],
                    **kwargs))

    # Paths
    checkdir(htmlfile)
    res = dicttree_relpath(res, os.path.dirname(htmlfile))
    res = {"ARM sensitivity analyses": res}

    # Render with template
    render_and_export_html_template('dict2tree.html',
                                    htmlfile,
                                    title="ARM sensitivity analyses",
                                    content=res)
    arm.created(htmlfile)
    return htmlfile
Exemple #7
0
xisize = 1. * xsize / dpi
yisize = xisize * aspect

sonat_size = 145. * xisize / 8.
o_size = 50 * xisize / 8.

figfiles = []
P.figure(figsize=(xisize, yisize), dpi=dpi)
kw = dict(ha='center', va='center', color=color, weight='bold')
ts = P.figtext(.5, .41, 'SONAT', size=sonat_size, **kw)
to = P.figtext(.302, .49, 'O', size=o_size, **kw)
shadow = ''

bgcolor = 'transp'
figfiles.append(figpat.format(**locals()))
checkdir(figfiles[-1], asfile=True)
P.savefig(figfiles[-1], transparent=True)

bgcolor = 'white'
figfiles.append(figpat.format(**locals()))
P.savefig(figfiles[-1])

bgcolor = 'blue'
figfiles.append(figpat.format(**locals()))
P.savefig(figfiles[-1], facecolor="#2882B9")

shadow = True
add_shadow(ts, width=9, alpha=.5, xoffset=6, yoffset=-6)
add_shadow(to, width=9, alpha=.5, xoffset=6, yoffset=-6)
P.axis('off')
shadow = '-shadow'
Exemple #8
0
    def plot_diags(
            self,
            mean=True,
            variance=True,
            kurtosis=True,
            skew=True,
            skewtest=True,
            kurtosistest=True,
            normaltest=True,
            titlepat='{var_name} - {diag_long_name} - {slice_loc}',
            surf=None,
            bottom=None,
            horiz_sections=None,  #points=None,
            zonal_sections=None,
            merid_sections=None,
            fmtlonlat='{:.2f}',
            fmtdep='{:04.0f}m',
            figpat_slice='sonat.ens.{diag_name}_{var_name}_{slice_type}_{slice_loc}.png',
            figpat_generic='sonat.ens.{diag_name}.png',
            show=False,
            savefig=True,
            props=None,
            **kwargs):
        """Create figures for diagnostics

        Diagnostic arguments are passed to :meth:`get_diags`,
        like ``variance=True``.

        Parameters
        ----------
        surf: bool
        bottom: bool
        horiz_sections: floats
            List of depths for horizontal slices. All by default.
            It accepts scalars or list of floats and of the two special values
            "bottom" and "surf". In the latter case, these variable must
            be explicitely included in the ensemble, like "temp_surf". A depth
            of 0 is not equivalent to "surf", despite results may be similar,
            or equivalent when the sea level is always 0.
            In the case of floats, the
            ensemble must contain 3d variables.
        zonal_sections: None, list, floats
            Latitudes
        merid_sections: None, list, floats
            Longitudes
        props: dict, None
            Dictionary of graphic properties passed as keywords to plotting
            functions. The keys must be valid diagnostics names such as
            "mean", or one of the follow plotting function: map, curve.

        Return
        ------
        dict
            A tree of :class:`dict` with the following branch types,
            with all keys capitalised:

            - Diagnostic
            - Variable
            - Type of slice
            - Location of slice
        """

        # Diags
        diags = self.get_diags(mean=mean,
                               variance=variance,
                               kurtosis=kurtosis,
                               skew=skew,
                               skewtest=skewtest,
                               kurtosistest=kurtosistest,
                               normaltest=normaltest)

        # Inits
        self.verbose('Plotting ensemble diagnostics')
        figs = OrderedDict()
        if props is None:
            props = {}
        kwprops = dict_merge(props, DEFAULT_PLOT_KWARGS_PER_ENS_DIAG)

        # Loop on diags
        figs = OrderedDict()
        for diag_name, diag_var in diags.items():

            diag_long_name = diag_name.title().replace('_', ' ')
            self.debug('Plotting ens diag: ' + diag_long_name)

            # Explained variance
            if diag_name == 'spectrum':

                self.debug(' Variable: spectrum')
                figfile = figpat_generic.format(**locals())
                checkdir(figfile)
                kw = kwprops.get(diag_name, {})
                dict_check_defaults(kw,
                                    xmin=.5,
                                    xmax=len(diag_var) + .5,
                                    width=.5,
                                    xminor_locator=False,
                                    ymax=1.1 * diag_var.max(),
                                    savefig=figfile,
                                    fig='new',
                                    xlabel='%(xlong_name)s',
                                    xlocator=MultipleLocator(1),
                                    **DEFAULT_PLOT_KWARGS)
                p = bar(diag_var, **kw)
                self.created(figfile)
                figs[diag_long_name] = figfile

            # Plot other sliced variables
            else:

                ff = self.plot_fields(
                    diag_var,
                    surf=surf,
                    bottom=bottom,
                    horiz_sections=horiz_sections,
                    zonal_sections=zonal_sections,
                    merid_sections=merid_sections,
                    #                                      points=points,
                    figpat=figpat_slice,
                    savefig=savefig,
                    titlepat=titlepat,
                    fmtlonlat=fmtlonlat,
                    fmtdep=fmtdep,
                    subst={
                        'diag_long_name': diag_long_name,
                        'diag_name': diag_name
                    },
                    props=props,
                )
                if ff:
                    figs[diag_long_name] = ff

        return figs
Exemple #9
0
def generate_pseudo_ensemble(ncpat,
                             varnames=None,
                             nrens=50,
                             enrich=2.,
                             norms=None,
                             getmodes=False,
                             logger=None,
                             asdicts=False,
                             anomaly=True,
                             ncensfile=None,
                             **kwargs):
    """Generate a static pseudo-ensemble from a single simulation


    Parameters
    ----------
    ncpat: string
        netcdf file name or pattern
    nrens: int
        Ensemble size
    enrich: float
        Enrichment factor
    getmodes: bool
        Get also EOFs end eigen values
    **kwargs:
        Extra parameters are passed to :func:`load_model_at_dates`

    Return
    ------
    list (or dict) of arrays:
        variables with their name as keys
    dict: eofs, ev and variance, optional
        eofs: list (or dict) of arrays(nmodes, ...), optional
            EOFs
        ev: array(nmodes), optional
            Eigen values
        var: array
            Variance

    """
    # Logger
    kwlog = kwfilter(kwargs, 'logger_')
    if logger is None:
        logger = get_logger(**kwlog)
    logger.verbose('Generating pseudo-ensemble')

    # Ensembe size
    enrich = max(enrich, 1.)
    nt = int(nrens * enrich)
    logger.debug(
        ' enrich={enrich},  nt={nt}, ncpat={ncpat}, varnames={varnames}'.
        format(**locals()))

    # Read variables
    logger.debug('Reading the model at {} dates'.format(nt))
    data = load_model_at_regular_dates(ncpat,
                                       varnames=varnames,
                                       nt=nt,
                                       asdict=False,
                                       **kwargs)
    single = not isinstance(data, list)

    # Norms
    if isinstance(norms, dict):
        norms = var_prop_dict2list(data, norms)

    # Enrichment
    witheofs = nrens != nt
    if witheofs:
        logger.debug('Computing reduced rank ensemble with EOFs analysis')

        # Stack packed variables together
        stacker = Stacker(data, norms=norms, logger=logger)
        meanstate = N.zeros(stacker.ns)
        states = N.asfortranarray(stacker.stacked_data.copy())

        # Compute EOFs
        stddev, svals, svecs, status = f_eofcovar(dim_fields=stacker.ns,
                                                  offsets=1,
                                                  remove_mstate=0,
                                                  do_mv=0,
                                                  states=states,
                                                  meanstate=meanstate)
        if status != 0:
            raise SONATError('Error while calling fortran eofcovar routine')
        neof = svals.size  # computed
        neofr = nrens - 1  # retained
        svals = svals[:neofr] * N.sqrt(
            (neof - 1.) / neof)  # to be consistent with total variance
        svecs = svecs[:, :neofr]

        # Generate ensemble
        sens = f_sampleens(svecs, svals, meanstate, flag=0)

        # Unstack
        ens = stacker.unstack(sens,
                              format=2,
                              rescale='norm' if anomaly else True)
        if getmodes:

            # Modes
            mode_axis = create_axis(N.arange(1, neofr + 1, dtype='i'),
                                    id='mode')
            eofs = stacker.unstack(svecs,
                                   firstdims=mode_axis,
                                   id='{id}_eof',
                                   rescale=False,
                                   format=1)
            svals = MV2.array(svals,
                              axes=[mode_axis],
                              id='ev',
                              attributes={'long_name': 'Eigen values'})
            svals.total_variance = float(stacker.ns)

            # Variance
            vv = stacker.format_arrays([d.var(axis=0) for d in stacker.datas],
                                       id='{id}_variance',
                                       mode=1)
            variance = stacker.unmap(vv)

    else:  # No enrichment -> take the anomaly if requested

        logger.debug('Getting the anomaly to build the ensemble')
        ens = data

        if anomaly:
            if single:
                ens[:] = ens.asma() - ens.asma().mean(axis=0)
            else:
                for i, e in enumerate(ens):
                    ens[i][:] = e.asma() - e.asma().mean(axis=0)

    # Finalize
    getmodes = getmodes and witheofs
    member_axis = create_axis(N.arange(nrens, dtype='i'),
                              id='member',
                              long_name='Member')
    if single:
        ens.setAxis(0, member_axis)
    else:
        for var in ens:
            var.setAxis(0, member_axis)

    # Dump to file
    if ncensfile:
        logger.debug('Dump the ensemble to netcdf')
        checkdir(ncensfile)
        f = cdms2.open(ncensfile, 'w')
        ensvars = list(ens) if not single else [ens]
        if getmodes:
            if single:
                ensvars.append(eofs)
                ensvars.append(variance)
            else:
                ensvars.extend(eofs)
                ensvars.extend(variance)
            ensvars.append(svals)
        for var in ensvars:
            f.write(var)
        f.close()
        logger.created(ncensfile)

    # As dicts
    if asdicts:
        if single:
            ens = OrderedDict([(ens.id, ens)])
            if getmodes:
                eofs = OrderedDict([(eofs.id, eofs)])
                variance = OrderedDict([(variance.id, variance)])
        else:
            ens = OrderedDict([(var.id, var) for var in ens])
            if getmodes:
                eofs = OrderedDict([(var.id, var) for var in eofs])
                variance = OrderedDict([(var.id, var) for var in variance])

    # Return
    if not getmodes:
        return ens
    return ens, dict(eofs=eofs, eigenvalues=svals, variance=variance)
Exemple #10
0
    def plot_fields(self,
                    variables,
                    surf=None,
                    bottom=None,
                    horiz_sections=False,
                    points=None,
                    zonal_sections=None,
                    merid_sections=None,
                    titlepat="{var_name} - {slice_loc}",
                    props=None,
                    subst={},
                    figpat='sonat.ens.{var_name}_{slice_type}_{slice_loc}.png',
                    fmtlonlat=u'{:.2f}°{}',
                    fmtdep='{:04.0f}m',
                    savefig=True,
                    obs=None,
                    close=True,
                    show=False,
                    **kwargs):
        """Plot one or several platform like variables

        Parameters
        ----------
        depths: string, floats
            List of depths for horizontal slices. All by default.
            It accepts scalars or list of floats and of the two special values
            "bottom" and "surf". In the latter case, these variable must
            be explicitely included in the ensemble, like "temp_surf". A depth
            of 0 is not equivalent to "surf", despite results may be similar,
            or equivalent when the sea level is always 0.
            In the case of floats, the
            ensemble must contain 3d variables.
        props: dict, None
            Dictionary of graphic properties passed as keywords to plotting
            functions. The keys must be valid diagnostics names such as
            "mean", or one of the follow plotting function: map, curve.

        """
        # Init
        if props is None:
            props = {}
        kwprops = dict_merge(props, DEFAULT_PLOT_KWARGS_PER_ENS_DIAG)
        kwprops.update(fig='new')  # TODO: must be more flexible like for obs
        figs = OrderedDict()
        kwobs = kwfilter(kwargs, 'obs')
        kwobs.update(surf=False,
                     bottom=False,
                     zonal_sections=None,
                     merid_sections=None,
                     horiz_sections=None)

        # Loop on variables
        for variable in ArgList(variables).get():
            var_short_name, var_depth, diag_name = split_varname(variable)
            diag_name = getattr(variable, 'sonat_ens_diag', diag_name)
            var_name = (var_short_name if var_depth is None else
                        (var_short_name + '_' + var_depth))
            varname = var_name
            id = variable.id
            self.debug(' Variable: ' + id)
            order = variable.getOrder()
            if diag_name:
                kw = kwprops.get(diag_name, {})
            else:
                kw = {}
            toplot = []

            # Maps
            if (horiz_sections is not False or surf is not False
                    or bottom is not False):

                # Surf/bottom/3D
                if ((var_depth == 'surf' and surf is not False) or
                    (var_depth == 'bottom' and bottom is not False)):  # 2D
                    slice_loc = var_depth
                    toplot.append(
                        dict(slice_loc=slice_loc,
                             kw=kw,
                             keys=(var_short_name, 'map', slice_loc),
                             obs_plot={var_depth: True},
                             slice_type='map'))

                elif (variable.getLevel() is not None
                      and horiz_sections is not False
                      and horiz_sections is not None):  # 3D

                    # Surf and bottom
                    for ok, slice_loc in [(surf, 'surf'), (bottom, 'bottom')]:
                        if ok is True:
                            toplot.append(
                                dict(
                                    slice_loc=slice_loc,
                                    slice_type='map',
                                    keys=(var_short_name, 'map', slice_loc),
                                    kw=dict_merge(dict(depth=slice_loc), kw),
                                    obs_plot={slice_loc: True},
                                ))

                    # Loop on 3D depths specs
                    if horiz_sections is True:
                        depths = variable.getLevel()
                    else:
                        depths = horiz_sections
                        if N.isscalar(depths):
                            depths = [depths]
                    depths = list(depths)
                    for depth in depths:
                        if isinstance(depth, str):
                            slice_loc = depth
                        else:
                            slice_loc = fmtdep.format(abs(depth))
                        toplot.append(
                            dict(
                                slice_loc=slice_loc,
                                slice_type='map',
                                keys=(var_name, 'map', slice_loc),
                                kw=dict_merge(dict(depth=depth), kw),
                                obs_plot={'horiz_sections': depth},
                            ))

            # Zonal sections
            if zonal_sections is not None and zonal_sections is not False:
                slice_type = 'zonal'
                if N.isscalar(zonal_sections):
                    zonal_sections = [zonal_sections]
                for lat in zonal_sections:
                    slice_loc = latlab(lat, no_symbol=True)
                    toplot.append(
                        dict(
                            slice_loc=slice_loc,
                            slice_type=slice_type,
                            keys=(var_name, slice_type, slice_loc),
                            kw=dict_merge(dict(lat=lat), kw),
                            obs_plot={'zonal_sections': lat},
                        ))

            # Meridional sections
            if merid_sections is not None and merid_sections is not False:
                slice_type = 'merid'
                if N.isscalar(merid_sections):
                    merid_sections = [merid_sections]
                for lon in merid_sections:
                    slice_loc = lonlab(lon, no_symbol=True)
                    toplot.append(
                        dict(
                            slice_loc=slice_loc,
                            slice_type=slice_type,
                            keys=(var_name, slice_type, slice_loc),
                            kw=dict_merge(dict(lon=lon), kw),
                            obs_plot={'merid_sections': lon},
                        ))

#            # Points
#            if points:
#                slice_type = 'point'
#                if isinstance(points, tuple):
#                    points = [points]
#                for lon, lat in points:
#                    slice_loc = (lonlab(lon, no_symbol=True) + '-' +
#                                 latlab(lat, no_symbol=True))
#                    toplot.append(dict(
#                        slice_loc=slice_loc,
#                        slice_type=slice_type,
#                        keys=(var_name, slice_type, slice_loc),
#                        kw=dict_merge(dict(lon=lon, lat=lat), kw),
#                        obs_plot={'points':(lon, lat)},
#                        ))

# Make all pending plots for this variable
            for spec in toplot:

                # Get specs
                slice_loc = spec['slice_loc']
                slice_type = spec['slice_type']
                obs_plot = spec['obs_plot']
                keys = map(str.title, spec['keys'])
                kw = kwargs.copy()
                kw.update(spec['kw'])
                self.debug('  Location: ' + slice_loc)

                # Setup
                dfmt = locals()
                dfmt.update(subst)
                title = titlepat.format(**dfmt)
                kw.update(title=title, savefig=False, close=False)
                dict_check_defaults(kw,
                                    fig='new',
                                    **DEFAULT_PLOT_KWARGS_PER_ENS_DIAG)

                # Plot
                p = plot_gridded_var(variable, **kw)  # Action !

                # Plot obs locations on 2D plots only
                if obs and isinstance(p, Plot2D):
                    #FIXME: should we pass lon/lat/level from variable?
                    kwo = kwobs.copy()
                    kwo.update(obs_plot,
                               full3d=False,
                               full2d=False,
                               plotter=p,
                               savefig=False,
                               close=False,
                               title=False,
                               colorbar=False)
                    #                    obs.set_cached_plot('locations', slice_type, slice_loc, p)
                    obs.plot('locations', **kwo)

                # Finalise
                if savefig:
                    figfile = figpat.format(**dfmt)
                    checkdir(figfile)
                    p.savefig(figfile)
                    close = True
                    self.created(figfile)
                    kw = {keys[-1]: figfile, '__class__': OrderedDict}
                    dicttree_set(figs, *keys[:-1], **kw)
                if not show and close:
                    p.close()
        if show:
            P.show()
        elif close:
            P.close()

        return figs