Package ucar.nc2.dt.fmrc

Source Code of ucar.nc2.dt.fmrc.FmrcInventory$Inventory

/*
* Copyright 1998-2009 University Corporation for Atmospheric Research/Unidata
*
* Portions of this software were developed by the Unidata Program at the
* University Corporation for Atmospheric Research.
*
* Access and use of this software shall impose the following obligations
* and understandings on the user. The user is granted the right, without
* any fee or cost, to use, copy, modify, alter, enhance and distribute
* this software, and any derivative works thereof, and its supporting
* documentation for any purpose whatsoever, provided that this entire
* notice appears in all copies of the software, derivative works and
* supporting documentation.  Further, UCAR requests that the user credit
* UCAR/Unidata in any publications that result from the use of this
* software or in any product that includes this software. The names UCAR
* and/or Unidata, however, may not be used in any advertising or publicity
* to endorse or promote any products or commercial entity unless specific
* written permission is obtained from UCAR/Unidata. The user also
* understands that UCAR/Unidata is not obligated to provide the user with
* any support, consulting, training or assistance of any kind with regard
* to the use, operation and performance of this software nor to provide
* the user with any updates, revisions, new versions or "bug fixes."
*
* THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package ucar.nc2.dt.fmrc;

import ucar.nc2.units.DateFormatter;
import ucar.nc2.util.DiskCache2;

import java.util.*;
import java.io.*;
import java.text.SimpleDateFormat;

import org.jdom.output.XMLOutputter;
import org.jdom.output.Format;
import org.jdom.Document;
import org.jdom.Element;
import thredds.catalog.crawl.CatalogCrawler;
import thredds.catalog.InvDataset;
import thredds.catalog.InvAccess;
import thredds.catalog.ServiceType;
import thredds.catalog.InvCatalogRef;
import ucar.unidata.util.StringUtil;

/**
* A collection of ForecastModelRun (aka "run").
* The Set if {Run, TimeCoord, Grid} are grouped into "run Sequences" {{Run, TimeCoord} X {Grid}}
*
* The FmrcDefinition object defines what is to be expected.
* The TimeMatrixDataset object keeps an inventory for all variables for the ForecastModelRunCollection.
*
* The set of possible valid times vs run times is thought of as a 2D time matrix.
*
* All this rigamorole is because NCEP grid files are so irregular.
*
* <pre>
* Data Structures
*
*  List RunTime Date
*  List ForecastTime Date
*  List Offsets Double
*
*  List VertTimeCoord
*    double[] values
*
*  List TimeCoord
*    double[] offsetHour
*
*  List RunSeq    // sequence of runs; ie sequence of TimeCoords; ie actual time coord
*    List Run run;
*      Date runDate
*      TimeCoord
*
*    List UberGrid
*     String name
*     List RunExpected  // corresponds to the runs in the RunSeq, matches to expected inventory
*       Run run;                              //  actual time coord
*       ForecastModelRun.Grid grid;           //  contains actual vert coord for this Run
*       ForecastModelRun.TimeCoord expected;  // expected time coord
*       FmrcDefinition.Grid expectedGrid;     // expected grid, vertCoord
*
* </pre>
*
* @author caron
*/
public class FmrcInventory {
  static private org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(FmrcInventory.class);

  private static SimpleDateFormat dateFormatShort = new java.text.SimpleDateFormat("MM-dd HH.mm");
  private static SimpleDateFormat dateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd HH.mm'Z'");
  private static boolean debug = false;

  static {
    dateFormat.setTimeZone(java.util.TimeZone.getTimeZone("GMT")); // same as UTC
    dateFormatShort.setTimeZone(java.util.TimeZone.getTimeZone("GMT")); // same as UTC
  }

  private String name; // name of ForecastModelRunCollection

  // list of unique ForecastModelRunInventory.TimeCoord
  private int tc_seqno = 0;
  private List<ForecastModelRunInventory.TimeCoord> timeCoords = new ArrayList<ForecastModelRunInventory.TimeCoord>();

  // list of unique ForecastModelRunInventory.EnsCoord
  private int ec_seqno = 0;
  private List<ForecastModelRunInventory.EnsCoord> ensCoords = new ArrayList<ForecastModelRunInventory.EnsCoord>();

  // list of unique ForecastModelRunInventory.VertCoord
  private int vc_seqno = 0;
  private List<ForecastModelRunInventory.VertCoord> vertCoords = new ArrayList<ForecastModelRunInventory.VertCoord>();

  // list of all unique RunSeq objects
  private int run_seqno = 0;
  private List<RunSeq> runSequences = new ArrayList<RunSeq>();

  // the variables
  private Map<String,UberGrid> uvHash = new HashMap<String,UberGrid>(); // hash of UberGrid
  private List<UberGrid> varList;              // sorted list of UberGrid

  // all run times
  private Set<Date> runTimeHash = new HashSet<Date>();
  private List<Date> runTimeList;                // sorted list of Date : all run times

  // all offsets
  private Set<Double> offsetHash = new HashSet<Double>();
  private List<Double> offsetList;                // sorted list of Double : all offset hours

  // all forecast times
  private Set<Date> forecastTimeHash = new HashSet<Date>();
  private List<Date> forecastTimeList;          // sorted list of Date : all forecast times

  // optional definition, describes what is expected
  private String fmrcDefinitionDir;
  private FmrcDefinition definition = null;

  private TimeMatrixDataset tmAll;
  private Calendar cal = new GregorianCalendar(); // for date computations
  private DateFormatter formatter = new  DateFormatter();


  /**
   * Open a collection of ForecastModelRun. An optional definition file may exist.
   * Call addRun() to add the ForecastModelRuns.
   *
   * @param fmrcDefinitionDir put optional definition file in this directory
   * @param name name for the collection, the definition file = name + ".fmrcDefinition.xml";
   * @throws IOException on io error
   */
  FmrcInventory(String fmrcDefinitionDir, String name) throws IOException {
    this.fmrcDefinitionDir = fmrcDefinitionDir;
    int pos = name.indexOf(".fmrcDefinition.xml");
    if (pos > 0)
      this.name = name.substring(0,pos);
    else
      this.name = name;

    // see if theres a definition file
    FmrcDefinition fmrc = new FmrcDefinition();
    if (fmrc.readDefinitionXML( getDefinitionPath()))
      definition = fmrc;
    else
      log.warn("FmrcCollection has no Definition "+getDefinitionPath());

    cal.setTimeZone( TimeZone.getTimeZone("UTC"));
  }

  public String getName() { return name; }
  public String getDefinitionPath() {
    if (fmrcDefinitionDir != null) {
      return fmrcDefinitionDir + name + ".fmrcDefinition.xml";
    }
    return "./" + name + ".fmrcDefinition.xml";
  }
  public List<ForecastModelRunInventory.TimeCoord> getTimeCoords() { return timeCoords; }
  public List<RunSeq> getRunSequences() { return runSequences; }
  public List<ForecastModelRunInventory.EnsCoord> getEnsCoords() { return ensCoords; }
  public List<ForecastModelRunInventory.VertCoord> getVertCoords() { return vertCoords; }

