///////////////////////////////////////////////////////////////////////////////// // // 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 // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "RifStimPlanXmlReader.h" #include "RiaEclipseUnitTools.h" #include "RigStimPlanFractureDefinition.h" #include "RiaLogging.h" #include #include #include // Needed for HUGE_VAL on Linux //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- cvf::ref RifStimPlanXmlReader::readStimPlanXMLFile(const QString& stimPlanFileName, double conductivityScalingFactor, QString * errorMessage) { RiaLogging::info(QString("Starting to open StimPlan XML file: '%1'").arg(stimPlanFileName)); RiaEclipseUnitTools::UnitSystemType unitSystem = RiaEclipseUnitTools::UNITS_UNKNOWN; cvf::ref stimPlanFileData = new RigStimPlanFractureDefinition; size_t startingNegXsValues = 0; { QFile dataFile(stimPlanFileName); if (!dataFile.open(QFile::ReadOnly)) { if (errorMessage) (*errorMessage) += "Could not open the File: " + (stimPlanFileName) + "\n"; return nullptr; } QXmlStreamReader xmlStream; xmlStream.setDevice(&dataFile); xmlStream.readNext(); startingNegXsValues = readStimplanGridAndTimesteps(xmlStream, stimPlanFileData.p(), unitSystem); stimPlanFileData->setUnitSet(unitSystem); if (unitSystem != RiaEclipseUnitTools::UNITS_UNKNOWN) RiaLogging::info(QString("Setting unit system for StimPlan fracture template %1 to %2").arg(stimPlanFileName).arg(unitSystem.uiText())); else RiaLogging::error(QString("Found invalid units for %1. Unit system not set.").arg(stimPlanFileName)); if (xmlStream.hasError()) { RiaLogging::error(QString("Failed to parse file '%1'").arg(dataFile.fileName())); RiaLogging::error(xmlStream.errorString()); } dataFile.close(); } size_t numberOfDepthValues = stimPlanFileData->depthCount(); RiaLogging::debug(QString("Grid size X: %1, Y: %2").arg(QString::number(stimPlanFileData->gridXCount()), QString::number(numberOfDepthValues))); size_t numberOfTimeSteps = stimPlanFileData->timeSteps().size(); RiaLogging::debug(QString("Number of time-steps: %1").arg(numberOfTimeSteps)); //Start reading from top: QFile dataFile(stimPlanFileName); if (!dataFile.open(QFile::ReadOnly)) { if (errorMessage) (*errorMessage) += "Could not open the File: " + (stimPlanFileName) + "\n"; return nullptr; } QXmlStreamReader xmlStream2; xmlStream2.setDevice(&dataFile); QString parameter; QString unit; RiaLogging::info(QString("Properties available in file:")); while (!xmlStream2.atEnd()) { xmlStream2.readNext(); if (xmlStream2.isStartElement()) { if (xmlStream2.name() == "property") { unit = getAttributeValueString(xmlStream2, "uom"); parameter = getAttributeValueString(xmlStream2, "name"); RiaLogging::info(QString("%1 [%2]").arg(parameter, unit)); } else if (xmlStream2.name() == "time") { double timeStepValue = getAttributeValueDouble(xmlStream2, "value"); std::vector> propertyValuesAtTimestep = getAllDepthDataAtTimeStep(xmlStream2, startingNegXsValues); bool valuesOK = stimPlanFileData->numberOfParameterValuesOK(propertyValuesAtTimestep); if (!valuesOK) { RiaLogging::error(QString("Inconsistency detected in reading XML file: '%1'").arg(dataFile.fileName())); return nullptr; } stimPlanFileData->setDataAtTimeValue(parameter, unit, propertyValuesAtTimestep, timeStepValue, conductivityScalingFactor); } } } dataFile.close(); if (xmlStream2.hasError()) { RiaLogging::error(QString("Failed to parse file: '%1'").arg(dataFile.fileName())); RiaLogging::error(xmlStream2.errorString()); } else if (dataFile.error() != QFile::NoError) { RiaLogging::error(QString("Cannot read file: '%1'").arg(dataFile.fileName())); RiaLogging::error(dataFile.errorString()); } else { RiaLogging::info(QString("Successfully read XML file: '%1'").arg(stimPlanFileName)); } return stimPlanFileData; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- size_t RifStimPlanXmlReader::readStimplanGridAndTimesteps(QXmlStreamReader &xmlStream, RigStimPlanFractureDefinition* stimPlanFileData, RiaEclipseUnitTools::UnitSystemType& unit) { size_t startNegValuesXs = 0; size_t startNegValuesYs = 0; QString gridunit = "unknown"; xmlStream.readNext(); //First, read time steps and grid to establish data structures for putting data into later. while (!xmlStream.atEnd()) { xmlStream.readNext(); if (xmlStream.isStartElement()) { if (xmlStream.name() == "grid") { gridunit = getAttributeValueString(xmlStream, "uom"); } if (xmlStream.name() == "xs") { std::vector gridValues; getGriddingValues(xmlStream, gridValues, startNegValuesXs); stimPlanFileData->setGridXs(gridValues); } else if (xmlStream.name() == "ys") { std::vector gridValues; getGriddingValues(xmlStream, gridValues, startNegValuesYs); stimPlanFileData->setGridYs(gridValues); stimPlanFileData->reorderYgridToDepths(); } else if (xmlStream.name() == "time") { double timeStepValue = getAttributeValueDouble(xmlStream, "value"); stimPlanFileData->addTimeStep(timeStepValue); } } } if (gridunit == "m") unit = RiaEclipseUnitTools::UNITS_METRIC; else if (gridunit == "ft") unit = RiaEclipseUnitTools::UNITS_FIELD; else unit = RiaEclipseUnitTools::UNITS_UNKNOWN; if (startNegValuesYs > 0) { RiaLogging::error(QString("Negative depth values detected in XML file")); } return startNegValuesXs; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector> RifStimPlanXmlReader::getAllDepthDataAtTimeStep(QXmlStreamReader &xmlStream, size_t startingNegValuesXs) { std::vector> propertyValuesAtTimestep; while (!(xmlStream.isEndElement() && xmlStream.name() == "time")) { xmlStream.readNext(); if (xmlStream.name() == "depth") { double depth = xmlStream.readElementText().toDouble(); std::vector propertyValuesAtDepth; xmlStream.readNext(); //read end depth token xmlStream.readNext(); //read cdata section with values if (xmlStream.isCDATA()) { QString depthDataStr = xmlStream.text().toString(); for (int i = 0; i < depthDataStr.split(' ').size(); i++) { if (i < static_cast(startingNegValuesXs)) continue; else { QString value = depthDataStr.split(' ')[i]; if ( value != "") { propertyValuesAtDepth.push_back(value.toDouble()); } } } } propertyValuesAtTimestep.push_back(propertyValuesAtDepth); } } return propertyValuesAtTimestep; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RifStimPlanXmlReader::getGriddingValues(QXmlStreamReader &xmlStream, std::vector& gridValues, size_t& startNegValues) { QString gridValuesString = xmlStream.readElementText().replace('\n', ' '); for (QString value : gridValuesString.split(' ')) { if (value.size() > 0) { double gridValue = value.toDouble(); if (gridValue > -1e-5) //tolerance of 1e-5 { gridValues.push_back(gridValue); } else startNegValues++; } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- double RifStimPlanXmlReader::getAttributeValueDouble(QXmlStreamReader &xmlStream, QString parameterName) { double value = HUGE_VAL; for (const QXmlStreamAttribute &attr : xmlStream.attributes()) { if (attr.name() == parameterName) { value = attr.value().toString().toDouble(); } } return value; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RifStimPlanXmlReader::getAttributeValueString(QXmlStreamReader &xmlStream, QString parameterName) { QString parameterValue; for (const QXmlStreamAttribute &attr : xmlStream.attributes()) { if (attr.name() == parameterName) { parameterValue = attr.value().toString(); } } return parameterValue; }