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); + } +}