Merge pull request #6079 from OPM/layers-fracture-model-plot-6036

Layers fracture model plot 6036
This commit is contained in:
Kristian Bendiksen 2020-06-19 09:38:12 +02:00 committed by GitHub
commit 034f75f64e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1423 additions and 86 deletions

View File

@ -45,6 +45,9 @@ public:
static double mmToMeter( double mm ) { return mm / 1000.0; }
static double meterToMm( double meter ) { return 1000.0 * meter; }
static double barToPsi( double bar ) { return bar * 14.5038; }
static double barPerMeterToPsiPerFeet( double barPerMeter ) { return barPerMeter * 4.42075; }
static double darcysConstant( UnitSystem unitSystem );
static RiaDefines::DepthUnitType depthUnit( UnitSystem unit );

View File

@ -92,6 +92,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RicDeleteTemporaryLgrsFeature.h
${CMAKE_CURRENT_LIST_DIR}/RicExportContourMapToTextFeature.h
${CMAKE_CURRENT_LIST_DIR}/RicExportContourMapToTextUi.h
${CMAKE_CURRENT_LIST_DIR}/RicNewMultiPlotFeature.h
${CMAKE_CURRENT_LIST_DIR}/RicExportFractureModelPlotToFileFeature.h
)
@ -182,6 +183,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RicDeleteTemporaryLgrsFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicExportContourMapToTextFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicExportContourMapToTextUi.cpp
${CMAKE_CURRENT_LIST_DIR}/RicNewMultiPlotFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicExportFractureModelPlotToFileFeature.cpp
)

View File

