#!/usr/bin/env perl

use strict;
use Cwd;
use English;
use Getopt::Long;
use File::Basename;
use IO::File;
use IO::Handle;

sub usage {
    die <<'EOF';

SYNOPSIS 

  getTiming -lid [options]

  running this command in $CASEROOT/Tools
  The output will be a local file in $CASEROOT/timing
  (assumes that this utility exists in $CASEROOT/Tools)

OPTIONS

  -lid           yymmdd-hhmmss
  -help [or -h]  Print usage to STDOUT (optional).


OPTIONS
  -help [or -h]  Print usage to STDOUT (optional).

EOF
} 

my %opts = (lid => '999999-999999');

GetOptions(
    "lid=s"   => \$opts{'lid'},
    "h|help"  => \$opts{'help'},
    )  or usage();


my $CASE	 = `./xmlquery CASE         -value `;
my $CASEROOT	 = `./xmlquery CASEROOT	    -value `;
my $CCSMUSER	 = `./xmlquery CCSMUSER	    -value `;
my $CONTINUE_RUN = `./xmlquery CONTINUE_RUN -value `;
my $RUNDIR	 = `./xmlquery RUNDIR	    -value `;
my $RUN_TYPE	 = `./xmlquery RUN_TYPE	    -value `;

my $lid = $opts{'lid'};
my $timeroot = "$CASEROOT/Tools";
my $date = `date`;

my $inittype = "FALSE";
if ($CONTINUE_RUN eq "FALSE" && $RUN_TYPE eq "startup") {$inittype = "TRUE";}
if ($CONTINUE_RUN eq "FALSE" && $RUN_TYPE eq "hybrid" ) {$inittype = "TRUE";}

my $bin = "${RUNDIR}/timing/cesm_timing_stats";
if (! -e "$bin") {
    die "$bin not found";
}

my $fin  = "${CASEROOT}/timing/cesm_timing_stats.${lid}";
my $fout = "${CASEROOT}/timing/cesm_timing.$CASE.${lid}";

my $sysmod = "cp -f ${bin} ${fin}";
system($sysmod) == 0 or die "$sysmod failed: $?\n";

gettiming2 ($fin, $fout);  

exit (0);