  public String getSuffixFilter() {
    return (definition == null) ? null  : definition.getSuffixFilter();
  }

  /* public void report( PrintStream out, boolean showMising ) {
    if (definition != null)
      definition.report( this, out, showMising);
    else
      out.println("No definition found");
  } */

  public FmrcDefinition getDefinition() { return definition; }

  /**
   * Add a ForecastModelRun to the collection.
   * @param fmr add this ForecastModelRun to the collection
   */
  void addRun(ForecastModelRunInventory fmr) {
    if (debug) System.out.println(" Adding ForecastModelRun "+fmr.getRunDateString());

    // track all run times
    runTimeHash.add( fmr.getRunDate());

    // add each time coord, variable
    for (ForecastModelRunInventory.TimeCoord tc : fmr.getTimeCoords()) {
      // Construct list of unique TimeCoords. Reset their id so they are unique.
      // Note that after this, we ignore the nested variables.
      ForecastModelRunInventory.TimeCoord tcUse = findTime(tc);
      if (tcUse == null) {
        this.timeCoords.add(tc);
        tcUse = tc;
        tc.setId(Integer.toString(tc_seqno));
        tc_seqno++;
      }

      // create a Run object, encapsolating this ForecastModelRun
      Run run = new Run(fmr.getRunDate(), tcUse);
      for (double offset : tcUse.getOffsetHours()) {
        Date fcDate = addHour(fmr.getRunDate(), offset);
        Inventory inv = new Inventory(fmr.getRunDate(), fcDate, offset);
        run.invList.add(inv);
        forecastTimeHash.add(fcDate); // track all forecast times
        offsetHash.add(offset); // track all offset hours
      }

      // Construct list of Variables across all runs in the collection.
      for (ForecastModelRunInventory.Grid grid :  tc.getGrids()) {
        UberGrid uv = uvHash.get(grid.name);
        if (uv == null) {
          // we may not have this variable in the definition
          if ((definition != null) && (null == definition.findSeqForVariable(grid.name))) {
            log.warn("FmrcCollection Definition " + name + " does not contain variable " + grid.name);
            continue; // skip it
          } else {
            uv = new UberGrid(grid.name);
            uvHash.put(grid.name, uv);
          }
        }
        uv.addRun(run, grid);
      }
    }
  }

  // call after adding all runs
  void finish() {
    // create the overall list of variables
    varList = new ArrayList<UberGrid>(uvHash.values());
    Collections.sort(varList);

    // create the overall list of run times
    runTimeList = Arrays.asList((Date[]) runTimeHash.toArray( new Date[ runTimeHash.size()]));
    Collections.sort(runTimeList);

    // create the overall list of forecast times
    forecastTimeList = Arrays.asList((Date[]) forecastTimeHash.toArray( new Date[ forecastTimeHash.size()]));
    Collections.sort(forecastTimeList);

    // create the overall list of offsets
    offsetList = Arrays.asList((Double[]) offsetHash.toArray( new Double[ offsetHash.size()]));
    Collections.sort(offsetList);

    // finish the variables, assign to a RunSeq
    for (UberGrid uv : varList) {
      uv.finish();
      uv.seq = findRunSequence(uv.runs); // assign to a unique RunSeq
      uv.seq.addVariable(uv); // add to list of vars for that RunSeq
    }
  }

  private UberGrid findVar(String varName) {
    for (UberGrid uv : varList) {
      if (uv.name.equals(varName))
        return uv;
    }
    return null;
  }

  /* public void report(PrintStream out) {

    // show all the forecast time coordinates
    for (int i = 0; i < times.size(); i++) {
      ForecastModelRun.TimeCoord tc = (ForecastModelRun.TimeCoord) times.get(i);
      out.print("TC"+tc.getId() +" =[");
      double[] offsets = tc.getOffsetHours();
      for (int j = 0; j < offsets.length; j++) {
        if (j > 0) out.print(",");
        out.print(offsets[j]);
      }
      out.println("]");
    }

    TimeSeq tseq = null;
    for (int i = 0; i < varList.size(); i++) {
      UberGrid uv = (UberGrid) varList.get(i);
      uv.tm.finish();

      if (tseq != uv.seq) {
        tseq = uv.seq;
        out.print("\n" + tseq.name + "= [");
        for (int j = 0; j < tseq.seq.size(); j++) {
          ForecastModelRun.TimeCoord tc = (ForecastModelRun.TimeCoord) tseq.seq.get(j);
          if (j > 0) out.print(",");
          if (tc == null)
            out.print("null");
          else
            out.print(tc.getId());
        }
        out.println("]");
      }
      out.println("  "+uv.name);

      if (false) {
        out.println("matrix for " + uv.name + ":");

        List rtimes = uv.tm.getRunTimes();
        List ftimes = uv.tm.getForecastTimes();
        for (int k = ftimes.size() - 1; k >= 0; k--) {
          Date ftime = (Date) ftimes.get(k);
          out.print(" " + DateUnit.getStandardDateString2(ftime) + ": ");

          for (int j = rtimes.size() - 1; j >= 0; j--) {
            Date rtime = (Date) rtimes.get(j);
            if (uv.tm.isPresent(rtime, ftime))
              out.print("X");
            else
              out.print(" ");
          }
          out.println();
        }
        out.println();

        out.println();
      }
    }
  } */


  /////////////////////////////////////////////////

  private class Inventory {
    Date forecastTime;
    Date runTime;
    double hourOffset;

    Inventory(Date runTime, Date forecastTime, double hourOffset) {
      this.runTime = runTime;
      this.hourOffset = hourOffset;
      this.forecastTime = forecastTime;
    }
  }

  // another abstraction of ForecastModelRun
  static class Run implements Comparable {
    ForecastModelRunInventory.TimeCoord tc;
    List<Inventory> invList; // list of Inventory
    Date runTime;

    Run(Date runTime, ForecastModelRunInventory.TimeCoord tc) {
      this.runTime = runTime;
      this.tc = tc;
      invList = new ArrayList<Inventory>();
    }

    public int compareTo(Object o) {
      Run other = (Run) o;
      return runTime.compareTo(other.runTime);
    }

        // Instances that have the same offsetHours are equal
    public boolean equalsData(Run orun) {
      if (invList.size() != orun.invList.size())
        return false;
      for (int i = 0; i < invList.size() ; i++) {
        Inventory inv = invList.get(i);
        Inventory oinv = orun.invList.get(i);
        if (inv.hourOffset != oinv.hourOffset)
          return false;
      }
      return true;
    }