@ -36,6 +36,7 @@
#include "RimFractureModelCurve.h"
#include "RimFractureModelPlot.h"
#include "RimFractureModelPlotCollection.h"
#include "RimLayerCurve.h"
#include "RimMainPlotCollection.h"
#include "RimModeledWellPath.h"
#include "RimProject.h"
@ -68,6 +69,7 @@ RimFractureModelPlot*
caf::ProgressInfo progInfo( 100, "Creating Fracture Model Plot" );
RimFractureModelPlot* plot = createFractureModelPlot( true, "Fracture Model" );
plot->setFractureModel( fractureModel );
{
auto task = progInfo.task( "Creating formation track", 2 );
@ -79,29 +81,42 @@ RimFractureModelPlot*
createFaciesTrack( plot, fractureModel, eclipseCase );
}
{
auto task = progInfo.task( "Creating layers track", 2 );
createLayersTrack( plot, fractureModel, eclipseCase );
}
{
auto task = progInfo.task( "Creating parameters track", 15 );
std::vector<std::tuple<QString, RiaDefines::ResultCatType, RimFractureModelCurve::MissingValueStrategy>> results =
{std::make_tuple( "PORO",
RiaDefines::ResultCatType::STATIC_NATIVE,
RimFractureModelCurve::MissingValueStrategy::DEFAULT_VALUE ),
std::make_tuple( "PRESSURE",
RiaDefines::ResultCatType::DYNAMIC_NATIVE,
RimFractureModelCurve::MissingValueStrategy::LINEAR_INTERPOLATION ),
std::make_tuple( "PERMX",
RiaDefines::ResultCatType::STATIC_NATIVE,
RimFractureModelCurve::MissingValueStrategy::DEFAULT_VALUE )};
std::map<QString, PlotDefVector> plots;
for ( auto result : results )
plots["Porosity"] = {std::make_tuple( "PORO",
RiaDefines::ResultCatType::STATIC_NATIVE,
RimFractureModelCurve::MissingValueStrategy::DEFAULT_VALUE,
false )};
plots["Pressure"] = {std::make_tuple( "PRESSURE",
RiaDefines::ResultCatType::DYNAMIC_NATIVE,
RimFractureModelCurve::MissingValueStrategy::LINEAR_INTERPOLATION,
false ),
std::make_tuple( "PRESSURE",
RiaDefines::ResultCatType::DYNAMIC_NATIVE,
RimFractureModelCurve::MissingValueStrategy::LINEAR_INTERPOLATION,
true )};
plots["Permeability"] = {std::make_tuple( "PERMX",
RiaDefines::ResultCatType::STATIC_NATIVE,
RimFractureModelCurve::MissingValueStrategy::DEFAULT_VALUE,
false ),
std::make_tuple( "PERMZ",
RiaDefines::ResultCatType::STATIC_NATIVE,
RimFractureModelCurve::MissingValueStrategy::DEFAULT_VALUE,
false )};
for ( auto result : plots )
{
createParametersTrack( plot,
fractureModel,
eclipseCase,
timeStep,
std::get<0>( result ),
std::get<1>( result ),
std::get<2>( result ) );
createParametersTrack( plot, fractureModel, eclipseCase, timeStep, result.first, result.second );
}
}
@ -112,7 +127,11 @@ RimFractureModelPlot*
{RimElasticPropertiesCurve::PropertyType::YOUNGS_MODULUS,
RimElasticPropertiesCurve::PropertyType::POISSONS_RATIO,
RimElasticPropertiesCurve::PropertyType::K_IC,
RimElasticPropertiesCurve::PropertyType::PROPPANT_EMBEDMENT};
RimElasticPropertiesCurve::PropertyType::PROPPANT_EMBEDMENT,
RimElasticPropertiesCurve::PropertyType::BIOT_COEFFICIENT,
RimElasticPropertiesCurve::PropertyType::K0,
RimElasticPropertiesCurve::PropertyType::FLUID_LOSS_COEFFICIENT,
RimElasticPropertiesCurve::PropertyType::SPURT_LOSS};
for ( auto result : results )
{
@ -252,16 +271,68 @@ void RicNewFractureModelPlotFeature::createFaciesTrack( RimFractureModelPlot* pl
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimFractureModelCurve*
RicNewFractureModelPlotFeature::createParametersTrack( RimFractureModelPlot* plot,
RimFractureModel* fractureModel,
RimEclipseCase* eclipseCase,
int timeStep,
const QString& resultVariable,
RiaDefines::ResultCatType resultCategoryType,
RimFractureModelCurve::MissingValueStrategy missingValueStrategy )
void RicNewFractureModelPlotFeature::createLayersTrack( RimFractureModelPlot* plot,
RimFractureModel* fractureModel,
RimEclipseCase* eclipseCase )
{
RimWellLogTrack* plotTrack = RicNewWellLogPlotFeatureImpl::createWellLogPlotTrack( false, resultVariable, plot );
QString defaultProperty = "OPERNUM_1";
RimWellLogTrack* faciesTrack = RicNewWellLogPlotFeatureImpl::createWellLogPlotTrack( false, "Layers", plot );
faciesTrack->setFormationWellPath( fractureModel->thicknessDirectionWellPath() );
faciesTrack->setFormationCase( eclipseCase );
// faciesTrack->setAnnotationType( RiuPlotAnnotationTool::RegionAnnotationType::RESULT_PROPERTY_ANNOTATIONS );
// faciesTrack->setRegionPropertyResultType( RiaDefines::ResultCatType::INPUT_PROPERTY, defaultProperty );
RimColorLegend* faciesColors = RimProject::current()->colorLegendCollection()->findByName( "Facies colors" );
if ( faciesColors )
{
faciesTrack->setColorShadingLegend( faciesColors );
}
faciesTrack->setVisibleXRange( 0.0, 0.0 );
faciesTrack->setColSpan( RimPlot::ONE );
caf::ColorTable colors = RiaColorTables::contrastCategoryPaletteColors();
std::vector<RiuQwtPlotCurve::LineStyleEnum> lineStyles = {RiuQwtPlotCurve::STYLE_SOLID,
RiuQwtPlotCurve::STYLE_DASH,
RiuQwtPlotCurve::STYLE_DASH_DOT};
RimLayerCurve* curve = new RimLayerCurve;
curve->setFractureModel( fractureModel );
curve->setCase( eclipseCase );
// curve->setEclipseResultCategory( RiaDefines::ResultCatType::INPUT_PROPERTY );
// curve->setEclipseResultVariable( defaultProperty );
curve->setColor( colors.cycledColor3f( 0 ) );
curve->setLineStyle( lineStyles[0] );
curve->setLineThickness( 2 );
curve->setAutoNameComponents( false, true, false, false, false );
faciesTrack->addCurve( curve );
faciesTrack->setAutoScaleXEnabled( true );
curve->loadDataAndUpdate( true );
curve->updateConnectedEditors();
faciesTrack->updateConnectedEditors();
plot->updateConnectedEditors();
RiaApplication::instance()->project()->updateConnectedEditors();
RiaGuiApplication::instance()->getOrCreateMainPlotWindow();
RiuPlotMainWindowTools::selectAsCurrentItem( curve );
RiuPlotMainWindowTools::showPlotMainWindow();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicNewFractureModelPlotFeature::createParametersTrack( RimFractureModelPlot* plot,
RimFractureModel* fractureModel,
RimEclipseCase* eclipseCase,
int timeStep,
const QString& trackTitle,
const PlotDefVector& curveConfigurations )
{
RimWellLogTrack* plotTrack = RicNewWellLogPlotFeatureImpl::createWellLogPlotTrack( false, trackTitle, plot );
plotTrack->setFormationWellPath( fractureModel->thicknessDirectionWellPath() );
plotTrack->setColSpan( RimPlot::TWO );
plotTrack->setVisibleXRange( 0.0, 2.0 );
@ -277,32 +348,54 @@ RimFractureModelCurve*
RiuQwtPlotCurve::STYLE_DASH,
RiuQwtPlotCurve::STYLE_DASH_DOT};
RimFractureModelCurve* curve = new RimFractureModelCurve;
curve->setFractureModel( fractureModel );
curve->setCase( eclipseCase );
curve->setEclipseResultVariable( resultVariable );
curve->setEclipseResultCategory( resultCategoryType );
curve->setMissingValueStrategy( missingValueStrategy );
curve->setColor( colors.cycledColor3f( 0 ) );
curve->setLineStyle( lineStyles[0] );
curve->setLineThickness( 2 );
curve->setAutoNameComponents( false, true, false, false, false );
int colorIndex = 0;
for ( auto curveConfig : curveConfigurations )
{
QString resultVariable = std::get<0>( curveConfig );
RiaDefines::ResultCatType resultCategoryType = std::get<1>( curveConfig );
RimFractureModelCurve::MissingValueStrategy missingValueStrategy = std::get<2>( curveConfig );
bool fixedInitialTimeStep = std::get<3>( curveConfig );
plotTrack->addCurve( curve );
plotTrack->setAutoScaleXEnabled( true );
curve->loadDataAndUpdate( true );
RimFractureModelCurve* curve = new RimFractureModelCurve;
curve->setFractureModel( fractureModel );
curve->setCase( eclipseCase );
curve->setEclipseResultVariable( resultVariable );
curve->setEclipseResultCategory( resultCategoryType );
curve->setMissingValueStrategy( missingValueStrategy );
curve->setColor( colors.cycledColor3f( colorIndex ) );
curve->setLineStyle( lineStyles[0] );
curve->setLineThickness( 2 );
if ( fixedInitialTimeStep )
{
curve->setAutoNameComponents( false, false, false, false, false );
curve->setCustomName( QString( "INITIAL %1" ).arg( resultVariable ) );
curve->setCurrentTimeStep( 0 );
}
else
{
curve->setAutoNameComponents( false, true, false, false, false );
curve->setCurrentTimeStep( timeStep );
}
plotTrack->addCurve( curve );
plotTrack->setAutoScaleXEnabled( true );
curve->loadDataAndUpdate( true );
curve->updateConnectedEditors();
colorIndex++;
}
curve->updateConnectedEditors();
plotTrack->updateConnectedEditors();
plot->updateConnectedEditors();
RiaApplication::instance()->project()->updateConnectedEditors();
RiaGuiApplication::instance()->getOrCreateMainPlotWindow();
RiuPlotMainWindowTools::selectAsCurrentItem( curve );
// RiuPlotMainWindowTools::selectAsCurrentItem( curve );
RiuPlotMainWindowTools::showPlotMainWindow();
return curve;
}
//--------------------------------------------------------------------------------------------------

View File

@ -30,6 +30,9 @@ class RimFractureModelPlot;
class RimFractureModelPlotCollection;
class RimFractureModel;
typedef std::tuple<QString, RiaDefines::ResultCatType, RimFractureModelCurve::MissingValueStrategy, bool> PlotDef;
typedef std::vector<PlotDef> PlotDefVector;
//==================================================================================================
///
//==================================================================================================
@ -50,14 +53,16 @@ private:
static void
createFormationTrack( RimFractureModelPlot* plot, RimFractureModel* fractureModel, RimEclipseCase* eclipseCase );
static void
createFaciesTrack( RimFractureModelPlot* plot, RimFractureModel* fractureModel, RimEclipseCase* eclipseCase );
static RimFractureModelCurve* createParametersTrack( RimFractureModelPlot* plot,
RimFractureModel* fractureModel,
RimEclipseCase* eclipseCase,
int timeStep,
const QString& resultVariable,
RiaDefines::ResultCatType resultCategoryType,
RimFractureModelCurve::MissingValueStrategy missingValueStrategy );
createFaciesTrack( RimFractureModelPlot* plot, RimFractureModel* fractureModel, RimEclipseCase* eclipseCase );
static void
createLayersTrack( RimFractureModelPlot* plot, RimFractureModel* fractureModel, RimEclipseCase* eclipseCase );
static void createParametersTrack( RimFractureModelPlot* plot,
RimFractureModel* fractureModel,
RimEclipseCase* eclipseCase,
int timeStep,
const QString& trackTitle,
const PlotDefVector& curveConfiguration );
static void createElasticPropertiesTrack( RimFractureModelPlot* plot,
RimFractureModel* fractureModel,

View File

@ -62,7 +62,6 @@ void RicElasticPropertiesImportTools::importElasticPropertiesFromFile( const QSt
}
RimElasticProperties* rimElasticProperties = new RimElasticProperties;
// rimElasticProperties->setFilePath();
for ( FaciesKey key : faciesKeys )
{
std::vector<RifElasticProperties> matchingFacies;
@ -93,7 +92,11 @@ void RicElasticPropertiesImportTools::importElasticPropertiesFromFile( const QSt
item.youngsModulus,
item.poissonsRatio,
item.K_Ic,
item.proppantEmbedment );
item.proppantEmbedment,
item.biotCoefficient,
item.k0,
item.fluidLossCoefficient,
item.spurtLoss );
}
rimElasticProperties->setPropertiesForFacies( key, rigElasticProperties );

View File

@ -0,0 +1,77 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2020- Equinor 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 "RicExportFractureModelPlotToFileFeature.h"
#include "RiaApplication.h"
#include "RimFractureModelPlot.h"
#include "RifFractureModelPlotExporter.h"
#include "cafSelectionManager.h"
#include "cafUtils.h"
#include <QAction>
#include <QFileDialog>
CAF_CMD_SOURCE_INIT( RicExportFractureModelPlotToFileFeature, "RicExportFractureModelPlotToFileFeature" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RicExportFractureModelPlotToFileFeature::isCommandEnabled()
{
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicExportFractureModelPlotToFileFeature::onActionTriggered( bool isChecked )
{
RimFractureModelPlot* fractureModelPlot =
caf::SelectionManager::instance()->selectedItemOfType<RimFractureModelPlot>();
if ( !fractureModelPlot ) return;
RiaApplication* app = RiaApplication::instance();
QString defaultDir = app->lastUsedDialogDirectory( "FRACTURE_MODEL_PLOT" );
QString fileNameCandidate = "Geological";
QString defaultFileName = defaultDir + "/" + caf::Utils::makeValidFileBasename( fileNameCandidate ) + ".frk";
QString fileName = QFileDialog::getSaveFileName( nullptr,
"Select File for Fracture Model Plot Export",
defaultFileName,
"Geologic Model File(*.frk);;All files(*.*)" );
if ( fileName.isEmpty() ) return;
RifFractureModelPlotExporter::writeToFile( fractureModelPlot, fileName );
// Remember the path to next time
app->setLastUsedDialogDirectory( "FRACTURE_MODEL_PLOT", QFileInfo( fileName ).absolutePath() );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicExportFractureModelPlotToFileFeature::setupActionLook( QAction* actionToSetup )
{
actionToSetup->setText( "Export Fracture Model Plot to File" );
actionToSetup->setIcon( QIcon( ":/Save.png" ) );
}

View File

@ -0,0 +1,35 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2020- Equinor 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 "cafCmdFeature.h"
//==================================================================================================
///
//==================================================================================================
class RicExportFractureModelPlotToFileFeature : public caf::CmdFeature
{
CAF_CMD_HEADER_INIT;
protected:
// Overrides
bool isCommandEnabled() override;
void onActionTriggered( bool isChecked ) override;
void setupActionLook( QAction* actionToSetup ) override;
};

View File

@ -54,6 +54,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RifSurfaceReader.h
${CMAKE_CURRENT_LIST_DIR}/RifRoffReader.h
${CMAKE_CURRENT_LIST_DIR}/RifColorLegendData.h
${CMAKE_CURRENT_LIST_DIR}/RifElasticPropertiesReader.h
${CMAKE_CURRENT_LIST_DIR}/RifFractureModelPlotExporter.h
# HDF5 file reader is directly included in ResInsight main CmakeList.txt
#${CMAKE_CURRENT_LIST_DIR}/RifHdf5Reader.h
@ -112,6 +113,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RifSurfaceReader.cpp
${CMAKE_CURRENT_LIST_DIR}/RifRoffReader.cpp
${CMAKE_CURRENT_LIST_DIR}/RifColorLegendData.cpp
${CMAKE_CURRENT_LIST_DIR}/RifElasticPropertiesReader.cpp
${CMAKE_CURRENT_LIST_DIR}/RifFractureModelPlotExporter.cpp
# HDF5 file reader is directly included in ResInsight main CmakeList.txt
#${CMAKE_CURRENT_LIST_DIR}/RifHdf5Reader.cpp

View File

@ -79,7 +79,7 @@ RifElasticProperties
{
QStringList tokens = tokenize( line, "," );
if ( tokens.size() != 8 )
if ( tokens.size() != 12 )
{
throw FileParseException( QString( "Incomplete data on line %1: %2" ).arg( lineNumber ).arg( filePath ) );
}
@ -93,18 +93,26 @@ RifElasticProperties
<< "Young's Modulus"
<< "Poisson's Ratio"
<< "K-Ic"
<< "Proppant Embedment";
<< "Proppant Embedment"
<< "Biot Coefficient"
<< "k0"
<< "Fluid Loss Coefficient"
<< "Spurt Loss";
verifyNonEmptyTokens( tokens, nameOfNonEmptyTokens, lineNumber, filePath );
RifElasticProperties elasticProperties;
elasticProperties.fieldName = tokens[0];
elasticProperties.formationName = tokens[1];
elasticProperties.faciesName = tokens[2];
elasticProperties.porosity = parseDouble( tokens[3], "Porosity", lineNumber, filePath );
elasticProperties.youngsModulus = parseDouble( tokens[4], "Young's Modulus", lineNumber, filePath );
elasticProperties.poissonsRatio = parseDouble( tokens[5], "Poisson's Ratio", lineNumber, filePath );
elasticProperties.K_Ic = parseDouble( tokens[6], "K-Ic", lineNumber, filePath );
elasticProperties.proppantEmbedment = parseDouble( tokens[7], "Proppant Embedment", lineNumber, filePath );
elasticProperties.fieldName = tokens[0];
elasticProperties.formationName = tokens[1];
elasticProperties.faciesName = tokens[2];
elasticProperties.porosity = parseDouble( tokens[3], "Porosity", lineNumber, filePath );
elasticProperties.youngsModulus = parseDouble( tokens[4], "Young's Modulus", lineNumber, filePath );
elasticProperties.poissonsRatio = parseDouble( tokens[5], "Poisson's Ratio", lineNumber, filePath );
elasticProperties.K_Ic = parseDouble( tokens[6], "K-Ic", lineNumber, filePath );
elasticProperties.proppantEmbedment = parseDouble( tokens[7], "Proppant Embedment", lineNumber, filePath );
elasticProperties.biotCoefficient = parseDouble( tokens[8], "Biot Coefficient", lineNumber, filePath );
elasticProperties.k0 = parseDouble( tokens[9], "k0", lineNumber, filePath );
elasticProperties.fluidLossCoefficient = parseDouble( tokens[10], "Fluid Loss Coefficient", lineNumber, filePath );
elasticProperties.spurtLoss = parseDouble( tokens[11], "Spurt Loss", lineNumber, filePath );
return elasticProperties;
}

View File

@ -32,6 +32,10 @@ struct RifElasticProperties
double poissonsRatio;
double K_Ic;
double proppantEmbedment;
double biotCoefficient;
double k0;
double fluidLossCoefficient;
double spurtLoss;
};
//==================================================================================================

View File

@ -0,0 +1,127 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2020- Equinor 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 "RifFractureModelPlotExporter.h"
#include "RimFractureModelPlot.h"
#include <QFile>
#include <QTextStream>
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RifFractureModelPlotExporter::writeToFile( RimFractureModelPlot* plot, const QString& filepath )
{
std::vector<QString> labels;
// TVD depth of top of zone (ft)
labels.push_back( "dpthlyr" );
// Stress at top of zone (psi)
labels.push_back( "strs" );
// Stress gradient (psi/ft)
labels.push_back( "strsg" );
// Young's modulus (MMpsi)
labels.push_back( "elyr" );
// Poisson's Ratio
labels.push_back( "poissonr" );
// K-Ic (psi*sqrt(in)
labels.push_back( "tuflyr" );
// Fluid Loss Coefficient
labels.push_back( "clyrc" );
// Spurt loss (gal/100f^2)
labels.push_back( "clyrs" );
// Proppand Embedmeent (lb/ft^2)
labels.push_back( "pembed" );
bool useDetailedLoss = false;
if ( useDetailedLoss )
{
// B2 Detailed Loss
// Reservoir Pressure (psi)
labels.push_back( "zoneResPres" );
// Porosity (fraction)
labels.push_back( "zonePorosity" );
// Horizontal Perm (md)
labels.push_back( "zoneHorizPerm" );
// Vertical Perm (md)
labels.push_back( "zoneVertPerm" );
}
std::map<QString, std::vector<double>> values;
values["dpthlyr"] = plot->calculateTrueVerticalDepth();
values["strs"] = plot->calculateStress();
values["strsg"] = plot->calculateStressGradient();
values["elyr"] = plot->calculateYoungsModulus();
values["poissonr"] = plot->calculatePoissonsRatio();
values["tuflyr"] = plot->calculateKIc();
values["clyrc"] = plot->calculateFluidLossCoefficient();
values["clyrs"] = plot->calculateSpurtLoss();
values["pembed"] = plot->calculateProppandEmbedment();
values["zoneResPres"] = plot->calculateReservoirPressure();
values["zonePorosity"] = plot->calculatePorosity();
values["zoneHorizPerm"] = plot->calculateHorizontalPermeability();
values["zoneVertPerm"] = plot->calculateVerticalPermeability();
QFile data( filepath );
if ( !data.open( QFile::WriteOnly | QFile::Truncate ) )
{
return false;
}
QTextStream stream( &data );
for ( QString label : labels )
{
appendToStream( stream, label, values[label] );
}
return true;
}
void RifFractureModelPlotExporter::appendToStream( QTextStream& stream, const QString& label, const std::vector<double>& values )
{
stream << "<cNamedSet>"
<< "\n"
<< label << "\n"
<< "<dimCount>"
<< "\n"
<< 1 << "\n"
<< "<sizes>"
<< "\n"
<< values.size() << "\n"
<< "<data>"
<< "\n";
for ( auto val : values )
{
stream << val << "\n";
}
stream << "</cNamedSet>"
<< "\n";
}

View File

@ -0,0 +1,37 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2020- Equinor 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 <vector>
class RimFractureModelPlot;
class QString;
class QTextStream;
//==================================================================================================
//
//==================================================================================================
class RifFractureModelPlotExporter
{
public:
static bool writeToFile( RimFractureModelPlot* plot, const QString& filepath );
private:
static void appendToStream( QTextStream& stream, const QString& label, const std::vector<double>& values );
};

View File

@ -160,6 +160,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RimFractureModelPlotCollection.h
${CMAKE_CURRENT_LIST_DIR}/RimFractureModelCurve.h
${CMAKE_CURRENT_LIST_DIR}/RimElasticProperties.h
${CMAKE_CURRENT_LIST_DIR}/RimElasticPropertiesCurve.h
${CMAKE_CURRENT_LIST_DIR}/RimLayerCurve.h
)
@ -324,6 +325,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RimFractureModelPlotCollection.cpp
${CMAKE_CURRENT_LIST_DIR}/RimFractureModelCurve.cpp
${CMAKE_CURRENT_LIST_DIR}/RimElasticProperties.cpp
${CMAKE_CURRENT_LIST_DIR}/RimElasticPropertiesCurve.cpp
${CMAKE_CURRENT_LIST_DIR}/RimLayerCurve.cpp
)
list(APPEND CODE_HEADER_FILES

View File

@ -108,6 +108,13 @@ RimFractureModel::RimFractureModel()
CAF_PDM_InitField( &m_defaultPorosity, "DefaultPorosity", 0.0, "Default Porosity", "", "", "" );
CAF_PDM_InitField( &m_defaultPermeability, "DefaultPermeability", 10.0e-6, "Default Permeability", "", "", "" );
// Stress unit: bar
// Stress gradient unit: bar/m
// Depth is meter
CAF_PDM_InitField( &m_verticalStress, "VerticalStress", 879.0, "Vertical Stress", "", "", "" );
CAF_PDM_InitField( &m_verticalStressGradient, "VerticalStressGradient", 0.238, "Vertical Stress Gradient", "", "", "" );
CAF_PDM_InitField( &m_stressDepth, "StressDepth", 1000.0, "Stress Depth", "", "", "" );
CAF_PDM_InitFieldNoDefault( &m_elasticProperties, "ElasticProperties", "Elastic Properties", "", "", "" );
m_elasticProperties.uiCapability()->setUiHidden( true );
m_elasticProperties.uiCapability()->setUiTreeHidden( true );
@ -512,7 +519,7 @@ double RimFractureModel::getDefaultForMissingValue( const QString& keyword ) con
{
return defaultPorosity();
}
else if ( keyword == QString( "PERMX" ) )
else if ( keyword == QString( "PERMX" ) || keyword == QString( "PERMZ" ) )
{
return defaultPermeability();
}
@ -522,3 +529,27 @@ double RimFractureModel::getDefaultForMissingValue( const QString& keyword ) con
return std::numeric_limits<double>::infinity();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RimFractureModel::verticalStress() const
{
return m_verticalStress;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RimFractureModel::verticalStressGradient() const
{
return m_verticalStressGradient;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RimFractureModel::stressDepth() const
{
return m_stressDepth;
}

View File

@ -63,6 +63,10 @@ public:
double defaultPorosity() const;
double defaultPermeability() const;
double verticalStress() const;
double verticalStressGradient() const;
double stressDepth() const;
// RimWellPathCompletionsInterface overrides.
RiaDefines::WellPathComponentType componentType() const override;
QString componentLabel() const override;
@ -105,4 +109,7 @@ protected:
caf::PdmChildField<RimElasticProperties*> m_elasticProperties;
caf::PdmField<double> m_defaultPorosity;
caf::PdmField<double> m_defaultPermeability;
caf::PdmField<double> m_verticalStress;
caf::PdmField<double> m_verticalStressGradient;
caf::PdmField<double> m_stressDepth;
};

View File

@ -71,6 +71,7 @@
#include "RimFormationNames.h"
#include "RimFormationNamesCollection.h"
#include "RimFractureModel.h"
#include "RimFractureModelPlot.h"
#include "RimFractureTemplate.h"
#include "RimFractureTemplateCollection.h"
#include "RimGeoMechCase.h"
@ -433,6 +434,10 @@ caf::CmdFeatureMenuBuilder RimContextCommandBuilder::commandsFromSelection()
menuBuilder << "RicImportFaciesFeature";
menuBuilder << "RicImportElasticPropertiesFeature";
}
else if ( dynamic_cast<RimFractureModelPlot*>( firstUiItem ) )
{
menuBuilder << "RicExportFractureModelPlotToFileFeature";
}
else if ( dynamic_cast<Rim3dWellLogCurveCollection*>( firstUiItem ) ||
dynamic_cast<Rim3dWellLogExtractionCurve*>( firstUiItem ) ||
dynamic_cast<Rim3dWellLogFileCurve*>( firstUiItem ) ||

View File

@ -130,6 +130,10 @@ QString RimElasticProperties::generatePropertiesTable()
" <th>Poisson's<br>Ratio</th>"
" <th>K-Ic</th>"
" <th>Proppant<br>Embedment</th>"
" <th>Biot<br>Coefficient</th>"
" <th>k0</th>"
" <th>Fluid Loss<br>Coefficient</th>"
" <th>Spurt Loss</th>"
" </tr>"
" </thead>"
" <tbody>" );
@ -137,12 +141,16 @@ QString RimElasticProperties::generatePropertiesTable()
QString body;
for ( auto prop : m_properties )
{
const QString& fieldName = prop.second.fieldName();
const std::vector<double>& porosity = prop.second.porosity();
const std::vector<double>& youngsModulus = prop.second.youngsModulus();
const std::vector<double>& poissonsRatio = prop.second.poissonsRatio();
const std::vector<double>& K_Ic = prop.second.K_Ic();
const std::vector<double>& proppantEmbedment = prop.second.proppantEmbedment();
const QString& fieldName = prop.second.fieldName();
const std::vector<double>& porosity = prop.second.porosity();
const std::vector<double>& youngsModulus = prop.second.youngsModulus();
const std::vector<double>& poissonsRatio = prop.second.poissonsRatio();
const std::vector<double>& K_Ic = prop.second.K_Ic();
const std::vector<double>& proppantEmbedment = prop.second.proppantEmbedment();
const std::vector<double>& biotCoefficient = prop.second.biotCoefficient();
const std::vector<double>& k0 = prop.second.k0();
const std::vector<double>& fluidLossCoefficient = prop.second.fluidLossCoefficient();
const std::vector<double>& spurtLoss = prop.second.spurtLoss();
for ( size_t i = 0; i < porosity.size(); i++ )
{
@ -155,6 +163,10 @@ QString RimElasticProperties::generatePropertiesTable()
" <td align=right>%6</td>"
" <td align=right>%7</td>"
" <td align=right>%8</td>"
" <td align=right>%9</td>"
" <td align=right>%10</td>"
" <td align=right>%11</td>"
" <td align=right>%12</td>"
"</tr>" );
QString line = format.arg( fieldName )
@ -164,7 +176,11 @@ QString RimElasticProperties::generatePropertiesTable()
.arg( youngsModulus[i] )
.arg( poissonsRatio[i] )
.arg( K_Ic[i] )
.arg( proppantEmbedment[i] );
.arg( proppantEmbedment[i] )
.arg( biotCoefficient[i] )
.arg( k0[i] )
.arg( fluidLossCoefficient[i] )
.arg( spurtLoss[i] );
body.append( line );
}

View File

@ -67,6 +67,12 @@ void AppEnum<RimElasticPropertiesCurve::PropertyType>::setUp()
addItem( RimElasticPropertiesCurve::PropertyType::POISSONS_RATIO, "POISSONS_RATIO", "Poisson's Ratio" );
addItem( RimElasticPropertiesCurve::PropertyType::K_IC, "K_IC", "K-Ic" );
addItem( RimElasticPropertiesCurve::PropertyType::PROPPANT_EMBEDMENT, "PROPPANT_EMBEDMENT", "Proppant Embedment" );
addItem( RimElasticPropertiesCurve::PropertyType::BIOT_COEFFICIENT, "BIOT_COEFFICIENT", "Biot Coefficient" );
addItem( RimElasticPropertiesCurve::PropertyType::K0, "K0", "k0" );
addItem( RimElasticPropertiesCurve::PropertyType::FLUID_LOSS_COEFFICIENT,
"FLUID_LOSS_COEFFICIENT",
"Fluid Loss Coefficient" );
addItem( RimElasticPropertiesCurve::PropertyType::SPURT_LOSS, "SPURT_LOSS", "Spurt Loss" );
setDefault( RimElasticPropertiesCurve::PropertyType::YOUNGS_MODULUS );
}
}; // namespace caf
@ -255,6 +261,26 @@ void RimElasticPropertiesCurve::performDataExtraction( bool* isUsingPseudoLength
double val = rigElasticProperties.getProppantEmbedment( porosity );
values.push_back( val );
}
else if ( m_propertyType() == PropertyType::BIOT_COEFFICIENT )
{
double val = rigElasticProperties.getBiotCoefficient( porosity );
values.push_back( val );
}
else if ( m_propertyType() == PropertyType::K0 )
{
double val = rigElasticProperties.getK0( porosity );
values.push_back( val );
}
else if ( m_propertyType() == PropertyType::FLUID_LOSS_COEFFICIENT )
{
double val = rigElasticProperties.getFluidLossCoefficient( porosity );
values.push_back( val );
}
else if ( m_propertyType() == PropertyType::SPURT_LOSS )
{
double val = rigElasticProperties.getSpurtLoss( porosity );
values.push_back( val );
}
}
else
{

View File

@ -45,7 +45,11 @@ public:
YOUNGS_MODULUS,
POISSONS_RATIO,
K_IC,
PROPPANT_EMBEDMENT
PROPPANT_EMBEDMENT,
BIOT_COEFFICIENT,
K0,
FLUID_LOSS_COEFFICIENT,
SPURT_LOSS
};
RimElasticPropertiesCurve();

