コード例 #1
0
# -*- coding: utf-8 -*-

from pycmbs.plots import GlecklerPlot
import matplotlib.pyplot as plt

# this is just an artificial example illustrating principle usage of the
# Portraet diagram

fig = plt.figure(figsize=(8, 6))

G = GlecklerPlot(fig=fig)
# register models
G.add_model('MPI-ESM-MR')
G.add_model('MPI-ESM-LR')
G.add_model('MPI-ESM-P')

# then register variables
G.add_variable('Ta')
G.add_variable('P')

# after that you can add values to be plotted
# pos=1 top triangle, pos2= lower triangle
# different positions are for different observations
G.add_data('Ta', 'MPI-ESM-MR', 0.5, pos=1)
G.add_data('Ta', 'MPI-ESM-MR', -0.2, pos=2)

G.add_data('Ta', 'MPI-ESM-LR', 0.3, pos=1)
G.add_data('Ta', 'MPI-ESM-LR', 0.2, pos=2)

G.add_data('Ta', 'MPI-ESM-P', 0.05, pos=1)
G.add_data('Ta', 'MPI-ESM-P', -0.1, pos=2)
コード例 #2
0
ファイル: test_diagnostic.py プロジェクト: zengeo/pycmbs
    def test_gleckler_index(self):
        """
        test Reichler index/Gleckler plot
        """

        # generate sample data
        # sample data
        tmp = np.zeros((5, 3, 1))
        tmp[:,0,0] = np.ones(5)*1.
        tmp[:,1,0] = np.ones(5)*2.
        tmp[:,2,0] = np.ones(5)*5.

        # The data is like ...
        #| 1 | 2 | 5 |
        #| 1 | 2 | 5 |
        #| 1 | 2 | 5 |
        #| 1 | 2 | 5 |
        #| 1 | 2 | 5 |

        x = self.D.copy()
        x._temporal_subsetting(0, 4)

        x.data = np.ma.array(tmp, mask=tmp!=tmp)
        x.std = np.ones(x.data.shape)
        x.time[0] = pl.datestr2num('2000-02-15')
        x.time[1] = pl.datestr2num('2000-03-15')
        x.time[2] = pl.datestr2num('2000-04-15')
        x.time[3] = pl.datestr2num('2000-05-15')
        x.time[4] = pl.datestr2num('2000-06-15')

        y = self.D.copy()
        y._temporal_subsetting(0, 4)
        tmp = np.ones(x.data.shape)  # sample data 2
        y.data = np.ma.array(tmp, mask=tmp!=tmp)
        y.time[0] = pl.datestr2num('2000-02-15')
        y.time[1] = pl.datestr2num('2000-03-15')
        y.time[2] = pl.datestr2num('2000-04-15')
        y.time[3] = pl.datestr2num('2000-05-15')
        y.time[4] = pl.datestr2num('2000-06-15')

        # Case 1: same area weights
        # cell area
        tmp = np.ones((3, 1))
        x.cell_area = tmp*1.

        #| 1-1 | 2-1 | 5-1 |
        #| 1-1 | 2-1 | 5-1 |
        #| 1-1 | 2-1 | 5-1 |
        #| 1-1 | 2-1 | 5-1 |
        #| 1-1 | 2-1 | 5-1 |
        #===================
        #| 0   | 5   | 5*4**2=5*16. = 80 |
        #==> E2 = sqrt(85./(15.))
        D = GlecklerPlot()
        r = D.calc_index(x, y, 'a', 'b', time_weighting=False)

        wt = np.ones(5) / 5.
        ref = np.sqrt(((85./15.) * wt).sum())
        t = np.abs(1. - r / ref)
        self.assertLess(t, 0.000001)  # relative error

        D = GlecklerPlot()
        r = D.calc_index(x, y, 'a', 'b')

        wt = np.asarray([29., 31., 30., 31., 30.])
        wt = wt / wt.sum()
        ref = np.sqrt(((85./15.) * wt).sum())
        t = np.abs(1. - r / ref)
        self.assertLess(t, 0.000001)  # relative error



        # Case 2: Different area weights
        # cell area
        tmp = np.ones((3, 1))
        tmp[1, 0] = 2.
        x.cell_area = tmp*1.

        #| 1-1=0 | 2-1=1 | 5-1=16 |
        #| 1-1=0 | 2-1=1 | 5-1=16 |
        #| 1-1=0 | 2-1=1 | 5-1=16 |
        #| 1-1=0 | 2-1=1 | 5-1=16 |
        #| 1-1=0 | 2-1=1 | 5-1=16 |
        #--------------------------
        # w = 0.25 w = 0.5  w=0.25|
        #--------------------------

        # 0.25*0 + 0.5 * 1 + 0.25 * 16 = 0 + 0.5 + 4 = 4.5
        # the mean of that is 4.5 for each timestep
        # mean because the overall weights are calculated as such that
        # they give a total weight if 1

        #  diagnostic
        D = GlecklerPlot()
        r = D.calc_index(x, y, 'a', 'b', time_weighting=False)

        wt = np.ones(5) / 5.
        ref = np.sqrt((4.5 * wt).sum())
        t = np.abs(1. - r / ref)
        self.assertLess(t, 0.000001)  # relative error

        wt = np.asarray([29., 31., 30., 31., 30.])
        wt = wt / wt.sum()
        ref = np.sqrt((4.5 * wt).sum())
        t = np.abs(1. - r / ref)
        self.assertLess(t, 0.000001)  # relative error

        # Case 3: use different std
        x.std = np.ones(x.data.shape)
        x.std[:, 2, 0] = 0.5

        #| 1-1=0 | 2-1=1  | 5-1=16 / 0.5 |
        #| 1-1=0 | 2-1=1  | 5-1=16 / 0.5 |
        #| 1-1=0 | 2-1=1  | 5-1=16 / 0.5 |
        #| 1-1=0 | 2-1=1  | 5-1=16 / 0.5 |
        #| 1-1=0 | 2-1=1  | 5-1=16 / 0.5 |
        #--------------------------------
        # w = 0.25    w = 0.5    w=0.25|
        #    0    +   0.5   +  0.25*32 = 0.5 + 8 = 8.5

        D = GlecklerPlot()
        r = D.calc_index(x, y, 'a', 'b', time_weighting=False)

        wt = np.ones(5) / 5.
        ref = np.sqrt((8.5 * wt).sum())
        t = np.abs(1. - r / ref)
        self.assertLess(t, 0.000001)  # relative error

        wt = np.asarray([29., 31., 30., 31., 30.])
        wt = wt / wt.sum()
        ref = np.sqrt((8.5 * wt).sum())
        t = np.abs(1. - r / ref)
        self.assertLess(t, 0.000001)  # relative error
