2018-12-14 14:59:53 +01:00
|
|
|
/*
|
|
|
|
MDAL - mMesh Data Abstraction Library (MIT License)
|
|
|
|
Copyright (C) 2016 Lutra Consulting
|
|
|
|
Copyright (C) 2018 Peter Petrik (zilolv at gmail dot com)
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
#include <string>
|
|
|
|
#include <cmath>
|
|
|
|
#include <limits>
|
|
|
|
#include <iterator>
|
|
|
|
#include "assert.h"
|
|
|
|
|
|
|
|
#include "mdal_hec2d.hpp"
|
|
|
|
#include "mdal_hdf5.hpp"
|
|
|
|
#include "mdal_utils.hpp"
|
2020-03-09 05:59:51 +01:00
|
|
|
#include "mdal_logger.hpp"
|
2018-12-14 14:59:53 +01:00
|
|
|
|
|
|
|
static HdfFile openHdfFile( const std::string &fileName )
|
|
|
|
{
|
2019-11-29 15:15:01 +01:00
|
|
|
HdfFile file( fileName, HdfFile::ReadOnly );
|
2020-03-09 05:59:51 +01:00
|
|
|
if ( !file.isValid() ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Unable to open Hdf file " + fileName );
|
2018-12-14 14:59:53 +01:00
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HdfGroup openHdfGroup( const HdfFile &hdfFile, const std::string &name )
|
|
|
|
{
|
|
|
|
HdfGroup grp = hdfFile.group( name );
|
2020-03-09 05:59:51 +01:00
|
|
|
if ( !grp.isValid() ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Unable to open Hdf group " + name + " from file" );
|
2018-12-14 14:59:53 +01:00
|
|
|
return grp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HdfGroup openHdfGroup( const HdfGroup &hdfGroup, const std::string &name )
|
|
|
|
{
|
|
|
|
HdfGroup grp = hdfGroup.group( name );
|
2020-03-09 05:59:51 +01:00
|
|
|
if ( !grp.isValid() ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Unable to open Hdf group " + name + " from group" );
|
2018-12-14 14:59:53 +01:00
|
|
|
return grp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HdfDataset openHdfDataset( const HdfGroup &hdfGroup, const std::string &name )
|
|
|
|
{
|
|
|
|
HdfDataset dsFileType = hdfGroup.dataset( name );
|
2020-03-09 05:59:51 +01:00
|
|
|
if ( !dsFileType.isValid() ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Unable to open Hdf dataset " + name );
|
2018-12-14 14:59:53 +01:00
|
|
|
return dsFileType;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string openHdfAttribute( const HdfFile &hdfFile, const std::string &name )
|
|
|
|
{
|
|
|
|
HdfAttribute attr = hdfFile.attribute( name );
|
2020-03-09 05:59:51 +01:00
|
|
|
if ( !attr.isValid() ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Unable to open Hdf attribute " + name + " from file" );
|
2018-12-14 14:59:53 +01:00
|
|
|
return attr.readString();
|
|
|
|
}
|
|
|
|
|
2019-10-14 09:19:14 +02:00
|
|
|
static std::string openHdfAttribute( const HdfDataset &hdfDataset, const std::string &name )
|
|
|
|
{
|
|
|
|
HdfAttribute attr = hdfDataset.attribute( name );
|
2020-03-09 05:59:51 +01:00
|
|
|
if ( !attr.isValid() ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Unable to open Hdf group " + name + " from dataset" );
|
2019-10-14 09:19:14 +02:00
|
|
|
return attr.readString();
|
|
|
|
}
|
|
|
|
|
2018-12-14 14:59:53 +01:00
|
|
|
static HdfGroup getBaseOutputGroup( const HdfFile &hdfFile )
|
|
|
|
{
|
|
|
|
HdfGroup gResults = openHdfGroup( hdfFile, "Results" );
|
|
|
|
HdfGroup gUnsteady = openHdfGroup( gResults, "Unsteady" );
|
|
|
|
HdfGroup gOutput = openHdfGroup( gUnsteady, "Output" );
|
|
|
|
HdfGroup gOBlocks = openHdfGroup( gOutput, "Output Blocks" );
|
|
|
|
HdfGroup gBaseO = openHdfGroup( gOBlocks, "Base Output" );
|
|
|
|
return gBaseO;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HdfGroup get2DFlowAreasGroup( const HdfFile &hdfFile, const std::string loc )
|
|
|
|
{
|
|
|
|
HdfGroup gBaseO = getBaseOutputGroup( hdfFile );
|
|
|
|
HdfGroup gLoc = openHdfGroup( gBaseO, loc );
|
|
|
|
HdfGroup g2DFlowRes = openHdfGroup( gLoc, "2D Flow Areas" );
|
|
|
|
return g2DFlowRes;
|
|
|
|
}
|
|
|
|
|
2019-10-14 09:19:14 +02:00
|
|
|
static std::string getDataTimeUnit( HdfDataset &dsTime )
|
|
|
|
{
|
|
|
|
// Initially we expect data to be in hours
|
|
|
|
std::string dataTimeUnit = "Hours";
|
|
|
|
|
|
|
|
// First we look for the Time attribute
|
|
|
|
if ( dsTime.hasAttribute( "Time" ) )
|
|
|
|
{
|
|
|
|
dataTimeUnit = openHdfAttribute( dsTime, "Time" );
|
|
|
|
return dataTimeUnit;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Some variants of HEC_RAS have time unit stored in Variables attribute
|
|
|
|
// With read values looking like "Time|Days "
|
|
|
|
if ( dsTime.hasAttribute( "Variables" ) )
|
|
|
|
{
|
|
|
|
dataTimeUnit = openHdfAttribute( dsTime, "Variables" );
|
|
|
|
// Removing the "Time|" prefix
|
|
|
|
dataTimeUnit = MDAL::replace( dataTimeUnit, "Time|", "" );
|
|
|
|
}
|
|
|
|
|
|
|
|
return dataTimeUnit;
|
|
|
|
}
|
|
|
|
|
2019-12-13 09:33:23 +01:00
|
|
|
static std::vector<MDAL::RelativeTimestamp> convertTimeData( std::vector<float> ×, const std::string &originalTimeDataUnit )
|
2019-10-14 09:19:14 +02:00
|
|
|
{
|
2019-12-13 09:33:23 +01:00
|
|
|
std::vector<MDAL::RelativeTimestamp> convertedTime( times.size() );
|
|
|
|
|
|
|
|
MDAL::RelativeTimestamp::Unit unit = MDAL::parseDurationTimeUnit( originalTimeDataUnit );
|
|
|
|
|
|
|
|
for ( size_t i = 0; i < times.size(); i++ )
|
2019-10-14 09:19:14 +02:00
|
|
|
{
|
2019-12-13 09:33:23 +01:00
|
|
|
convertedTime[i] = MDAL::RelativeTimestamp( double( times[i] ), unit );
|
|
|
|
}
|
|
|
|
return convertedTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static MDAL::DateTime convertToDateTime( const std::string strDateTime )
|
|
|
|
{
|
|
|
|
//HECRAS format date is 01JAN2000
|
|
|
|
|
|
|
|
auto data = MDAL::split( strDateTime, " " );
|
|
|
|
if ( data.size() < 2 )
|
|
|
|
return MDAL::DateTime();
|
|
|
|
|
|
|
|
std::string dateStr = data[0];
|
|
|
|
|
|
|
|
int year = 0;
|
|
|
|
int month = 0;
|
|
|
|
int day = 0;
|
|
|
|
|
|
|
|
if ( dateStr.size() == 9 )
|
|
|
|
{
|
|
|
|
day = MDAL::toInt( dateStr.substr( 0, 2 ) );
|
|
|
|
std::string monthStr = dateStr.substr( 2, 3 );
|
|
|
|
year = MDAL::toInt( dateStr.substr( 5, 4 ) );
|
|
|
|
|
|
|
|
if ( monthStr == "JAN" )
|
|
|
|
month = 1;
|
|
|
|
else if ( monthStr == "FEB" )
|
|
|
|
month = 2;
|
|
|
|
else if ( monthStr == "MAR" )
|
|
|
|
month = 3;
|
|
|
|
else if ( monthStr == "APR" )
|
|
|
|
month = 4;
|
|
|
|
else if ( monthStr == "MAY" )
|
|
|
|
month = 5;
|
|
|
|
else if ( monthStr == "JUN" )
|
|
|
|
month = 6;
|
|
|
|
else if ( monthStr == "JUL" )
|
|
|
|
month = 7;
|
|
|
|
else if ( monthStr == "AUG" )
|
|
|
|
month = 8;
|
|
|
|
else if ( monthStr == "SEP" )
|
|
|
|
month = 9;
|
|
|
|
else if ( monthStr == "OCT" )
|
|
|
|
month = 10;
|
|
|
|
else if ( monthStr == "NOV" )
|
|
|
|
month = 11;
|
|
|
|
else if ( monthStr == "DEC" )
|
|
|
|
month = 12;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string timeStr = data[1];
|
|
|
|
|
|
|
|
auto timeData = MDAL::split( timeStr, ':' );
|
|
|
|
|
|
|
|
int hours = 0;
|
|
|
|
int min = 0;
|
|
|
|
double sec = 0;
|
|
|
|
|
|
|
|
if ( timeData.size() == 3 )
|
|
|
|
{
|
|
|
|
hours = MDAL::toInt( timeData[0] );
|
|
|
|
min = MDAL::toInt( timeData[1] );
|
|
|
|
sec = MDAL::toDouble( timeData[2] );
|
2019-10-14 09:19:14 +02:00
|
|
|
}
|
2019-12-13 09:33:23 +01:00
|
|
|
|
|
|
|
return MDAL::DateTime( year, month, day, hours, min, sec );
|
2019-10-14 09:19:14 +02:00
|
|
|
}
|
|
|
|
|
2019-12-13 09:33:23 +01:00
|
|
|
static MDAL::DateTime readReferenceDateTime( const HdfFile &hdfFile )
|
2019-10-14 09:19:14 +02:00
|
|
|
{
|
|
|
|
std::string refTime;
|
|
|
|
HdfGroup gBaseO = getBaseOutputGroup( hdfFile );
|
|
|
|
HdfGroup gUnsteadTS = openHdfGroup( gBaseO, "Unsteady Time Series" );
|
|
|
|
HdfDataset dsTimeDateStamp = openHdfDataset( gUnsteadTS, "Time Date Stamp" );
|
|
|
|
std::vector<std::string> timeStamps = dsTimeDateStamp.readArrayString();
|
|
|
|
|
|
|
|
if ( timeStamps.size() > 0 )
|
2019-12-13 09:33:23 +01:00
|
|
|
return convertToDateTime( timeStamps[0] );
|
|
|
|
|
|
|
|
return MDAL::DateTime();
|
2019-10-14 09:19:14 +02:00
|
|
|
}
|
|
|
|
|
2019-12-13 09:33:23 +01:00
|
|
|
static std::vector<MDAL::RelativeTimestamp> readTimes( const HdfFile &hdfFile )
|
2018-12-14 14:59:53 +01:00
|
|
|
{
|
|
|
|
HdfGroup gBaseO = getBaseOutputGroup( hdfFile );
|
|
|
|
HdfGroup gUnsteadTS = openHdfGroup( gBaseO, "Unsteady Time Series" );
|
2019-10-14 09:19:14 +02:00
|
|
|
HdfDataset dsTime = openHdfDataset( gUnsteadTS, "Time" );
|
|
|
|
std::string dataTimeUnits = getDataTimeUnit( dsTime );
|
|
|
|
std::vector<float> times = dsTime.readArray();
|
2019-12-13 09:33:23 +01:00
|
|
|
return convertTimeData( times, dataTimeUnits );
|
2018-12-14 14:59:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static std::vector<int> readFace2Cells( const HdfFile &hdfFile, const std::string &flowAreaName, size_t *nFaces )
|
|
|
|
{
|
|
|
|
// First read face to node mapping
|
|
|
|
HdfGroup gGeom = openHdfGroup( hdfFile, "Geometry" );
|
|
|
|
HdfGroup gGeom2DFlowAreas = openHdfGroup( gGeom, "2D Flow Areas" );
|
|
|
|
HdfGroup gArea = openHdfGroup( gGeom2DFlowAreas, flowAreaName );
|
|
|
|
HdfDataset dsFace2Cells = openHdfDataset( gArea, "Faces Cell Indexes" );
|
|
|
|
|
|
|
|
std::vector<hsize_t> fdims = dsFace2Cells.dims();
|
|
|
|
std::vector<int> face2Cells = dsFace2Cells.readArrayInt(); //2x nFaces
|
|
|
|
|
|
|
|
*nFaces = fdims[0];
|
|
|
|
return face2Cells;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MDAL::DriverHec2D::readFaceOutput( const HdfFile &hdfFile,
|
|
|
|
const HdfGroup &rootGroup,
|
|
|
|
const std::vector<size_t> &areaElemStartIndex,
|
|
|
|
const std::vector<std::string> &flowAreaNames,
|
|
|
|
const std::string rawDatasetName,
|
|
|
|
const std::string datasetName,
|
2019-12-13 09:33:23 +01:00
|
|
|
const std::vector<RelativeTimestamp> ×,
|
|
|
|
const DateTime &referenceTime )
|
2018-12-14 14:59:53 +01:00
|
|
|
{
|
|
|
|
double eps = std::numeric_limits<double>::min();
|
|
|
|
|
|
|
|
std::shared_ptr<DatasetGroup> group = std::make_shared< DatasetGroup >(
|
2019-01-04 18:18:34 +01:00
|
|
|
name(),
|
2018-12-14 14:59:53 +01:00
|
|
|
mMesh.get(),
|
|
|
|
mFileName,
|
|
|
|
datasetName
|
|
|
|
);
|
2020-03-09 05:59:51 +01:00
|
|
|
group->setDataLocation( MDAL_DataLocation::DataOnFaces );
|
2018-12-14 14:59:53 +01:00
|
|
|
group->setIsScalar( true );
|
2019-10-14 09:19:14 +02:00
|
|
|
group->setReferenceTime( referenceTime );
|
2018-12-14 14:59:53 +01:00
|
|
|
|
2019-11-29 15:15:01 +01:00
|
|
|
std::vector<std::shared_ptr<MDAL::MemoryDataset2D>> datasets;
|
2018-12-14 14:59:53 +01:00
|
|
|
|
|
|
|
for ( size_t tidx = 0; tidx < times.size(); ++tidx )
|
|
|
|
{
|
2019-11-29 15:15:01 +01:00
|
|
|
std::shared_ptr<MDAL::MemoryDataset2D> dataset = std::make_shared< MemoryDataset2D >( group.get() );
|
2019-12-13 09:33:23 +01:00
|
|
|
dataset->setTime( times[tidx] );
|
2018-12-14 14:59:53 +01:00
|
|
|
datasets.push_back( dataset );
|
|
|
|
}
|
|
|
|
|
2019-11-29 15:15:01 +01:00
|
|
|
std::shared_ptr<MDAL::MemoryDataset2D> firstDataset;
|
2018-12-14 14:59:53 +01:00
|
|
|
|
|
|
|
for ( size_t nArea = 0; nArea < flowAreaNames.size(); ++nArea )
|
|
|
|
{
|
|
|
|
std::string flowAreaName = flowAreaNames[nArea];
|
|
|
|
|
|
|
|
size_t nFaces;
|
|
|
|
std::vector<int> face2Cells = readFace2Cells( hdfFile, flowAreaName, &nFaces );
|
|
|
|
|
|
|
|
HdfGroup gFlowAreaRes = openHdfGroup( rootGroup, flowAreaName );
|
|
|
|
HdfDataset dsVals = openHdfDataset( gFlowAreaRes, rawDatasetName );
|
|
|
|
std::vector<float> vals = dsVals.readArray();
|
|
|
|
|
|
|
|
for ( size_t tidx = 0; tidx < times.size(); ++tidx )
|
|
|
|
{
|
2019-11-29 15:15:01 +01:00
|
|
|
std::shared_ptr<MDAL::MemoryDataset2D> dataset = datasets[tidx];
|
2018-12-14 14:59:53 +01:00
|
|
|
double *values = dataset->values();
|
|
|
|
|
|
|
|
for ( size_t i = 0; i < nFaces; ++i )
|
|
|
|
{
|
|
|
|
size_t idx = tidx * nFaces + i;
|
|
|
|
double val = static_cast<double>( vals[idx] ); // This is value on face!
|
|
|
|
|
|
|
|
if ( !std::isnan( val ) && fabs( val ) > eps ) //not nan and not 0
|
|
|
|
{
|
|
|
|
for ( size_t c = 0; c < 2; ++c )
|
|
|
|
{
|
2019-01-22 10:29:53 +01:00
|
|
|
size_t cell_idx = static_cast<size_t>( face2Cells[2 * i + c] ) + areaElemStartIndex[nArea];
|
2018-12-14 14:59:53 +01:00
|
|
|
// Take just maximum
|
|
|
|
if ( std::isnan( values[cell_idx] ) || values[cell_idx] < val )
|
|
|
|
{
|
|
|
|
values[cell_idx] = val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( auto dataset : datasets )
|
|
|
|
{
|
|
|
|
dataset->setStatistics( MDAL::calculateStatistics( dataset ) );
|
|
|
|
group->datasets.push_back( dataset );
|
|
|
|
}
|
|
|
|
group->setStatistics( MDAL::calculateStatistics( group ) );
|
|
|
|
mMesh->datasetGroups.push_back( group );
|
|
|
|
}
|
|
|
|
|
|
|
|
void MDAL::DriverHec2D::readFaceResults( const HdfFile &hdfFile,
|
|
|
|
const std::vector<size_t> &areaElemStartIndex,
|
|
|
|
const std::vector<std::string> &flowAreaNames )
|
|
|
|
{
|
|
|
|
// UNSTEADY
|
|
|
|
HdfGroup flowGroup = get2DFlowAreasGroup( hdfFile, "Unsteady Time Series" );
|
2019-12-13 09:33:23 +01:00
|
|
|
MDAL::DateTime referenceDateTime = readReferenceDateTime( hdfFile );
|
|
|
|
readFaceOutput( hdfFile, flowGroup, areaElemStartIndex, flowAreaNames, "Face Shear Stress", "Face Shear Stress", mTimes, referenceDateTime );
|
|
|
|
readFaceOutput( hdfFile, flowGroup, areaElemStartIndex, flowAreaNames, "Face Velocity", "Face Velocity", mTimes, referenceDateTime );
|
2018-12-14 14:59:53 +01:00
|
|
|
|
|
|
|
// SUMMARY
|
|
|
|
flowGroup = get2DFlowAreasGroup( hdfFile, "Summary Output" );
|
2019-12-13 09:33:23 +01:00
|
|
|
std::vector<MDAL::RelativeTimestamp> dummyTimes( 1, MDAL::RelativeTimestamp() );
|
2018-12-14 14:59:53 +01:00
|
|
|
|
2019-12-13 09:33:23 +01:00
|
|
|
readFaceOutput( hdfFile, flowGroup, areaElemStartIndex, flowAreaNames, "Maximum Face Shear Stress", "Face Shear Stress/Maximums", dummyTimes, referenceDateTime );
|
|
|
|
readFaceOutput( hdfFile, flowGroup, areaElemStartIndex, flowAreaNames, "Maximum Face Velocity", "Face Velocity/Maximums", dummyTimes, referenceDateTime );
|
2018-12-14 14:59:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-11-29 15:15:01 +01:00
|
|
|
std::shared_ptr<MDAL::MemoryDataset2D> MDAL::DriverHec2D::readElemOutput( const HdfGroup &rootGroup,
|
2018-12-14 14:59:53 +01:00
|
|
|
const std::vector<size_t> &areaElemStartIndex,
|
|
|
|
const std::vector<std::string> &flowAreaNames,
|
|
|
|
const std::string rawDatasetName,
|
|
|
|
const std::string datasetName,
|
2019-12-13 09:33:23 +01:00
|
|
|
const std::vector<RelativeTimestamp> ×,
|
2019-11-29 15:15:01 +01:00
|
|
|
std::shared_ptr<MDAL::MemoryDataset2D> bed_elevation,
|
2019-12-13 09:33:23 +01:00
|
|
|
const DateTime &referenceTime )
|
2018-12-14 14:59:53 +01:00
|
|
|
{
|
|
|
|
double eps = std::numeric_limits<double>::min();
|
|
|
|
|
|
|
|
std::shared_ptr<DatasetGroup> group = std::make_shared< DatasetGroup >(
|
2019-01-04 18:18:34 +01:00
|
|
|
name(),
|
2018-12-14 14:59:53 +01:00
|
|
|
mMesh.get(),
|
|
|
|
mFileName,
|
|
|
|
datasetName
|
|
|
|
);
|
2020-03-09 05:59:51 +01:00
|
|
|
group->setDataLocation( MDAL_DataLocation::DataOnFaces );
|
2018-12-14 14:59:53 +01:00
|
|
|
group->setIsScalar( true );
|
2019-10-14 09:19:14 +02:00
|
|
|
group->setReferenceTime( referenceTime );
|
2018-12-14 14:59:53 +01:00
|
|
|
|
2019-11-29 15:15:01 +01:00
|
|
|
std::vector<std::shared_ptr<MDAL::MemoryDataset2D>> datasets;
|
2018-12-14 14:59:53 +01:00
|
|
|
|
|
|
|
for ( size_t tidx = 0; tidx < times.size(); ++tidx )
|
|
|
|
{
|
2019-11-29 15:15:01 +01:00
|
|
|
std::shared_ptr<MDAL::MemoryDataset2D> dataset = std::make_shared< MemoryDataset2D >( group.get() );
|
2019-12-13 09:33:23 +01:00
|
|
|
dataset->setTime( times[tidx] );
|
2018-12-14 14:59:53 +01:00
|
|
|
datasets.push_back( dataset );
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( size_t nArea = 0; nArea < flowAreaNames.size(); ++nArea )
|
|
|
|
{
|
|
|
|
size_t nAreaElements = areaElemStartIndex[nArea + 1] - areaElemStartIndex[nArea];
|
|
|
|
std::string flowAreaName = flowAreaNames[nArea];
|
|
|
|
HdfGroup gFlowAreaRes = openHdfGroup( rootGroup, flowAreaName );
|
|
|
|
|
|
|
|
HdfDataset dsVals = openHdfDataset( gFlowAreaRes, rawDatasetName );
|
|
|
|
std::vector<float> vals = dsVals.readArray();
|
|
|
|
|
|
|
|
for ( size_t tidx = 0; tidx < times.size(); ++tidx )
|
|
|
|
{
|
2019-11-29 15:15:01 +01:00
|
|
|
std::shared_ptr<MDAL::MemoryDataset2D> dataset = datasets[tidx];
|
2018-12-14 14:59:53 +01:00
|
|
|
double *values = dataset->values();
|
|
|
|
|
|
|
|
for ( size_t i = 0; i < nAreaElements; ++i )
|
|
|
|
{
|
|
|
|
size_t idx = tidx * nAreaElements + i;
|
|
|
|
size_t eInx = areaElemStartIndex[nArea] + i;
|
|
|
|
double val = static_cast<double>( vals[idx] );
|
|
|
|
if ( !std::isnan( val ) )
|
|
|
|
{
|
|
|
|
if ( !bed_elevation )
|
|
|
|
{
|
|
|
|
// we are populating bed elevation dataset
|
|
|
|
values[eInx] = val;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( datasetName == "Depth" )
|
|
|
|
{
|
|
|
|
if ( fabs( val ) > eps ) // 0 Depth is no-data
|
|
|
|
{
|
|
|
|
values[eInx] = val;
|
|
|
|
}
|
|
|
|
}
|
2019-12-11 14:04:34 +01:00
|
|
|
else //Water surface
|
2018-12-14 14:59:53 +01:00
|
|
|
{
|
|
|
|
assert( bed_elevation );
|
2019-12-11 14:04:34 +01:00
|
|
|
double bed_elev = bed_elevation->scalarValue( eInx );
|
2018-12-14 14:59:53 +01:00
|
|
|
if ( std::isnan( bed_elev ) || fabs( bed_elev - val ) > eps ) // change from bed elevation
|
|
|
|
{
|
|
|
|
values[eInx] = val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( auto dataset : datasets )
|
|
|
|
{
|
|
|
|
dataset->setStatistics( MDAL::calculateStatistics( dataset ) );
|
|
|
|
group->datasets.push_back( dataset );
|
|
|
|
}
|
|
|
|
group->setStatistics( MDAL::calculateStatistics( group ) );
|
|
|
|
mMesh->datasetGroups.push_back( group );
|
|
|
|
|
|
|
|
return datasets[0];
|
|
|
|
}
|
|
|
|
|
2019-11-29 15:15:01 +01:00
|
|
|
std::shared_ptr<MDAL::MemoryDataset2D> MDAL::DriverHec2D::readBedElevation(
|
2018-12-14 14:59:53 +01:00
|
|
|
const HdfGroup &gGeom2DFlowAreas,
|
|
|
|
const std::vector<size_t> &areaElemStartIndex,
|
|
|
|
const std::vector<std::string> &flowAreaNames )
|
|
|
|
{
|
2019-12-13 09:33:23 +01:00
|
|
|
std::vector<MDAL::RelativeTimestamp> times( 1 );
|
|
|
|
DateTime referenceTime;
|
2019-10-14 09:19:14 +02:00
|
|
|
|
2018-12-14 14:59:53 +01:00
|
|
|
return readElemOutput(
|
|
|
|
gGeom2DFlowAreas,
|
|
|
|
areaElemStartIndex,
|
|
|
|
flowAreaNames,
|
|
|
|
"Cells Minimum Elevation",
|
|
|
|
"Bed Elevation",
|
|
|
|
times,
|
2019-11-29 15:15:01 +01:00
|
|
|
std::shared_ptr<MDAL::MemoryDataset2D>(),
|
2019-10-14 09:19:14 +02:00
|
|
|
referenceTime
|
2018-12-14 14:59:53 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MDAL::DriverHec2D::readElemResults(
|
|
|
|
const HdfFile &hdfFile,
|
2019-11-29 15:15:01 +01:00
|
|
|
std::shared_ptr<MDAL::MemoryDataset2D> bed_elevation,
|
2018-12-14 14:59:53 +01:00
|
|
|
const std::vector<size_t> &areaElemStartIndex,
|
|
|
|
const std::vector<std::string> &flowAreaNames )
|
|
|
|
{
|
|
|
|
// UNSTEADY
|
|
|
|
HdfGroup flowGroup = get2DFlowAreasGroup( hdfFile, "Unsteady Time Series" );
|
|
|
|
|
|
|
|
readElemOutput(
|
|
|
|
flowGroup,
|
|
|
|
areaElemStartIndex,
|
|
|
|
flowAreaNames,
|
|
|
|
"Water Surface",
|
|
|
|
"Water Surface",
|
2019-12-13 09:33:23 +01:00
|
|
|
mTimes,
|
2019-10-14 09:19:14 +02:00
|
|
|
bed_elevation,
|
2019-12-13 09:33:23 +01:00
|
|
|
mReferenceTime );
|
2018-12-14 14:59:53 +01:00
|
|
|
readElemOutput(
|
|
|
|
flowGroup,
|
|
|
|
areaElemStartIndex,
|
|
|
|
flowAreaNames,
|
|
|
|
"Depth",
|
|
|
|
"Depth",
|
2019-12-13 09:33:23 +01:00
|
|
|
mTimes,
|
2019-10-14 09:19:14 +02:00
|
|
|
bed_elevation,
|
2019-12-13 09:33:23 +01:00
|
|
|
mReferenceTime );
|
2018-12-14 14:59:53 +01:00
|
|
|
|
|
|
|
// SUMMARY
|
|
|
|
flowGroup = get2DFlowAreasGroup( hdfFile, "Summary Output" );
|
2019-12-13 09:33:23 +01:00
|
|
|
|
|
|
|
std::vector<RelativeTimestamp> dummyTimes( 1, MDAL::RelativeTimestamp() );
|
2018-12-14 14:59:53 +01:00
|
|
|
|
|
|
|
readElemOutput(
|
|
|
|
flowGroup,
|
|
|
|
areaElemStartIndex,
|
|
|
|
flowAreaNames,
|
|
|
|
"Maximum Water Surface",
|
|
|
|
"Water Surface/Maximums",
|
2019-12-13 09:33:23 +01:00
|
|
|
dummyTimes,
|
2019-10-14 09:19:14 +02:00
|
|
|
bed_elevation,
|
2019-12-13 09:33:23 +01:00
|
|
|
mReferenceTime
|
2018-12-14 14:59:53 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-01-22 10:29:53 +01:00
|
|
|
std::vector<std::string> MDAL::DriverHec2D::read2DFlowAreasNamesOld( HdfGroup gGeom2DFlowAreas ) const
|
2018-12-14 14:59:53 +01:00
|
|
|
{
|
|
|
|
HdfDataset dsNames = openHdfDataset( gGeom2DFlowAreas, "Names" );
|
|
|
|
std::vector<std::string> names = dsNames.readArrayString();
|
2020-03-09 05:59:51 +01:00
|
|
|
if ( names.empty() ) throw MDAL::Error( MDAL_Status::Err_InvalidData, "Unable to read 2D Flow area names, no names found" );
|
2018-12-14 14:59:53 +01:00
|
|
|
return names;
|
|
|
|
}
|
|
|
|
|
2019-01-22 10:29:53 +01:00
|
|
|
/**
|
|
|
|
For 5.0.5+ format
|
|
|
|
|
|
|
|
DATATYPE H5T_COMPOUND {
|
|
|
|
H5T_STRING {
|
|
|
|
STRSIZE 16;
|
|
|
|
STRPAD H5T_STR_NULLTERM;
|
|
|
|
CSET H5T_CSET_ASCII;
|
|
|
|
CTYPE H5T_C_S1;
|
|
|
|
} "Name";
|
|
|
|
H5T_IEEE_F32LE "Mann";
|
|
|
|
H5T_IEEE_F32LE "Cell Vol Tol";
|
|
|
|
H5T_IEEE_F32LE "Cell Min Area Fraction";
|
|
|
|
H5T_IEEE_F32LE "Face Profile Tol";
|
|
|
|
H5T_IEEE_F32LE "Face Area Tol";
|
|
|
|
H5T_IEEE_F32LE "Face Conv Ratio";
|
|
|
|
H5T_IEEE_F32LE "Laminar Depth";
|
|
|
|
H5T_IEEE_F32LE "Spacing dx";
|
|
|
|
H5T_IEEE_F32LE "Spacing dy";
|
|
|
|
H5T_IEEE_F32LE "Shift dx";
|
|
|
|
H5T_IEEE_F32LE "Shift dy";
|
|
|
|
H5T_STD_I32LE "Cell Count";
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
typedef struct FlowAreasAttribute505
|
|
|
|
{
|
|
|
|
char name[HDF_MAX_NAME];
|
|
|
|
float mann;
|
|
|
|
float cellVolTol;
|
|
|
|
float cellMinAreaFraction;
|
|
|
|
float faceProfileTol;
|
|
|
|
float faceAreaTol;
|
|
|
|
float faceConvRatio;
|
|
|
|
float laminarDepth;
|
|
|
|
float spacingDx;
|
|
|
|
float spacingDy;
|
|
|
|
float shifyDx;
|
|
|
|
float shifyDy;
|
|
|
|
int cellCount;
|
|
|
|
} FlowAreasAttribute505;
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<std::string> MDAL::DriverHec2D::read2DFlowAreasNames505( HdfGroup gGeom2DFlowAreas ) const
|
|
|
|
{
|
|
|
|
HdfDataset dsAttributes = openHdfDataset( gGeom2DFlowAreas, "Attributes" );
|
|
|
|
hid_t attributeHID = H5Tcreate( H5T_COMPOUND, sizeof( FlowAreasAttribute505 ) );
|
|
|
|
hid_t stringHID = H5Tcopy( H5T_C_S1 );
|
|
|
|
H5Tset_size( stringHID, HDF_MAX_NAME );
|
|
|
|
H5Tinsert( attributeHID, "Name", HOFFSET( FlowAreasAttribute505, name ), stringHID );
|
|
|
|
H5Tinsert( attributeHID, "Mann", HOFFSET( FlowAreasAttribute505, mann ), H5T_NATIVE_FLOAT );
|
|
|
|
H5Tinsert( attributeHID, "Cell Vol Tol", HOFFSET( FlowAreasAttribute505, cellVolTol ), H5T_NATIVE_FLOAT );
|
|
|
|
H5Tinsert( attributeHID, "Cell Min Area Fraction", HOFFSET( FlowAreasAttribute505, cellMinAreaFraction ), H5T_NATIVE_FLOAT );
|
|
|
|
H5Tinsert( attributeHID, "Face Profile Tol", HOFFSET( FlowAreasAttribute505, faceProfileTol ), H5T_NATIVE_FLOAT );
|
|
|
|
H5Tinsert( attributeHID, "Face Area Tol", HOFFSET( FlowAreasAttribute505, faceAreaTol ), H5T_NATIVE_FLOAT );
|
|
|
|
H5Tinsert( attributeHID, "Face Conv Ratio", HOFFSET( FlowAreasAttribute505, faceConvRatio ), H5T_NATIVE_FLOAT );
|
|
|
|
H5Tinsert( attributeHID, "Laminar Depth", HOFFSET( FlowAreasAttribute505, laminarDepth ), H5T_NATIVE_FLOAT );
|
|
|
|
H5Tinsert( attributeHID, "Spacing dx", HOFFSET( FlowAreasAttribute505, spacingDx ), H5T_NATIVE_FLOAT );
|
|
|
|
H5Tinsert( attributeHID, "Spacing dy", HOFFSET( FlowAreasAttribute505, spacingDy ), H5T_NATIVE_FLOAT );
|
|
|
|
H5Tinsert( attributeHID, "Shift dx", HOFFSET( FlowAreasAttribute505, shifyDx ), H5T_NATIVE_FLOAT );
|
|
|
|
H5Tinsert( attributeHID, "Shift dy", HOFFSET( FlowAreasAttribute505, shifyDy ), H5T_NATIVE_FLOAT );
|
|
|
|
H5Tinsert( attributeHID, "Cell Count", HOFFSET( FlowAreasAttribute505, cellCount ), H5T_NATIVE_INT );
|
|
|
|
std::vector<FlowAreasAttribute505> attributes = dsAttributes.readArray<FlowAreasAttribute505>( attributeHID );
|
|
|
|
H5Tclose( attributeHID );
|
|
|
|
H5Tclose( stringHID );
|
|
|
|
std::vector<std::string> names;
|
2020-03-09 05:59:51 +01:00
|
|
|
if ( attributes.empty() ) throw MDAL::Error( MDAL_Status::Err_InvalidData, "Unable to read 2D Flow Area Names, no attributes found" );
|
2019-01-22 10:29:53 +01:00
|
|
|
|
|
|
|
for ( const auto &attr : attributes )
|
|
|
|
{
|
|
|
|
std::string dat = std::string( attr.name );
|
|
|
|
names.push_back( MDAL::trim( dat ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
return names;
|
|
|
|
}
|
|
|
|
|
2018-12-14 14:59:53 +01:00
|
|
|
void MDAL::DriverHec2D::setProjection( HdfFile hdfFile )
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
std::string proj_wkt = openHdfAttribute( hdfFile, "Projection" );
|
|
|
|
mMesh->setSourceCrsFromWKT( proj_wkt );
|
|
|
|
}
|
|
|
|
catch ( MDAL_Status ) { /* projection not set */}
|
2020-03-09 05:59:51 +01:00
|
|
|
catch ( MDAL::Error ) { /* projection not set */}
|
2018-12-14 14:59:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void MDAL::DriverHec2D::parseMesh(
|
|
|
|
HdfGroup gGeom2DFlowAreas,
|
|
|
|
std::vector<size_t> &areaElemStartIndex,
|
|
|
|
const std::vector<std::string> &flowAreaNames )
|
|
|
|
{
|
|
|
|
Faces faces;
|
|
|
|
Vertices vertices;
|
|
|
|
|
|
|
|
size_t maxVerticesInFace = 0;
|
|
|
|
|
|
|
|
for ( size_t nArea = 0; nArea < flowAreaNames.size(); ++nArea )
|
|
|
|
{
|
|
|
|
std::string flowAreaName = flowAreaNames[nArea];
|
|
|
|
|
|
|
|
HdfGroup gArea = openHdfGroup( gGeom2DFlowAreas, flowAreaName );
|
|
|
|
|
|
|
|
HdfDataset dsCoords = openHdfDataset( gArea, "FacePoints Coordinate" );
|
|
|
|
std::vector<hsize_t> cdims = dsCoords.dims();
|
|
|
|
std::vector<double> coords = dsCoords.readArrayDouble(); //2xnNodes matrix in array
|
|
|
|
size_t nNodes = cdims[0];
|
|
|
|
size_t areaNodeStartIndex = vertices.size();
|
|
|
|
vertices.resize( areaNodeStartIndex + nNodes );
|
|
|
|
for ( size_t n = 0; n < nNodes; ++n )
|
|
|
|
{
|
|
|
|
size_t nIdx = areaNodeStartIndex + n;
|
|
|
|
vertices[nIdx].x = coords[cdims[1] * n];
|
|
|
|
vertices[nIdx].y = coords[cdims[1] * n + 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
HdfDataset dsElems = openHdfDataset( gArea, "Cells FacePoint Indexes" );
|
|
|
|
std::vector<hsize_t> edims = dsElems.dims();
|
|
|
|
size_t nElems = edims[0];
|
|
|
|
size_t maxFaces = edims[1]; // elems have up to 8 faces, but sometimes the table has less than 8 columns
|
|
|
|
std::vector<int> elem_nodes = dsElems.readArrayInt(); //maxFacesxnElements matrix in array
|
|
|
|
areaElemStartIndex[nArea] = faces.size();
|
|
|
|
faces.resize( faces.size() + nElems );
|
|
|
|
for ( size_t e = 0; e < nElems; ++e )
|
|
|
|
{
|
|
|
|
size_t eIdx = areaElemStartIndex[nArea] + e;
|
|
|
|
std::vector<size_t> idx( maxFaces );
|
|
|
|
size_t nValidVertexes = maxFaces;
|
|
|
|
for ( size_t fi = 0; fi < maxFaces; ++fi )
|
|
|
|
{
|
|
|
|
int elem_node_idx = elem_nodes[edims[1] * e + fi];
|
|
|
|
|
|
|
|
if ( elem_node_idx == -1 )
|
|
|
|
{
|
|
|
|
nValidVertexes = fi;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
idx[fi] = areaNodeStartIndex + static_cast<size_t>( elem_node_idx ); // shift by this area start node index
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( nValidVertexes > 0 )
|
|
|
|
faces[eIdx].assign( idx.begin(), std::next( idx.begin(), nValidVertexes ) );
|
|
|
|
|
|
|
|
if ( nValidVertexes > maxVerticesInFace )
|
|
|
|
maxVerticesInFace = nValidVertexes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
areaElemStartIndex[flowAreaNames.size()] = faces.size();
|
|
|
|
|
|
|
|
mMesh.reset(
|
|
|
|
new MemoryMesh(
|
2019-01-04 18:18:34 +01:00
|
|
|
name(),
|
2018-12-14 14:59:53 +01:00
|
|
|
vertices.size(),
|
2020-03-09 05:59:51 +01:00
|
|
|
0,
|
2018-12-14 14:59:53 +01:00
|
|
|
faces.size(),
|
|
|
|
maxVerticesInFace,
|
|
|
|
computeExtent( vertices ),
|
|
|
|
mFileName
|
|
|
|
)
|
|
|
|
);
|
|
|
|
mMesh->faces = faces;
|
|
|
|
mMesh->vertices = vertices;
|
|
|
|
}
|
|
|
|
|
|
|
|
MDAL::DriverHec2D::DriverHec2D()
|
|
|
|
: Driver( "HEC2D",
|
|
|
|
"HEC-RAS 2D",
|
|
|
|
"*.hdf",
|
2019-01-04 18:18:34 +01:00
|
|
|
Capability::ReadMesh )
|
2018-12-14 14:59:53 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
MDAL::DriverHec2D *MDAL::DriverHec2D::create()
|
|
|
|
{
|
|
|
|
return new DriverHec2D();
|
|
|
|
}
|
|
|
|
|
2019-11-29 15:15:01 +01:00
|
|
|
bool MDAL::DriverHec2D::canReadMesh( const std::string &uri )
|
2018-12-14 14:59:53 +01:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
HdfFile hdfFile = openHdfFile( uri );
|
|
|
|
std::string fileType = openHdfAttribute( hdfFile, "File Type" );
|
2019-01-22 10:29:53 +01:00
|
|
|
return canReadOldFormat( fileType ) || canReadFormat505( fileType );
|
2018-12-14 14:59:53 +01:00
|
|
|
}
|
|
|
|
catch ( MDAL_Status )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2020-03-09 05:59:51 +01:00
|
|
|
catch ( MDAL::Error )
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2019-01-22 10:29:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool MDAL::DriverHec2D::canReadOldFormat( const std::string &fileType ) const
|
|
|
|
{
|
|
|
|
return fileType == "HEC-RAS Results";
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MDAL::DriverHec2D::canReadFormat505( const std::string &fileType ) const
|
|
|
|
{
|
|
|
|
return fileType == "HEC-RAS Geometry";
|
2018-12-14 14:59:53 +01:00
|
|
|
}
|
|
|
|
|
2020-04-14 02:17:15 -04:00
|
|
|
std::unique_ptr<MDAL::Mesh> MDAL::DriverHec2D::load( const std::string &resultsFile, const std::string & )
|
2018-12-14 14:59:53 +01:00
|
|
|
{
|
|
|
|
mFileName = resultsFile;
|
2020-03-09 05:59:51 +01:00
|
|
|
MDAL::Log::resetLastStatus();
|
2018-12-14 14:59:53 +01:00
|
|
|
mMesh.reset();
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
HdfFile hdfFile = openHdfFile( mFileName );
|
|
|
|
|
|
|
|
// Verify it is correct file
|
|
|
|
std::string fileType = openHdfAttribute( hdfFile, "File Type" );
|
2019-01-22 10:29:53 +01:00
|
|
|
bool oldFormat = canReadOldFormat( fileType );
|
2018-12-14 14:59:53 +01:00
|
|
|
|
|
|
|
HdfGroup gGeom = openHdfGroup( hdfFile, "Geometry" );
|
|
|
|
HdfGroup gGeom2DFlowAreas = openHdfGroup( gGeom, "2D Flow Areas" );
|
|
|
|
|
2019-01-22 10:29:53 +01:00
|
|
|
std::vector<std::string> flowAreaNames;
|
|
|
|
if ( oldFormat )
|
|
|
|
flowAreaNames = read2DFlowAreasNamesOld( gGeom2DFlowAreas );
|
|
|
|
else
|
|
|
|
flowAreaNames = read2DFlowAreasNames505( gGeom2DFlowAreas );
|
|
|
|
|
2018-12-14 14:59:53 +01:00
|
|
|
std::vector<size_t> areaElemStartIndex( flowAreaNames.size() + 1 );
|
|
|
|
|
|
|
|
parseMesh( gGeom2DFlowAreas, areaElemStartIndex, flowAreaNames );
|
|
|
|
setProjection( hdfFile );
|
|
|
|
|
2019-12-13 09:33:23 +01:00
|
|
|
mTimes = readTimes( hdfFile );
|
|
|
|
mReferenceTime = readReferenceDateTime( hdfFile );
|
|
|
|
|
2018-12-14 14:59:53 +01:00
|
|
|
//Elevation
|
2019-11-29 15:15:01 +01:00
|
|
|
std::shared_ptr<MDAL::MemoryDataset2D> bed_elevation = readBedElevation( gGeom2DFlowAreas, areaElemStartIndex, flowAreaNames );
|
2018-12-14 14:59:53 +01:00
|
|
|
|
|
|
|
// Element centered Values
|
|
|
|
readElemResults( hdfFile, bed_elevation, areaElemStartIndex, flowAreaNames );
|
|
|
|
|
|
|
|
// Face centered Values
|
|
|
|
readFaceResults( hdfFile, areaElemStartIndex, flowAreaNames );
|
2019-01-22 10:29:53 +01:00
|
|
|
|
2018-12-14 14:59:53 +01:00
|
|
|
}
|
|
|
|
catch ( MDAL_Status error )
|
|
|
|
{
|
2020-03-09 05:59:51 +01:00
|
|
|
MDAL::Log::error( error, name(), "Error occured while loading file " + resultsFile );
|
|
|
|
mMesh.reset();
|
|
|
|
}
|
|
|
|
catch ( MDAL::Error err )
|
|
|
|
{
|
|
|
|
MDAL::Log::error( err, name() );
|
2018-12-14 14:59:53 +01:00
|
|
|
mMesh.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::unique_ptr<Mesh>( mMesh.release() );
|
|
|
|
}
|