#----------------------------------------------------------------------------------
sub gettiming2 {

    my($fin, $fout) = @_;

    my $dirs = "$CASEROOT/Tools";
    unshift @INC, $dirs;
    require SetupTools;

    # Retreve and expand all case xml variables
    my %xml=();
    chdir $CASEROOT;
    SetupTools::getxmlvars($CASEROOT, \%xml);
    foreach my $attr (keys %xml) {
	$xml{$attr} = SetupTools::expand_xml_var($xml{$attr}, \%xml);
    }

    my $m999 = -999;
    open( F, $fin ) or {warn "Can't open $fin"};
    my @fin = <F>;
    close F;

    my $tlen = 1.0;
    if ($xml{NCPL_BASE_PERIOD} eq "decade") {$tlen = 3650.0;}
    if ($xml{NCPL_BASE_PERIOD} eq "year")   {$tlen = 365.0;}
    if ($xml{NCPL_BASE_PERIOD} eq "days")   {$tlen = 1.0;}
    if ($xml{NCPL_BASE_PERIOD} eq "hour")   {$tlen = 1.0/24.0;}

    my($nprocs,$ncount);
    &gettime2('CPL:CLOCK_ADVANCE ',$nprocs, $ncount, \@fin);

#    if ($nprocs <= 1) {die "WARNING getTiming2: 1 nprocs - running in mpiserial mode so no additional timing to be reported , exiting \n"};
    
    my $nsteps = $ncount / $nprocs;
    my $adays = $nsteps * $tlen / $xml{ATM_NCPL};
    my $odays = $adays;
    
    if ($inittype eq "TRUE") {$odays = $adays - ($tlen / $xml{OCN_NCPL}) ;}
    
    my $atm = $xml{NTASKS_ATM} * $xml{NTHRDS_ATM} ;
    my $lnd = $xml{NTASKS_LND} * $xml{NTHRDS_LND} ;
    my $rof = $xml{NTASKS_ROF} * $xml{NTHRDS_ROF} ;
    my $wav = $xml{NTASKS_WAV} * $xml{NTHRDS_WAV} ;
    my $ice = $xml{NTASKS_ICE} * $xml{NTHRDS_ICE} ;
    my $ocn = $xml{NTASKS_OCN} * $xml{NTHRDS_OCN} ;
    my $glc = $xml{NTASKS_GLC} * $xml{NTHRDS_GLC} ;
    my $cpl = $xml{NTASKS_CPL} * $xml{NTHRDS_CPL} ;
    
    my $apemin = $xml{ROOTPE_ATM};
    my $lpemin = $xml{ROOTPE_LND};
    my $wpemin = $xml{ROOTPE_ROF};
    my $rpemin = $xml{ROOTPE_WAV};
    my $ipemin = $xml{ROOTPE_ICE};
    my $opemin = $xml{ROOTPE_OCN};
    my $gpemin = $xml{ROOTPE_GLC};
    my $cpemin = $xml{ROOTPE_CPL};
    
    my $apemax = $xml{ROOTPE_ATM} + $xml{NTASKS_ATM} * $xml{PSTRID_ATM} - 1 ;
    my $lpemax = $xml{ROOTPE_LND} + $xml{NTASKS_LND} * $xml{PSTRID_LND} - 1 ;
    my $rpemax = $xml{ROOTPE_ROF} + $xml{NTASKS_ROF} * $xml{PSTRID_ROF} - 1 ;
    my $wpemax = $xml{ROOTPE_WAV} + $xml{NTASKS_WAV} * $xml{PSTRID_WAV} - 1 ;
    my $ipemax = $xml{ROOTPE_ICE} + $xml{NTASKS_ICE} * $xml{PSTRID_ICE} - 1 ;
    my $opemax = $xml{ROOTPE_OCN} + $xml{NTASKS_OCN} * $xml{PSTRID_OCN} - 1 ;
    my $gpemax = $xml{ROOTPE_GLC} + $xml{NTASKS_GLC} * $xml{PSTRID_GLC} - 1 ;
    my $cpemax = $xml{ROOTPE_CPL} + $xml{NTASKS_CPL} * $xml{PSTRID_CPL} - 1 ;
    
    my $peminmax = $apemin;
    if( $lpemin > $peminmax ) { $peminmax = $lpemin; }
    if( $rpemin > $peminmax ) { $peminmax = $rpemin; }
    if( $wpemin > $peminmax ) { $peminmax = $wpemin; }
    if( $ipemin > $peminmax ) { $peminmax = $ipemin; }
    if( $opemin > $peminmax ) { $peminmax = $opemin; }
    if( $gpemin > $peminmax ) { $peminmax = $gpemin; }
    ##if( $cpemin > $peminmax ) { $peminmax = $cpemin; }
    $peminmax = $peminmax + 1;   
    
    my $maxoffset = 40;
    my $extraoff  = 20;
    my $aoffset = int(($maxoffset * $apemin) / $peminmax) + $extraoff;
    my $loffset = int(($maxoffset * $lpemin) / $peminmax) + $extraoff;
    my $roffset = int(($maxoffset * $rpemin) / $peminmax) + $extraoff;
    my $woffset = int(($maxoffset * $wpemin) / $peminmax) + $extraoff;
    my $ioffset = int(($maxoffset * $ipemin) / $peminmax) + $extraoff;
    my $goffset = int(($maxoffset * $gpemin) / $peminmax) + $extraoff;
    my $ooffset = int(($maxoffset * $opemin) / $peminmax) + $extraoff;
    ##$coffset = int(($maxoffset * $cpemin) / $peminmax);
    my $coffset = 0;
    my $date = localtime();

    my $fh = IO::File->new($fout, '>' ) or die "can't open file: $fout\n";

    print $fh "---------------- CCSM TIMING PROFILE ---------------------\n";

    print $fh "  Case        : $xml{CASE}\n";
    print $fh "  LID         : $lid\n";
    print $fh "  Machine     : $xml{MACH}\n";
    print $fh "  Caseroot    : $xml{CASEROOT}\n";
    print $fh "  Timeroot    : $xml{CASEROOT}/Tools\n";
    print $fh "  CCSM User   : $xml{CCSMUSER}\n";
    print $fh "  CCSM Tag    : $xml{CCSM_REPOTAG}  (best guess)\n";
    print $fh "  Curr Date   : $date\n";

    print $fh "  grid        : $xml{GRID}\n";
    print $fh "  compset     : $xml{CCSM_COMPSET}\n";
    print $fh "  run_type    : $xml{RUN_TYPE}, continue_run = $xml{CONTINUE_RUN} (inittype = $inittype)\n";
    print $fh "  stop_option : $xml{STOP_OPTION}, stop_n = $xml{STOP_N}\n";
    print $fh "  run_length  : $adays days ($odays for ocean)\n\n";

    print  $fh "  component       comp_pes    root_pe   tasks  x threads instances (stride) \n";
    print  $fh "  ---------        ------     -------   ------   ------  ---------  ------  \n";
    printf $fh "  cpl = %-8s   %-6u      %-6u   %-6u x %-6u  %-6u (%-6u) \n",$xml{COMP_CPL},$cpl,$xml{ROOTPE_CPL},$xml{NTASKS_CPL},$xml{NTHRDS_CPL},1              ,$xml{PSTRID_CPL};
    printf $fh "  glc = %-8s   %-6u      %-6u   %-6u x %-6u  %-6u (%-6u) \n",$xml{COMP_GLC},$glc,$xml{ROOTPE_GLC},$xml{NTASKS_GLC},$xml{NTHRDS_GLC},$xml{NINST_GLC},$xml{PSTRID_GLC};
    printf $fh "  wav = %-8s   %-6u      %-6u   %-6u x %-6u  %-6u (%-6u) \n",$xml{COMP_WAV},$wav,$xml{ROOTPE_WAV},$xml{NTASKS_WAV},$xml{NTHRDS_WAV},$xml{NINST_WAV},$xml{PSTRID_WAV};
    printf $fh "  lnd = %-8s   %-6u      %-6u   %-6u x %-6u  %-6u (%-6u) \n",$xml{COMP_LND},$lnd,$xml{ROOTPE_LND},$xml{NTASKS_LND},$xml{NTHRDS_LND},$xml{NINST_LND},$xml{PSTRID_LND};
    printf $fh "  rof = %-8s   %-6u      %-6u   %-6u x %-6u  %-6u (%-6u) \n",$xml{COMP_ROF},$rof,$xml{ROOTPE_ROF},$xml{NTASKS_ROF},$xml{NTHRDS_ROF},$xml{NINST_ROF},$xml{PSTRID_ROF};
    printf $fh "  ice = %-8s   %-6u      %-6u   %-6u x %-6u  %-6u (%-6u) \n",$xml{COMP_ICE},$ice,$xml{ROOTPE_ICE},$xml{NTASKS_ICE},$xml{NTHRDS_ICE},$xml{NINST_ICE},$xml{PSTRID_ICE};
    printf $fh "  atm = %-8s   %-6u      %-6u   %-6u x %-6u  %-6u (%-6u) \n",$xml{COMP_ATM},$atm,$xml{ROOTPE_ATM},$xml{NTASKS_ATM},$xml{NTHRDS_ATM},$xml{NINST_ATM},$xml{PSTRID_ATM};
    printf $fh "  ocn = %-8s   %-6u      %-6u   %-6u x %-6u  %-6u (%-6u) \n",$xml{COMP_OCN},$ocn,$xml{ROOTPE_OCN},$xml{NTASKS_OCN},$xml{NTHRDS_OCN},$xml{NINST_OCN},$xml{PSTRID_OCN};

    my($nmin,$nmax,$tmin,$tmax,$wmin,$wmax,$nullf);
    my($fmin,$fmax,$lmin,$lmax,$rmin,$rmax,$imin,$imax,$gmin,$gmax);
    my($amin,$amax,$omin,$omax,$otmin,$otmax,$wtmin,$wtmax);

    gettime(' CPL:INIT '       ,$nmin ,$nmax,$nullf	,$fh);
    gettime(' CPL:RUN_LOOP '   ,$tmin ,$tmax,$nullf	,$fh);
    gettime(' CPL:TPROF_WRITE ',$wtmin,$wtmax,$nullf	,$fh);
    gettime(' CPL:FINAL '      ,$fmin ,$fmax,$nullf	,$fh);
    gettime(' CPL:LND_RUN '    ,$lmin ,$lmax,$nullf	,$fh);
    gettime(' CPL:ROF_RUN '    ,$rmin ,$rmax,$nullf	,$fh);
    gettime(' CPL:ICE_RUN '    ,$imin ,$imax,$nullf	,$fh);
    gettime(' CPL:GLC_RUN '    ,$gmin ,$gmax,$nullf	,$fh);
    gettime(' CPL:WAV_RUN '    ,$wmin ,$wmax,$nullf	,$fh);
    gettime(' CPL:ATM_RUN '    ,$amin ,$amax,$nullf	,$fh);
    gettime(' CPL:OCN_RUN '    ,$omin ,$omax,$nullf	,$fh);
    gettime(' CPL:OCNT_RUN '   ,$otmin,$otmax,$nullf	,$fh);

    # pick OCNT_RUN for tight coupling
    if ($otmax > $omax) {
	$omin = $otmin;
	$omax = $otmax;
    }
    my($cmin,$cmax,$xmin,$xmax,$ocnwaittime,$null);
    gettime(' CPL:RUN ' ,$cmin,$cmax,$nullf,$fh);
    gettime(' CPL:COMM ',$xmin,$xmax,$nullf,$fh);
    
    gettime(' CPL:C2O_INITWAIT ',$ocnwaittime,$null,$nullf,$fh);
    my $ocnrunitime;
    if ( $odays != 0.0 ) {
	$ocnrunitime = ($omax) * ($adays/$odays - 1.0);
    } else {
	$ocnrunitime = 0.0;
    }
    my $correction = $ocnrunitime - $ocnwaittime;
    if ($correction < 0) {$correction = 0.0;}
    
    $tmax = $tmax + $wtmax + $correction;
    $omax = $omax + $ocnrunitime;
    
    my $pecost = $xml{TOTALPES};
    if ($xml{COST_PES} > 0) {
	$pecost = $xml{COST_PES};
    }
    
    print $fh "\n";
    print $fh "  total pes active           : $xml{TOTALPES} \n";
    print $fh "  pes per node               : $xml{PES_PER_NODE} \n";
    print $fh "  pe count for cost estimate : $pecost \n";
    print $fh "\n";
    
    print  $fh "  Overall Metrics: \n";
    printf $fh "    Model Cost:         %10.2f   pe-hrs/simulated_year \n",($tmax*365.*$pecost)/(3600.*$adays);
    printf $fh "    Model Throughput:   %10.2f   simulated_years/day \n",(86400.*$adays)/($tmax*365.);
    print  $fh "\n";
    
    printf $fh "    Init Time   :  %10.3f seconds \n",$nmax;
    printf $fh "    Run Time    :  %10.3f seconds   %10.3f seconds/day \n",$tmax,$tmax/$adays;
    printf $fh "    Final Time  :  %10.3f seconds \n",$fmax;
    
    print  $fh "\n";
    
    printf $fh "    Actual Ocn Init Wait Time     :  %10.3f seconds \n",$ocnwaittime;
    printf $fh "    Estimated Ocn Init Run Time   :  %10.3f seconds \n",$ocnrunitime;
    printf $fh "    Estimated Run Time Correction :  %10.3f seconds \n",$correction;
    printf $fh "      (This correction has been applied to the ocean and total run times) \n";
    
    print $fh "\n";
    print $fh "Runs Time in total seconds, seconds/model-day, and model-years/wall-day \n";
    print $fh "CPL Run Time represents time in CPL pes alone, not including time associated with data exchange with other components \n";
    print $fh "\n";

    my $tmaxr = 0.0;
    my $lmaxr = 0.0;
    my $rmaxr = 0.0;
    my $imaxr = 0.0;
    my $amaxr = 0.0;
    my $omaxr = 0.0;
    my $gmaxr = 0.0;
    my $wmaxr = 0.0;
    my $cmaxr = 0.0;
    my $xmaxr = 0.0;

    if ($tmax > 0.0) { $tmaxr = ($adays*86400.)/($tmax*365.); }
    if ($lmax > 0.0) { $lmaxr = ($adays*86400.)/($lmax*365.); }
    if ($rmax > 0.0) { $rmaxr = ($adays*86400.)/($rmax*365.); }
    if ($imax > 0.0) { $imaxr = ($adays*86400.)/($imax*365.); }
    if ($amax > 0.0) { $amaxr = ($adays*86400.)/($amax*365.); }
    if ($omax > 0.0) { $omaxr = ($adays*86400.)/($omax*365.); }
    if ($gmax > 0.0) { $gmaxr = ($adays*86400.)/($gmax*365.); }
    if ($wmax > 0.0) { $wmaxr = ($adays*86400.)/($wmax*365.); }
    if ($cmax > 0.0) { $cmaxr = ($adays*86400.)/($cmax*365.); }
    if ($xmax > 0.0) { $xmaxr = ($adays*86400.)/($xmax*365.); }
    
    printf $fh "    TOT Run Time:  %10.3f seconds   %10.3f seconds/mday   %10.2f myears/wday \n",$tmax,$tmax/$adays,$tmaxr;
    printf $fh "    LND Run Time:  %10.3f seconds   %10.3f seconds/mday   %10.2f myears/wday \n",$lmax,$lmax/$adays,$lmaxr;
    printf $fh "    ROF Run Time:  %10.3f seconds   %10.3f seconds/mday   %10.2f myears/wday \n",$rmax,$rmax/$adays,$rmaxr;
    printf $fh "    ICE Run Time:  %10.3f seconds   %10.3f seconds/mday   %10.2f myears/wday \n",$imax,$imax/$adays,$imaxr;
    printf $fh "    ATM Run Time:  %10.3f seconds   %10.3f seconds/mday   %10.2f myears/wday \n",$amax,$amax/$adays,$amaxr;
    printf $fh "    OCN Run Time:  %10.3f seconds   %10.3f seconds/mday   %10.2f myears/wday \n",$omax,$omax/$adays,$omaxr;
    printf $fh "    GLC Run Time:  %10.3f seconds   %10.3f seconds/mday   %10.2f myears/wday \n",$gmax,$gmax/$adays,$gmaxr;
    printf $fh "    WAV Run Time:  %10.3f seconds   %10.3f seconds/mday   %10.2f myears/wday \n",$wmax,$wmax/$adays,$wmaxr;
    printf $fh "    CPL Run Time:  %10.3f seconds   %10.3f seconds/mday   %10.2f myears/wday \n",$cmax,$cmax/$adays,$cmaxr;
    printf $fh "    CPL COMM Time: %10.3f seconds   %10.3f seconds/mday   %10.2f myears/wday \n",$xmax,$xmax/$adays,$xmaxr;


    print $fh "\n\n---------------- DRIVER TIMING FLOWCHART --------------------- \n\n";

    my $pstrlen = 25;
    my $hoffset =  1;
    print $fh "   NOTE: min:max driver timers (seconds/day):   \n";

    my $xoff = $pstrlen+$hoffset+$coffset;
    printf $fh " %${xoff}s CPL (pes %u to %u) \n",' ',$cpemin,$cpemax;

    $xoff = $pstrlen+$hoffset+$ooffset;
    printf $fh " %${xoff}s OCN (pes %u to %u) \n",' ',$opemin,$opemax;

    $xoff = $pstrlen+$hoffset+$loffset;
    printf $fh " %${xoff}s LND (pes %u to %u) \n",' ',$lpemin,$lpemax;

    $xoff = $pstrlen+$hoffset+$roffset;

    printf $fh " %${xoff}s ROF (pes %u to %u) \n",' ',$rpemin,$rpemax;
    $xoff = $pstrlen+$hoffset+$ioffset;

    printf $fh " %${xoff}s ICE (pes %u to %u) \n",' ',$ipemin,$ipemax;
    $xoff = $pstrlen+$hoffset+$aoffset;

    printf $fh " %${xoff}s ATM (pes %u to %u) \n",' ',$apemin,$apemax;
    $xoff = $pstrlen+$hoffset+$goffset;

    printf $fh " %${xoff}s GLC (pes %u to %u) \n",' ',$gpemin,$gpemax;
    $xoff = $pstrlen+$hoffset+$woffset;

    printf $fh " %${xoff}s WAV (pes %u to %u) \n",' ',$wpemin,$wpemax;
    print  $fh "\n";

    prttime(' CPL:CLOCK_ADVANCE '   ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:OCNPRE1_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:OCNPRE1 '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:ATMOCN1_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:ATMOCN1 '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:OCNPREP_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:OCNPREP '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:C2O_BARRIER '     ,$ooffset,$odays,$coffset	,$fh);
    prttime(' CPL:C2O '             ,$ooffset,$odays,$coffset	,$fh);
    prttime(' CPL:LNDPREP_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:LNDPREP '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:C2L_BARRIER '     ,$loffset,$adays,$coffset	,$fh);
    prttime(' CPL:C2L '             ,$loffset,$adays,$coffset	,$fh);
    prttime(' CPL:ICEPREP_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:ICEPREP '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:C2I_BARRIER '     ,$ioffset,$adays,$coffset	,$fh);
    prttime(' CPL:C2I '             ,$ioffset,$adays,$coffset	,$fh);
    prttime(' CPL:WAVPREP_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:WAVPREP '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:C2W_BARRIER '     ,$ioffset,$adays,$coffset	,$fh);
    prttime(' CPL:C2W '             ,$ioffset,$adays,$coffset	,$fh);
    prttime(' CPL:ROFPREP_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:ROFPREP '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:C2R_BARRIER '     ,$roffset,$adays,$coffset	,$fh);
    prttime(' CPL:C2R '             ,$roffset,$adays,$coffset	,$fh);
    prttime(' CPL:ICE_RUN_BARRIER ' ,$ioffset,$adays,$m999	,$fh);
    prttime(' CPL:ICE_RUN '         ,$ioffset,$adays,$m999	,$fh);
    prttime(' CPL:LND_RUN_BARRIER ' ,$loffset,$adays,$m999	,$fh);
    prttime(' CPL:LND_RUN '         ,$loffset,$adays,$m999	,$fh);
    prttime(' CPL:ROF_RUN_BARRIER ' ,$roffset,$adays,$m999	,$fh);
    prttime(' CPL:ROF_RUN '         ,$roffset,$adays,$m999	,$fh);
    prttime(' CPL:WAV_RUN_BARRIER ' ,$roffset,$adays,$m999	,$fh);
    prttime(' CPL:WAV_RUN '         ,$roffset,$adays,$m999	,$fh);
    prttime(' CPL:OCNT_RUN_BARRIER ',$ooffset,$odays,$m999	,$fh);
    prttime(' CPL:OCNT_RUN '        ,$ooffset,$odays,$m999	,$fh);
    prttime(' CPL:O2CT_BARRIER '    ,$ooffset,$odays,$coffset	,$fh);
    prttime(' CPL:O2CT '            ,$ooffset,$odays,$coffset	,$fh);
    prttime(' CPL:OCNPOSTT_BARRIER ',$coffset,$adays,$m999	,$fh);
    prttime(' CPL:OCNPOSTT '        ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:ATMOCNP_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:ATMOCNP '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:L2C_BARRIER '     ,$loffset,$adays,$coffset	,$fh);
    prttime(' CPL:L2C '             ,$loffset,$adays,$coffset	,$fh);
    prttime(' CPL:LNDPOST_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:LNDPOST '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:GLCPREP_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:GLCPREP '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:C2G_BARRIER '     ,$goffset,$adays,$coffset	,$fh);
    prttime(' CPL:C2G '             ,$goffset,$adays,$coffset	,$fh);
    prttime(' CPL:R2C_BARRIER '     ,$roffset,$adays,$coffset	,$fh);
    prttime(' CPL:R2C '             ,$roffset,$adays,$coffset	,$fh);
    prttime(' CPL:ROFPOST_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:ROFPOST '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:BUDGET1_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:BUDGET1 '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:I2C_BARRIER '     ,$ioffset,$adays,$coffset	,$fh);
    prttime(' CPL:I2C '             ,$ioffset,$adays,$coffset	,$fh);
    prttime(' CPL:ICEPOST_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:ICEPOST '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:FRACSET_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:FRACSET '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:ATMOCN2_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:ATMOCN2 '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:OCNPRE2_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:OCNPRE2 '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:C2O2_BARRIER '    ,$ooffset,$odays,$coffset	,$fh);
    prttime(' CPL:C2O2 '            ,$ooffset,$odays,$coffset	,$fh);
    prttime(' CPL:ATMOCNQ_BARRIER'  ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:ATMOCNQ '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:ATMPREP_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:ATMPREP '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:C2A_BARRIER '     ,$aoffset,$adays,$coffset	,$fh);
    prttime(' CPL:C2A '             ,$aoffset,$adays,$coffset	,$fh);
    prttime(' CPL:OCN_RUN_BARRIER ' ,$ooffset,$odays,$m999	,$fh);
    prttime(' CPL:OCN_RUN '         ,$ooffset,$odays,$m999	,$fh);
    prttime(' CPL:ATM_RUN_BARRIER ' ,$aoffset,$adays,$m999	,$fh);
    prttime(' CPL:ATM_RUN '         ,$aoffset,$adays,$m999	,$fh);
    prttime(' CPL:GLC_RUN_BARRIER ' ,$goffset,$adays,$m999	,$fh);
    prttime(' CPL:GLC_RUN '         ,$goffset,$adays,$m999	,$fh);
    prttime(' CPL:W2C_BARRIER '     ,$goffset,$adays,$coffset	,$fh);
    prttime(' CPL:W2C '             ,$goffset,$adays,$coffset	,$fh);
    prttime(' CPL:WAVPOST_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:WAVPOST '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:G2C_BARRIER '     ,$goffset,$adays,$coffset	,$fh);
    prttime(' CPL:G2C '             ,$goffset,$adays,$coffset	,$fh);
    prttime(' CPL:GLCPOST_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:GLCPOST '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:A2C_BARRIER '     ,$aoffset,$adays,$coffset	,$fh);
    prttime(' CPL:A2C '             ,$aoffset,$adays,$coffset	,$fh);
    prttime(' CPL:ATMPOST_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:ATMPOST '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:BUDGET2_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:BUDGET2 '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:BUDGET3_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:BUDGET3 '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:BUDGETF_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:BUDGETF '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:O2C_BARRIER '     ,$ooffset,$odays,$coffset	,$fh);
    prttime(' CPL:O2C '             ,$ooffset,$odays,$coffset	,$fh);
    prttime(' CPL:OCNPOST_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:OCNPOST '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:RESTART_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:RESTART'          ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:HISTORY_BARRIER ' ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:HISTORY '         ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:TSTAMP_WRITE '    ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:TPROF_WRITE '     ,$coffset,$adays,$m999	,$fh);
    prttime(' CPL:RUN_LOOP_BSTOP '  ,$coffset,$adays,$m999	,$fh);

    #print $fh "\n --- overall total --- \n";
    #prttime(' CPL:RUN_LOOP '    ,$coffset,$adays,$m999);

    print $fh "\n\n";
    
    print $fh "More info on coupler timing:\n";
    
    print $fh "\n";
    prttime(' CPL:OCNPRE1 '         ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:ocnpre1_atm2ocn ' ,$coffset,$adays,$m999,$fh);

    print $fh "\n";
    prttime(' CPL:OCNPREP '         ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:OCNPRE2 '         ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:ocnprep_avg '     ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:ocnprep_diagav '  ,$coffset,$adays,$m999,$fh);

    print $fh "\n";
    prttime(' CPL:LNDPREP '         ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:lndprep_atm2lnd ' ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:lndprep_mrgx2l '  ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:lndprep_diagav '  ,$coffset,$adays,$m999,$fh);

    print $fh "\n";
    prttime(' CPL:ICEPREP '         ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:iceprep_ocn2ice ' ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:iceprep_atm2ice ' ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:iceprep_mrgx2i '  ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:iceprep_diagav '  ,$coffset,$adays,$m999,$fh);

    print $fh "\n";
    prttime(' CPL:WAVPREP '         ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:wavprep_atm2wav ' ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:wavprep_ocn2wav ' ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:wavprep_ice2wav ' ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:wavprep_mrgx2w '  ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:wavprep_diagav '  ,$coffset,$adays,$m999,$fh);

    print $fh "\n";
    prttime(' CPL:ROFPREP '         ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:rofprep_l2xavg '  ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:rofprep_lnd2rof ' ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:rofprep_mrgx2r '  ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:rofprep_diagav '  ,$coffset,$adays,$m999,$fh);

    print $fh "\n";
    prttime(' CPL:GLCPREP '         ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:glcprep_avg '     ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:glcprep_lnd2glc ' ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:glcprep_mrgx2g '  ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:glcprep_diagav '  ,$coffset,$adays,$m999,$fh);

    print $fh "\n";
    prttime(' CPL:ATMPREP '         ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:atmprep_xao2atm ' ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:atmprep_ocn2atm ' ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:atmprep_alb2atm ' ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:atmprep_ice2atm ' ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:atmprep_lnd2atm ' ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:atmprep_mrgx2a '  ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:atmprep_diagav '  ,$coffset,$adays,$m999,$fh);

    print $fh "\n";
    prttime(' CPL:ATMOCNP '         ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:ATMOCN1 '         ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:ATMOCN2 '         ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:atmocnp_ice2ocn ' ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:atmocnp_wav2ocn ' ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:atmocnp_fluxo '   ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:atmocnp_fluxe '   ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:atmocnp_mrgx2o '  ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:atmocnp_accum '   ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:atmocnp_ocnalb '  ,$coffset,$adays,$m999,$fh);

    print $fh "\n";
    prttime(' CPL:ATMOCNQ '         ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:atmocnq_ocn2atm ' ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:atmocnq_fluxa '   ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:atmocnq_atm2ocnf ',$coffset,$adays,$m999,$fh);

    print $fh "\n";
    prttime(' CPL:OCNPOSTT '        ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:OCNPOST '         ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:ocnpost_diagav '  ,$coffset,$adays,$m999,$fh);

    print $fh "\n";
    prttime(' CPL:LNDPOST '         ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:lndpost_diagav '  ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:lndpost_acc2lr '  ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:lndpost_acc2lg '  ,$coffset,$adays,$m999,$fh);

    print $fh "\n";
    prttime(' CPL:ROFOST '          ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:rofpost_diagav '  ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:rofpost_histaux ' ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:rofpost_rof2lnd ' ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:rofpost_rof2ice ' ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:rofpost_rof2ocn ' ,$coffset,$adays,$m999,$fh);

    print $fh "\n";
    prttime(' CPL:ICEPOST '         ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:icepost_diagav '  ,$coffset,$adays,$m999,$fh);

    print $fh "\n";
    prttime(' CPL:WAVPOST '         ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:wavpost_diagav '  ,$coffset,$adays,$m999,$fh);

    print $fh "\n";
    prttime(' CPL:GLCPOST '         ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:glcpost_diagav '  ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:glcpost_glc2lnd ' ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:glcpost_glc2ice ' ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:glcpost_glc2ocn ' ,$coffset,$adays,$m999,$fh);

    print $fh "\n";
    prttime(' CPL:ATMPOST '         ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:atmpost_diagav '  ,$coffset,$adays,$m999,$fh);

    print $fh "\n";
    prttime(' CPL:BUDGET '          ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:BUDGET1 '         ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:BUDGET2 '         ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:BUDGET3 '         ,$coffset,$adays,$m999,$fh);
    prttime(' CPL:BUDGETF '         ,$coffset,$adays,$m999,$fh);
    print $fh "\n\n";

    close $fh;
}

#----------------------------------------------------------------------------------
sub prttime{

    my($str, $offset, $div,$coff, $fh) = @_;
    my($min,$max,$found,$zoff,$maxd,$mind);
    my($cstr,$clen,$csp);
    
    my $datalen = 20;
    
    $cstr = "<---->";
    $clen = 6;
    
    gettime($str,$min,$max,$found);
    $mind = $min;
    $maxd = $max;
    if ($div >= 1.0) {
	$mind = $min/$div;
	$maxd = $max/$div;
    }
    
    my $pstrlen = 25;
    if ($mind >= 0.0 && $maxd >= 0.0 && $found > 0.5) {
	if ($coff >= 0) {
	    $zoff = $pstrlen + $coff + int(($datalen-$clen)/2);
	    $csp = $offset   - $coff - int(($datalen-$clen)/2);
	    printf $fh " %-${zoff}s%-${csp}s %8.3f:%8.3f \n",$str,$cstr,$mind,$maxd;
	}
	else {
	    $zoff = $pstrlen + $offset;
	    printf $fh " %-${zoff}s %8.3f:%8.3f \n",$str,$mind,$maxd;
	}
    }
}

#----------------------------------------------------------------------------------
sub gettime{

    my($str, $min, $max, $found, $fh) = @_;
    my(@tmp,@tmp2);
    
    #  set max here to something nonzero but small to avoid divide by zero
    $found = 0;
    $min = 0;
    $max = 0;

    my $strw = $str;
    $strw =~ s/^\s+//;
    $strw =~ s/\s+$//;
    @tmp=`grep -w "$strw" $fin | grep -E '\\(' | grep -v hashtable`;
    
    #   print $fh "tcx1 $#tmp $tmp[0]\n";
    if ($#tmp == 0) {
	#       print $fh "tcx2 $tmp[0]\n";
	#pw       if ($tmp[0] =~ m/\s*${strw}\s*\d+\s*(\d*\.\d+)\s*\(.*\)\s*(\d*\.\d+)\s*\(.*\)/) {
	#pw2 if ($tmp[0] =~ m/\s*${strw}\s*[^\(]+\s+(\d*\.\d+)\s*\(.*\)\s*(\d*\.\d+)\s*\(.*\)/) {
	if ($tmp[0] =~ m/\s*${strw}\s*\d+\s*\d+\s*\S+\s*\S+\s*(\d*\.\d+)\s*\(.*\)\s*(\d*\.\d+)\s*\(.*\)/) {
	    #	   print $fh "tcxa $tmp[0]\n";
	    #           print $fh "tcxb $1 $2 \n";
	    $max=$1;
	    $min=$2;
	    $found=1.0;
	}
    }
    
    @_[1]=$min;
    @_[2]=$max;
    @_[3]=$found;
    
}

#----------------------------------------------------------------------------------
sub gettime2{
    my($str,$procs,$count,$fin_ref)=@_;
    my(@tmp,@tmp2);
    
#  initialize procs and count
    $procs = 0;
    $count = 0;
    
    my $strw = $str;
    $strw =~ s/^\s+//;
    $strw =~ s/\s+$//;
    my @tmp = grep( /$strw/, @$fin_ref );

    if ($#tmp == 0) {
	#pw2 if ($tmp[0] =~ /\s*${strw}\s+([\.\de\+]+)\s+(\d+)\s/) {
	if ($tmp[0] =~ m/\s*${strw}\s*(\d+)\s*\d+\s*(\S+)/) {
#pw	    $procs=$2;
#pw	    $count=$1;
	    $procs=$1;
	    $count=$2;
	}
	elsif ($tmp[0] =~ /\s*${strw}\s+(\d+)\s/) {
	    $procs = 1;
	    $count = $1;
	}
    }
    @_[1]=$procs;
    @_[2]=$count;
}