コード例 #3
0
# -*- coding: utf-8 -*-
"""
This file is part of pyCMBS.
(c) 2012- Alexander Loew
For COPYING and LICENSE details, please refer to the LICENSE file
"""
from pycmbs.plots import GlecklerPlot
import matplotlib.pyplot as plt


# this is just an artificial example illustrating principle usage of the
# Portraet diagram

fig = plt.figure(figsize=(8,6))

G = GlecklerPlot(fig=fig)
# register models
G.add_model('MPI-ESM-MR')
G.add_model('MPI-ESM-LR')
G.add_model('MPI-ESM-P')

# then register variables
G.add_variable('Ta')
G.add_variable('P')

# after that you can add values to be plotted
# pos=1 top triangle, pos2= lower triangle
# different positions are for different observations
G.add_data('Ta','MPI-ESM-MR',0.5,pos=1)
G.add_data('Ta','MPI-ESM-MR',-0.2,pos=2)
コード例 #4
0
 def test_GlecklerPlot_4obs(self):
     G = GlecklerPlot()
     G.add_model('echam5')
     G.add_model('mpi-esm')
     G.add_variable('ta')
     G.add_variable('P')
     G.add_data('P', 'echam5', 0.5, pos=1)
     G.add_data('P', 'echam5', 0.25, pos=2)
     G.add_data('P', 'echam5', -0.25, pos=3)
     G.add_data('P', 'mpi-esm', -0.25, pos=4)
     G.plot()
コード例 #5
0
    def test_GlecklerPlot(self):
        G = GlecklerPlot()
        G.add_model('echam5')
        G.add_model('mpi-esm')
        G.add_variable('ta')
        G.add_variable('P')
        G.add_data('ta', 'echam5', 0.5, pos=1)
        G.add_data('P', 'echam5', 0.25, pos=1)
        G.add_data('P', 'echam5', -0.25, pos=2)
        G.add_data('P', 'mpi-esm', -0.25, pos=1)
        G.plot()

        G.plot_model_error('ta')
        G.plot_model_ranking('ta')
        G.write_ranking_table('ta',
                              self._tmpdir + os.sep + 'nix.tex',
                              fmt='latex')
        self.assertTrue(os.path.exists(self._tmpdir + os.sep + 'nix.tex'))
        if os.path.exists(self._tmpdir + os.sep + 'nix.tex'):
            os.remove(self._tmpdir + os.sep + 'nix.tex')
        G.write_ranking_table('ta',
                              self._tmpdir + os.sep + 'nix1',
                              fmt='latex')
        self.assertTrue(os.path.exists(self._tmpdir + os.sep + 'nix1.tex'))
        if os.path.exists(self._tmpdir + os.sep + 'nix1.tex'):
            os.remove(self._tmpdir + os.sep + 'nix1.tex')
