diff --git a/crh/gov.noaa.nws.crh.edex.grib.decoderpostprocessor/.classpath b/crh/gov.noaa.nws.crh.edex.grib.decoderpostprocessor/.classpath
new file mode 100644
index 0000000000..098194ca4b
--- /dev/null
+++ b/crh/gov.noaa.nws.crh.edex.grib.decoderpostprocessor/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/crh/gov.noaa.nws.crh.edex.grib.decoderpostprocessor/.project b/crh/gov.noaa.nws.crh.edex.grib.decoderpostprocessor/.project
new file mode 100644
index 0000000000..028524cee6
--- /dev/null
+++ b/crh/gov.noaa.nws.crh.edex.grib.decoderpostprocessor/.project
@@ -0,0 +1,28 @@
+
+
+ gov.noaa.nws.crh.edex.grib.decoderpostprocessor
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/crh/gov.noaa.nws.crh.edex.grib.decoderpostprocessor/META-INF/MANIFEST.MF b/crh/gov.noaa.nws.crh.edex.grib.decoderpostprocessor/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..53d2a65752
--- /dev/null
+++ b/crh/gov.noaa.nws.crh.edex.grib.decoderpostprocessor/META-INF/MANIFEST.MF
@@ -0,0 +1,15 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Gribpostprocessor
+Bundle-SymbolicName: gov.noaa.nws.crh.edex.grib.decoderpostprocessor
+Bundle-Version: 1.0.0.qualifier
+Bundle-Vendor: CRH
+Require-Bundle: com.raytheon.edex.common,
+ com.raytheon.edex.plugin.grib,
+ com.raytheon.uf.common.dataplugin,
+ com.raytheon.uf.common.dataplugin.grid,
+ com.raytheon.uf.common.parameter,
+ com.raytheon.uf.edex.plugin.grid,
+ com.raytheon.uf.common.datastorage,
+ javax.measure
+Bundle-RequiredExecutionEnvironment: JavaSE-1.6
diff --git a/crh/gov.noaa.nws.crh.edex.grib.decoderpostprocessor/build.properties b/crh/gov.noaa.nws.crh.edex.grib.decoderpostprocessor/build.properties
new file mode 100644
index 0000000000..34d2e4d2da
--- /dev/null
+++ b/crh/gov.noaa.nws.crh.edex.grib.decoderpostprocessor/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/crh/gov.noaa.nws.crh.edex.grib.decoderpostprocessor/src/gov/noaa/nws/crh/edex/grib/decoderpostprocessor/NamNestPostProcessor.java b/crh/gov.noaa.nws.crh.edex.grib.decoderpostprocessor/src/gov/noaa/nws/crh/edex/grib/decoderpostprocessor/NamNestPostProcessor.java
new file mode 100644
index 0000000000..aa145e77c1
--- /dev/null
+++ b/crh/gov.noaa.nws.crh.edex.grib.decoderpostprocessor/src/gov/noaa/nws/crh/edex/grib/decoderpostprocessor/NamNestPostProcessor.java
@@ -0,0 +1,208 @@
+package gov.noaa.nws.crh.edex.grib.decoderpostprocessor;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+
+import com.raytheon.edex.plugin.grib.exception.GribException;
+import com.raytheon.uf.common.dataplugin.PluginException;
+import com.raytheon.uf.common.dataplugin.grid.GridConstants;
+import com.raytheon.uf.common.dataplugin.grid.GridRecord;
+import com.raytheon.uf.common.dataquery.db.QueryParam.QueryOperand;
+import com.raytheon.uf.edex.database.DataAccessLayerException;
+import com.raytheon.uf.edex.database.query.DatabaseQuery;
+import com.raytheon.uf.edex.plugin.grid.dao.GridDao;
+
+/**
+ * Grib post processor implementation to generate 1-hr precipitation grids from
+ * the cycling (1-hr, 2-hr, 3-hr, 1-hr, 2-hr, 3-hr, etc.) precip grids in the
+ * NAM Nest output.
+ *
+ *
+ *
+ * SOFTWARE HISTORY
+ *
+ * Date Ticket# Engineer Description
+ * ------------- -------- ----------- --------------------------
+ * Sep 05, 2014 M. Foster Initial Creation
+ *
+ *
+ *
+ *
+ * @author matthew.foster
+ * @version 1.0
+ */
+
+public class NamNestPostProcessor extends OneHrPrecipGridProcessor {
+
+ @Override
+ public GridRecord[] process(GridRecord record) throws GribException {
+ // Post process the data if this is a Total Precipitation grid
+ if (record.getParameter().getAbbreviation().equals("TP2hr") ||
+ record.getParameter().getAbbreviation().equals("TP3hr")) {
+ return super.process(record);
+ }
+ return new GridRecord[] { record };
+ }
+
+ /**
+ * Retrieves a grid inventory for the provided datasetid and parameter
+ *
+ * @param datasetid
+ * The datasetid of the model being worked on
+ * @param parm
+ * The parameter being retrieved (e.g. TP3hr)
+ * @param refTime
+ * The refTime (cycle time) of the model
+ * @return A List of GridRecord
+ * @throws GribException
+ */
+ @SuppressWarnings("unchecked")
+ protected List getPrecipInventory(String datasetid,
+ String parm, Date refTime) throws GribException {
+ GridDao dao = null;
+ try {
+ dao = new GridDao();
+ } catch (PluginException e) {
+ throw new GribException("Error instantiating grib dao!", e);
+ }
+ DatabaseQuery query = new DatabaseQuery(GridRecord.class);
+ query.addQueryParam(GridConstants.PARAMETER_ABBREVIATION, parm);
+ query.addQueryParam(GridConstants.DATASET_ID, datasetid);
+ query.addQueryParam("dataTime.refTime", refTime);
+ query.addOrder("dataTime.fcstTime", true);
+ try {
+ return (List) dao.queryByCriteria(query);
+ } catch (DataAccessLayerException e) {
+ throw new GribException(
+ String.format("Error getting Precip inventory for %s!",
+ datasetid), e);
+ }
+ }
+
+ /**
+ *
+ * @param refTime
+ * The reftime (cycle time) of the model being worked on
+ * @return List of Integer of the fcstTimes of the current 1hr precip
+ * inventory
+ * @throws GribException
+ */
+ @SuppressWarnings("unchecked")
+ protected HashSet getPrecip1hrInventory(String datasetId, Date refTime)
+ throws GribException {
+ GridDao dao = null;
+ try {
+ dao = new GridDao();
+ } catch (PluginException e) {
+ throw new GribException("Error instantiating grib dao!", e);
+ }
+ DatabaseQuery query = new DatabaseQuery(GridRecord.class);
+ query.addQueryParam(GridConstants.PARAMETER_ABBREVIATION, "TP1hr");
+ query.addQueryParam(GridConstants.DATASET_ID, datasetId,
+ QueryOperand.EQUALS);
+ query.addQueryParam("dataTime.refTime", refTime);
+ query.addReturnedField("dataTime.fcstTime");
+ query.setDistinct(true);
+ try {
+ return new HashSet((List) dao.queryByCriteria(query));
+ } catch (DataAccessLayerException e) {
+ throw new GribException(
+ "Error getting Precip inventory for NAMNest!", e);
+ }
+ }
+
+ /**
+ * Generates the 1 hour accumulated grid from the run accumulated
+ * precipitation grids. This function will look in the inventory and
+ * generate any 1 hr grids that can be generated.
+ *
+ * @param record
+ * The grib record for which to generate the 1 hour accumulated
+ * precipitation grid
+ * @return The generated 1-hr precipitation grids
+ * @throws GribException
+ */
+ protected synchronized GridRecord[] generate1hrPrecipGrids(GridRecord record)
+ throws GribException {
+
+ List currInventory;
+ List prevInventory;
+ HashSet precip1hrInventory;
+
+ if (record.getParameter().getAbbreviation().equals("TP3hr")) {
+ // Get an inventory of TP3hr grids
+ currInventory = getPrecipInventory(record.getDatasetId(), "TP3hr",
+ record.getDataTime().getRefTime());
+
+ // Get an inventory of TP2hr grids
+ prevInventory = getPrecipInventory(record.getDatasetId(), "TP2hr",
+ record.getDataTime().getRefTime());
+
+ // The current 1hr precip inventory
+ precip1hrInventory = getPrecip1hrInventory(record.getDatasetId(),
+ record.getDataTime().getRefTime());
+
+ } else if (record.getParameter().getAbbreviation().equals("TP2hr")) {
+ // Get an inventory of TP2hr grids
+ currInventory = getPrecipInventory(record.getDatasetId(), "TP2hr",
+ record.getDataTime().getRefTime());
+ // Get an inventory of TP1hr grids
+ prevInventory = getPrecipInventory(record.getDatasetId(), "TP1hr",
+ record.getDataTime().getRefTime());
+
+ precip1hrInventory = new HashSet();
+ for (GridRecord rec : prevInventory) {
+ precip1hrInventory.add(rec.getDataTime().getFcstTime());
+ }
+ } else {
+ throw new GribException("Didn't get TP3hr or TP2hr grid");
+ }
+
+ // Adds the current record to the precip inventory
+ float[] currentData = (float[]) record.getMessageData();
+ record.setMessageData(currentData);
+ currInventory.add(record);
+
+ // Examine each grid in the inventory and generate the 1hr precipitation
+ // grid if possible
+ List generatedRecords = new ArrayList();
+ for (GridRecord currRecord : currInventory) {
+ // Check if the 1hr precipitation grid has already been produced
+ if (! precip1hrInventory.contains(currRecord.getDataTime()
+ .getFcstTime())) {
+ List generated1hrPrecips = generate1hrPrecip(
+ currRecord, prevInventory);
+ for (GridRecord newRecord : generated1hrPrecips) {
+ // Add the generated grid to the current inventory
+ if (newRecord != null) {
+ precip1hrInventory.add(newRecord.getDataTime()
+ .getFcstTime());
+ generatedRecords.add(newRecord);
+ }
+ }
+ }
+ }
+
+ return generatedRecords.toArray(new GridRecord[] {});
+ }
+
+ /**
+ * Calculates the new data by subtracting the previous inventory data from
+ * the current data
+ *
+ * @param inventoryData
+ * The data from the previous precipitation record
+ * @param newData
+ * The data from the current precipitation record
+ */
+ protected void calculatePrecipValues(float[] inventoryData, float[] newData) {
+ for (int i = 0; i < inventoryData.length; i++) {
+ newData[i] = newData[i] - inventoryData[i];
+ if (newData[i] < 0) {
+ newData[i] = 0;
+ }
+ }
+ }
+}
diff --git a/crh/gov.noaa.nws.crh.edex.grib.decoderpostprocessor/src/gov/noaa/nws/crh/edex/grib/decoderpostprocessor/OneHrPrecipGridProcessor.java b/crh/gov.noaa.nws.crh.edex.grib.decoderpostprocessor/src/gov/noaa/nws/crh/edex/grib/decoderpostprocessor/OneHrPrecipGridProcessor.java
new file mode 100644
index 0000000000..4419e04796
--- /dev/null
+++ b/crh/gov.noaa.nws.crh.edex.grib.decoderpostprocessor/src/gov/noaa/nws/crh/edex/grib/decoderpostprocessor/OneHrPrecipGridProcessor.java
@@ -0,0 +1,184 @@
+package gov.noaa.nws.crh.edex.grib.decoderpostprocessor;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+
+import com.raytheon.edex.plugin.grib.decoderpostprocessors.IDecoderPostProcessor;
+import com.raytheon.edex.plugin.grib.exception.GribException;
+import com.raytheon.uf.common.dataplugin.PluginException;
+import com.raytheon.uf.common.dataplugin.grid.GridRecord;
+import com.raytheon.uf.common.datastorage.records.FloatDataRecord;
+import com.raytheon.uf.common.parameter.Parameter;
+import com.raytheon.uf.common.time.DataTime;
+import com.raytheon.uf.common.time.TimeRange;
+import com.raytheon.uf.edex.plugin.grid.dao.GridDao;
+
+/**
+ * Abstract class to generate 1-hour precip grids
+ *
+ *
+ *
+ * SOFTWARE HISTORY
+ *
+ * Date Ticket# Engineer Description
+ * ------------- -------- ------------- --------------------------
+ * Sep 05, 2014 M. Foster Initial creation
+ *
+ *
+ *
+ * @author matthew.foster
+ * @version 1.0
+ *
+ */
+
+public abstract class OneHrPrecipGridProcessor implements IDecoderPostProcessor {
+ /** The number of seconds in 1 hour */
+ protected static final int SECONDS_IN_1_HR = 3600;
+
+ public GridRecord[] process(GridRecord record) throws GribException {
+
+ // Post process the data if this is a 2hr or 3hr precip accumulation
+
+ GridRecord[] newRecords = generate1hrPrecipGrids(record);
+ GridRecord[] retVal = new GridRecord[newRecords.length + 1];
+ retVal[0] = record;
+ for (int i = 1; i < retVal.length; i++) {
+ retVal[i] = newRecords[i - 1];
+ }
+ return retVal;
+
+ }
+
+ protected abstract GridRecord[] generate1hrPrecipGrids(GridRecord record)
+ throws GribException;
+
+ /**
+ * Generates the 1hr precipitation grid
+ *
+ * @param record
+ * The current record to clone and modify to produce the new 1hr
+ * grid
+ * @param precipInventory
+ * The current run accumulated grid inventory
+ * @return The generated 1hr precipitation grid
+ * @throws GribException
+ */
+ protected List generate1hrPrecip(GridRecord record,
+ List precipInventory)
+ throws GribException {
+ List tp1hrRecords = new ArrayList();
+ int currentFcstTime = record.getDataTime().getFcstTime();
+
+ for (GridRecord rec : precipInventory) {
+ if (rec.getDataTime().getFcstTime() == (currentFcstTime - SECONDS_IN_1_HR)) {
+ tp1hrRecords.add(calculate1hrPrecip(rec, record));
+ }
+ }
+ return tp1hrRecords;
+ }
+
+ /**
+ * Generates the 1hr precipitation grid from the current grid and the
+ * previous grid
+ *
+ * @param inventoryRecord
+ * The previous grid from the inventory
+ * @param currentRecord
+ * The current grid
+ * @return The generated 1hr precipitation grid
+ * @throws GribException
+ */
+ protected GridRecord calculate1hrPrecip(GridRecord inventoryRecord,
+ GridRecord currentRecord) throws GribException {
+
+ // Clone the current record and set the ID to 0 so Hibernate will
+ // recognize it as a new record
+ GridRecord tp1hrRecord = new GridRecord(currentRecord);
+ tp1hrRecord.setId(0);
+ if (currentRecord.getMessageData() == null) {
+ GridDao dao = null;
+ try {
+ dao = new GridDao();
+ currentRecord.setMessageData(((FloatDataRecord) dao
+ .getHDF5Data(currentRecord, -1)[0]).getFloatData());
+ } catch (PluginException e) {
+ throw new GribException("Error populating grib data!", e);
+ }
+ }
+
+ // Copy the data to the new record so the data from the original record
+ // does not get modified
+ float[] currentData = (float[]) currentRecord.getMessageData();
+ currentRecord.setMessageData(currentData);
+ float[] newData = new float[currentData.length];
+ System.arraycopy(currentData, 0, newData, 0, currentData.length);
+ tp1hrRecord.setMessageData(newData);
+
+ // Assign the new parameter abbreviation and cache it if necessary
+
+ Parameter param = new Parameter("TP1hr", "Precip Accum 1 hr",
+ currentRecord.getParameter().getUnit());
+ tp1hrRecord.setParameter(param);
+ tp1hrRecord.getInfo().setId(null);
+ // Change the data time to include the 1-hr time range
+ modifyDataTime(tp1hrRecord);
+
+ // Calculate the new data values
+ if (inventoryRecord != null) {
+ if (inventoryRecord.getMessageData() == null) {
+ GridDao dao = null;
+ try {
+ dao = new GridDao();
+ inventoryRecord
+ .setMessageData(((FloatDataRecord) dao.getHDF5Data(
+ inventoryRecord, 0)[0]).getFloatData());
+ } catch (PluginException e) {
+ throw new GribException("Error populating grib data!", e);
+ }
+ }
+ calculatePrecipValues((float[]) inventoryRecord.getMessageData(),
+ (float[]) tp1hrRecord.getMessageData());
+ }
+ return tp1hrRecord;
+ }
+
+ /**
+ * Calculates the new data by subtracting the previous inventory data from
+ * the current data
+ *
+ * @param inventoryData
+ * The data from the previous precipitation record
+ * @param newData
+ * The data from the current precipitation record
+ */
+ protected abstract void calculatePrecipValues(float[] messageData,
+ float[] messageData2);
+
+ /**
+ * Modifies the DataTime of the provided record to include a 1hr time range
+ *
+ * @param record
+ * The record to modify the datatime for
+ */
+ protected void modifyDataTime(GridRecord record) {
+
+ Calendar refTime = record.getDataTime().getRefTimeAsCalendar();
+ int fcstTime = record.getDataTime().getFcstTime();
+
+ // Calculate the start time by subtracting 1 hour from the reference
+ // time + forecast time
+ Calendar startTime = (Calendar) refTime.clone();
+ startTime.add(Calendar.SECOND, fcstTime - SECONDS_IN_1_HR);
+
+ // Calculate the end time by adding the reference time + forecast time
+ Calendar endTime = (Calendar) refTime.clone();
+ endTime.add(Calendar.SECOND, fcstTime);
+ TimeRange validPeriod = new TimeRange(startTime, endTime);
+ DataTime newDataTime = new DataTime(refTime, fcstTime, validPeriod);
+
+ // Reset the datauri since the datauri contains the DataTime
+ record.setDataTime(newDataTime);
+ record.setDataURI(null);
+ }
+}