View File

@ -18,11 +18,14 @@
#include "RimFractureModelPlot.h"
#include "RiaDefines.h"
#include "RiaLogging.h"
#include "RicfCommandObject.h"
#include "RimEclipseCase.h"
#include "RimFractureModel.h"
#include "RimFractureModelCurve.h"
#include "RimLayerCurve.h"
#include "RigWellLogCurveData.h"
@ -39,6 +42,18 @@ CAF_PDM_SOURCE_INIT( RimFractureModelPlot, "FractureModelPlot" );
RimFractureModelPlot::RimFractureModelPlot()
{
CAF_PDM_InitScriptableObject( "Fracture Model Plot", "", "", "A fracture model plot" );
CAF_PDM_InitFieldNoDefault( &m_fractureModel, "FractureModel", "Fracture Model", "", "", "" );
m_fractureModel.uiCapability()->setUiTreeChildrenHidden( true );
m_fractureModel.uiCapability()->setUiHidden( true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimFractureModelPlot::setFractureModel( RimFractureModel* fractureModel )
{
m_fractureModel = fractureModel;
}
//--------------------------------------------------------------------------------------------------
@ -90,3 +105,378 @@ void RimFractureModelPlot::getPorosityValues( std::vector<double>& values ) cons
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimFractureModelPlot::calculateLayers( std::vector<std::pair<double, double>>& layerBoundaryDepths,
std::vector<std::pair<size_t, size_t>>& layerBoundaryIndexes ) const
{
std::vector<RimLayerCurve*> curves;
descendantsIncludingThisOfType( curves );
if ( curves.empty() )
{
return;
}
// Expect to have only one of these
RimLayerCurve* layerCurve = curves[0];
const RigWellLogCurveData* curveData = layerCurve->curveData();
// Find
std::vector<double> depths = curveData->depths( RiaDefines::DepthTypeEnum::TRUE_VERTICAL_DEPTH );
std::vector<double> layerValues = curveData->xValues();
size_t startIndex = 0;
for ( size_t i = 0; i < depths.size(); i++ )
{
if ( startIndex != i && ( layerValues[startIndex] != layerValues[i] || i == depths.size() - 1 ) )
{
layerBoundaryDepths.push_back( std::make_pair( depths[startIndex], depths[i] ) );
layerBoundaryIndexes.push_back( std::make_pair( startIndex, i ) );
startIndex = i;
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RimFractureModelPlot::computeValueAtDepth( const std::vector<double>& values,
std::vector<std::pair<double, double>>& layerBoundaryDepths,
double depth )
{
for ( size_t i = 0; i < layerBoundaryDepths.size(); i++ )
{
if ( layerBoundaryDepths[i].first <= depth && layerBoundaryDepths[i].second >= depth )
{
return values[i];
}
}
RiaLogging::error( QString( "Failed to compute value at depth: %1" ).arg( depth ) );
return std::numeric_limits<double>::infinity();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimFractureModelPlot::computeAverageByLayer( const std::vector<std::pair<size_t, size_t>>& layerBoundaryIndexes,
const std::vector<double>& inputVector,
std::vector<double>& result )
{
for ( auto boundaryIndex : layerBoundaryIndexes )
{
double sum = 0.0;
int nValues = 0;
for ( size_t i = boundaryIndex.first; i < boundaryIndex.second; i++ )
{
sum += inputVector[i];
nValues++;
}
result.push_back( sum / nValues );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellLogExtractionCurve* RimFractureModelPlot::findCurveByName( const QString& curveName ) const
{
std::vector<RimWellLogExtractionCurve*> curves;
descendantsIncludingThisOfType( curves );
for ( auto curve : curves )
{
// TODO: This will not work if the user has changed the name of the curve: do something smarter.
if ( curve->curveName() == curveName )
{
return curve;
}
}
return nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<double> RimFractureModelPlot::calculateTrueVerticalDepth() const
{
std::vector<std::pair<double, double>> layerBoundaryDepths;
std::vector<std::pair<size_t, size_t>> layerBoundaryIndexes;
calculateLayers( layerBoundaryDepths, layerBoundaryIndexes );
std::vector<double> tvdTopZone;
for ( auto p : layerBoundaryDepths )
{
double depthInFeet = RiaEclipseUnitTools::meterToFeet( p.first );
tvdTopZone.push_back( depthInFeet );
}
return tvdTopZone;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<double> RimFractureModelPlot::findCurveAndComputeLayeredAverage( const QString& curveName ) const
{
RimWellLogExtractionCurve* curve = findCurveByName( curveName );
if ( !curve )
{
RiaLogging::error( QString( "No curve named '%1' found" ).arg( curveName ) );
return std::vector<double>();
}
std::vector<std::pair<double, double>> layerBoundaryDepths;
std::vector<std::pair<size_t, size_t>> layerBoundaryIndexes;
calculateLayers( layerBoundaryDepths, layerBoundaryIndexes );
const RigWellLogCurveData* curveData = curve->curveData();
std::vector<double> values = curveData->xValues();
std::vector<double> result;
computeAverageByLayer( layerBoundaryIndexes, values, result );
return result;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<double> RimFractureModelPlot::calculatePorosity() const
{
return findCurveAndComputeLayeredAverage( "PORO" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<double> RimFractureModelPlot::calculateReservoirPressure() const
{
return findCurveAndComputeLayeredAverage( "PRESSURE" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<double> RimFractureModelPlot::calculateHorizontalPermeability() const
{
return findCurveAndComputeLayeredAverage( "PERMX" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<double> RimFractureModelPlot::calculateVerticalPermeability() const
{
return findCurveAndComputeLayeredAverage( "PERMZ" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<double> RimFractureModelPlot::calculateStress() const
{
std::vector<double> stress;
std::vector<double> stressGradients;
calculateStressWithGradients( stress, stressGradients );
return stress;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimFractureModelPlot::calculateStressWithGradients( std::vector<double>& stress,
std::vector<double>& stressGradients ) const
{
// Reference stress
const double verticalStressRef = m_fractureModel->verticalStress();
const double verticalStressGradientRef = m_fractureModel->verticalStressGradient();
const double stressDepthRef = m_fractureModel->stressDepth();
std::vector<std::pair<double, double>> layerBoundaryDepths;
std::vector<std::pair<size_t, size_t>> layerBoundaryIndexes;
calculateLayers( layerBoundaryDepths, layerBoundaryIndexes );
// Biot coefficient
RimWellLogExtractionCurve* biotCurve = findCurveByName( "Biot Coefficient" );
if ( !biotCurve )
{
RiaLogging::error( "Biot coefficient data not found." );
return false;
}
std::vector<double> biotData = biotCurve->curveData()->xValues();
// Biot coefficient
RimWellLogExtractionCurve* k0Curve = findCurveByName( "k0" );
if ( !k0Curve )
{
RiaLogging::error( "k0 data not found." );
return false;
}
std::vector<double> k0Data = k0Curve->curveData()->xValues();
// Pressure at the give time step
RimWellLogExtractionCurve* timeStepPressureCurve = findCurveByName( "PRESSURE" );
if ( !timeStepPressureCurve )
{
RiaLogging::error( "Pressure data for time step not found." );
return false;
}
std::vector<double> timeStepPressureData = timeStepPressureCurve->curveData()->xValues();
// Initial pressure
RimWellLogExtractionCurve* initialPressureCurve = findCurveByName( "INITIAL PRESSURE" );
if ( !initialPressureCurve )
{
RiaLogging::error( "Initial pressure data not found." );
return false;
}
std::vector<double> initialPressureData = initialPressureCurve->curveData()->xValues();
// Poissons ratio
RimWellLogExtractionCurve* poissonsRatioCurve = findCurveByName( "Poisson's Ratio" );
if ( !poissonsRatioCurve )
{
RiaLogging::error( "Poisson's ratio data not found." );
return false;
}
std::vector<double> poissonsRatioData = poissonsRatioCurve->curveData()->xValues();
std::vector<double> stressForGradients;
std::vector<double> pressureForGradients;
std::vector<double> depthForGradients;
// Calculate the stress
for ( size_t i = 0; i < layerBoundaryDepths.size(); i++ )
{
double depthTopOfZone = layerBoundaryDepths[i].first;
double depthBottomOfZone = layerBoundaryDepths[i].second;
// Data from curves at the top zone depth
double k0 = computeValueAtDepth( k0Data, layerBoundaryDepths, depthTopOfZone );
double biot = computeValueAtDepth( biotData, layerBoundaryDepths, depthTopOfZone );
double poissonsRatio = computeValueAtDepth( poissonsRatioData, layerBoundaryDepths, depthTopOfZone );
double initialPressure = computeValueAtDepth( initialPressureData, layerBoundaryDepths, depthTopOfZone );
double timeStepPressure = computeValueAtDepth( timeStepPressureData, layerBoundaryDepths, depthTopOfZone );
// Vertical stress
// Use difference between reference depth and depth of top of zone
double depthDiff = depthTopOfZone - stressDepthRef;
double Sv = verticalStressRef + verticalStressGradientRef * depthDiff;
double Sh_init = k0 * Sv + initialPressure * ( 1.0 - k0 );
double pressureDiff = timeStepPressure - initialPressure;
// Vertical stress diff assumed to be zero
double Sv_diff = 0.0;
double deltaHorizontalStress = poissonsRatio / ( 1.0 - poissonsRatio ) * ( Sv_diff - biot * pressureDiff ) +
( biot * pressureDiff );
double depletionStress = Sh_init + deltaHorizontalStress;
stress.push_back( RiaEclipseUnitTools::barToPsi( depletionStress ) );
// Cache some results for the gradients calculation
stressForGradients.push_back( Sv );
pressureForGradients.push_back( initialPressure );
depthForGradients.push_back( depthTopOfZone );
if ( i == layerBoundaryDepths.size() - 1 )
{
// Use the bottom of the last layer to compute gradient for last layer
double bottomInitialPressure =
computeValueAtDepth( initialPressureData, layerBoundaryDepths, depthBottomOfZone );
double bottomDepthDiff = depthBottomOfZone - stressDepthRef;
double bottomSv = verticalStressRef + verticalStressGradientRef * bottomDepthDiff;
stressForGradients.push_back( bottomSv );
pressureForGradients.push_back( bottomInitialPressure );
depthForGradients.push_back( depthBottomOfZone );
}
}
assert( stressForGradients.size() == layerBoundaryDepths.size() + 1 );
assert( pressureForGradients.size() == layerBoundaryDepths.size() + 1 );
assert( depthForGradients.size() == layerBoundaryDepths.size() + 1 );
// Second pass to calculate the stress gradients
for ( size_t i = 0; i < layerBoundaryDepths.size(); i++ )
{
double diffStress = stressForGradients[i + 1] - stressForGradients[i];
double diffPressure = pressureForGradients[i + 1] - pressureForGradients[i];
double diffDepth = depthForGradients[i + 1] - depthForGradients[i];
double k0 = computeValueAtDepth( k0Data, layerBoundaryDepths, depthForGradients[i] );
double gradient = ( diffStress * k0 + diffPressure * ( 1.0 - k0 ) ) / diffDepth;
stressGradients.push_back( RiaEclipseUnitTools::barPerMeterToPsiPerFeet( gradient ) );
}
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<double> RimFractureModelPlot::calculateStressGradient() const
{
std::vector<double> stress;
std::vector<double> stressGradients;
calculateStressWithGradients( stress, stressGradients );
return stressGradients;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<double> RimFractureModelPlot::calculateYoungsModulus() const
{
std::vector<double> valuesGPa = findCurveAndComputeLayeredAverage( "Young's Modulus" );
std::vector<double> valuesMMpsi;
for ( auto value : valuesGPa )
{
valuesMMpsi.push_back( value * 0.000145037737 );
}
return valuesMMpsi;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<double> RimFractureModelPlot::calculatePoissonsRatio() const
{
return findCurveAndComputeLayeredAverage( "Poisson's Ratio" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<double> RimFractureModelPlot::calculateKIc() const
{
return findCurveAndComputeLayeredAverage( "K-Ic" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<double> RimFractureModelPlot::calculateFluidLossCoefficient() const
{
return findCurveAndComputeLayeredAverage( "Fluid Loss Coefficient" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<double> RimFractureModelPlot::calculateSpurtLoss() const
{
return findCurveAndComputeLayeredAverage( "Spurt Loss" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<double> RimFractureModelPlot::calculateProppandEmbedment() const
{
return findCurveAndComputeLayeredAverage( "Proppant Embedment" );
}

View File

@ -22,6 +22,11 @@
#include "cafPdmField.h"
#include "cafPdmPtrField.h"
#include <vector>
class RimWellLogExtractionCurve;
class RimFractureModel;
class RimFractureModelPlot : public RimDepthTrackPlot
{
CAF_PDM_HEADER_INIT;
@ -29,13 +34,44 @@ class RimFractureModelPlot : public RimDepthTrackPlot
public:
RimFractureModelPlot();
void setFractureModel( RimFractureModel* fractureModel );
void getPorosityValues( std::vector<double>& values ) const;
std::vector<double> calculateTrueVerticalDepth() const;
std::vector<double> calculatePorosity() const;
std::vector<double> calculateVerticalPermeability() const;
std::vector<double> calculateHorizontalPermeability() const;
std::vector<double> calculateReservoirPressure() const;
std::vector<double> calculateStress() const;
std::vector<double> calculateStressGradient() const;
std::vector<double> calculateYoungsModulus() const;
std::vector<double> calculatePoissonsRatio() const;
std::vector<double> calculateKIc() const;
std::vector<double> calculateFluidLossCoefficient() const;
std::vector<double> calculateSpurtLoss() const;
std::vector<double> calculateProppandEmbedment() const;
protected:
std::vector<double> findCurveAndComputeLayeredAverage( const QString& curveName ) const;
void calculateLayers( std::vector<std::pair<double, double>>& layerBoundaryDepths,
std::vector<std::pair<size_t, size_t>>& layerBoundaryIndexes ) const;
RimWellLogExtractionCurve* findCurveByName( const QString& curveName ) const;
bool calculateStressWithGradients( std::vector<double>& stress, std::vector<double>& stressGradients ) const;
static double computeValueAtDepth( const std::vector<double>& values,
std::vector<std::pair<double, double>>& layerBoundaryDepths,
double depth );
static void computeAverageByLayer( const std::vector<std::pair<size_t, size_t>>& layerBoundaryIndexes,
const std::vector<double>& inputVector,
std::vector<double>& result );
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
void onLoadDataAndUpdate() override;
private:
void applyDataSource();
caf::PdmPtrField<RimFractureModel*> m_fractureModel;
};

View File

@ -0,0 +1,276 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2020- Equinor 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 "RimLayerCurve.h"
#include "RigEclipseCaseData.h"
#include "RigEclipseWellLogExtractor.h"
#include "RigElasticProperties.h"
#include "RigResultAccessorFactory.h"
#include "RigWellLogCurveData.h"
#include "RigWellPath.h"
#include "RimCase.h"
#include "RimColorLegend.h"
#include "RimColorLegendCollection.h"
#include "RimColorLegendItem.h"
#include "RimEclipseCase.h"
#include "RimEclipseResultDefinition.h"
#include "RimElasticProperties.h"
#include "RimFractureModel.h"
#include "RimFractureModelPlot.h"
#include "RimModeledWellPath.h"
#include "RimProject.h"
#include "RimTools.h"
#include "RimWellLogFile.h"
#include "RimWellLogPlot.h"
#include "RimWellLogTrack.h"
#include "RimWellPath.h"
#include "RimWellPathCollection.h"
#include "RimWellPlotTools.h"
#include "RiuQwtPlotCurve.h"
#include "RiuQwtPlotWidget.h"
#include "RiaApplication.h"
#include "RiaLogging.h"
#include "RiaPreferences.h"
#include "cafPdmUiTreeOrdering.h"
#include <QFileInfo>
#include <QMessageBox>
CAF_PDM_SOURCE_INIT( RimLayerCurve, "LayerCurve" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimLayerCurve::RimLayerCurve()
{
CAF_PDM_InitObject( "Fracture Model Curve", "", "", "" );
CAF_PDM_InitFieldNoDefault( &m_fractureModel, "FractureModel", "Fracture Model", "", "", "" );
m_fractureModel.uiCapability()->setUiTreeChildrenHidden( true );
m_fractureModel.uiCapability()->setUiHidden( true );
m_wellPath = nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimLayerCurve::~RimLayerCurve()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimLayerCurve::setFractureModel( RimFractureModel* fractureModel )
{
m_fractureModel = fractureModel;
m_wellPath = fractureModel->thicknessDirectionWellPath();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
// void RimLayerCurve::setEclipseResultCategory( RiaDefines::ResultCatType catType )
// {
// m_eclipseResultDefinition->setResultType( catType );
// }
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
// void RimLayerCurve::setPropertyType( PropertyType propertyType )
// {
// m_propertyType = propertyType;
// }
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimLayerCurve::performDataExtraction( bool* isUsingPseudoLength )
{
std::vector<double> values;
std::vector<double> measuredDepthValues;
std::vector<double> tvDepthValues;
double rkbDiff = 0.0;
RiaDefines::DepthUnitType depthUnit = RiaDefines::DepthUnitType::UNIT_METER;
QString xUnits = RiaWellLogUnitTools<double>::noUnitString();
*isUsingPseudoLength = false;
RimEclipseCase* eclipseCase = dynamic_cast<RimEclipseCase*>( m_case.value() );
if ( eclipseCase )
{
RigEclipseWellLogExtractor eclExtractor( eclipseCase->eclipseCaseData(),
m_fractureModel->thicknessDirectionWellPath()->wellPathGeometry(),
"fracture model" );
measuredDepthValues = eclExtractor.cellIntersectionMDs();
tvDepthValues = eclExtractor.cellIntersectionTVDs();
rkbDiff = eclExtractor.wellPathData()->rkbDiff();
// Extract formation data
cvf::ref<RigResultAccessor> formationResultAccessor = RigResultAccessorFactory::
createFromResultAddress( eclipseCase->eclipseCaseData(),
0,
RiaDefines::PorosityModelType::MATRIX_MODEL,
0,
RigEclipseResultAddress( RiaDefines::ResultCatType::FORMATION_NAMES,
RiaDefines::activeFormationNamesResultName() ) );
if ( !formationResultAccessor.notNull() )
{
RiaLogging::error( QString( "No formation result found." ) );
return;
}
CurveSamplingPointData curveData =
RimWellLogTrack::curveSamplingPointData( &eclExtractor, formationResultAccessor.p() );
std::vector<std::pair<double, double>> yValues;
std::vector<QString> formationNamesVector = RimWellLogTrack::formationNamesVector( eclipseCase );
std::vector<QString> formationNamesToPlot;
RimWellLogTrack::findRegionNamesToPlot( curveData,
formationNamesVector,
RiaDefines::DepthTypeEnum::TRUE_VERTICAL_DEPTH,
&formationNamesToPlot,
&yValues );
// Extract facies data
m_eclipseResultDefinition->setResultVariable( "OPERNUM_1" );
m_eclipseResultDefinition->setResultType( RiaDefines::ResultCatType::INPUT_PROPERTY );
m_eclipseResultDefinition->setEclipseCase( eclipseCase );
m_eclipseResultDefinition->loadResult();
cvf::ref<RigResultAccessor> faciesResultAccessor =
RigResultAccessorFactory::createFromResultDefinition( eclipseCase->eclipseCaseData(),
0,
m_timeStep,
m_eclipseResultDefinition );
if ( !faciesResultAccessor.notNull() )
{
RiaLogging::error( QString( "No facies result found." ) );
return;
}
std::vector<double> faciesValues;
eclExtractor.curveData( faciesResultAccessor.p(), &faciesValues );
assert( faciesValues.size() == formationNamesToPlot.size() );
values.resize( faciesValues.size() ); // formationNamesToPlot.size() );
int layerNo = 0;
QString previousFormationName = "";
double previousFacies = -1.0;
for ( size_t i = 0; i < faciesValues.size(); i++ )
{
QString formationName = findFormationNameForDepth( formationNamesToPlot, yValues, tvDepthValues[i] );
if ( previousFormationName != formationName || previousFacies != faciesValues[i] )
{
layerNo++;
}
values[i] = layerNo;
previousFormationName = formationName;
previousFacies = faciesValues[i];
}
RiaEclipseUnitTools::UnitSystem eclipseUnitsType = eclipseCase->eclipseCaseData()->unitsType();
if ( eclipseUnitsType == RiaEclipseUnitTools::UnitSystem::UNITS_FIELD )
{
// See https://github.com/OPM/ResInsight/issues/538
depthUnit = RiaDefines::DepthUnitType::UNIT_FEET;
}
}
bool performDataSmoothing = false;
if ( !values.empty() && !measuredDepthValues.empty() )
{
if ( tvDepthValues.empty() )
{
this->setValuesAndDepths( values,
measuredDepthValues,
RiaDefines::DepthTypeEnum::MEASURED_DEPTH,
0.0,
depthUnit,
!performDataSmoothing,
xUnits );
}
else
{
this->setValuesWithMdAndTVD( values,
measuredDepthValues,
tvDepthValues,
rkbDiff,
depthUnit,
!performDataSmoothing,
xUnits );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
// QString RimLayerCurve::findFaciesName( const RimColorLegend& colorLegend, double value )
// {
// for ( auto item : colorLegend.colorLegendItems() )
// {
// if ( item->categoryValue() == static_cast<int>( value ) ) return item->categoryName();
// }
// return "not found";
// }
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimLayerCurve::findFormationNameForDepth( const std::vector<QString>& formationNames,
const std::vector<std::pair<double, double>>& depthRanges,
double depth )
{
// assert(formationNames.size() == depthRanges.size());
for ( size_t i = 0; i < formationNames.size(); i++ )
{
double high = depthRanges[i].second;
double low = depthRanges[i].first;
if ( depth >= low && depth <= high )
{
return formationNames[i];
}
}
return "not found";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimLayerCurve::createCurveAutoName()
{
return "Layers";
}

View File

@ -0,0 +1,56 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2020- Equinor 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 "RimWellLogExtractionCurve.h"
#include "cafPdmField.h"
#include "cafPdmPtrField.h"
class RimFractureModel;
class RimColorLegend;
//==================================================================================================
///
//==================================================================================================
class RimLayerCurve : public RimWellLogExtractionCurve
{
CAF_PDM_HEADER_INIT;
public:
RimLayerCurve();
~RimLayerCurve() override;
void setFractureModel( RimFractureModel* fractureModel );
void setEclipseResultCategory( RiaDefines::ResultCatType catType );
protected:
QString createCurveAutoName() override;
void performDataExtraction( bool* isUsingPseudoLength ) override;
// static QString findFaciesName( const RimColorLegend& colorLegend, double value );
static QString findFormationNameForDepth( const std::vector<QString>& formationNames,
const std::vector<std::pair<double, double>>& depthRanges,
double depth );
caf::PdmPtrField<RimFractureModel*> m_fractureModel;
// caf::PdmField<caf::AppEnum<PropertyType>> m_propertyType;
};

View File

@ -94,6 +94,38 @@ const std::vector<double>& RigElasticProperties::proppantEmbedment() const
return m_proppantEmbedment;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const std::vector<double>& RigElasticProperties::biotCoefficient() const
{
return m_biotCoefficient;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const std::vector<double>& RigElasticProperties::k0() const
{
return m_k0;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const std::vector<double>& RigElasticProperties::fluidLossCoefficient() const
{
return m_fluidLossCoefficient;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const std::vector<double>& RigElasticProperties::spurtLoss() const
{
return m_spurtLoss;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@ -101,13 +133,21 @@ void RigElasticProperties::appendValues( double porosity,
double youngsModulus,
double poissonsRatio,
double K_Ic,
double proppantEmbedment )
double proppantEmbedment,
double biotCoefficient,
double k0,
double fluidLossCoefficient,
double spurtLoss )
{
m_porosity.push_back( porosity );
m_youngsModulus.push_back( youngsModulus );
m_poissonsRatio.push_back( poissonsRatio );
m_K_Ic.push_back( K_Ic );
m_proppantEmbedment.push_back( proppantEmbedment );
m_biotCoefficient.push_back( biotCoefficient );
m_k0.push_back( k0 );
m_fluidLossCoefficient.push_back( fluidLossCoefficient );
m_spurtLoss.push_back( spurtLoss );
}
//--------------------------------------------------------------------------------------------------
@ -141,3 +181,35 @@ double RigElasticProperties::getProppantEmbedment( double porosity ) const
{
return RiaInterpolationTools::linear( m_porosity, m_proppantEmbedment, porosity );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RigElasticProperties::getBiotCoefficient( double porosity ) const
{
return RiaInterpolationTools::linear( m_porosity, m_biotCoefficient, porosity );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RigElasticProperties::getK0( double porosity ) const
{
return RiaInterpolationTools::linear( m_porosity, m_k0, porosity );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RigElasticProperties::getFluidLossCoefficient( double porosity ) const
{
return RiaInterpolationTools::linear( m_porosity, m_fluidLossCoefficient, porosity );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RigElasticProperties::getSpurtLoss( double porosity ) const
{
return RiaInterpolationTools::linear( m_porosity, m_spurtLoss, porosity );
}

View File

@ -33,17 +33,33 @@ public:
const QString& formationName() const;
const QString& faciesName() const;
void appendValues( double porosity, double youngsModulus, double poissonsRatio, double m_K_Ic, double proppantEmbedment );
void appendValues( double porosity,
double youngsModulus,
double poissonsRatio,
double m_K_Ic,
double proppantEmbedment,
double biotCoefficient,
double k0,
double fluidLossCoefficient,
double spurtLoss );
double getYoungsModulus( double porosity ) const;
double getPoissonsRatio( double porosity ) const;
double getK_Ic( double porosity ) const;
double getProppantEmbedment( double porosity ) const;
double getBiotCoefficient( double porosity ) const;
double getK0( double porosity ) const;
double getFluidLossCoefficient( double porosity ) const;
double getSpurtLoss( double porosity ) const;
const std::vector<double>& porosity() const;
const std::vector<double>& youngsModulus() const;
const std::vector<double>& poissonsRatio() const;
const std::vector<double>& K_Ic() const;
const std::vector<double>& proppantEmbedment() const;
const std::vector<double>& biotCoefficient() const;
const std::vector<double>& k0() const;
const std::vector<double>& fluidLossCoefficient() const;
const std::vector<double>& spurtLoss() const;
private:
QString m_fieldName;
@ -55,4 +71,8 @@ private:
std::vector<double> m_poissonsRatio;
std::vector<double> m_K_Ic;
std::vector<double> m_proppantEmbedment;
std::vector<double> m_biotCoefficient;
std::vector<double> m_k0;
std::vector<double> m_fluidLossCoefficient;
std::vector<double> m_spurtLoss;
};

View File

@ -37,8 +37,8 @@ TEST( RifElasticPropertiesReaderTest, ReadCorrectInputFile )
{
QTextStream out( &file );
out << "Norne,Not,Sand,0.00,25,0.25,2000,0.2\n"
<< "Norne,Not,Sand,0.10,19,0.27,2099,0.3\n";
out << "Norne,Not,Sand,0.00,25,0.25,2000,0.2,0.3,0.4,0.5,0.6\n"
<< "Norne,Not,Sand,0.10,19,0.27,2099,0.3,0.4,0.5,0.2,0.5\n";
}
QStringList filePaths;
@ -119,7 +119,7 @@ TEST( RifElasticPropertiesReaderTest, ReadShortLinesFileThrows )
{
QTextStream out( &file );
out << "Norne,Not,Sand,0.00,25,0.25,2000,0.2\n"
out << "Norne,Not,Sand,0.00,25,0.25,2000,0.2,0.3,0.4,0.5,0.6\n"
<< "Norne,Not,Sand,0.10,19,0.27\n";
}
@ -140,8 +140,8 @@ TEST( RifElasticPropertiesReaderTest, ReadEmptyFieldNameThrows )
{
QTextStream out( &file );
out << "Norne,Not,Sand,0.00,25,0.25,2000,0.2\n"
<< ",Not,Sand,0.10,19,0.27,2099,0.3\n";
out << "Norne,Not,Sand,0.00,25,0.25,2000,0.2,0.3,0.4,0.5,0.6\n"
<< ",Not,Sand,0.10,19,0.27,2099,0.3,0.3,0.4,0.5,0.6\n";
}
QStringList filePaths;
@ -161,8 +161,8 @@ TEST( RifElasticPropertiesReaderTest, ReadInvalidMeasureDepthThrows )
{
QTextStream out( &file );
out << "Norne,Not,Sand,0.00,25,0.25,2000,0.2\n"
<< "Norne,Not,Sand, not a number,23.4,0.27,2099,0.3\n";
out << "Norne,Not,Sand,0.00,25,0.25,2000,0.2,0.3,0.4,0.5,0.6\n"
<< "Norne,Not,Sand, not a number,23.4,0.27,2099,0.3,0.3,0.4,0.5,0.6\n";
}
QStringList filePaths;
@ -191,14 +191,14 @@ TEST( RifElasticPropertiesReaderTest, CommentsAndEmptyLinesAreIgnored )
out << "\t\n";
out << " \n";
// Then some data
out << "Norne,Not,Sand,0.00,25,0.25,2000,0.2\n";
out << "Norne,Not,Sand,0.00,25,0.25,2000,0.2,0.3,0.4,0.5,0.6\n";
// Comment in-between data should be ignored
out << "# One more comment in-between the data\n";
out << "Norne,Not,Silt,0.00,25,0.25,2000,0.2\n";
out << "Norne,Not,Silt,0.00,25,0.25,2000,0.2,0.3,0.4,0.5,0.6\n";
// Empty line in-between data should be ignored
out << "\n";
// Data with comment sign inside it is not ignored
out << "Norne,Not,Shale,0.00,25,0.25,2000,0.2\n";
out << "Norne,Not,Shale,0.00,25,0.25,2000,0.2,0.3,0.4,0.5,0.6\n";
// Trailing empty lines should be ignored
out << "\n\n\n";
}