    double[] getOffsetHours() {
      if (tc != null)
        return tc.getOffsetHours();

      double[] result = new double[ invList.size()];
      for (int i = 0; i < invList.size(); i++) {
        Inventory inv = invList.get(i);
        result[i] = inv.hourOffset;
      }
      return result;
    }

  }

  /** Represents a sequence of Run, each run has a particular TimeCoord.
   * keep track of all the Variables with the same RunSeq */
  class RunSeq {
    List<Run> runs; // list of Run
    List<UberGrid> vars = new ArrayList<UberGrid>(); // list of UberGrid
    String name;

    RunSeq(List<RunExpected> runs) {
      this.runs = new ArrayList<Run>();
      for (RunExpected rune : runs) {
        this.runs.add(rune.run);
      }
      name = "RunSeq" + run_seqno;
      run_seqno++;
    }

    /**
     * Decide if this RunSeq is equivilent to the list of Runs.
     * @param oruns list of RunExpected
     * @return true if it has an equivilent set of runs.
     */
    boolean equalsData(List<RunExpected> oruns) {
      if (runs.size() != oruns.size()) return false;
      for (int i = 0; i < runs.size(); i++) {
        Run run = runs.get(i);
        RunExpected orune = oruns.get(i);
        if (!run.runTime.equals(orune.run.runTime)) return false;
        if (!run.equalsData(orune.run)) return false;
      }
      return true;
    }

    // keep track of all the Variables with the this RunSeq
    void addVariable( UberGrid uv) { vars.add( uv); }

    List<UberGrid> getVariables() {
      return vars;
    }
  }

  private RunSeq findRunSequence(List<RunExpected> runs) {
    for (RunSeq seq : runSequences) {
      if (seq.equalsData(runs)) return seq;
    }
    RunSeq seq = new RunSeq(runs);
    runSequences.add(seq);
    return seq;
  }

  private ForecastModelRunInventory.TimeCoord findTime(ForecastModelRunInventory.TimeCoord want) {
    for (ForecastModelRunInventory.TimeCoord tc : timeCoords) {
      if (want.equalsData(tc))
        return tc;
    }
    return null;
  }

  private ForecastModelRunInventory.EnsCoord findEnsCoord(ForecastModelRunInventory.EnsCoord want) {
    for (ForecastModelRunInventory.EnsCoord ec : ensCoords) {
      if (want.equalsData(ec))
        return ec;
    }
    return null;
  }

  private ForecastModelRunInventory.VertCoord findVertCoord(ForecastModelRunInventory.VertCoord want) {
    for (ForecastModelRunInventory.VertCoord vc : vertCoords) {
      if (want.equalsData(vc))
        return vc;
    }
    return null;
  }

  ////////////////////////////////////////////////////////////////////

  // The collection across runs of one variable
  class UberGrid implements Comparable {
    String name;
    List<RunExpected> runs = new ArrayList<RunExpected>()// List of RunExpected
    ForecastModelRunInventory.VertCoord vertCoordUnion = null;
    ForecastModelRunInventory.EnsCoord ensCoordUnion = null;
    int countInv, countExpected;

    RunSeq seq;                        // which seq this belongs to
    FmrcDefinition.RunSeq expectedSeq; // expected sequence
    FmrcDefinition.Grid expectedGrid; // expected vert coordinate (optional)

    UberGrid(String name) {
      this.name = name;
      if (definition != null) {
        expectedSeq = definition.findSeqForVariable( name);
        expectedGrid = expectedSeq.findGrid( name);
      }
    }

    String getName() { return name; }

    void addRun(Run run, ForecastModelRunInventory.Grid grid) {
      ForecastModelRunInventory.TimeCoord xtc = (expectedSeq == null) ? null : expectedSeq.findTimeCoordByRuntime( run.runTime);
      RunExpected rune = new RunExpected( run, xtc, grid, expectedGrid);
      runs.add( rune);

      // now we can generate the list of possible forecast hours
      if (rune.expected != null) {
        for (double offset : rune.expected.getOffsetHours()) {
          Date fcDate = addHour(run.runTime, offset);
          forecastTimeHash.add(fcDate); // track all forecast times
          offsetHash.add(offset); // track all offset hours
        }
      }
    }

    void finish() {
      Collections.sort( runs);

      // run over all ensCoords and construct the union
      List<ForecastModelRunInventory.EnsCoord> eextendList = new ArrayList<ForecastModelRunInventory.EnsCoord>();
      ForecastModelRunInventory.EnsCoord ec_union = null;
      for (RunExpected rune : runs) {
        ForecastModelRunInventory.EnsCoord ec = rune.grid.ec;
        if (ec == null) continue;
        if (ec_union == null)
          ec_union = new ForecastModelRunInventory.EnsCoord( ec );
        else if (!ec_union.equalsData(ec))
          eextendList.add(ec);
      }

      if (ec_union != null) {
        normalize( ec_union, eextendList)

        // now find unique within collection
        ensCoordUnion = findEnsCoord( ec_union);
        if (ensCoordUnion == null) {
          ensCoords.add(ec_union);
          ensCoordUnion = ec_union;
          ec_union.setId(Integer.toString(ec_seqno));
          ec_seqno++;
        }
      }

      // run over all vertCoords and construct the union
      List<ForecastModelRunInventory.VertCoord> extendList = new ArrayList<ForecastModelRunInventory.VertCoord>();
      ForecastModelRunInventory.VertCoord vc_union = null;
      for (RunExpected rune : runs) {
        ForecastModelRunInventory.VertCoord vc = rune.grid.vc;
        if (vc == null) continue;
        if (vc_union == null)
          vc_union = new ForecastModelRunInventory.VertCoord(vc);
        else if (!vc_union.equalsData(vc))
          extendList.add(vc);
      }

      if (vc_union != null) {
        normalize( vc_union, extendList);

        // now find unique within collection
        vertCoordUnion = findVertCoord( vc_union);
        if (vertCoordUnion == null) {
          vertCoords.add(vc_union);
          vertCoordUnion = vc_union;
          vc_union.setId(Integer.toString(vc_seqno));
          vc_seqno++;
        }
      }

    }

    /**
     * Extend with all the values in the list of EnsCoord
     *  .
     * @param ecList list of EnsCoord, may be empty
     */
    public void normalize(ForecastModelRunInventory.EnsCoord result,
                          List<ForecastModelRunInventory.EnsCoord> ecList) {
      List<ForecastModelRunInventory.EnsCoord> extra = new ArrayList<ForecastModelRunInventory.EnsCoord>();
      for (ForecastModelRunInventory.EnsCoord ec : ecList) {
        if ( ! result.equalsData( ec ) ) {
          // differences can only be greater
          extra.add( ec );
        }
      }
      if( extra.size() == 0 )
        return;
       for (ForecastModelRunInventory.EnsCoord ec : extra ) {
         if ( ec.getNEnsembles() < result.getNEnsembles() )
           continue;
         result = ec;
       }

    }