コード例 #6
0
def main():
    plt.close('all')
    if len(sys.argv) > 1:
        if len(sys.argv) == 2:
            # a single argument was provides as option
            if sys.argv[1] == 'init':
                # copy INI files and a template configuration file
                # to current directory
                create_dummy_configuration()
                sys.exit()
            else:
                file = sys.argv[1]  # name of config file
                if not os.path.exists(file):
                    raise ValueError('Configuration file can not be \
                                      found: %s' % file)
        else:
            raise ValueError('Currently not more than one command \
                               line parameter supported!')
    else:  # default
        print('*******************************************')
        print('* WELCOME to pycmbs.py                    *')
        print('* Happy benchmarking ...                  *')
        print('*******************************************')
        print ''
        print 'please specify a configuration filename as argument'
        sys.exit()

    ####################################################################
    # CONFIGURATION and OPTIONS
    ####################################################################

    # read configuration file
    CF = config.ConfigFile(file)

    # read plotting options
    PCFG = config.PlotOptions()
    PCFG.read(CF)
    plot_options = PCFG

    ####################################################################
    # REMOVE previous Data warnings
    ####################################################################
    outdir = CF.options['outputdir']
    if outdir[-1] != os.sep:
        outdir += os.sep

    os.environ['PYCMBS_OUTPUTDIR'] = outdir
    os.environ['PYCMBS_OUTPUTFORMAT'] = CF.options['report_format']

    os.environ['DATA_WARNING_FILE'] = outdir + 'data_warnings_' \
        + CF.options['report'] + '.log'
    if os.path.exists(os.environ['DATA_WARNING_FILE']):
        os.remove(os.environ['DATA_WARNING_FILE'])

    for thevar in plot_options.options.keys():
        if thevar in plot_options.options.keys():
            print('Variable: %s' % thevar)
            for k in plot_options.options[thevar].keys():
                print('    Observation: %s' % k)

    if CF.options['basemap']:
        f_fast = False
    else:
        f_fast = True
    shift_lon = use_basemap = not f_fast

    ########################################################################
    # TIMES
    ########################################################################
    s_start_time = CF.start_date
    s_stop_time = CF.stop_date
    start_time = pylab.num2date(pylab.datestr2num(s_start_time))
    stop_time = pylab.num2date(pylab.datestr2num(s_stop_time))

    ########################################################################
    # INIT METHODS
    ########################################################################
    # names of analysis scripts for all variables ---
    scripts = CF.get_analysis_scripts()

    # get dictionary with methods how to read data for model variables to be
    # analyzed
    variables = CF.variables
    varmethods = CF.get_methods4variables(CF.variables)

    # READ DATA
    # create a Model instance for each model specified
    # in the configuration file
    #
    # read the data for all variables and return a list
    # of Data objects for further processing

    model_cnt = 1
    proc_models = []

    for i in range(len(CF.models)):
        # assign model information from configuration
        data_dir = CF.dirs[i]
        model = CF.models[i]
        experiment = CF.experiments[i]

        # create model object and read data
        # results are stored in individual variables namex modelXXXXX
        if CF.dtypes[i].upper() == 'CMIP5':
            themodel = CMIP5Data(data_dir,
                                 model,
                                 experiment,
                                 varmethods,
                                 intervals=CF.intervals,
                                 lat_name='lat',
                                 lon_name='lon',
                                 label=model,
                                 start_time=start_time,
                                 stop_time=stop_time,
                                 shift_lon=shift_lon)
        elif CF.dtypes[i].upper() == 'CMIP5RAW':
            themodel = CMIP5RAWData(data_dir,
                                    model,
                                    experiment,
                                    varmethods,
                                    intervals=CF.intervals,
                                    lat_name='lat',
                                    lon_name='lon',
                                    label=model,
                                    start_time=start_time,
                                    stop_time=stop_time,
                                    shift_lon=shift_lon)
        elif 'CMIP5RAWSINGLE' in CF.dtypes[i].upper():
            themodel = CMIP5RAW_SINGLE(data_dir,
                                       model,
                                       experiment,
                                       varmethods,
                                       intervals=CF.intervals,
                                       lat_name='lat',
                                       lon_name='lon',
                                       label=model,
                                       start_time=start_time,
                                       stop_time=stop_time,
                                       shift_lon=shift_lon)

        elif CF.dtypes[i].upper() == 'JSBACH_BOT':
            themodel = JSBACH_BOT(data_dir,
                                  varmethods,
                                  experiment,
                                  intervals=CF.intervals,
                                  start_time=start_time,
                                  stop_time=stop_time,
                                  name=model,
                                  shift_lon=shift_lon)
        elif CF.dtypes[i].upper() == 'JSBACH_RAW':
            themodel = JSBACH_RAW(data_dir,
                                  varmethods,
                                  experiment,
                                  intervals=CF.intervals,
                                  name=model,
                                  shift_lon=shift_lon,
                                  start_time=start_time,
                                  stop_time=stop_time)
        elif CF.dtypes[i].upper() == 'JSBACH_RAW2':
            themodel = JSBACH_RAW2(data_dir,
                                   varmethods,
                                   experiment,
                                   intervals=CF.intervals,
                                   start_time=start_time,
                                   stop_time=stop_time,
                                   name=model,
                                   shift_lon=shift_lon)  # ,
            # model_dict=model_dict)
        elif CF.dtypes[i].upper() == 'JSBACH_SPECIAL':
            themodel = JSBACH_SPECIAL(data_dir,
                                      varmethods,
                                      experiment,
                                      intervals=CF.intervals,
                                      start_time=start_time,
                                      stop_time=stop_time,
                                      name=model,
                                      shift_lon=shift_lon)  # ,
            # model_dict=model_dict)
        elif CF.dtypes[i].upper() == 'CMIP3':
            themodel = CMIP3Data(data_dir,
                                 model,
                                 experiment,
                                 varmethods,
                                 intervals=CF.intervals,
                                 lat_name='lat',
                                 lon_name='lon',
                                 label=model,
                                 start_time=start_time,
                                 stop_time=stop_time,
                                 shift_lon=shift_lon)
        else:
            raise ValueError('Invalid model type: %s' % CF.dtypes[i])

        # read data for current model

        # options that specify regrid options etc.
        themodel._global_configuration = CF
        themodel.plot_options = plot_options
        themodel.get_data()

        # copy current model to a variable named modelXXXX
        cmd = 'model' + str(model_cnt).zfill(4) + ' = ' \
            + 'themodel.copy(); del themodel'
        exec(cmd)  # store copy of cmip5 model in separate variable

        # append model to list of models ---
        proc_models.append('model' + str(model_cnt).zfill(4))
        model_cnt += 1

    ########################################################################
    # MULTIMODEL MEAN
    # here we have now all the model and variables read.
    # The list of all models is contained in the variable proc_models.
    f_mean_model = True
    if f_mean_model:
        # calculate climatological mean values: The models contain already
        # climatological information in the variables[] list. Thus there is
        # not need to take care for the different timesteps here. This
        # should have been handled already in the preprocessing.
        # generate instance of MeanModel to store result
        MEANMODEL = MeanModel(varmethods, intervals=CF.intervals)

        # sum up all models
        for i in range(len(proc_models)):
            exec('actmodel = ' + proc_models[i] + '.copy()')
            MEANMODEL.add_member(actmodel)
            del actmodel

        # calculate ensemble mean
        MEANMODEL.ensmean()

        # save mean model to file
        # include filename of configuration file
        MEANMODEL.save(get_temporary_directory(),
                       prefix='MEANMODEL_' + file[:-4])

        # add mean model to general list of models to process in analysis
        proc_models.append('MEANMODEL')

    ########################################################################
    # END MULTIMODEL MEAN
    ########################################################################

    ########################################################################
    # INIT reporting and plotting and diagnostics
    ########################################################################
    # Gleckler Plot
    global_gleckler = GlecklerPlot()

    # Report
    rep = Report(CF.options['report'],
                 'pyCMBS report - ' + CF.options['report'],
                 CF.options['author'],
                 outdir=outdir,
                 dpi=300,
                 format=CF.options['report_format'])
    cmd = 'cp ' + os.environ['PYCMBSPATH'] + os.sep + \
        'logo' + os.sep + 'Phytonlogo5.pdf ' + rep.outdir
    os.system(cmd)

    ########################################################################
    ########################################################################
    ########################################################################
    # MAIN ANALYSIS LOOP: perform analysis for each model and variable
    ########################################################################
    ########################################################################
    ########################################################################
    skeys = scripts.keys()
    for variable in variables:

        # register current variable in Gleckler Plot
        global_gleckler.add_variable(variable)

        # call analysis scripts for each variable
        for k in range(len(skeys)):
            if variable == skeys[k]:

                print 'Doing analysis for variable ... ', variable
                print '   ... ', scripts[variable]
                # model list is reformatted so it can be evaluated properly
                model_list = str(proc_models).replace("'", "")
                cmd = 'analysis.' + scripts[variable] + '(' + model_list \
                    + ',GP=global_gleckler,shift_lon=shift_lon, \
                        use_basemap=use_basemap,report=rep,\
                        interval=CF.intervals[variable],\
                        plot_options=PCFG)'

                eval(cmd)

    ########################################################################
    # GLECKLER PLOT finalization ...
    ########################################################################
    # generate Gleckler analysis plot for all variables and models analyzed ///
    global_gleckler.plot(vmin=-0.1,
                         vmax=0.1,
                         nclasses=16,
                         show_value=False,
                         ticks=[-0.1, -0.05, 0., 0.05, 0.1])
    oname = outdir + 'gleckler.pkl'
    if os.path.exists(oname):
        os.remove(oname)
    pickle.dump(global_gleckler.models,
                open(outdir + 'gleckler_models.pkl', 'w'))
    pickle.dump(global_gleckler.variables,
                open(outdir + 'gleckler_variables.pkl', 'w'))
    pickle.dump(global_gleckler.data, open(outdir + 'gleckler_data.pkl', 'w'))
    pickle.dump(global_gleckler._raw_data,
                open(outdir + 'gleckler_rawdata.pkl', 'w'))

    rep.section('Summary error statistics')
    rep.subsection('Gleckler metric')
    rep.figure(global_gleckler.fig,
               caption='Gleckler et al. (2008) model performance index',
               width='10cm')
    global_gleckler.fig.savefig(outdir + 'portraet_diagram.png',
                                dpi=200,
                                bbox_inches='tight')
    global_gleckler.fig.savefig(outdir + 'portraet_diagram.pdf',
                                dpi=200,
                                bbox_inches='tight')

    plt.close(global_gleckler.fig.number)

    # generate dictionary with observation labels for each variable
    labels_dict = {}
    for variable in variables:
        if variable not in PCFG.options.keys():
            continue
        varoptions = PCFG.options[variable]
        thelabels = {}
        for k in varoptions.keys():  # keys of observational datasets
            if k == 'OPTIONS':
                continue
            else:
                # only add observation to legend,
                # if option in INI file is set
                if varoptions[k]['add_to_report']:
                    # generate dictionary for GlecklerPLot legend
                    thelabels.update(
                        {int(varoptions[k]['gleckler_position']): k})
        labels_dict.update({variable: thelabels})
        del thelabels

    # legend for gleckler plot ///
    lcnt = 1
    for variable in variables:
        if variable not in PCFG.options.keys():
            continue
        varoptions = PCFG.options[variable]
        thelabels = labels_dict[variable]
        fl = global_gleckler._draw_legend(thelabels, title=variable.upper())
        if fl is not None:
            rep.figure(fl, width='8cm', bbox_inches=None)
            fl.savefig(outdir + 'legend_portraet_' + str(lcnt).zfill(5) +
                       '.png',
                       bbox_inches='tight',
                       dpi=200)
            plt.close(fl.number)
        del fl
        lcnt += 1

    # plot model ranking between different observational datasets ///
    rep.subsection('Model ranking consistency')
    for v in global_gleckler.variables:
        rep.subsubsection(v.upper())
        tmpfig = global_gleckler.plot_model_ranking(v,
                                                    show_text=True,
                                                    obslabels=labels_dict[v])
        if tmpfig is not None:
            rep.figure(tmpfig,
                       width='8cm',
                       bbox_inches=None,
                       caption='Model RANKING for different observational \
                       datasets: ' + v.upper())
            plt.close(tmpfig.number)
        del tmpfig

        # write a table with model ranking
        tmp_filename = outdir + 'ranking_table_' + v + '.tex'
        rep.open_table()
        global_gleckler.write_ranking_table(v,
                                            tmp_filename,
                                            fmt='latex',
                                            obslabels=labels_dict[v])
        rep.input(tmp_filename)
        rep.close_table(caption='Model rankings for variable ' + v.upper())

        # plot absolute model error
        tmpfig = global_gleckler.plot_model_error(v, obslabels=labels_dict[v])
        if tmpfig is not None:
            rep.figure(tmpfig,
                       width='8cm',
                       bbox_inches=None,
                       caption='Model ERROR for different observational \
                       datasets: ' + v.upper())
            plt.close(tmpfig.number)
        del tmpfig

    ########################################################################
    # CLEAN up and finish
    ########################################################################
    plt.close('all')
    rep.close()

    print('##########################################')
    print('# BENCHMARKING FINIHSED!                 #')
    print('##########################################')
