//  [python]
def main(inargs):
    """Run the program."""

    # Process data
    bmin, bmax = inargs.bin_bounds
    bin_edges = numpy.arange(bmin, bmax + inargs.bin_width, inargs.bin_width)
    xvals = (bin_edges[1:] + bin_edges[:-1]) / 2

    label_counts = initialise_label_counts(inargs.labels)
    ref_datasets = ['EN4']
    hist_dict = collections.OrderedDict()
    metadata_dict = {}
    for groupnum, infiles in enumerate(inargs.data_files):
        label = inargs.labels[groupnum]
        print(label)
        vfile = inargs.volume_files[groupnum]
        bfile = inargs.basin_files[groupnum]
        ref = (label in ref_datasets) and len(inargs.labels) > 1
        vcube, bcube, time_constraint = read_supporting_inputs(vfile, bfile, inargs, ref=ref)
        dcube = combine_infiles(infiles, inargs, time_constraint)
        metadata_dict = update_metadata(metadata_dict, vfile, vcube, bfile, bcube, infiles[0], dcube)
        voldist_timeseries = numpy.array([])
        V_timeseries = numpy.array([])
        for time_slice in dcube.slices_over('time'):
            df = create_df(time_slice, inargs.variable, vcube, bcube, basin=inargs.basin, abort=operator.not_(inargs.no_abort))
            voldist, edges, binnum = s.binned_statistic(df[inargs.variable].values, df['volume'].values, statistic='sum', bins=bin_edges)
            V = voldist.cumsum()
            voldist_timeseries = numpy.vstack([voldist_timeseries, voldist]) if voldist_timeseries.size else voldist
            V_timeseries = numpy.vstack([V_timeseries, V]) if V_timeseries.size else V
        for metric in inargs.metrics:
            hist_dict[(label, metric, label_counts[label])] = calc_metric(voldist_timeseries, V_timeseries, metric)
        label_counts[label] = label_counts[label] + 1
    
    # Plot individual model/run results
    nrows, ncols = inargs.subplot_config
    fig, axes = plt.subplots(nrows, ncols, figsize=(9*ncols, 6*nrows))
    for plotnum, metric in enumerate(inargs.metrics):
        ax = axes.flatten()[plotnum] if type(axes) == numpy.ndarray else axes

        labelnum = 0
        labelled = []
        for key, yvals in hist_dict.items():
            label, dict_metric, count = key
            if dict_metric == metric:
                if not ((label in ref_datasets) and (metric not in ['dV/dT', 'dV/dS'])):
                    plot_label = label if not label in labelled else None
                    ax.plot(xvals, yvals, 'o-', color=inargs.colors[labelnum], label=plot_label)
                    labelled.append(label) 
                labelnum = labelnum + 1

        ax.set_title(metric_names[metric])
        xlabel, ylabel = get_labels(time_slice, inargs.variable, metric)
        ax.set_xlabel(xlabel)
        ax.set_ylabel(ylabel)
        ax.grid(True)
        set_axis_limits(ax, plotnum, inargs.xlim, inargs.ylim)
        if metric in ['dVdt/dVdT', 'dVdt/dVdS']:
            ax.legend(loc=2)
        else:
            ax.legend(loc=1)
        plt.ticklabel_format(style='sci', axis='y', scilimits=(0,0), useMathText=True)
        ax.yaxis.major.formatter._useMathText = True

    title = get_title(dcube, inargs.basin)
    plt.suptitle(title)

    # Save output
    dpi = inargs.dpi if inargs.dpi else plt.savefig.__globals__['rcParams']['figure.dpi']
    print('dpi =', dpi)
    plt.savefig(inargs.outfile, bbox_inches='tight', dpi=dpi)
    
    # Metadata
    log_text = cmdprov.new_log(infile_history=metadata_dict, git_repo=repo_dir)
    log_file = re.sub('.png', '.met', inargs.outfile)
    cmdprov.write_log(log_file, log_text)