    /**
     * Extend with all the values in the list of VertCoord
     * Sort the values and recreate the double[] values array.
     * @param vcList list of VertCoord, may be empty
     */
    public void normalize(ForecastModelRunInventory.VertCoord result, List<ForecastModelRunInventory.VertCoord> vcList) {
      // get all values into a HashSet of LevelCoord
      Set<LevelCoord> valueSet = new HashSet<LevelCoord>();
      addValues( valueSet, result.getValues1(), result.getValues2());
      for (ForecastModelRunInventory.VertCoord vc : vcList) {
        addValues(valueSet, vc.getValues1(), vc.getValues2());
      }

      // now create a sorted list, transfer to values array
      List<LevelCoord> valueList = Arrays.asList( (LevelCoord[]) valueSet.toArray( new LevelCoord[ valueSet.size()]));
      Collections.sort( valueList);
      double[] values1 = new double[valueList.size()];
      double[] values2 = new double[valueList.size()];
      boolean has_values2 = false;
      for (int i = 0; i < valueList.size(); i++) {
        LevelCoord lc = valueList.get(i);
        values1[i] = lc.value1;
        values2[i] = lc.value2;
        if (lc.value2 != 0.0)
          has_values2 = true;
      }
      result.setValues1(values1);
      if (has_values2)
        result.setValues2(values2);
    }

    private void addValues(Set<LevelCoord> valueSet, double[] values1, double[] values2) {
      for (int i = 0; i < values1.length; i++) {
        double val2 = (values2 == null) ? 0.0 : values2[i];
        valueSet.add( new LevelCoord(values1[i], val2));
      }
    }

    public int compareTo(Object o) {
      UberGrid uv = (UberGrid) o;
      return name.compareTo( uv.name);
    }

    RunExpected findRun(Date runTime) {
      for (RunExpected rune : runs) {
        if (runTime.equals(rune.run.runTime))
          return rune;
      }
      return null;
    }

    /* ForecastModelRun.TimeCoord findExpectedTimeCoord( Date runTime) {
      if (expectedSeq == null)
        return null;
      return expectedSeq.findTimeCoordByRuntime( runTime);
    }

    /* String showInventory(Date runTime, Date forecastTime) {

      if ((rune != null) && (null != findByForecast(rune.run.invList, forecastTime))) {
        return "X";

      } else if ((rune != null) && (rune.expected != null)) {
        double offset = getOffsetHour(runTime, forecastTime);
        if (rune.expected.findIndex(offset) >= 0) {
           return "O";
        }
      }

      return null;
    }  */

  } // end UberGrid

  class  LevelCoord implements Comparable {
    double mid;
    double value1, value2;
    LevelCoord( double value1, double value2) {
      this.value1 = value1;
      this.value2 = value2;
      mid = (value2 == 0) ? value1 : (value1 + value2)/2;
    }

    public int compareTo(Object o) {
      LevelCoord other = (LevelCoord) o;
      //if (closeEnough(value1, other.value1) && closeEnough(value2, other.value2)) return 0;
      if (mid < other.mid) return -1;
      if (mid > other.mid) return 1;
      return 0;
    }

    public boolean equals(Object oo) {
      if (this == oo) return true;
      if ( !(oo instanceof LevelCoord)) return false;
      LevelCoord other = (LevelCoord) oo;
      return (ucar.nc2.util.Misc.closeEnough(value1, other.value1) && ucar.nc2.util.Misc.closeEnough(value2, other.value2));
    }

    public int hashCode() {
      return (int) (value1 * 100000 + value2 * 100);
    }
  }

  class RunExpected implements Comparable {
    Run run;                              // this has actual time coord
    ForecastModelRunInventory.Grid grid;           //  grid containing actual vert coord
    ForecastModelRunInventory.TimeCoord expected;  // expected time coord
    FmrcDefinition.Grid expectedGrid;     // expected grid

     RunExpected(Run run, ForecastModelRunInventory.TimeCoord expected, ForecastModelRunInventory.Grid grid, FmrcDefinition.Grid expectedGrid) {
       this.run = run;
       this.expected = expected;
       this.grid = grid;
       this.expectedGrid = expectedGrid;
     }

    public int compareTo(Object o) {
      RunExpected other = (RunExpected) o;
      return run.runTime.compareTo(other.run.runTime);
    }

    int countInventory( double hourOffset) {
      boolean hasExpected = (expected != null) && (expected.findIndex(hourOffset) >= 0);
      return hasExpected ? grid.countInventory(hourOffset) : 0;
    }

    int countExpected( double hourOffset) {
      if (expected != null) {
        boolean hasExpected = expected.findIndex(hourOffset) >= 0;
        return hasExpected ? expectedGrid.countVertCoords(hourOffset) : 0;
      } else {
        return grid.countTotal();
      }
    }

  }


  /////////////////////////