コード例 #7
0
 def test_GlecklerPlot_InvalidNumberOfObservations(self):
     G = GlecklerPlot()
     G.add_model('echam5')
     G.add_model('mpi-esm')
     G.add_variable('ta')
     G.add_data('ta', 'echam5', 0.5, pos=1)
     G.add_data('ta', 'echam5', 0.25, pos=2)
     G.add_data('ta', 'echam5', -0.25, pos=3)
     G.add_data('ta', 'mpi-esm', -0.25, pos=4)
     G.add_data('ta', 'mpi-esm', -0.25, pos=5)
     with self.assertRaises(ValueError):
         G.plot()
コード例 #8
0
ファイル: test_plots.py プロジェクト: zengeo/pycmbs
    def test_GlecklerPlot(self):
        G = GlecklerPlot()
        G.add_model('echam5')
        G.add_model('mpi-esm')
        G.add_variable('ta')
        G.add_variable('P')
        G.add_data('ta', 'echam5', 0.5,pos=1)
        G.add_data('P', 'echam5',0.25,pos=1)
        G.add_data('P', 'echam5',-0.25,pos=2)
        G.add_data('P', 'mpi-esm',-0.25,pos=1)
        G.plot()

        G.plot_model_error('ta')
        G.plot_model_ranking('ta')
        G.write_ranking_table('ta', self._tmpdir + os.sep + 'nix.tex', fmt='latex')
        self.assertTrue(os.path.exists(self._tmpdir + os.sep + 'nix.tex'))
        if os.path.exists(self._tmpdir + os.sep + 'nix.tex'):
            os.remove(self._tmpdir + os.sep + 'nix.tex')
        G.write_ranking_table('ta', self._tmpdir + os.sep + 'nix1', fmt='latex')
        self.assertTrue(os.path.exists(self._tmpdir + os.sep + 'nix1.tex'))
        if os.path.exists(self._tmpdir + os.sep + 'nix1.tex'):
            os.remove(self._tmpdir + os.sep + 'nix1.tex')
コード例 #9
0
ファイル: test_plots.py プロジェクト: zengeo/pycmbs
 def test_GlecklerPlot_4obs(self):
     G = GlecklerPlot()
     G.add_model('echam5')
     G.add_model('mpi-esm')
     G.add_variable('ta')
     G.add_variable('P')
     G.add_data('P', 'echam5', 0.5,pos=1)
     G.add_data('P', 'echam5',0.25,pos=2)
     G.add_data('P', 'echam5',-0.25,pos=3)
     G.add_data('P', 'mpi-esm',-0.25,pos=4)
     G.plot()
