mirror of
https://github.com/OPM/ResInsight.git
synced 2025-02-25 18:55:39 -06:00
#2066 Observed Data : Major rewrite of varying column width parsing
Add TableInfo to hold a table with several ColumnInfo objects Improved detection of RifEclipseSummaryAddress from all variants
This commit is contained in:
@@ -36,6 +36,7 @@ ${CEE_CURRENT_LIST_DIR}RifColumnBasedUserData.h
|
||||
${CEE_CURRENT_LIST_DIR}RifKeywordVectorUserData.h
|
||||
${CEE_CURRENT_LIST_DIR}RifWellRftAddress.h
|
||||
${CEE_CURRENT_LIST_DIR}RifWellRftAddressQMetaType.h
|
||||
${CEE_CURRENT_LIST_DIR}RifEclipseUserDataKeywordTools.h
|
||||
|
||||
# HDF5 file reader is directly included in ResInsight main CmakeList.txt
|
||||
#${CEE_CURRENT_LIST_DIR}RifHdf5Reader.h
|
||||
@@ -77,6 +78,7 @@ ${CEE_CURRENT_LIST_DIR}RifHdf5ReaderInterface.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RifColumnBasedUserData.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RifKeywordVectorUserData.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RifWellRftAddress.cpp
|
||||
${CEE_CURRENT_LIST_DIR}RifEclipseUserDataKeywordTools.cpp
|
||||
|
||||
|
||||
# HDF5 file reader is directly included in ResInsight main CmakeList.txt
|
||||
|
||||
@@ -18,10 +18,12 @@
|
||||
|
||||
#include "RifColumnBasedUserData.h"
|
||||
|
||||
#include "RiaQDateTimeTools.h"
|
||||
#include "RiaDateStringParser.h"
|
||||
#include "RiaLogging.h"
|
||||
#include "RiaQDateTimeTools.h"
|
||||
|
||||
#include "RifColumnBasedUserDataParser.h"
|
||||
#include "RifEclipseUserDataKeywordTools.h"
|
||||
#include "RifEclipseUserDataParserTools.h"
|
||||
|
||||
#include "cafUtils.h"
|
||||
@@ -64,130 +66,7 @@ bool RifColumnBasedUserData::parse(const QString& data, const QString& customWel
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t tableIndex = 0; tableIndex < m_parser->tables().size(); tableIndex++)
|
||||
{
|
||||
// Find time index
|
||||
size_t dateColumnIndex = m_parser->tables()[tableIndex].size();
|
||||
size_t dayOrYearColumnIndex = m_parser->tables()[tableIndex].size();
|
||||
|
||||
for (size_t columIndex = 0; columIndex < m_parser->tables()[tableIndex].size(); columIndex++)
|
||||
{
|
||||
const ColumnInfo& ci = m_parser->tables()[tableIndex][columIndex];
|
||||
if (!ci.isAVector)
|
||||
{
|
||||
QString unit = QString::fromStdString(ci.unitName).trimmed().toUpper();
|
||||
if (unit == "DATE" ||
|
||||
unit == "DATES")
|
||||
{
|
||||
dateColumnIndex = columIndex;
|
||||
}
|
||||
else if (unit == "DAY" ||
|
||||
unit == "DAYS" ||
|
||||
unit == "YEAR" ||
|
||||
unit == "YEARS")
|
||||
{
|
||||
dayOrYearColumnIndex = columIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t timeColumnIndex = m_parser->tables()[tableIndex].size();
|
||||
if (dayOrYearColumnIndex < m_parser->tables()[tableIndex].size())
|
||||
{
|
||||
timeColumnIndex = dayOrYearColumnIndex;
|
||||
}
|
||||
else if (dateColumnIndex < m_parser->tables()[tableIndex].size())
|
||||
{
|
||||
timeColumnIndex = dateColumnIndex;
|
||||
}
|
||||
|
||||
if (timeColumnIndex == m_parser->tables()[tableIndex].size())
|
||||
{
|
||||
RiaLogging::warning(QString("Failed to find time data for table %1 in file %2").arg(tableIndex));
|
||||
RiaLogging::warning(QString("No data for this table is imported"));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_timeSteps.resize(m_timeSteps.size() + 1);
|
||||
std::vector<time_t>& timeSteps = m_timeSteps.back();
|
||||
|
||||
const ColumnInfo& ci = m_parser->tables()[tableIndex][timeColumnIndex];
|
||||
|
||||
if (timeColumnIndex == dateColumnIndex)
|
||||
{
|
||||
for (const auto& timeStepValue : ci.observationDateTimes)
|
||||
{
|
||||
timeSteps.push_back(timeStepValue.toTime_t());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QDateTime startDate;
|
||||
if (ci.startQDateTime.isValid())
|
||||
{
|
||||
startDate = ci.startQDateTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
QString startDateString = QString::fromStdString(ci.startDateString);
|
||||
if (!startDateString.isEmpty())
|
||||
{
|
||||
QString dateFormatString = "ddMMyyyy";
|
||||
|
||||
startDate = RiaQDateTimeTools::fromString(startDateString, dateFormatString);
|
||||
}
|
||||
else
|
||||
{
|
||||
startDate = RiaQDateTimeTools::epoch();
|
||||
}
|
||||
}
|
||||
|
||||
QString unit = QString::fromStdString(ci.unitName).trimmed().toUpper();
|
||||
|
||||
if (unit == "DAY" || unit == "DAYS")
|
||||
{
|
||||
for (const auto& timeStepValue : ci.values)
|
||||
{
|
||||
QDateTime dateTime = RiaQDateTimeTools::addDays(startDate, timeStepValue);
|
||||
timeSteps.push_back(dateTime.toTime_t());
|
||||
}
|
||||
}
|
||||
else if (unit == "YEAR" || unit == "YEARS")
|
||||
{
|
||||
for (const auto& timeStepValue : ci.values)
|
||||
{
|
||||
QDateTime dateTime = RiaQDateTimeTools::addYears(startDate, timeStepValue);
|
||||
timeSteps.push_back(dateTime.toTime_t());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t columIndex = 0; columIndex < m_parser->tables()[tableIndex].size(); columIndex++)
|
||||
{
|
||||
const ColumnInfo& ci = m_parser->tables()[tableIndex][columIndex];
|
||||
if (ci.isAVector)
|
||||
{
|
||||
RifEclipseSummaryAddress sumAddress = ci.summaryAddress;
|
||||
|
||||
if (customWellName.size() > 0)
|
||||
{
|
||||
sumAddress.setWellName(customWellName.toStdString());
|
||||
}
|
||||
|
||||
if (customWellGroupName.size() > 0)
|
||||
{
|
||||
sumAddress.setWellGroupName(customWellGroupName.toStdString());
|
||||
}
|
||||
|
||||
m_allResultAddresses.push_back(sumAddress);
|
||||
|
||||
m_mapFromAddressToTimeStepIndex[sumAddress] = m_timeSteps.size() - 1;
|
||||
m_mapFromAddressToResultIndex[sumAddress] = std::make_pair(tableIndex, columIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
buildTimeStepsFromTables();
|
||||
|
||||
return true;
|
||||
|
||||
@@ -203,8 +82,10 @@ bool RifColumnBasedUserData::values(const RifEclipseSummaryAddress& resultAddres
|
||||
{
|
||||
std::pair<size_t, size_t> tableColIndices = search->second;
|
||||
|
||||
const ColumnInfo& ci = m_parser->tables()[tableColIndices.first][tableColIndices.second];
|
||||
for (const auto& v : ci.values)
|
||||
const ColumnInfo* ci = m_parser->columnInfo(tableColIndices.first, tableColIndices.second);
|
||||
if (!ci) return false;
|
||||
|
||||
for (const auto& v : ci->values)
|
||||
{
|
||||
values->push_back(v);
|
||||
}
|
||||
@@ -239,10 +120,119 @@ std::string RifColumnBasedUserData::unitName(const RifEclipseSummaryAddress& res
|
||||
{
|
||||
std::pair<size_t, size_t> tableColIndices = search->second;
|
||||
|
||||
const ColumnInfo& ci = m_parser->tables()[tableColIndices.first][tableColIndices.second];
|
||||
|
||||
return ci.unitName;
|
||||
const ColumnInfo* ci = m_parser->columnInfo(tableColIndices.first, tableColIndices.second);
|
||||
if (ci)
|
||||
{
|
||||
return ci->unitName;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RifColumnBasedUserData::buildTimeStepsFromTables()
|
||||
{
|
||||
for (size_t tableIndex = 0; tableIndex < m_parser->tableData().size(); tableIndex++)
|
||||
{
|
||||
auto tableData = m_parser->tableData()[tableIndex];
|
||||
|
||||
// Find time index
|
||||
size_t dateColumnIndex = tableData.columnInfos().size();
|
||||
size_t dayOrYearColumnIndex = tableData.columnInfos().size();
|
||||
|
||||
for (size_t columIndex = 0; columIndex < tableData.columnInfos().size(); columIndex++)
|
||||
{
|
||||
const ColumnInfo& ci = tableData.columnInfos()[columIndex];
|
||||
if (dateColumnIndex == tableData.columnInfos().size() &&
|
||||
RifEclipseUserDataKeywordTools::isDate(ci.summaryAddress.quantityName()))
|
||||
{
|
||||
dateColumnIndex = columIndex;
|
||||
}
|
||||
else if (dayOrYearColumnIndex == tableData.columnInfos().size() &&
|
||||
RifEclipseUserDataParserTools::hasTimeUnit(ci.unitName))
|
||||
{
|
||||
dayOrYearColumnIndex = columIndex;
|
||||
}
|
||||
}
|
||||
|
||||
if (dateColumnIndex == tableData.columnInfos().size() &&
|
||||
dayOrYearColumnIndex == tableData.columnInfos().size())
|
||||
{
|
||||
RiaLogging::warning(QString("Failed to find time data for table %1 in file %2").arg(tableIndex));
|
||||
RiaLogging::warning(QString("No data for this table is imported"));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_timeSteps.resize(m_timeSteps.size() + 1);
|
||||
std::vector<time_t>& timeSteps = m_timeSteps.back();
|
||||
|
||||
if (dateColumnIndex != tableData.columnInfos().size())
|
||||
{
|
||||
const ColumnInfo& ci = tableData.columnInfos()[dateColumnIndex];
|
||||
|
||||
QString dateFormat;
|
||||
for (auto s : ci.stringValues)
|
||||
{
|
||||
QDateTime dt = RiaDateStringParser::parseDateString(s);
|
||||
|
||||
timeSteps.push_back(dt.toTime_t());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QDateTime startDate = RiaQDateTimeTools::epoch();
|
||||
|
||||
if (!tableData.startDate().empty())
|
||||
{
|
||||
QDateTime candidate = RiaDateStringParser::parseDateString(tableData.startDate());
|
||||
if (candidate.isValid())
|
||||
{
|
||||
startDate = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
if (dayOrYearColumnIndex != tableData.columnInfos().size())
|
||||
{
|
||||
const ColumnInfo& ci = tableData.columnInfos()[dayOrYearColumnIndex];
|
||||
|
||||
QString unit = QString::fromStdString(ci.unitName).trimmed().toUpper();
|
||||
|
||||
if (unit == "DAY" || unit == "DAYS")
|
||||
{
|
||||
for (const auto& timeStepValue : ci.values)
|
||||
{
|
||||
QDateTime dateTime = RiaQDateTimeTools::addDays(startDate, timeStepValue);
|
||||
timeSteps.push_back(dateTime.toTime_t());
|
||||
}
|
||||
}
|
||||
else if (unit == "YEAR" || unit == "YEARS")
|
||||
{
|
||||
for (const auto& timeStepValue : ci.values)
|
||||
{
|
||||
QDateTime dateTime = RiaQDateTimeTools::addYears(startDate, timeStepValue);
|
||||
timeSteps.push_back(dateTime.toTime_t());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t columIndex = 0; columIndex < tableData.columnInfos().size(); columIndex++)
|
||||
{
|
||||
const ColumnInfo& ci = tableData.columnInfos()[columIndex];
|
||||
if (!ci.isStringData)
|
||||
{
|
||||
RifEclipseSummaryAddress sumAddress = ci.summaryAddress;
|
||||
|
||||
m_allResultAddresses.push_back(sumAddress);
|
||||
|
||||
m_mapFromAddressToTimeStepIndex[sumAddress] = m_timeSteps.size() - 1;
|
||||
m_mapFromAddressToResultIndex[sumAddress] = std::make_pair(tableIndex, columIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,9 @@ public:
|
||||
|
||||
std::string unitName(const RifEclipseSummaryAddress& resultAddress) const override;
|
||||
|
||||
private:
|
||||
void buildTimeStepsFromTables();
|
||||
|
||||
private:
|
||||
std::unique_ptr<RifColumnBasedUserDataParser> m_parser;
|
||||
std::vector< std::vector<time_t> > m_timeSteps;
|
||||
|
||||
@@ -35,85 +35,71 @@
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RifColumnBasedUserDataParser::RifColumnBasedUserDataParser(const QString& data)
|
||||
{
|
||||
parseData(data);
|
||||
parseTableData(data);
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const std::vector< std::vector<ColumnInfo> >& RifColumnBasedUserDataParser::tables() const
|
||||
const std::vector<TableData>& RifColumnBasedUserDataParser::tableData() const
|
||||
{
|
||||
return m_tables;
|
||||
return m_tableDatas;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
const ColumnInfo* RifColumnBasedUserDataParser::columnInfo(size_t tableIndex, size_t columnIndex) const
|
||||
{
|
||||
if (tableIndex >= m_tableDatas.size()) return nullptr;
|
||||
|
||||
if (columnIndex >= m_tableDatas[tableIndex].columnInfos().size()) return nullptr;
|
||||
|
||||
return &(m_tableDatas[tableIndex].columnInfos()[columnIndex]);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RifColumnBasedUserDataParser::parseData(const QString& data)
|
||||
void RifColumnBasedUserDataParser::parseTableData(const QString& data)
|
||||
{
|
||||
std::stringstream streamData;
|
||||
streamData.str(data.toStdString());
|
||||
|
||||
do
|
||||
do
|
||||
{
|
||||
std::vector<ColumnInfo> table = RifEclipseUserDataParserTools::columnInfoForTable(streamData);
|
||||
size_t columnCount = table.size();
|
||||
auto table = RifEclipseUserDataParserTools::tableDataFromText(streamData);
|
||||
std::vector<ColumnInfo>& columnInfos = table.columnInfos();
|
||||
int columnCount = static_cast<int>(columnInfos.size());
|
||||
if (columnCount == 0) break;
|
||||
|
||||
|
||||
std::string line;
|
||||
std::getline(streamData, line);
|
||||
|
||||
size_t dateColumnIndex = table.size();
|
||||
for (size_t i = 0; i < columnCount; i++)
|
||||
{
|
||||
if (table[i].summaryAddress.quantityName() == "DATE" ||
|
||||
table[i].summaryAddress.quantityName() == "DATES")
|
||||
{
|
||||
dateColumnIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// If a DATE column is present, use the first date as the start date of the samples
|
||||
// This date is then used as basis for times defined by days or years given as double values
|
||||
QDateTime startDate;
|
||||
|
||||
std::vector<double> values;
|
||||
QString qLine;
|
||||
do
|
||||
{
|
||||
qLine = QString::fromStdString(line);
|
||||
QString qLine = QString::fromStdString(line);
|
||||
QStringList entries = qLine.split(" ", QString::SkipEmptyParts);
|
||||
|
||||
if (entries.size() < static_cast<int>(columnCount)) break;
|
||||
if (entries.size() < columnCount) break;
|
||||
|
||||
for (size_t i = 0; i < columnCount; i++)
|
||||
for (int i = 0; i < columnCount; i++)
|
||||
{
|
||||
if (dateColumnIndex < columnCount)
|
||||
if (columnInfos[i].isStringData)
|
||||
{
|
||||
QDateTime observationDate = RiaDateStringParser::parseDateString(entries[static_cast<int>(dateColumnIndex)]);
|
||||
|
||||
if (observationDate.isValid() && !startDate.isValid())
|
||||
{
|
||||
startDate = observationDate;
|
||||
}
|
||||
|
||||
table[i].observationDateTimes.push_back(observationDate);
|
||||
columnInfos[i].stringValues.push_back(entries[i].toStdString());
|
||||
}
|
||||
else
|
||||
{
|
||||
double entry = entries[i].toDouble();
|
||||
columnInfos[i].values.push_back(entry);
|
||||
}
|
||||
|
||||
double entry = entries.at(static_cast<int>(i)).toDouble();
|
||||
table[i].values.push_back(entry);
|
||||
}
|
||||
} while (std::getline(streamData, line));
|
||||
|
||||
if (startDate.isValid())
|
||||
{
|
||||
for (auto& ci : table)
|
||||
{
|
||||
ci.startQDateTime = startDate;
|
||||
}
|
||||
}
|
||||
|
||||
m_tables.push_back(table);
|
||||
m_tableDatas.push_back(table);
|
||||
|
||||
} while (streamData.good());
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
struct ColumnInfo;
|
||||
class ColumnInfo;
|
||||
class TableData;
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
@@ -33,11 +34,13 @@ class RifColumnBasedUserDataParser
|
||||
{
|
||||
public:
|
||||
RifColumnBasedUserDataParser(const QString& data);
|
||||
const std::vector< std::vector<ColumnInfo> >& tables() const;
|
||||
const std::vector<TableData>& tableData() const;
|
||||
|
||||
const ColumnInfo* columnInfo(size_t tableIndex, size_t columnIndex) const;
|
||||
|
||||
private:
|
||||
void parseData(const QString& data);
|
||||
void parseTableData(const QString& data);
|
||||
|
||||
private:
|
||||
std::vector< std::vector<ColumnInfo> > m_tables;
|
||||
std::vector<TableData> m_tableDatas;
|
||||
};
|
||||
|
||||
@@ -115,6 +115,18 @@ RifEclipseSummaryAddress RifEclipseSummaryAddress::calculatedCurveAddress(const
|
||||
return fieldAddr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RifEclipseSummaryAddress RifEclipseSummaryAddress::miscAddress(const std::string& quantityName)
|
||||
{
|
||||
RifEclipseSummaryAddress fieldAddr;
|
||||
fieldAddr.m_variableCategory = SUMMARY_MISC;
|
||||
fieldAddr.m_quantityName = quantityName;
|
||||
|
||||
return fieldAddr;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -106,6 +106,7 @@ public:
|
||||
|
||||
static RifEclipseSummaryAddress fieldVarAddress(const std::string& fieldVarName);
|
||||
static RifEclipseSummaryAddress calculatedCurveAddress(const std::string& curveName);
|
||||
static RifEclipseSummaryAddress miscAddress(const std::string& quantityName);
|
||||
|
||||
// Access methods
|
||||
|
||||
|
||||
248
ApplicationCode/FileInterface/RifEclipseUserDataKeywordTools.cpp
Normal file
248
ApplicationCode/FileInterface/RifEclipseUserDataKeywordTools.cpp
Normal file
@@ -0,0 +1,248 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017- Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RifEclipseUserDataKeywordTools.h"
|
||||
|
||||
#include "RiaLogging.h"
|
||||
|
||||
#include "RifEclipseUserDataParserTools.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<size_t> RifEclipseUserDataKeywordTools::requiredItemsPerLineForKeyword(const std::string& identifier)
|
||||
{
|
||||
if (identifier.size() < 2) return {};
|
||||
|
||||
char firstLetter = identifier[0];
|
||||
switch (firstLetter)
|
||||
{
|
||||
case 'B': return { 3 }; // Block triplet
|
||||
case 'C': return { 1, 3 }; // Well Name and completion triplet
|
||||
case 'G': return { 1 }; // Group
|
||||
case 'R': return { 1 }; // Region number
|
||||
case 'S': return { 1, 1 }; // Well name and segment number
|
||||
case 'W': return { 1 }; // Well Name
|
||||
}
|
||||
|
||||
std::string firstTwoLetters = identifier.substr(0, 2);
|
||||
|
||||
if (firstTwoLetters == "LB") return { 1, 3 }; // LGR name and block triplet
|
||||
else if (firstTwoLetters == "LC") return { 1, 1, 3 }; // LGR name, well name and block triplet
|
||||
else if (firstTwoLetters == "LW") return { 1, 1 }; // LGR name and well name
|
||||
|
||||
return { };
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<std::vector<std::string>> RifEclipseUserDataKeywordTools::buildColumnHeaderText(const std::vector<std::string>& quantityNames,
|
||||
const std::vector<std::vector<std::string>>& restOfHeaderRows,
|
||||
std::vector<std::string>* errorText)
|
||||
{
|
||||
std::vector<std::vector<std::string>> tableHeaderText;
|
||||
|
||||
std::vector<size_t> headerLineWordIndices(restOfHeaderRows.size(), 0);
|
||||
|
||||
for (size_t i = 0; i < quantityNames.size(); i++)
|
||||
{
|
||||
std::vector<std::string> columnHeaderText;
|
||||
|
||||
auto quantityName = quantityNames[i];
|
||||
columnHeaderText.push_back(quantityName);
|
||||
|
||||
auto itemCountPerLine = RifEclipseUserDataKeywordTools::requiredItemsPerLineForKeyword(quantityName);
|
||||
if (itemCountPerLine.size() > restOfHeaderRows.size())
|
||||
{
|
||||
std::string text = "Detected too few header lines";
|
||||
if (errorText) errorText->push_back(text);
|
||||
|
||||
return std::vector<std::vector<std::string>>();
|
||||
}
|
||||
|
||||
for (size_t lineIdx = 0; lineIdx < itemCountPerLine.size(); lineIdx++)
|
||||
{
|
||||
auto line = restOfHeaderRows[lineIdx];
|
||||
|
||||
for (size_t itemIndex = 0; itemIndex < itemCountPerLine[lineIdx]; itemIndex++)
|
||||
{
|
||||
size_t wordIndex = headerLineWordIndices[lineIdx];
|
||||
if (wordIndex >= line.size())
|
||||
{
|
||||
std::string text = "Detected too few items for header line";
|
||||
if (errorText) errorText->push_back(text);
|
||||
|
||||
return std::vector<std::vector<std::string>>();
|
||||
}
|
||||
|
||||
auto word = line[wordIndex];
|
||||
|
||||
columnHeaderText.push_back(word);
|
||||
|
||||
headerLineWordIndices[lineIdx]++;
|
||||
}
|
||||
}
|
||||
|
||||
tableHeaderText.push_back(columnHeaderText);
|
||||
}
|
||||
|
||||
return tableHeaderText;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RifEclipseUserDataKeywordTools::isDate(const std::string& identifier)
|
||||
{
|
||||
if (identifier.find("DATE") != std::string::npos) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RifEclipseSummaryAddress RifEclipseUserDataKeywordTools::makeAndFillAddress(const std::string quantityName, const std::vector<std::string>& columnHeaderText)
|
||||
{
|
||||
RifEclipseSummaryAddress::SummaryVarCategory category = RifEclipseUserDataParserTools::identifyCategory(quantityName);
|
||||
|
||||
if (category == RifEclipseSummaryAddress::SUMMARY_INVALID)
|
||||
{
|
||||
return RifEclipseSummaryAddress::miscAddress(quantityName);
|
||||
}
|
||||
|
||||
int regionNumber = -1;
|
||||
int regionNumber2 = -1;
|
||||
std::string wellGroupName = "";
|
||||
std::string wellName = "";
|
||||
int wellSegmentNumber = -1;
|
||||
std::string lgrName = "";
|
||||
int cellI = -1;
|
||||
int cellJ = -1;
|
||||
int cellK = -1;
|
||||
|
||||
switch (category)
|
||||
{
|
||||
case RifEclipseSummaryAddress::SUMMARY_FIELD:
|
||||
break;
|
||||
case RifEclipseSummaryAddress::SUMMARY_AQUIFER:
|
||||
break;
|
||||
case RifEclipseSummaryAddress::SUMMARY_NETWORK:
|
||||
break;
|
||||
case RifEclipseSummaryAddress::SUMMARY_MISC:
|
||||
break;
|
||||
case RifEclipseSummaryAddress::SUMMARY_REGION:
|
||||
{
|
||||
if (columnHeaderText.size() > 0)
|
||||
{
|
||||
regionNumber = std::stoi(columnHeaderText[0]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RifEclipseSummaryAddress::SUMMARY_REGION_2_REGION:
|
||||
break;
|
||||
case RifEclipseSummaryAddress::SUMMARY_WELL_GROUP:
|
||||
{
|
||||
if (columnHeaderText.size() > 0)
|
||||
{
|
||||
wellGroupName = columnHeaderText[0];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RifEclipseSummaryAddress::SUMMARY_WELL:
|
||||
{
|
||||
if (columnHeaderText.size() > 0)
|
||||
{
|
||||
wellName = columnHeaderText[0];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RifEclipseSummaryAddress::SUMMARY_WELL_COMPLETION:
|
||||
{
|
||||
if (columnHeaderText.size() > 3)
|
||||
{
|
||||
wellName = columnHeaderText[0];
|
||||
cellI = std::stoi(columnHeaderText[1]);
|
||||
cellJ = std::stoi(columnHeaderText[2]);
|
||||
cellK = std::stoi(columnHeaderText[3]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case RifEclipseSummaryAddress::SUMMARY_WELL_LGR:
|
||||
if (columnHeaderText.size() > 1)
|
||||
{
|
||||
wellName = columnHeaderText[0];
|
||||
lgrName = columnHeaderText[1];
|
||||
}
|
||||
break;
|
||||
case RifEclipseSummaryAddress::SUMMARY_WELL_COMPLETION_LGR:
|
||||
if (columnHeaderText.size() > 4)
|
||||
{
|
||||
wellName = columnHeaderText[0];
|
||||
lgrName = columnHeaderText[1];
|
||||
cellI = std::stoi(columnHeaderText[2]);
|
||||
cellJ = std::stoi(columnHeaderText[3]);
|
||||
cellK = std::stoi(columnHeaderText[4]);
|
||||
}
|
||||
break;
|
||||
case RifEclipseSummaryAddress::SUMMARY_WELL_SEGMENT:
|
||||
if (columnHeaderText.size() > 1)
|
||||
{
|
||||
wellName = columnHeaderText[0];
|
||||
wellSegmentNumber = std::stoi(columnHeaderText[1]);
|
||||
}
|
||||
break;
|
||||
case RifEclipseSummaryAddress::SUMMARY_BLOCK:
|
||||
if (columnHeaderText.size() > 2)
|
||||
{
|
||||
cellI = std::stoi(columnHeaderText[0]);
|
||||
cellJ = std::stoi(columnHeaderText[1]);
|
||||
cellK = std::stoi(columnHeaderText[2]);
|
||||
}
|
||||
break;
|
||||
case RifEclipseSummaryAddress::SUMMARY_BLOCK_LGR:
|
||||
if (columnHeaderText.size() > 3)
|
||||
{
|
||||
lgrName = columnHeaderText[0];
|
||||
cellI = std::stoi(columnHeaderText[1]);
|
||||
cellJ = std::stoi(columnHeaderText[2]);
|
||||
cellK = std::stoi(columnHeaderText[3]);
|
||||
}
|
||||
break;
|
||||
case RifEclipseSummaryAddress::SUMMARY_CALCULATED:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return RifEclipseSummaryAddress(category,
|
||||
quantityName,
|
||||
regionNumber,
|
||||
regionNumber2,
|
||||
wellGroupName,
|
||||
wellName,
|
||||
wellSegmentNumber,
|
||||
lgrName,
|
||||
cellI,
|
||||
cellJ,
|
||||
cellK);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2017- Statoil ASA
|
||||
//
|
||||
// ResInsight is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE.
|
||||
//
|
||||
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
|
||||
// for more details.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RifEclipseUserDataParserTools.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class RifEclipseUserDataKeywordTools
|
||||
{
|
||||
public:
|
||||
static std::vector<size_t> requiredItemsPerLineForKeyword(const std::string& identifier);
|
||||
|
||||
static std::vector<std::vector<std::string>> buildColumnHeaderText(const std::vector<std::string>& quantityNames,
|
||||
const std::vector<std::vector<std::string>>& restOfHeaderRows,
|
||||
std::vector<std::string>* errorText = nullptr);
|
||||
|
||||
static bool isDate(const std::string& identifier);
|
||||
|
||||
static RifEclipseSummaryAddress makeAndFillAddress(const std::string quantityName, const std::vector<std::string>& columnHeaderText);
|
||||
};
|
||||
|
||||
@@ -18,8 +18,11 @@
|
||||
|
||||
#include "RifEclipseUserDataParserTools.h"
|
||||
|
||||
#include "RiaDateStringParser.h"
|
||||
#include "RiaLogging.h"
|
||||
|
||||
#include "RifEclipseUserDataKeywordTools.h"
|
||||
|
||||
#include "cvfAssert.h"
|
||||
|
||||
#include <QString>
|
||||
@@ -80,7 +83,7 @@ bool RifEclipseUserDataParserTools::isLineSkippable(const std::string& line)
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RifEclipseUserDataParserTools::isAComment(const std::string& word)
|
||||
{
|
||||
if (word.size() > 1 && word.substr(0, 2) == "--")
|
||||
if (word.find("--") != std::string::npos)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -161,77 +164,6 @@ size_t RifEclipseUserDataParserTools::findFirstNonEmptyEntryIndex(std::vector<st
|
||||
return list.size();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RifEclipseSummaryAddress RifEclipseUserDataParserTools::makeAndFillAddress(std::string quantityName, std::vector< std::string > headerColumn)
|
||||
{
|
||||
int regionNumber = -1;
|
||||
int regionNumber2 = -1;
|
||||
std::string wellGroupName = "";
|
||||
std::string wellName = "";
|
||||
int wellSegmentNumber = -1;
|
||||
std::string lgrName = "";
|
||||
int cellI = -1;
|
||||
int cellJ = -1;
|
||||
int cellK = -1;
|
||||
|
||||
RifEclipseSummaryAddress::SummaryVarCategory category = identifyCategory(quantityName);
|
||||
|
||||
switch (category) //TODO: More categories
|
||||
{
|
||||
case (RifEclipseSummaryAddress::SUMMARY_INVALID):
|
||||
{
|
||||
break;
|
||||
}
|
||||
case (RifEclipseSummaryAddress::SUMMARY_WELL):
|
||||
{
|
||||
size_t index = findFirstNonEmptyEntryIndex(headerColumn);
|
||||
if (index < headerColumn.size())
|
||||
{
|
||||
wellName = headerColumn[index];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case (RifEclipseSummaryAddress::SUMMARY_WELL_GROUP):
|
||||
{
|
||||
size_t index = findFirstNonEmptyEntryIndex(headerColumn);
|
||||
if (index < headerColumn.size())
|
||||
{
|
||||
wellGroupName = headerColumn[index];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case (RifEclipseSummaryAddress::SUMMARY_REGION):
|
||||
{
|
||||
size_t index = findFirstNonEmptyEntryIndex(headerColumn);
|
||||
if (index < headerColumn.size())
|
||||
{
|
||||
try
|
||||
{
|
||||
regionNumber = std::stoi(headerColumn[index]);
|
||||
}
|
||||
catch (...){}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return RifEclipseSummaryAddress(category,
|
||||
quantityName,
|
||||
regionNumber,
|
||||
regionNumber2,
|
||||
wellGroupName,
|
||||
wellName,
|
||||
wellSegmentNumber,
|
||||
lgrName,
|
||||
cellI,
|
||||
cellJ,
|
||||
cellK);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -248,7 +180,19 @@ bool RifEclipseUserDataParserTools::keywordParser(const std::string& line, std::
|
||||
else if (words[0] == "STARTDATE")
|
||||
{
|
||||
words.erase(words.begin());
|
||||
startDate = std::accumulate(words.begin(), words.end(), std::string(""));
|
||||
|
||||
for (size_t i = 0; i < words.size(); i++)
|
||||
{
|
||||
std::string s = words[i];
|
||||
|
||||
startDate += s;
|
||||
|
||||
if (i < words.size() - 1)
|
||||
{
|
||||
startDate += " ";
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (words[0] == "DATEFORMAT")
|
||||
@@ -259,162 +203,6 @@ bool RifEclipseUserDataParserTools::keywordParser(const std::string& line, std::
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<ColumnInfo> RifEclipseUserDataParserTools::columnInfoForTable(std::stringstream& streamData)
|
||||
{
|
||||
std::vector<ColumnInfo> table;
|
||||
|
||||
std::string origin = "";
|
||||
std::string dateFormat = "";
|
||||
std::string startDate = "";
|
||||
|
||||
std::string line;
|
||||
std::getline(streamData, line);
|
||||
|
||||
while (isLineSkippable(line) || keywordParser(line, origin, dateFormat, startDate))
|
||||
{
|
||||
if (!streamData.good()) return table;
|
||||
|
||||
std::getline(streamData, line);
|
||||
}
|
||||
|
||||
std::vector<std::string> quantityNames = splitLineAndRemoveComments(line);
|
||||
size_t columnCount = quantityNames.size();
|
||||
|
||||
std::vector< std::vector< std::string > > allHeaderRows;
|
||||
|
||||
{
|
||||
std::stringstream::pos_type posAtStartOfLine = streamData.tellg();
|
||||
|
||||
std::string secondLine;
|
||||
std::getline(streamData, line);
|
||||
|
||||
std::stringstream::pos_type posAtStartOfSecondLine = streamData.tellg();
|
||||
std::getline(streamData, secondLine);
|
||||
|
||||
bool header = true;
|
||||
while (header)
|
||||
{
|
||||
std::vector<std::string> words = splitLineAndRemoveComments(line);
|
||||
std::vector<std::string> wordsSecondLine = splitLineAndRemoveComments(secondLine);
|
||||
|
||||
if (words.size() == columnCount &&
|
||||
wordsSecondLine.size() == columnCount &&
|
||||
hasOnlyValidDoubleValues(words) &&
|
||||
hasOnlyValidDoubleValues(wordsSecondLine))
|
||||
{
|
||||
header = false;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (words.size() > columnCount) break;
|
||||
|
||||
size_t diff = columnCount - words.size();
|
||||
|
||||
if (diff == columnCount)
|
||||
{
|
||||
std::vector< std::string > vectorOfEmptyStrings(columnCount, "");
|
||||
allHeaderRows.push_back(vectorOfEmptyStrings);
|
||||
}
|
||||
else
|
||||
{
|
||||
words.insert(words.begin(), diff, "");
|
||||
allHeaderRows.push_back(words);
|
||||
}
|
||||
}
|
||||
|
||||
posAtStartOfLine = posAtStartOfSecondLine;
|
||||
line = secondLine;
|
||||
|
||||
posAtStartOfSecondLine = streamData.tellg();
|
||||
std::getline(streamData, secondLine);
|
||||
}
|
||||
|
||||
streamData.seekg(posAtStartOfLine);
|
||||
}
|
||||
|
||||
std::vector<std::string> unitNames;
|
||||
std::vector<double> scaleFactors;
|
||||
std::vector< std::vector< std::string > > restOfHeaderRows;
|
||||
|
||||
for (const auto& wordsForRow : allHeaderRows)
|
||||
{
|
||||
bool excludeFromHeader = false;
|
||||
if (unitNames.size() == 0)
|
||||
{
|
||||
for (const std::string& word : wordsForRow)
|
||||
{
|
||||
if (hasTimeUnit(word))
|
||||
{
|
||||
unitNames = wordsForRow;
|
||||
excludeFromHeader = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (scaleFactors.size() == 0)
|
||||
{
|
||||
std::vector<double> values;
|
||||
|
||||
if (hasOnlyValidDoubleValues(wordsForRow, &values))
|
||||
{
|
||||
scaleFactors = values;
|
||||
excludeFromHeader = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!excludeFromHeader)
|
||||
{
|
||||
restOfHeaderRows.push_back(wordsForRow);
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::string& unit : unitNames)
|
||||
{
|
||||
ColumnInfo columnInfo;
|
||||
columnInfo.unitName = unit;
|
||||
columnInfo.origin = origin;
|
||||
columnInfo.dateFormatString = dateFormat;
|
||||
columnInfo.startDateString = startDate;
|
||||
table.push_back(columnInfo);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < table.size(); i++)
|
||||
{
|
||||
if (scaleFactors.size() == table.size())
|
||||
{
|
||||
table[i].scaleFactor = scaleFactors[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
table[i].scaleFactor = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < table.size(); i++)
|
||||
{
|
||||
std::vector< std::string > restOfHeaderColumn;
|
||||
for (std::vector< std::string > restOfHeaderRow : restOfHeaderRows)
|
||||
{
|
||||
restOfHeaderColumn.push_back(restOfHeaderRow.at(i));
|
||||
}
|
||||
table[i].summaryAddress = makeAndFillAddress(quantityNames.at(i), restOfHeaderColumn);
|
||||
}
|
||||
|
||||
for (ColumnInfo& column : table)
|
||||
{
|
||||
if (column.summaryAddress.category() != RifEclipseSummaryAddress::SUMMARY_INVALID)
|
||||
{
|
||||
column.isAVector = true;
|
||||
}
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -486,19 +274,234 @@ bool RifEclipseUserDataParserTools::hasOnlyValidDoubleValues(const std::vector<s
|
||||
{
|
||||
char* end;
|
||||
|
||||
bool onlyValidValues = true;
|
||||
|
||||
for (const auto& word : words)
|
||||
{
|
||||
double doubleVal = strtod(word.data(), &end);
|
||||
if (end == word.data())
|
||||
if (word.find_first_not_of("0123456789.eE-") != std::string::npos)
|
||||
{
|
||||
return false;
|
||||
onlyValidValues = false;
|
||||
}
|
||||
|
||||
if (doubleValues)
|
||||
else
|
||||
{
|
||||
double doubleVal = strtod(word.data(), &end);
|
||||
doubleValues->push_back(doubleVal);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return onlyValidValues;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RifEclipseUserDataParserTools::hasDateUnit(const std::string& word)
|
||||
{
|
||||
if (word.find("DATE") != std::string::npos) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RifEclipseUserDataParserTools::isValidTableData(size_t columnCount, const std::string& line)
|
||||
{
|
||||
std::vector<std::string> words = splitLineAndRemoveComments(line);
|
||||
|
||||
if (words.size() != columnCount) return false;
|
||||
|
||||
std::vector<double> doubleValues;
|
||||
RifEclipseUserDataParserTools::hasOnlyValidDoubleValues(words, &doubleValues);
|
||||
if (doubleValues.size() == columnCount) return true;
|
||||
|
||||
size_t columnsWithDate = 0;
|
||||
for (auto w : words)
|
||||
{
|
||||
if (RiaDateStringParser::parseDateString(w).isValid())
|
||||
{
|
||||
columnsWithDate++;
|
||||
}
|
||||
}
|
||||
|
||||
if (columnsWithDate == 1 && doubleValues.size() == columnCount - 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TableData RifEclipseUserDataParserTools::tableDataFromText(std::stringstream& streamData, std::vector<std::string>* errorText)
|
||||
{
|
||||
TableData emptyTable;
|
||||
|
||||
std::string origin = "";
|
||||
std::string dateFormat = "";
|
||||
std::string startDate = "";
|
||||
|
||||
std::string firstLine;
|
||||
std::getline(streamData, firstLine);
|
||||
|
||||
while (isLineSkippable(firstLine) || keywordParser(firstLine, origin, dateFormat, startDate))
|
||||
{
|
||||
if (!streamData.good())
|
||||
{
|
||||
if (errorText) errorText->push_back("Failed to detect start of table header");
|
||||
|
||||
return emptyTable;
|
||||
}
|
||||
|
||||
std::getline(streamData, firstLine);
|
||||
}
|
||||
|
||||
std::vector<std::string> quantityNames = splitLineAndRemoveComments(firstLine);
|
||||
size_t columnCount = quantityNames.size();
|
||||
|
||||
if (columnCount == 0)
|
||||
{
|
||||
if (errorText) errorText->push_back("No quantities detected in table");
|
||||
|
||||
return emptyTable;
|
||||
}
|
||||
|
||||
std::vector< std::vector< std::string > > allHeaderRows;
|
||||
|
||||
{
|
||||
std::stringstream::pos_type posAtStartOfFirstLine = streamData.tellg();
|
||||
|
||||
std::string secondLine;
|
||||
std::getline(streamData, firstLine);
|
||||
|
||||
std::stringstream::pos_type posAtStartOfSecondLine = streamData.tellg();
|
||||
std::getline(streamData, secondLine);
|
||||
|
||||
bool header = true;
|
||||
while (header)
|
||||
{
|
||||
if (isValidTableData(columnCount, firstLine) &&
|
||||
isValidTableData(columnCount, secondLine))
|
||||
{
|
||||
header = false;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<std::string> words = splitLineAndRemoveComments(firstLine);
|
||||
if (words.size() > 0)
|
||||
{
|
||||
allHeaderRows.push_back(words);
|
||||
}
|
||||
}
|
||||
|
||||
posAtStartOfFirstLine = posAtStartOfSecondLine;
|
||||
firstLine = secondLine;
|
||||
|
||||
posAtStartOfSecondLine = streamData.tellg();
|
||||
std::getline(streamData, secondLine);
|
||||
|
||||
if (!streamData.good())
|
||||
{
|
||||
header = false;
|
||||
}
|
||||
}
|
||||
|
||||
streamData.seekg(posAtStartOfFirstLine);
|
||||
}
|
||||
|
||||
std::vector<std::string> unitNames;
|
||||
std::vector<double> scaleFactors;
|
||||
std::vector< std::vector< std::string > > headerRows;
|
||||
|
||||
for (const auto& rowWords : allHeaderRows)
|
||||
{
|
||||
bool excludeFromHeader = false;
|
||||
|
||||
if (rowWords.size() == columnCount)
|
||||
{
|
||||
if (unitNames.size() == 0)
|
||||
{
|
||||
for (const std::string& word : rowWords)
|
||||
{
|
||||
if (hasTimeUnit(word))
|
||||
{
|
||||
unitNames = rowWords;
|
||||
excludeFromHeader = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (scaleFactors.size() == 0)
|
||||
{
|
||||
std::vector<double> values;
|
||||
|
||||
if (hasOnlyValidDoubleValues(rowWords, &values))
|
||||
{
|
||||
scaleFactors = values;
|
||||
excludeFromHeader = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!excludeFromHeader)
|
||||
{
|
||||
headerRows.push_back(rowWords);
|
||||
}
|
||||
}
|
||||
|
||||
if (columnCount != unitNames.size())
|
||||
{
|
||||
if (errorText) errorText->push_back("Number of quantities is different from number of units");
|
||||
|
||||
return emptyTable;
|
||||
}
|
||||
|
||||
|
||||
std::vector<ColumnInfo> columnInfos;
|
||||
|
||||
// Create string vectors for each column
|
||||
{
|
||||
std::vector<std::string> parserErrors;
|
||||
std::vector<std::vector<std::string>> tableHeaderText = RifEclipseUserDataKeywordTools::buildColumnHeaderText(quantityNames, headerRows, &parserErrors);
|
||||
if (parserErrors.size() > 0)
|
||||
{
|
||||
if (errorText) errorText->insert(errorText->end(), parserErrors.begin(), parserErrors.end());
|
||||
|
||||
return emptyTable;
|
||||
}
|
||||
|
||||
|
||||
// For each column header, create rif adress and date time
|
||||
for (size_t i = 0; i < tableHeaderText.size(); i++)
|
||||
{
|
||||
auto columnText = tableHeaderText[i];
|
||||
if (columnText.size() == 0)
|
||||
{
|
||||
if (errorText) errorText->push_back("Detected column with no content");
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string quantity = columnText[0];
|
||||
std::string unit = unitNames[i];
|
||||
|
||||
std::vector<std::string> columnHeader;
|
||||
|
||||
if (columnText.size() > 1) columnHeader.insert(columnHeader.begin(), columnText.begin() + 1, columnText.end());
|
||||
|
||||
RifEclipseSummaryAddress adr = RifEclipseUserDataKeywordTools::makeAndFillAddress(quantity, columnHeader);
|
||||
|
||||
ColumnInfo ci(adr, unit);
|
||||
if (quantity == "DATE")
|
||||
{
|
||||
ci.isStringData = true;
|
||||
}
|
||||
|
||||
columnInfos.push_back(ci);
|
||||
}
|
||||
}
|
||||
|
||||
return TableData(origin, dateFormat, startDate, columnInfos);
|
||||
}
|
||||
|
||||
@@ -28,18 +28,86 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct ColumnInfo
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class ColumnInfo
|
||||
{
|
||||
bool isAVector = false;
|
||||
public:
|
||||
ColumnInfo()
|
||||
: scaleFactor(1.0),
|
||||
isStringData(false)
|
||||
{
|
||||
}
|
||||
|
||||
ColumnInfo(const RifEclipseSummaryAddress& adr, const std::string& unit)
|
||||
: summaryAddress(adr),
|
||||
scaleFactor(1.0),
|
||||
unitName(unit),
|
||||
isStringData(false)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
RifEclipseSummaryAddress summaryAddress;
|
||||
std::string unitName;
|
||||
double scaleFactor;
|
||||
std::vector<double> values;
|
||||
std::string origin;
|
||||
std::string dateFormatString;
|
||||
std::string startDateString;
|
||||
QDateTime startQDateTime;
|
||||
std::vector<QDateTime> observationDateTimes;
|
||||
bool isStringData;
|
||||
std::vector<std::string> stringValues;
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
//==================================================================================================
|
||||
class TableData
|
||||
{
|
||||
public:
|
||||
TableData()
|
||||
{}
|
||||
|
||||
TableData(const std::string& origin,
|
||||
const std::string& dateFormat,
|
||||
const std::string& startDate,
|
||||
const std::vector<ColumnInfo>& columnInfos)
|
||||
: m_origin(origin),
|
||||
m_dateFormat(dateFormat),
|
||||
m_startDate(startDate),
|
||||
m_columnInfos(columnInfos)
|
||||
{
|
||||
}
|
||||
|
||||
std::string origin() const
|
||||
{
|
||||
return m_origin;
|
||||
}
|
||||
|
||||
std::string startDate() const
|
||||
{
|
||||
return m_startDate;
|
||||
}
|
||||
|
||||
std::string dateFormat() const
|
||||
{
|
||||
return m_dateFormat;
|
||||
}
|
||||
|
||||
std::vector<ColumnInfo>& columnInfos()
|
||||
{
|
||||
return m_columnInfos;
|
||||
}
|
||||
|
||||
const std::vector<ColumnInfo>& columnInfos() const
|
||||
{
|
||||
return m_columnInfos;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_origin;
|
||||
std::string m_dateFormat;
|
||||
std::string m_startDate;
|
||||
|
||||
std::vector<ColumnInfo> m_columnInfos;
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
@@ -54,12 +122,15 @@ public:
|
||||
static RifEclipseSummaryAddress::SummaryVarCategory identifyCategory(const std::string& word);
|
||||
static void splitLineToDoubles(const std::string& line, std::vector<double>& values);
|
||||
static size_t findFirstNonEmptyEntryIndex(std::vector<std::string>& list);
|
||||
static RifEclipseSummaryAddress makeAndFillAddress(std::string quantityName, std::vector< std::string > headerColumn);
|
||||
static bool keywordParser(const std::string& line, std::string& origin, std::string& dateFormat, std::string& startDate);
|
||||
static std::vector<ColumnInfo> columnInfoForTable(std::stringstream& data);
|
||||
static bool isANumber(const std::string& line);
|
||||
static std::vector<std::string> headerReader(std::stringstream& streamData, std::string& line);
|
||||
|
||||
static bool hasTimeUnit(const std::string& line);
|
||||
static bool hasOnlyValidDoubleValues(const std::vector<std::string>& words, std::vector<double>* doubleValues = nullptr);
|
||||
|
||||
static bool hasDateUnit(const std::string& word);
|
||||
static bool isValidTableData(size_t columnCount, const std::string& line);
|
||||
|
||||
static TableData tableDataFromText(std::stringstream& data, std::vector<std::string>* errorText = nullptr);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user