  /* inventory for one variable
  private class TimeMatrix implements ForecastModelRunCollectionSave.TimeMatrix {
    private String varName;

    private ArrayList forecastTimes = new ArrayList(); // list Forecast
    private ArrayList runTimes = new ArrayList(); // list of Run
    // private int ntimes, nruns;
    private byte[][] present; // present[ntimes][nruns] = 1 if Inventory present
    private byte[][] expect; // expect[ntimes][nruns] = 1 if Inventory expected
    private HashSet offsetSet = new HashSet(); // hash Double
    private List offsetHours = new ArrayList(); // list Double
    private int total = 0; // total # Inventory

    TimeMatrix (String varName) {
      this.varName = varName;
    }

    public List getRunTimes() {
      ArrayList result = new ArrayList(runTimes.size());
      for (int i = 0; i < runTimes.size(); i++) {
        Run run = (Run) runTimes.get(i);
        result.add(run.runTime);
      }
      return result;
    }

    public List getForecastTimes() {
      ArrayList result = new ArrayList(forecastTimes.size());
      for (int i = 0; i < forecastTimes.size(); i++) {
        Forecast fc = (Forecast) forecastTimes.get(i);
        result.add(fc.forecastTime);
      }
      return result;
    }

    public boolean isPresent(Date runTime, Date forecastTime) {
      int time = findForecastIndex(forecastTime);
      int run = findRunIndex(runTime);
      if ((time < 0) || (run < 0))
        return false;
      return present[time][run] != 0;
    }

    public String showInventory(Date runTime, Date forecastTime) {
      int time = findForecastIndex(forecastTime);
      int run = findRunIndex(runTime);
      if ((time < 0) || (run < 0))
        return null;
      if (present[time][run] != 0)
        return "X";
      if (expect[time][run] != 0)
        return "O";
      return null;
    }

    public int getTotalNumberGrids() {
      return total;
    }

    void addRun(Date runDate, double[] offset) {
      Run run = new Run(runDate, null);
      runTimes.add(run);

      DateUnit du;
      try {
        du = new DateUnit("hours since "+DateUnit.getStandardDateString(runDate));
      } catch (Exception e) {
        e.printStackTrace();
        return;
      }

      for (int i = 0; i < offset.length; i++) {
        Date forecastDate = du.makeDate( offset[i]);
        Inventory inv = new Inventory(runDate, forecastDate, offset[i]);
        offsetSet.add(new Double(inv.hourOffset));

        run.invList.add(inv);
        int idx = findForecastIndex(forecastDate);
        Forecast fc;
        if (idx < 0) {
          fc = new Forecast(forecastDate);
          forecastTimes.add(fc);
        } else
          fc = (Forecast) forecastTimes.get(idx);

        fc.invList.add(inv);
      }
    }

    void finish() {
      present = new byte[ntimes][nruns];

      // make sure everything is sorted
      Collections.sort(forecastTimes);
      Collections.sort(runTimes);

      InventorySortByRunTime runTimeSorter = new InventorySortByRunTime();
      InventorySortByForecastTime forecastTimeSorter = new InventorySortByForecastTime();
      for (int i = 0; i < runTimes.size(); i++) {
        Run run = (Run) runTimes.get(i);
        Collections.sort(run.invList, forecastTimeSorter);
      }

      for (int i=0; i<ntimes; i++) {
        Forecast tc = (Forecast) forecastTimes.get(i);
        Collections.sort(tc.invList, runTimeSorter);

        // construct the isPresent array
        int fcIndex = findForecastIndex(tc.forecastTime);
        for (int j = 0; j < tc.invList.size(); j++) {
          Inventory inv = (Inventory) tc.invList.get(j);
          int runIndex = findRunIndex(inv.runTime);
          present[fcIndex][runIndex] = 1;
          total++;
        }
      }

      offsetHours = Arrays.asList(offsetSet.toArray());
      Collections.sort(offsetHours);

      // the expected inventory
      if (definition == null)
        return;

      expect = new byte[ntimes][nruns];

      FmrcDefinition.RunSeq seq = definition.findSeqForVariable( varName);

      for (int i = 0; i < runTimes.size(); i++) {
        Run run = (Run) runTimes.get(i);
        FmrcDefinition.OffsetHours oh = seq.findOffsetHoursByRuntime( run.runTime);
        int runIndex = findRunIndex(run.runTime);

        for (int j = 0; j < oh.offset.length; j++) {
          double offsetHour = oh.offset[j];

          Date forecastTime = addHour( run.runTime, offsetHour);
          int fcIndex = findForecastIndex(forecastTime);
          if (fcIndex >= 0) {
            expect[fcIndex][runIndex] = 1;
          } else
            System.out.println(" cat find Forecast= "+forecastTime);
        }
      }
    }

    int findRunIndex(Date runTime) {
      for (int i = 0; i < runTimes.size(); i++) {
        Run rt = (Run) runTimes.get(i);
        if (rt.runTime.equals(runTime))
          return i;
      }
      return -1;
    }

    int findForecastIndex(Date forecastTime) {
      for (int i = 0; i < forecastTimes.size(); i++) {
        Forecast rt = (Forecast) forecastTimes.get(i);
        if (rt.forecastTime.equals(forecastTime))
          return i;
      }
      return -1;
    }

  } */

  private class TimeMatrixDataset {
    private int ntimes, nruns, noffsets;
    private short[][] countInv; // count[ntimes][nruns] = actual # Inventory missing at that time, run
    private short[][] expected; // expected[ntimes][nruns] = expected # Inventory at that time, run
    private short[][] countOffsetInv; // countOffset[nruns][noffsets] = # Inventory missing at that run, offset, summed over Variables
    private short[][] expectedOffset; // expectedOffset[nruns][noffsets] = expected # Inventory at that run, offset

    private int[] countTotalRunInv; // countTotalRun[nruns] = actual # Inventory missing at that run
    private int[] expectedTotalRun; // expectedTotalRun[nruns] = expected # Inventory at that run

    TimeMatrixDataset() {
      nruns = runTimeList.size();
      ntimes = forecastTimeList.size();
      noffsets = offsetList.size();

      countInv = new short[ntimes][nruns];
      expected = new short[ntimes][nruns];
      countOffsetInv = new short[nruns][noffsets];
      expectedOffset = new short[nruns][noffsets];

      for (UberGrid uv : varList) {
        addInventory(uv);
      }

      // sum each run
      countTotalRunInv = new int[nruns];
      expectedTotalRun = new int[nruns];
      for (int i = 0; i < nruns; i++) {
        for (int j = 0; j < noffsets; j++) {
          countTotalRunInv[i] += countOffsetInv[i][j];
          expectedTotalRun[i] += expectedOffset[i][j];
        }
      }
    }

    // after makeArrays, this gets called for each uv
    void addInventory(UberGrid uv) {
      uv.countInv = 0;
      uv.countExpected = 0;

      for (int runIndex = 0; runIndex < runTimeList.size(); runIndex++) {
        Date runTime = runTimeList.get(runIndex);
        RunExpected rune = uv.findRun( runTime);
        if (rune == null)
          continue;

        for (int offsetIndex = 0; offsetIndex < offsetList.size(); offsetIndex++) {
          double hourOffset = offsetList.get(offsetIndex);
          int invCount = rune.countInventory(hourOffset);
          int expectedCount = rune.countExpected(hourOffset);

          Date forecastTime = addHour( runTime, hourOffset);
          int forecastIndex = findForecastIndex(forecastTime);
          if (forecastIndex < 0) {
            log.debug("No Forecast for runTime="+formatter.toDateTimeString(runTime)+" OffsetHour="+hourOffset+
              " dataset="+name);
          } else {
            countInv[forecastIndex][runIndex] += invCount;
            expected[forecastIndex][runIndex] += expectedCount;
          }

          countOffsetInv[runIndex][offsetIndex] += invCount;
          expectedOffset[runIndex][offsetIndex] += expectedCount;

          uv.countInv += invCount;
          uv.countExpected += expectedCount;
        }
      }

      /* for (int i = 0; i < uv.runs.size(); i++) {
        RunExpected rune = (RunExpected) uv.runs.get(i);
        int runIndex = findRunIndex(rune.run.runTime);


        // missing inventory
        for (int j = 0; j < rune.run.invList.size(); j++) {
          Inventory inv = (Inventory) rune.run.invList.get(j);
          int missing = rune.grid.countMissing(inv.hourOffset);
          int forecastIndex = findForecastIndex(inv.forecastTime);
          int offsetIndex = findOffsetIndex(inv.hourOffset);

          countMissing[forecastIndex][runIndex] += missing;
          countOffsetMissing[runIndex][offsetIndex] += missing;
        }

        // the expected inventory
        if (rune.expected != null) {
          double[] offsets = rune.expected.getOffsetHours();
          for (int j = 0; j < offsets.length; j++) {
            Date fcDate = addHour(rune.run.runTime, offsets[j]);
            int forecastIndex = findForecastIndex(fcDate);
            int offsetIndex = findOffsetIndex(offsets[j]);

            expected[forecastIndex][runIndex]++;
            expectedOffset[runIndex][offsetIndex]++;
            //uv.totalExpectedGrids++;
          }
        }

      } */
    }