コード例 #10
0
ファイル: test_plots.py プロジェクト: zengeo/pycmbs
 def test_GlecklerPlot_InvalidNumberOfObservations(self):
     G = GlecklerPlot()
     G.add_model('echam5')
     G.add_model('mpi-esm')
     G.add_variable('ta')
     G.add_data('ta', 'echam5', 0.5,pos=1)
     G.add_data('ta', 'echam5',0.25,pos=2)
     G.add_data('ta', 'echam5',-0.25,pos=3)
     G.add_data('ta', 'mpi-esm',-0.25,pos=4)
     G.add_data('ta', 'mpi-esm',-0.25,pos=5)
     with self.assertRaises(ValueError):
         G.plot()
コード例 #11
0
def main():
    plt.close('all')
    if len(sys.argv) > 1:
        if len(sys.argv) == 2:
            # a single argument was provides as option
            if sys.argv[1] == 'init':
                # copy INI files and a template configuration file
                # to current directory
                create_dummy_configuration()
                sys.exit()
            else:
                file = sys.argv[1]  # name of config file
                if not os.path.exists(file):
                    raise ValueError('Configuration file can not be \
                                      found: %s' % file)
        else:
            raise ValueError('Currently not more than one command \
                               line parameter supported!')
    else:  # default
        file = 'pyCMBS.cfg'
        print('*******************************************')
        print('* WELCOME to pycmbs.py                    *')
        print('* Happy benchmarking ...                  *')
        print('*******************************************')

    ####################################################################
    # CONFIGURATION and OPTIONS
    ####################################################################

    # read configuration file
    CF = config.ConfigFile(file)

    # read plotting options
    PCFG = config.PlotOptions()
    PCFG.read(CF)
    plot_options = PCFG

    ####################################################################
    # REMOVE previous Data warnings
    ####################################################################
    outdir = CF.options['outputdir']
    if outdir[-1] != os.sep:
        outdir += os.sep
    os.environ['PYCMBS_OUTPUTDIR'] = CF.options['outputdir']
    os.environ['PYCMBS_OUTPUTFORMAT'] = CF.options['report_format']

    os.environ['DATA_WARNING_FILE'] = outdir + 'data_warnings_' \
        + CF.options['report'] + '.log'
    if os.path.exists(os.environ['DATA_WARNING_FILE']):
        os.remove(os.environ['DATA_WARNING_FILE'])

    # init regions
    REGIONS = config.AnalysisRegions()

    for thevar in plot_options.options.keys():
        if thevar in plot_options.options.keys():
            print('Variable: %s' % thevar)
            for k in plot_options.options[thevar].keys():
                print('    Observation: %s' % k)

    if CF.options['basemap']:
        f_fast = False
    else:
        f_fast = True
    shift_lon = use_basemap = not f_fast

    ########################################################################
    # TIMES
    ########################################################################
    s_start_time = CF.start_date
    s_stop_time = CF.stop_date
    start_time = pylab.num2date(pylab.datestr2num(s_start_time))
    stop_time = pylab.num2date(pylab.datestr2num(s_stop_time))

    model_dict = {'rain': {'CMIP5':
                           {
                               'variable': 'pr',
                               'unit': 'mm/day',
                               'lat_name': 'lat',
                               'lon_name': 'lon',
                               'model_suffix': 'ensmean',
                               'model_prefix': 'Amon',
                               'file_format': 'nc',
                               'scale_factor': 86400.,
                               'valid_mask': 'ocean'
                           },


                           'JSBACH_RAW2':
                           {
                               'variable': 'precip_acc',
                               'unit': 'mm/day',
                               'lat_name': 'lat',
                               'lon_name': 'lon',
                               'file_format': 'nc',
                               'scale_factor': 86400.,
                               'valid_mask': 'global'
                           }
                           },


                  'evap': {'CMIP5':
                           {
                               'variable': 'evspsbl',
                               'unit': 'mm/day',
                               'lat_name': 'lat',
                               'lon_name': 'lon',
                               'model_suffix': 'ensmean',
                               'file_format': 'nc',
                               'model_prefix': 'Amon',
                               'scale_factor': 86400.,
                               'valid_mask': 'ocean'
                           }
                           },

                  'twpa': {'CMIP5':
                           {
                               'variable': 'clwvi',
                               'unit': 'kg/m^2',
                               'lat_name': 'lat',
                               'lon_name': 'lon',
                               'model_suffix': 'ensmean',
                               'file_format': 'nc',
                               'model_prefix': 'Amon',
                               'scale_factor': 1.,
                               'valid_mask': 'ocean'
                           }
                           },

                  'wind': {'CMIP5':
                          {
                              'variable': 'sfcWind',
                              'unit': 'm/s',
                              'lat_name': 'lat',
                              'lon_name': 'lon',
                              'model_suffix': 'ensmean',
                              'file_format': 'nc',
                              'model_prefix': 'Amon',
                              'scale_factor': 1.,
                              'valid_mask': 'ocean'
                          }
                  },

                  'wvpa': {'CMIP5':
                           {
                               'variable': 'prw',
                               'unit': 'kg m^2',
                               'lat_name': 'lat',
                               'lon_name': 'lon',
                               'model_suffix': 'ensmean',
                               'file_format': 'nc',
                               'model_prefix': 'Amon',
                               'scale_factor': 1,
                               'valid_mask': 'ocean'
                           }
                           },

                  'late': {'CMIP5':
                           {
                               'variable': 'hfls',
                               'unit': 'W/m^2',
                               'lat_name': 'lat',
                               'lon_name': 'lon',
                               'model_suffix': 'ensmean',
                               'file_format': 'nc',
                               'model_prefix': 'Amon',
                               'scale_factor': 1,
                               'valid_mask': 'ocean'
                           }
                           },

                  'hair': {'CMIP5':
                           {
                               'variable': 'huss',
                               'unit': '$kg/kg^2$',
                               'lat_name': 'lat',
                               'lon_name': 'lon',
                               'model_suffix': 'ensmean',
                               'file_format': 'nc',
                               'model_prefix': 'Amon',
                               'scale_factor': 1,
                               'valid_mask': 'ocean'
                           }
                           },

                  'seaice_concentration': {'CMIP5':
                                           {
                                               'variable': 'sic',
                                               'unit': '-',
                                               'lat_name': 'lat',
                                               'lon_name': 'lon',
                                               'model_suffix': 'ens_mean_185001-200512',
                                               'file_format': 'nc',
                                               'model_prefix': 'OImon',
                                               'scale_factor': 1,
                                               'valid_mask': 'ocean',
                                               'custom_path': '/home/m300028/shared/dev/svn/pyCMBS/dirk'
                                           },

                                           'CMIP3':
                                           {
                                               'variable': 'SICOMO',
                                               'unit': '-',
                                               'lat_name': 'lat',
                                               'lon_name': 'lon',
                                               'model_suffix': '1860-2100.ext',
                                               'file_format': 'nc',
                                               'model_prefix': '',
                                               'scale_factor': 100.,
                                               'valid_mask': 'ocean',
                                               'custom_path': '/home/m300028/shared/dev/svn/pyCMBS/dirk',
                                               'level': 0
                                           },
                                           },

                  'seaice_extent': {'CMIP5':
                                   {
                                    'variable': 'sic',
                                    'unit': '-',
                                    'lat_name': 'lat',
                                    'lon_name': 'lon',
                                    'model_suffix': 'ens_mean_185001-200512',
                                    'file_format': 'nc',
                                    'model_prefix': 'OImon',
                                    'scale_factor': 1,
                                    'valid_mask': 'ocean',
                                    'custom_path': '/home/m300028/shared/dev/svn/pyCMBS/dirk'
                                    },

                                    'CMIP3':
                                   {
                                    'variable': 'SICOMO',
                                       'unit': '-',
                                       'lat_name': 'lat',
                                       'lon_name': 'lon',
                                       'model_suffix': '1860-2100.ext',
                                       'file_format': 'nc',
                                       'model_prefix': '',
                                       'scale_factor': 100.,
                                       'valid_mask': 'ocean',
                                       'custom_path': '/home/m300028/shared/dev/svn/pyCMBS/dirk',
                                       'level': 0
                                   },
                  },

                  'budg': {'CMIP5':
                           {
                               'variable': 'budg',
                               'unit': 'mm/d',
                               'lat_name': 'lat',
                               'lon_name': 'lon',
                               'model_suffix': 'ensmean',
                               'file_format': 'nc',
                               'model_prefix': 'Amon',
                               'scale_factor': 86400.,
                               'valid_mask': 'ocean',
                               'custom_path': '/net/nas2/export/eo/workspace/m300036/pycmbs-cmsaf/data'
                           }
                           },


                  'sis': {'JSBACH_RAW2':
                          {
                              'variable': 'swdown_acc',
                              'unit': '$W/m^2$',
                              'lat_name': 'lat',
                              'lon_name': 'lon',
                              'file_format': 'nc',
                              'scale_factor': 1.,
                              'valid_mask': 'land'
                          },
                          'CMIP5':
                          {
                              'valid_mask': 'land'
                          }

                          },

                  'surface_upward_flux': {'JSBACH_RAW2':
                                          {
                                              'variable': 'swdown_reflect_acc',
                                              'unit': '$W/m^2$',
                                              'lat_name': 'lat',
                                              'lon_name': 'lon',
                                              'file_format': 'nc',
                                              'scale_factor': 1.,
                                              'valid_mask': 'land'
                                          }
                                          },

                  'albedo_vis': {'JSBACH_RAW2':
                                 {
                                     'variable': 'var14',
                                     'unit': '-',
                                     'lat_name': 'lat',
                                     'lon_name': 'lon',
                                     'file_format': 'nc',
                                     'scale_factor': 1.,
                                     'valid_mask': 'land'
                                 }
                                 },

                  'albedo_nir': {'JSBACH_RAW2':
                                 {
                                     'variable': 'var15',
                                     'unit': '-',
                                     'lat_name': 'lat',
                                     'lon_name': 'lon',
                                     'file_format': 'nc',
                                     'scale_factor': 1.,
                                     'valid_mask': 'land'
                                 }
                                 },

                  'temperature': {
                      'JSBACH_RAW2':
                      {
                          'variable': 'temp2',
                          'unit': 'K',
                          'lat_name': 'lat',
                          'lon_name': 'lon',
                          'file_format': 'nc',
                          'scale_factor': 1.,
                          'valid_mask': 'global'
                      }
                  }

                  }

    ########################################################################
    # INIT METHODS
    ########################################################################
    # names of analysis scripts for all variables ---
    scripts = CF.get_analysis_scripts()

    # get dictionary with methods how to read data for variables to be analyzed
    variables = CF.variables
    varmethods = CF.get_methods4variables(variables, model_dict)

    #/// READ DATA ///
    """
    create a Model instance for each model specified
    in the configuration file

    read the data for all variables and return a list
    of Data objects for further processing
    """

    model_cnt = 1
    proc_models = []

    for i in range(len(CF.models)):
        # assign model information from configuration
        data_dir = CF.dirs[i]
        model = CF.models[i]
        experiment = CF.experiments[i]

        #--- create model object and read data ---
        # results are stored in individual variables namex modelXXXXX
        if CF.dtypes[i].upper() == 'CMIP5':
            themodel = CMIP5Data(data_dir, model, experiment, varmethods,
                                 intervals=CF.intervals, lat_name='lat',
                                 lon_name='lon', label=model,
                                 start_time=start_time,
                                 stop_time=stop_time,
                                 shift_lon=shift_lon)
        elif CF.dtypes[i].upper() == 'CMIP5RAW':
            themodel = CMIP5RAWData(data_dir, model, experiment, varmethods,
                                    intervals=CF.intervals, lat_name='lat',
                                    lon_name='lon', label=model,
                                    start_time=start_time,
                                    stop_time=stop_time,
                                    shift_lon=shift_lon)
        elif CF.dtypes[i].upper() == 'JSBACH_BOT':
            themodel = JSBACH_BOT(data_dir, varmethods, experiment,
                                  intervals=CF.intervals,
                                  start_time=start_time,
                                  stop_time=stop_time,
                                  name=model, shift_lon=shift_lon)
        elif CF.dtypes[i].upper() == 'JSBACH_RAW':
            themodel = JSBACH_RAW(data_dir, varmethods, experiment,
                                  intervals=CF.intervals,
                                  name=model,
                                  shift_lon=shift_lon,
                                  start_time=start_time,
                                  stop_time=stop_time
                                  )
        elif CF.dtypes[i].upper() == 'JSBACH_RAW2':
            themodel = JSBACH_RAW2(data_dir, varmethods, experiment,
                                   intervals=CF.intervals,
                                   start_time=start_time,
                                   stop_time=stop_time,
                                   name=model, shift_lon=shift_lon,
                                   model_dict=model_dict)
        elif CF.dtypes[i].upper() == 'CMIP3':
            themodel = CMIP3Data(data_dir, model, experiment, varmethods,
                                 intervals=CF.intervals, lat_name='lat',
                                 lon_name='lon', label=model,
                                 start_time=start_time,
                                 stop_time=stop_time,
                                 shift_lon=shift_lon)
        else:
            raise ValueError('Invalid model type: %s' % CF.dtypes[i])

        #--- read data for current model ---

        # options that specify regrid options etc.
        themodel.plot_options = plot_options
        themodel.get_data()

        # copy current model to a variable named modelXXXX ---
        cmd = 'model' + str(model_cnt).zfill(4) + ' = ' \
            + 'themodel.copy(); del themodel'
        exec(cmd)  # store copy of cmip5 model in separate variable

        # append model to list of models ---
        proc_models.append('model' + str(model_cnt).zfill(4))
        model_cnt += 1

    ########################################################################
    # MULTIMODEL MEAN
    # here we have now all the model and variables read.
    # The list of all models is contained in the variable proc_models.
    f_mean_model = True
    if f_mean_model:
        # calculate climatological mean values: The models contain already
        # climatological information in the variables[] list. Thus there is
        # not need to take care for the different timesteps here. This
        # should have been handled already in the preprocessing.
        # generate instance of MeanModel to store result
        MEANMODEL = MeanModel(varmethods, intervals=CF.intervals)

        # sum up all models
        for i in range(len(proc_models)):
            exec('actmodel = ' + proc_models[i] + '.copy()')
            MEANMODEL.add_member(actmodel)
            del actmodel

        # calculate ensemble mean
        MEANMODEL.ensmean()

        # save mean model to file
        MEANMODEL.save(get_temporary_directory(), prefix='MEANMODEL_' + file[:-4])  # include filename of configuration file

        # add mean model to general list of models to process in analysis
        proc_models.append('MEANMODEL')

    ########################################################################
    # END MULTIMODEL MEAN
    ########################################################################
    ########################################################################
    # INIT reporting and plotting and diagnostics
    ########################################################################
    # Gleckler Plot
    global_gleckler = GlecklerPlot()

    # Report
    rep = Report(CF.options['report'],
                 'pyCMBS report - ' + CF.options['report'],
                 CF.options['author'],
                 outdir=outdir,
                 dpi=300, format=CF.options['report_format'])
    cmd = 'cp ' + os.environ['PYCMBSPATH'] + '/logo/Phytonlogo5.pdf ' + rep.outdir
    os.system(cmd)

    ########################################################################
    ########################################################################
    ########################################################################
    # MAIN ANALYSIS LOOP: perform analysis for each model and variable
    ########################################################################
    ########################################################################
    ########################################################################
    skeys = scripts.keys()
    for variable in variables:

        #/// register current variable in Gleckler Plot
        global_gleckler.add_variable(variable)

        #/// call analysis scripts for each variable
        for k in range(len(skeys)):
            if variable == skeys[k]:
                print 'Doing analysis for variable ... ', variable
                print '   ... ', scripts[variable]
                # model list is reformatted so it can be evaluated properly
                model_list = str(proc_models).replace("'", "")
                cmd = 'analysis.' + scripts[variable] + '(' + model_list \
                    + ',GP=global_gleckler,shift_lon=shift_lon, \
                        use_basemap=use_basemap,report=rep,\
                        interval=CF.intervals[variable],\
                        plot_options=PCFG,regions=REGIONS.regions)'
                eval(cmd)

    ########################################################################
    # GLECKLER PLOT finalization ...
    ########################################################################
    #/// generate Gleckler analysis plot for all variables and models analyzed ///
    global_gleckler.plot(vmin=-0.1, vmax=0.1, nclasses=16, show_value=False, ticks=[-0.1, -0.05, 0., 0.05, 0.1])
    oname = outdir + 'gleckler.pkl'
    if os.path.exists(oname):
        os.remove(oname)
    pickle.dump(global_gleckler.models,
                open(outdir + 'gleckler_models.pkl', 'w'))
    pickle.dump(global_gleckler.variables,
                open(outdir + 'gleckler_variables.pkl', 'w'))
    pickle.dump(global_gleckler.data,
                open(outdir + 'gleckler_data.pkl', 'w'))
    pickle.dump(global_gleckler._raw_data,
                open(outdir + 'gleckler_rawdata.pkl', 'w'))

    rep.section('Summary error statistics')
    rep.subsection('Gleckler metric')
    rep.figure(global_gleckler.fig,
               caption='Gleckler et al. (2008) model performance index',
               width='10cm')
    global_gleckler.fig.savefig(outdir + 'portraet_diagram.png', dpi=200, bbox_inches='tight')
    global_gleckler.fig.savefig(outdir + 'portraet_diagram.pdf', dpi=200, bbox_inches='tight')

    plt.close(global_gleckler.fig.number)



    # generate dictionary with observation labels for each variable
    labels_dict = {}
    for variable in variables:
        if variable not in PCFG.options.keys():
            continue
        varoptions = PCFG.options[variable]
        thelabels = {}
        for k in varoptions.keys():  # keys of observational datasets
            if k == 'OPTIONS':
                continue
            else:
                # only add observation to legend,
                # if option in INI file is set
                if varoptions[k]['add_to_report']:
                    # generate dictionary for GlecklerPLot legend
                    thelabels.update({int(varoptions[k]['gleckler_position']): k})
        labels_dict.update({variable: thelabels})
        del thelabels

    #/// legend for gleckler plot ///
    lcnt = 1
    for variable in variables:
        if variable not in PCFG.options.keys():
            continue
        varoptions = PCFG.options[variable]
        thelabels = labels_dict[variable]
        fl = global_gleckler._draw_legend(thelabels, title=variable.upper())
        rep.figure(fl, width='8cm', bbox_inches=None)
        fl.savefig(outdir + 'legend_portraet_' + str(lcnt).zfill(5) + '.png', bbox_inches='tight', dpi=200)
        plt.close(fl.number)
        del fl
        lcnt += 1

    #/// plot model ranking between different observational datasets ///
    rep.subsection('Model ranking consistency')
    for v in global_gleckler.variables:
        rep.subsubsection(v.upper())
        tmpfig = global_gleckler.plot_model_ranking(v, show_text=True, obslabels=labels_dict[v])
        rep.figure(tmpfig, width='8cm', bbox_inches=None,
                   caption='Model RANKING for different observational \
                   datasets: ' + v.upper())
        plt.close(tmpfig.number)
        del tmpfig

        # write a table with model ranking
        tmp_filename = outdir + 'ranking_table_' + v + '.tex'
        rep.open_table()
        global_gleckler.write_ranking_table(v, tmp_filename, fmt='latex', obslabels=labels_dict[v])
        rep.input(tmp_filename)
        rep.close_table(caption='Model rankings for variable ' + v.upper())

        # plot absolute model error
        tmpfig = global_gleckler.plot_model_error(v, obslabels=labels_dict[v])
        rep.figure(tmpfig, width='8cm', bbox_inches=None,
                   caption='Model ERROR for different observational \
                   datasets: ' + v.upper())
        plt.close(tmpfig.number)
        del tmpfig

    ########################################################################
    # CLEAN up and finish
    ########################################################################
    plt.close('all')
    rep.close()

    print('##########################################')
    print('# BENCHMARKING FINIHSED!                 #')
    print('##########################################')
