///////////////////////////////////////////////////////////////////////////////// // // 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 "RiaFractureDefines.h" #include "RiaLogging.h" #include "RigStimPlanFractureDefinition.h" #include #include #include // Needed for HUGE_VAL on Linux //-------------------------------------------------------------------------------------------------- /// Internal functions //-------------------------------------------------------------------------------------------------- bool hasNegativeValues(std::vector xs); //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- cvf::ref RifStimPlanXmlReader::readStimPlanXMLFile(const QString& stimPlanFileName, double conductivityScalingFactor, double xScaleFactor, double yScaleFactor, double wellPathInterationY, MirrorMode mirrorMode, RiaEclipseUnitTools::UnitSystem requiredUnit, QString * errorMessage) { RiaLogging::info(QString("Starting to open StimPlan XML file: '%1'").arg(stimPlanFileName)); cvf::ref stimPlanFileData = new RigStimPlanFractureDefinition; { 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(); readStimplanGridAndTimesteps(xmlStream, stimPlanFileData.p(), mirrorMode, requiredUnit); if(xScaleFactor != 1.0) stimPlanFileData->scaleXs(xScaleFactor); if(yScaleFactor != 1.0) stimPlanFileData->scaleYs(yScaleFactor, wellPathInterationY); RiaEclipseUnitTools::UnitSystemType unitSystem = stimPlanFileData->unitSet(); 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 numberOfYValues = stimPlanFileData->yCount(); RiaLogging::debug(QString("Grid size X: %1, Y: %2").arg(QString::number(stimPlanFileData->xCount()), QString::number(numberOfYValues))); 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 = stimPlanFileData->generateDataLayoutFromFileDataLayout(getAllDepthDataAtTimeStep(xmlStream2)); bool valuesOK = stimPlanFileData->numberOfParameterValuesOK(propertyValuesAtTimestep); if (!valuesOK) { RiaLogging::error(QString("Inconsistency detected in reading XML file: '%1'").arg(dataFile.fileName())); return nullptr; } if (parameter.contains(RiaDefines::conductivityResultName(), Qt::CaseInsensitive)) { // Scale all parameters containing conductivity for (auto& dataAtDepth : propertyValuesAtTimestep) { for (auto& dataValue : dataAtDepth) { dataValue *= conductivityScalingFactor; } } } stimPlanFileData->setDataAtTimeValue(parameter, unit, propertyValuesAtTimestep, timeStepValue); } } } 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; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RifStimPlanXmlReader::readStimplanGridAndTimesteps(QXmlStreamReader &xmlStream, RigStimPlanFractureDefinition* stimPlanFileData, MirrorMode mirrorMode, RiaEclipseUnitTools::UnitSystem requiredUnit) { 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()) { RiaEclipseUnitTools::UnitSystem destinationUnit = requiredUnit; if (xmlStream.name() == "grid") { gridunit = getAttributeValueString(xmlStream, "uom"); if (gridunit == "m") stimPlanFileData->m_unitSet = RiaEclipseUnitTools::UNITS_METRIC; else if (gridunit == "ft") stimPlanFileData->m_unitSet = RiaEclipseUnitTools::UNITS_FIELD; else stimPlanFileData->m_unitSet = RiaEclipseUnitTools::UNITS_UNKNOWN; if (destinationUnit == RiaEclipseUnitTools::UNITS_UNKNOWN) { // Use file unit set if requested unit is unknown destinationUnit = stimPlanFileData->m_unitSet; } double tvdToTopPerfFt = getAttributeValueDouble(xmlStream, "TVDToTopPerfFt"); double tvdToBotPerfFt = getAttributeValueDouble(xmlStream, "TVDToBottomPerfFt"); double tvdToTopPerfRequestedUnit = RifStimPlanXmlReader::valueInRequiredUnitSystem(RiaEclipseUnitTools::UNITS_FIELD, destinationUnit, tvdToTopPerfFt); double tvdToBotPerfRequestedUnit = RifStimPlanXmlReader::valueInRequiredUnitSystem(RiaEclipseUnitTools::UNITS_FIELD, destinationUnit, tvdToBotPerfFt); stimPlanFileData->setTvdToTopPerf(tvdToTopPerfRequestedUnit); stimPlanFileData->setTvdToBottomPerf(tvdToBotPerfRequestedUnit); } if (xmlStream.name() == "xs") { std::vector gridValuesXs; { size_t dummy; std::vector gridValues; getGriddingValues(xmlStream, gridValues, dummy); gridValuesXs = RifStimPlanXmlReader::valuesInRequiredUnitSystem(stimPlanFileData->m_unitSet, destinationUnit, gridValues); } stimPlanFileData->m_fileXs = gridValuesXs; stimPlanFileData->generateXsFromFileXs(mirrorMode == MIRROR_AUTO ? !hasNegativeValues(gridValuesXs) : (bool)mirrorMode); } else if (xmlStream.name() == "ys") { std::vector gridValuesYs; { std::vector gridValues; getGriddingValues(xmlStream, gridValues, startNegValuesYs); gridValuesYs = RifStimPlanXmlReader::valuesInRequiredUnitSystem(stimPlanFileData->m_unitSet, destinationUnit, gridValues); } // Reorder and change sign std::vector ys; for (double y : gridValuesYs) { ys.insert(ys.begin(), -y); } stimPlanFileData->m_Ys = ys; } else if (xmlStream.name() == "time") { double timeStepValue = getAttributeValueDouble(xmlStream, "value"); stimPlanFileData->addTimeStep(timeStepValue); } } } if (startNegValuesYs > 0) { RiaLogging::error(QString("Negative depth values detected in XML file")); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector> RifStimPlanXmlReader::getAllDepthDataAtTimeStep(QXmlStreamReader &xmlStream) { std::vector> propertyValuesAtTimestep; while (!(xmlStream.isEndElement() && xmlStream.name() == "time")) { xmlStream.readNext(); if (xmlStream.name() == "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(); QStringList splitted = depthDataStr.split(' '); for (int i = 0; i < splitted.size(); i++) { QString value = splitted[i]; if ( value != "") { propertyValuesAtDepth.push_back(value.toDouble()); } } } propertyValuesAtTimestep.push_back(propertyValuesAtDepth); } } return propertyValuesAtTimestep; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RifStimPlanXmlReader::valuesInRequiredUnitSystem(RiaEclipseUnitTools::UnitSystem sourceUnit, RiaEclipseUnitTools::UnitSystem requiredUnit, const std::vector& values) { if (sourceUnit == RiaEclipseUnitTools::UNITS_FIELD && requiredUnit == RiaEclipseUnitTools::UNITS_METRIC) { std::vector convertedValues; for (const auto &valueInFeet : values) { convertedValues.push_back(RiaEclipseUnitTools::feetToMeter(valueInFeet)); } return convertedValues; } else if (sourceUnit == RiaEclipseUnitTools::UNITS_METRIC && requiredUnit == RiaEclipseUnitTools::UNITS_FIELD) { std::vector convertedValues; for (const auto &valueInMeter : values) { convertedValues.push_back(RiaEclipseUnitTools::meterToFeet(valueInMeter)); } return convertedValues; } return values; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- double RifStimPlanXmlReader::valueInRequiredUnitSystem(RiaEclipseUnitTools::UnitSystem sourceUnit, RiaEclipseUnitTools::UnitSystem requiredUnit, double value) { if (sourceUnit == RiaEclipseUnitTools::UNITS_FIELD && requiredUnit == RiaEclipseUnitTools::UNITS_METRIC) { return RiaEclipseUnitTools::feetToMeter(value); } else if (sourceUnit == RiaEclipseUnitTools::UNITS_METRIC && requiredUnit == RiaEclipseUnitTools::UNITS_FIELD) { return RiaEclipseUnitTools::meterToFeet(value); } return value; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- 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(); gridValues.push_back(gridValue); if(gridValue < -RigStimPlanFractureDefinition::THRESHOLD_VALUE) 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; } //-------------------------------------------------------------------------------------------------- /// Internal function //-------------------------------------------------------------------------------------------------- bool hasNegativeValues(std::vector xs) { return xs[0] < -RigStimPlanFractureDefinition::THRESHOLD_VALUE; }