    int findRunIndex(Date runTime) {
      for (int i = 0; i < runTimeList.size(); i++) {
        Date d = runTimeList.get(i);
        if (d.equals(runTime))
          return i;
      }
      return -1;
    }

    int findForecastIndex(Date forecastTime) {
      for (int i = 0; i < forecastTimeList.size(); i++) {
        Date d = forecastTimeList.get(i);
        if (d.equals(forecastTime))
          return i;
      }
      return -1;
    }

    int findOffsetIndex(double offsetHour) {
      for (int i = 0; i < offsetList.size(); i++) {
        if (offsetHour ==  offsetList.get(i))
          return i;
      }
      return -1;
    }

  }

  /* private class Forecast implements Comparable {
    ArrayList invList; // list of inventory
    Date forecastTime;

    Forecast(Date forecastTime) {
      this.forecastTime = forecastTime;
      invList = new ArrayList();
    }

    public int compareTo(Object o) {
      Forecast other = (Forecast) o;
      return forecastTime.compareTo(other.forecastTime);
    }
  }

  private class InventorySortByRunTime implements Comparator {
    public int compare(Object o1, Object o2) {
      Inventory inv1 = (Inventory) o1;
      Inventory inv2 = (Inventory) o2;
      return inv1.runTime.compareTo(inv2.runTime);
    }
  }

  private class InventorySortByForecastTime implements Comparator {
    public int compare(Object o1, Object o2) {
      Inventory inv1 = (Inventory) o1;
      Inventory inv2 = (Inventory) o2;
      return inv1.forecastTime.compareTo(inv2.forecastTime);
    }
  }  */

  private Date addHour( Date d, double hour) {
    cal.setTime( d);

    int ihour = (int) hour;
    int imin = (int) (hour - ihour) * 60;
    cal.add(Calendar.HOUR_OF_DAY, ihour);
    cal.add(Calendar.MINUTE, imin);
    return cal.getTime();
  }

  private double getOffsetHour( Date run, Date forecast) {
    double diff = forecast.getTime() - run.getTime();
    return diff / 1000.0 / 60.0 / 60.0;
  }

  /////////////////////////////////////////////////////////////////////////////////////////////////

  public String writeMatrixXML(String varName) {
    XMLOutputter fmt = new XMLOutputter(Format.getPrettyFormat());
    if (varName == null) {
      return fmt.outputString(makeMatrixDocument());
    } else {
      return fmt.outputString(makeMatrixDocument(varName));
    }
  }

  public void writeMatrixXML(String varName, OutputStream os) throws IOException {
    XMLOutputter fmt = new XMLOutputter(Format.getPrettyFormat());
    if (varName == null) {
      fmt.output(makeMatrixDocument(), os);
    } else {
      fmt.output(makeMatrixDocument(varName), os);
    }
  }

  /**
   * Create an XML document for the entire collection
   */
  public Document makeMatrixDocument() {
    if (tmAll == null)
      tmAll = new TimeMatrixDataset();

    Element rootElem = new Element("forecastModelRunCollectionInventory");
    Document doc = new Document(rootElem);
    rootElem.setAttribute("dataset", name);

        // list all the offset hours
    for (Double offset : offsetList) {
      Element offsetElem = new Element("offsetTime");
      rootElem.addContent(offsetElem);
      offsetElem.setAttribute("hours", offset.toString());
    }

    // list all the variables
    for (UberGrid uv : varList) {
      Element varElem = new Element("variable");
      rootElem.addContent(varElem);
      varElem.setAttribute("name", uv.name);

      addCountPercent(uv.countInv, uv.countExpected, varElem, false);
    }

    // list all the runs
    for (int i = runTimeList.size() - 1; i >= 0; i--) {
      Element runElem = new Element("run");
      rootElem.addContent(runElem);
      Date runTime = runTimeList.get(i);
      runElem.setAttribute("date", dateFormat.format(runTime));

      addCountPercent(tmAll.countTotalRunInv[i], tmAll.expectedTotalRun[i], runElem, true);

      for (int k = 0; k < offsetList.size(); k++) {
        Element offsetElem = new Element("offset");
        runElem.addContent(offsetElem);
        Double offset = offsetList.get(k);
        offsetElem.setAttribute("hours", offset.toString());

        addCountPercent(tmAll.countOffsetInv[i][k], tmAll.expectedOffset[i][k], offsetElem, false);
      }
    }

    // list all the forecasts
    for (int k = forecastTimeList.size() - 1; k >= 0; k--) {
      Element fcElem = new Element("forecastTime");
      rootElem.addContent(fcElem);

      Date ftime = forecastTimeList.get(k);
      fcElem.setAttribute("date", dateFormat.format(ftime));

      // list all the forecasts
      for (int j = runTimeList.size() - 1; j >= 0; j--) {
        Element rtElem = new Element("runTime");
        fcElem.addContent(rtElem);

        addCountPercent(tmAll.countInv[k][j], tmAll.expected[k][j], rtElem, false);
      }
    }

    return doc;
  }

  private void addCountPercent(int have, int want, Element elem, boolean always) {
    if (((have == want) || (want == 0)) && (have != 0)) {
      elem.setAttribute("count", Integer.toString(have));
      if (always) elem.setAttribute("percent", "100");
    } else if (want != 0) {
      int percent = (int) (100.0 * have / want);
      elem.setAttribute("count", have + "/" + want);
      elem.setAttribute("percent", Integer.toString(percent));
    }
  }

  /**
   * Create an XML document for a variable
   */
  public Document makeMatrixDocument(String varName) {
    UberGrid uv = findVar(varName);
    if (uv == null)
      throw new IllegalArgumentException("No variable named = " + varName);

    Element rootElem = new Element("forecastModelRunCollectionInventory");
    Document doc = new Document(rootElem);
    rootElem.setAttribute("dataset", name);
    rootElem.setAttribute("variable", uv.name);

    // list all the offset hours
    for (int k = 0; k < offsetList.size(); k++) {
      Element offsetElem = new Element("offsetTime");
      rootElem.addContent(offsetElem);
      Double offset = offsetList.get(k);
      offsetElem.setAttribute("hour", offset.toString());
    }

    // list all the runs
    for (int i = runTimeList.size() - 1; i >= 0; i--) {
      Element runElem = new Element("run");
      rootElem.addContent(runElem);
      Date runTime = runTimeList.get(i);
      runElem.setAttribute("date", dateFormat.format(runTime));

      RunExpected rune = uv.findRun( runTime);

      for (Double offset : offsetList) {
        Element offsetElem = new Element("offset");
        runElem.addContent(offsetElem);
        double hourOffset = offset.doubleValue();
        offsetElem.setAttribute("hour", offset.toString());

        int missing = rune.countInventory(hourOffset);
        int expected = rune.countExpected(hourOffset);
        addCountPercent(missing, expected, offsetElem, false);
      }
    }

    // list all the forecasts
    for (int k = forecastTimeList.size() - 1; k >= 0; k--) {
      Element fcElem = new Element("forecastTime");
      rootElem.addContent(fcElem);

      Date forecastTime = forecastTimeList.get(k);
      fcElem.setAttribute("date", dateFormat.format(forecastTime));

      // list all the forecasts
      for (int j = runTimeList.size() - 1; j >= 0; j--) {
        Element rtElem = new Element("runTime");
        fcElem.addContent(rtElem);

        Date runTime = runTimeList.get(j);

        RunExpected rune = uv.findRun(runTime);
        double hourOffset = getOffsetHour(runTime, forecastTime);
        int missing = rune.countInventory(hourOffset);
        int expected = rune.countExpected(hourOffset);
        addCountPercent(missing, expected, rtElem, false);
      }
    }

    return doc;
  }