コード例 #12
0
    def test_gleckler_index(self):
        """
        test Reichler index/Gleckler plot
        """

        # generate sample data
        # sample data
        tmp = np.zeros((5, 3, 1))
        tmp[:, 0, 0] = np.ones(5) * 1.
        tmp[:, 1, 0] = np.ones(5) * 2.
        tmp[:, 2, 0] = np.ones(5) * 5.

        # The data is like ...
        #| 1 | 2 | 5 |
        #| 1 | 2 | 5 |
        #| 1 | 2 | 5 |
        #| 1 | 2 | 5 |
        #| 1 | 2 | 5 |

        x = self.D.copy()
        x._temporal_subsetting(0, 4)

        x.data = np.ma.array(tmp, mask=tmp != tmp)
        x.std = np.ones(x.data.shape)
        x.time[0] = pl.datestr2num('2000-02-15')
        x.time[1] = pl.datestr2num('2000-03-15')
        x.time[2] = pl.datestr2num('2000-04-15')
        x.time[3] = pl.datestr2num('2000-05-15')
        x.time[4] = pl.datestr2num('2000-06-15')

        y = self.D.copy()
        y._temporal_subsetting(0, 4)
        tmp = np.ones(x.data.shape)  # sample data 2
        y.data = np.ma.array(tmp, mask=tmp != tmp)
        y.time[0] = pl.datestr2num('2000-02-15')
        y.time[1] = pl.datestr2num('2000-03-15')
        y.time[2] = pl.datestr2num('2000-04-15')
        y.time[3] = pl.datestr2num('2000-05-15')
        y.time[4] = pl.datestr2num('2000-06-15')

        # Case 1: same area weights
        # cell area
        tmp = np.ones((3, 1))
        x.cell_area = tmp * 1.

        #| 1-1 | 2-1 | 5-1 |
        #| 1-1 | 2-1 | 5-1 |
        #| 1-1 | 2-1 | 5-1 |
        #| 1-1 | 2-1 | 5-1 |
        #| 1-1 | 2-1 | 5-1 |
        #===================
        #| 0   | 5   | 5*4**2=5*16. = 80 |
        #==> E2 = sqrt(85./(15.))
        D = GlecklerPlot()
        r = D.calc_index(x, y, 'a', 'b', time_weighting=False)

        wt = np.ones(5) / 5.
        ref = np.sqrt(((85. / 15.) * wt).sum())
        t = np.abs(1. - r / ref)
        self.assertLess(t, 0.000001)  # relative error

        D = GlecklerPlot()
        r = D.calc_index(x, y, 'a', 'b')

        wt = np.asarray([29., 31., 30., 31., 30.])
        wt = wt / wt.sum()
        ref = np.sqrt(((85. / 15.) * wt).sum())
        t = np.abs(1. - r / ref)
        self.assertLess(t, 0.000001)  # relative error

        # Case 2: Different area weights
        # cell area
        tmp = np.ones((3, 1))
        tmp[1, 0] = 2.
        x.cell_area = tmp * 1.

        #| 1-1=0 | 2-1=1 | 5-1=16 |
        #| 1-1=0 | 2-1=1 | 5-1=16 |
        #| 1-1=0 | 2-1=1 | 5-1=16 |
        #| 1-1=0 | 2-1=1 | 5-1=16 |
        #| 1-1=0 | 2-1=1 | 5-1=16 |
        #--------------------------
        # w = 0.25 w = 0.5  w=0.25|
        #--------------------------

        # 0.25*0 + 0.5 * 1 + 0.25 * 16 = 0 + 0.5 + 4 = 4.5
        # the mean of that is 4.5 for each timestep
        # mean because the overall weights are calculated as such that
        # they give a total weight if 1

        #  diagnostic
        D = GlecklerPlot()
        r = D.calc_index(x, y, 'a', 'b', time_weighting=False)

        wt = np.ones(5) / 5.
        ref = np.sqrt((4.5 * wt).sum())
        t = np.abs(1. - r / ref)
        self.assertLess(t, 0.000001)  # relative error

        wt = np.asarray([29., 31., 30., 31., 30.])
        wt = wt / wt.sum()
        ref = np.sqrt((4.5 * wt).sum())
        t = np.abs(1. - r / ref)
        self.assertLess(t, 0.000001)  # relative error

        # Case 3: use different std
        x.std = np.ones(x.data.shape)
        x.std[:, 2, 0] = 0.5

        #| 1-1=0 | 2-1=1  | 5-1=16 / 0.5 |
        #| 1-1=0 | 2-1=1  | 5-1=16 / 0.5 |
        #| 1-1=0 | 2-1=1  | 5-1=16 / 0.5 |
        #| 1-1=0 | 2-1=1  | 5-1=16 / 0.5 |
        #| 1-1=0 | 2-1=1  | 5-1=16 / 0.5 |
        #--------------------------------
        # w = 0.25    w = 0.5    w=0.25|
        #    0    +   0.5   +  0.25*32 = 0.5 + 8 = 8.5

        D = GlecklerPlot()
        r = D.calc_index(x, y, 'a', 'b', time_weighting=False)

        wt = np.ones(5) / 5.
        ref = np.sqrt((8.5 * wt).sum())
        t = np.abs(1. - r / ref)
        self.assertLess(t, 0.000001)  # relative error

        wt = np.asarray([29., 31., 30., 31., 30.])
        wt = wt / wt.sum()
        ref = np.sqrt((8.5 * wt).sum())
        t = np.abs(1. - r / ref)
        self.assertLess(t, 0.000001)  # relative error