  public String showOffsetHour(String varName, String offsetHour) {
    UberGrid uv = findVar(varName);
    if (uv == null)
      return "No variable named = " + varName;

    double hour = Double.parseDouble( offsetHour);

    StringBuilder sbuff = new StringBuilder();
    sbuff.append("Inventory for ").append(varName).append(" for offset hour= ").append(offsetHour).append("\n");

    for (RunExpected rune : uv.runs) {
      double[] vcoords = rune.grid.getVertCoords(hour);
      sbuff.append(" Run ");
      sbuff.append(formatter.toDateTimeString(rune.run.runTime));
      sbuff.append(": ");
      for (int j = 0; j < vcoords.length; j++) {
        if (j > 0) sbuff.append(",");
        sbuff.append(vcoords[j]);
      }
      sbuff.append("\n");
    }

    sbuff.append("\nExpected for ").append(varName).append(" for offset hour= ").append(offsetHour).append("\n");

    for (RunExpected rune : uv.runs) {
      double[] vcoords = rune.expectedGrid.getVertCoords(hour);
      sbuff.append(" Run ");
      sbuff.append(formatter.toDateTimeString(rune.run.runTime));
      sbuff.append(": ");
      for (int j = 0; j < vcoords.length; j++) {
        if (j > 0) sbuff.append(",");
        sbuff.append(vcoords[j]);
      }
      sbuff.append("\n");
    }
    return sbuff.toString();
  }


  /* private Inventory findByOffset(List invList, double offset) {
    for (int i = 0; i < invList.size(); i++) {
      Inventory inv = (Inventory) invList.get(i);
      if (inv.hourOffset == offset) return inv;
    }
    return null;
  }

  private Inventory findByForecast(List invList, Date forecast) {
    for (int i = 0; i < invList.size(); i++) {
      Inventory inv = (Inventory) invList.get(i);
      if (inv.forecastTime.equals(forecast)) return inv;
    }
    return null;
  } */


  ////////////////////////////////////////////////////////////////////////////////////////////////

  /**
   * Create a ForecastModelRun Collection from the files in a directory.
   * @param fmrcDefinitionPath put/look for fmrc definition files in this directory, may be null
   * @param collectionName the definition file = "name.fmrcDefinition.xml";
   * @param fmr_cache cache fmr inventory files here, may be null
   * @param dirName  scan this directory
   * @param suffix filter on this suffix
   * @param mode one of the ForecastModelRun.OPEN_ modes
   * @return ForecastModelRunCollection or null if no files exist
   * @throws Exception on bad
   */
  public static FmrcInventory makeFromDirectory(String fmrcDefinitionPath, String collectionName,
          ucar.nc2.util.DiskCache2 fmr_cache, String dirName, String suffix, int mode) throws Exception {

    long startTime = System.currentTimeMillis();
    FmrcInventory fmrCollection = new FmrcInventory(fmrcDefinitionPath, collectionName);

    // override the suffix by the definition attribute suffixFilter, if it exists
    if (fmrCollection.getSuffixFilter() != null)
      suffix = fmrCollection.getSuffixFilter();

    File dir = new File(dirName);
    File[] files = dir.listFiles();
    if (null == files)
      return null;

    for (File file : files) {
      if (!file.getPath().endsWith(suffix))
        continue;

      ForecastModelRunInventory fmr = ForecastModelRunInventory.open(fmr_cache, file.getPath(), mode, true);
      if (null != fmr)
        fmrCollection.addRun(fmr);
    }

    fmrCollection.finish();
    if (debugTiming) {
      long took = System.currentTimeMillis() - startTime;
      System.out.println("that took = "+took+" msecs");
    }

    return fmrCollection;
  }

  private static boolean debugTiming = false;
  public static void main2(String args[]) throws Exception {
    String dir = "nam/c20s";
    FmrcInventory fmrc = makeFromDirectory("R:/testdata/motherlode/grid/inv/new/", "NCEP-NAM-CONUS_20km-surface", null, "C:/data/grib/"+dir, "grib1",
            ForecastModelRunInventory.OPEN_FORCE_NEW);

    FmrcDefinition def = fmrc.getDefinition();
    if (null != def) {
      System.out.println("current definition = "+fmrc.getDefinitionPath());
      //def.addVertCoordsFromCollectionInventory( fmrc);
      System.out.println( def.writeDefinitionXML());
    } else {
      System.out.println("write definition to "+fmrc.getDefinitionPath());
      def = new FmrcDefinition();
      def.makeFromCollectionInventory( fmrc);
      FileOutputStream fos = new FileOutputStream( fmrc.getDefinitionPath());
      System.out.println( def.writeDefinitionXML());
      def.writeDefinitionXML( fos);
    }

    String varName = "Temperature";
    System.out.println( fmrc.writeMatrixXML( varName));
    FileOutputStream fos = new FileOutputStream("C:/data/grib/"+dir+"/fmrcMatrix.xml");
    fmrc.writeMatrixXML( varName, fos);

    System.out.println( fmrc.writeMatrixXML( null));
    FileOutputStream fos2 = new FileOutputStream("C:/data/grib/"+dir+"/fmrcMatrixAll.xml");
    fmrc.writeMatrixXML( null, fos2);

    System.out.println( fmrc.showOffsetHour(varName,"7.0"));
  }

  ///////////////////////////////////////////////////////////////////////////

  /* private static String[] catalogs  = {
          "NCEP/DGEX/Alaska_12km",
          "NCEP/DGEX/CONUS_12km",

          "NCEP/GFS/Alaska_191km",
          "NCEP/GFS/CONUS_191km",
          "NCEP/GFS/CONUS_80km",
          "NCEP/GFS/CONUS_95km",
          "NCEP/GFS/Global_0p5deg",
          "NCEP/GFS/Global_2p5deg",
          "NCEP/GFS/Global_onedeg",
          "NCEP/GFS/Hawaii_160km",
          "NCEP/GFS/N_Hemisphere_381km",
          "NCEP/GFS/Puerto_Rico_191km",

          "NCEP/NAM/Alaska_11km",
          "NCEP/NAM/Alaska_22km",
          "NCEP/NAM/Alaska_45km/conduit",
          "NCEP/NAM/Alaska_45km/noaaport",
          "NCEP/NAM/Alaska_95km",

          "NCEP/NAM/CONUS_12km",
          "NCEP/NAM/CONUS_12km/conduit",
          "NCEP/NAM/CONUS_20km/noaaport",
          "NCEP/NAM/CONUS_20km/selectsurface",
          "NCEP/NAM/CONUS_20km/surface",
          "NCEP/NAM/CONUS_40km/conduit",
          "NCEP/NAM/CONUS_80km",
          "NCEP/NAM/Polar_90km",

          "NCEP/NDFD/CONUS_5km", };

  private static String[] catalog24hours  = {
          // 24 hours
          "NCEP/RUC/CONUS_80km",
          "NCEP/RUC2/CONUS_20km/hybrid",
          "NCEP/RUC2/CONUS_20km/pressure",
          "NCEP/RUC2/CONUS_20km/surface",
          "NCEP/RUC2/CONUS_40km",
  }; */


  public static void main(String args[]) throws Exception
    for (String cat : FmrcDefinition.fmrcDatasets) {
      if (cat.contains("/RUC"))
        doOne( cat, 72);
      else
        doOne( cat, 12);
    }
  }

  public static void doOne(String cat, int n) throws Exception {
    String server = "http://motherlode.ucar.edu:8080/thredds/catalog/fmrc/";
    String writeDir = "D:/temp/modelDef/";
    new File(writeDir).mkdirs();

    String catName = server + cat + "/files/catalog.xml";
    FmrcInventory fmrCollection = makeFromCatalog(null, catName, catName, n, ForecastModelRunInventory.OPEN_FORCE_NEW);

    String writeFile = writeDir + StringUtil.replace(cat, "/","-") + ".fmrcDefinition.xml";
    System.out.println("write definition to " + writeFile);
    FmrcDefinition def = new FmrcDefinition();
    def.makeFromCollectionInventory(fmrCollection);
    FileOutputStream fos = new FileOutputStream(writeFile);
    def.writeDefinitionXML(fos);
    fos.close();
  }

  /**
    * Create a ForecastModelRun Collection from the datasets in a catalog.
    * @param catURL  scan this catalog
    * @throws Exception on bad
    */
   public static void writeDefinitionFromCatalog(String catURL, String collectionName, int maxDatasets) throws Exception {

     FmrcInventory fmrCollection = makeFromCatalog( catURL, collectionName, maxDatasets, ForecastModelRunInventory.OPEN_NORMAL);
     System.out.println("write definition to "+fmrCollection.getDefinitionPath());
     FmrcDefinition def = new FmrcDefinition();
     def.makeFromCollectionInventory( fmrCollection);
     FileOutputStream fos = new FileOutputStream( fmrCollection.getDefinitionPath());
     def.writeDefinitionXML( fos);
  }

  /**
    * Create a ForecastModelRun Collection from the datasets in a catalog.
    * @param catURL  scan this catalog
    * @throws Exception on bad
    */
  public static FmrcInventory makeFromCatalog(String catURL, String collectionName, int maxDatasets, int mode) throws Exception {
    DiskCache2 cache =  new DiskCache2("fmrcInventory/", true, 0, -1); // dont scour - messes up the TDS!

    return makeFromCatalog(cache, catURL, collectionName, maxDatasets,  mode);
  }

  public static FmrcInventory makeFromCatalog(DiskCache2 cache, String catURL, String collectionName, int maxDatasets, int mode) throws Exception {
     String fmrcDefinitionPath = (cache == null)? null : cache.getRootDirectory()+"/defs/";

     System.out.println("***makeFromCatalog "+catURL);
     long startTime = System.currentTimeMillis();
     FmrcInventory fmrCollection = new FmrcInventory(fmrcDefinitionPath, collectionName);

     CatalogCrawler crawler = new CatalogCrawler(CatalogCrawler.USE_ALL_DIRECT, false,
             new MyListener(fmrCollection, maxDatasets, mode, cache));

     crawler.crawl(catURL, null, System.out, null);
     fmrCollection.finish();

     if (debugTiming) {
       long took = System.currentTimeMillis() - startTime;
       System.out.println("that took = "+took+" msecs");
     }
     return fmrCollection;
  }

  private static class MyListener implements CatalogCrawler.Listener {
    FmrcInventory fmrCollection;
    DiskCache2 cache;
    int maxDatasets;
    int mode, count;
    boolean first = true;

    MyListener(FmrcInventory fmrCollection, int maxDatasets, int mode, DiskCache2 cache) {
      this.fmrCollection = fmrCollection;
      this.maxDatasets = maxDatasets;
      this.mode = mode;
      this.count = 0;
      this.cache = cache;
    }

    public void getDataset(InvDataset dd, Object context) {
      if ((count > maxDatasets) && (maxDatasets > 0)) return;

      InvAccess access = dd.getAccess(ServiceType.OPENDAP);
      if (access == null) {
        System.out.println(" no opendap access");
        return;
      }

      if (first) { // skip the first one
        System.out.println(" skip "+access.getStandardUrlName());
        first = false;
        return;
      }

      count++;     
      System.out.println(" access " + access.getStandardUrlName());
      ForecastModelRunInventory fmr;
      try {
        fmr = ForecastModelRunInventory.open(cache, access.getStandardUrlName(), mode, false);
        if (null != fmr) {
          fmrCollection.addRun(fmr);
          fmr.releaseDataset();
        }
      } catch (IOException e) {
        e.printStackTrace();
        return;
      }

    }

    public boolean getCatalogRef(InvCatalogRef dd, Object context) { return true; }

  }

  public static void main4(String args[]) throws Exception {
    /* String work = "R:/testdata2/motherlode/grid2/";
    fmrcDefinitionPath = work+"def/";
    cache = new DiskCache2(work+"inv/", false, -1, -1);

    File file = new File(work+"def/");
    file.mkdirs();

    for (int i = 0; i < catalogs.length; i++) {
      String urlFragment = catalogs[i];
      String catURL = "http://motherlode.ucar.edu:8080/thredds/catalog/model/"+urlFragment+"/catalog.xml";
      makeDefinitionFromCatalog(catURL);
    } */

    String dir = "nam/conus80";
    FmrcInventory fmrc = makeFromDirectory("C:/temp", "NCEP-NAM-CONUS_80km", null, "C:/data/grib/"+dir, "grib1",
            ForecastModelRunInventory.OPEN_FORCE_NEW);

  }

}
TOP

Related Classes of ucar.nc2.dt.fmrc.FmrcInventory$Inventory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.