#3089 Non-Darcy longitudinal : Add support for longitudinal fractures

- move non-darcy data from template to fracture, as this data is now depending of fracutre
- compute weighted average of stimplan width and conductivity based on fracture perforation length
- use referringPtrFields() to find fractures using this fracture template
This commit is contained in:
Magne Sjaastad 2018-08-15 09:44:27 +02:00
parent 4c75e2514d
commit 2ddc3d77df
11 changed files with 391 additions and 212 deletions

View File

@ -67,10 +67,12 @@ std::vector<RigCompletionData> RicExportFractureCompletionsImpl::generateCompdat
if (wellPath->fractureCollection()->isChecked())
{
for (const auto& frac : wellPath->fractureCollection()->fractures)
for (auto& frac : wellPath->fractureCollection()->fractures)
{
if (frac->isChecked())
{
frac->ensureValidNonDarcyProperties();
fracturesAlongWellPath.push_back(frac);
}
}
@ -174,11 +176,8 @@ std::vector<RigCompletionData>
//////
// Calculate Matrix To Fracture Trans
RigEclipseToStimPlanCalculator eclToFractureCalc(caseToApply,
fracture->transformMatrix(),
fracture->fractureTemplate()->skinFactor(),
cDarcyInCorrectUnit,
*fractureGrid);
RigEclipseToStimPlanCalculator eclToFractureCalc(
caseToApply, fracture->transformMatrix(), fracTemplate->skinFactor(), cDarcyInCorrectUnit, *fractureGrid);
eclToFractureCalc.appendDataToTransmissibilityCondenser(fracture, useFiniteConductivityInFracture, &transCondenser);
@ -248,32 +247,28 @@ std::vector<RigCompletionData>
////
// If fracture has orientation Azimuth or Transverse, assume only radial inflow
if (fracture->fractureTemplate()->orientationType() == RimFractureTemplate::AZIMUTH ||
fracture->fractureTemplate()->orientationType() == RimFractureTemplate::TRANSVERSE_WELL_PATH)
if (fracTemplate->orientationType() == RimFractureTemplate::AZIMUTH ||
fracTemplate->orientationType() == RimFractureTemplate::TRANSVERSE_WELL_PATH)
{
const RigFractureGrid* fracGrid = fracture->fractureTemplate()->fractureGrid();
if (fracGrid)
{
std::pair<size_t, size_t> wellCellIJ = fracGrid->fractureCellAtWellCenter();
size_t wellCellIndex = fracGrid->getGlobalIndexFromIJ(wellCellIJ.first, wellCellIJ.second);
std::pair<size_t, size_t> wellCellIJ = fractureGrid->fractureCellAtWellCenter();
size_t wellCellIndex = fractureGrid->getGlobalIndexFromIJ(wellCellIJ.first, wellCellIJ.second);
const RigFractureCell& wellCell = fractureGrid->cellFromIndex(wellCellIndex);
const RigFractureCell& wellCell = fractureGrid->cellFromIndex(wellCellIndex);
double radialTrans =
RigFractureTransmissibilityEquations::fractureCellToWellRadialTrans(wellCell.getConductivityValue(),
wellCell.cellSizeX(),
wellCell.cellSizeZ(),
fracture->wellRadius(),
fracTemplate->skinFactor(),
cDarcyInCorrectUnit);
double radialTrans =
RigFractureTransmissibilityEquations::fractureCellToWellRadialTrans(wellCell.getConductivityValue(),
wellCell.cellSizeX(),
wellCell.cellSizeZ(),
fracture->wellRadius(),
fracTemplate->skinFactor(),
cDarcyInCorrectUnit);
transCondenser.addNeighborTransmissibility(
{true, RigTransmissibilityCondenser::CellAddress::WELL, 1},
{false, RigTransmissibilityCondenser::CellAddress::STIMPLAN, wellCellIndex},
radialTrans);
}
transCondenser.addNeighborTransmissibility(
{true, RigTransmissibilityCondenser::CellAddress::WELL, 1},
{false, RigTransmissibilityCondenser::CellAddress::STIMPLAN, wellCellIndex},
radialTrans);
}
else if (fracture->fractureTemplate()->orientationType() == RimFractureTemplate::ALONG_WELL_PATH)
else if (fracTemplate->orientationType() == RimFractureTemplate::ALONG_WELL_PATH)
{
////
// If fracture has orientation along well, linear inflow along well and radial flow at endpoints
@ -332,7 +327,7 @@ std::vector<RigCompletionData>
fracture->fractureMD());
double diameter = 2.0 * fracture->wellRadius();
compDat.setFromFracture(trans, fracture->fractureTemplate()->skinFactor(), diameter);
compDat.setFromFracture(trans, fracTemplate->skinFactor(), diameter);
compDat.addMetadata(fracture->name(), QString::number(trans));
allCompletionsForOneFracture.push_back(compDat);
}
@ -341,10 +336,10 @@ std::vector<RigCompletionData>
/////
// Compute Non-Dracy Flow parameters
if (fracture->fractureTemplate()->isNonDarcyFlowEnabled())
if (fracTemplate->isNonDarcyFlowEnabled())
{
double dFactorForFracture = fracture->fractureTemplate()->dFactor();
double khForFracture = fracture->fractureTemplate()->kh();
double dFactorForFracture = fracture->nonDarcyProperties().dFactor;
double khForFracture = fracture->nonDarcyProperties().conductivity;
double sumOfTransmissibilitiesInFracture = 0.0;
for (const auto& c : allCompletionsForOneFracture)
@ -367,11 +362,7 @@ std::vector<RigCompletionData>
if (fractureDataReportItems)
{
QString fractureTemplateName;
if (fracture->fractureTemplate())
{
fractureTemplateName = fracture->fractureTemplate()->name();
}
QString fractureTemplateName = fracTemplate->name();
RicWellPathFractureReportItem reportItem(wellPathName, fracture->name(), fractureTemplateName);
double transmissibility = 0.0;

View File

@ -290,8 +290,6 @@ QString RicWellPathFractureTextReportFeatureImpl::createStimPlanFractureText(
RifEclipseOutputTableColumn(" "),
floatNumberColumn("WDiam"),
floatNumberColumn("Skin"),
floatNumberColumn("Dfac"),
floatNumberColumn("LPerf"),
};
formatter.header(header);
@ -302,8 +300,6 @@ QString RicWellPathFractureTextReportFeatureImpl::createStimPlanFractureText(
formatter.add("Orientation"); // Orientation
formatter.add("[m]"); // WDiam
formatter.add("[] "); // Skin
formatter.add("[...]"); // DFac
formatter.add("[m]"); // LPerf
formatter.rowCompleted();
}
@ -316,17 +312,6 @@ QString RicWellPathFractureTextReportFeatureImpl::createStimPlanFractureText(
formatter.add(stimPlanTemplate->wellDiameter());
formatter.add(stimPlanTemplate->skinFactor());
if (stimPlanTemplate->isNonDarcyFlowEnabled())
{
formatter.add(stimPlanTemplate->dFactor());
formatter.add(stimPlanTemplate->perforationLength());
}
else
{
formatter.add("NA");
formatter.add("NA");
}
formatter.rowCompleted();
}
@ -358,8 +343,6 @@ QString RicWellPathFractureTextReportFeatureImpl::createEllipseFractureText(
floatNumberColumn("Wf"),
floatNumberColumn("WDiam"),
floatNumberColumn("Skin"),
floatNumberColumn("Dfac"),
floatNumberColumn("LPerf"),
};
formatter.header(header);
@ -374,8 +357,6 @@ QString RicWellPathFractureTextReportFeatureImpl::createEllipseFractureText(
formatter.add("[m]"); // Wf
formatter.add("[m]"); // WDiam
formatter.add("[] "); // Skin
formatter.add("[...]"); // DFac
formatter.add("[m]"); // LPerf
formatter.rowCompleted();
}
@ -394,17 +375,6 @@ QString RicWellPathFractureTextReportFeatureImpl::createEllipseFractureText(
formatter.add(ellipseTemplate->wellDiameter());
formatter.add(ellipseTemplate->skinFactor());
if (ellipseTemplate->isNonDarcyFlowEnabled())
{
formatter.add(ellipseTemplate->dFactor());
formatter.add(ellipseTemplate->perforationLength());
}
else
{
formatter.add("NA");
formatter.add("NA");
}
formatter.rowCompleted();
}

View File

@ -231,7 +231,7 @@ void RimEllipseFractureTemplate::assignConductivityToCellsInsideEllipse()
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
FractureWidthAndConductivity RimEllipseFractureTemplate::widthAndConductivityAtWellPathIntersection() const
FractureWidthAndConductivity RimEllipseFractureTemplate::widthAndConductivityAtWellPathIntersection(const RimFracture* fractureInstance) const
{
FractureWidthAndConductivity values;
values.m_width = m_width;

View File

@ -76,7 +76,7 @@ private:
void assignConductivityToCellsInsideEllipse();
std::vector<cvf::Vec3f> fractureBorderPolygon() const;
FractureWidthAndConductivity widthAndConductivityAtWellPathIntersection() const override;
FractureWidthAndConductivity widthAndConductivityAtWellPathIntersection(const RimFracture* fractureInstance) const override;
private:
cvf::ref<RigFractureGrid> m_fractureGrid;

View File

@ -213,6 +213,8 @@ void RimFracture::fieldChangedByUi(const caf::PdmFieldHandle* changedField, cons
changedField == this->objectToggleField() || changedField == &m_dip || changedField == &m_tilt ||
changedField == &m_perforationLength)
{
clearCachedNonDarcyProperties();
RimEclipseView* rimView = nullptr;
this->firstAncestorOrThisOfType(rimView);
if (rimView)
@ -243,6 +245,46 @@ cvf::Vec3d RimFracture::fracturePosition() const
return m_anchorPosition;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const NonDarcyData& RimFracture::nonDarcyProperties() const
{
CVF_ASSERT(!m_cachedFractureProperties.isDirty());
return m_cachedFractureProperties;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimFracture::ensureValidNonDarcyProperties()
{
if (m_cachedFractureProperties.isDirty())
{
NonDarcyData props;
if (m_fractureTemplate)
{
props.width = m_fractureTemplate->computeFractureWidth(this);
props.conductivity = m_fractureTemplate->computeKh(this);
props.dFactor = m_fractureTemplate->computeDFactor(this);
props.effectivePermeability = m_fractureTemplate->computeEffectivePermeability(this);
props.isDataDirty = false;
}
m_cachedFractureProperties = props;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimFracture::clearCachedNonDarcyProperties()
{
m_cachedFractureProperties = NonDarcyData();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@ -644,6 +686,8 @@ void RimFracture::setFractureTemplate(RimFractureTemplate* fractureTemplate)
}
this->m_wellDiameter = fractureTemplate->wellDiameter();
this->m_perforationLength = fractureTemplate->perforationLength();
clearCachedNonDarcyProperties();
}
//--------------------------------------------------------------------------------------------------

View File

@ -41,6 +41,29 @@ class RimFractureTemplate;
class RigFracturedEclipseCellExportData;
class RigMainGrid;
class NonDarcyData
{
public:
NonDarcyData()
: width(std::numeric_limits<double>::infinity())
, conductivity(std::numeric_limits<double>::infinity())
, effectivePermeability(std::numeric_limits<double>::infinity())
, dFactor(std::numeric_limits<double>::infinity())
, isDataDirty(true)
{
}
bool isDirty() const
{
return isDataDirty;
}
double width;
double conductivity;
double effectivePermeability;
double dFactor;
bool isDataDirty;
};
//==================================================================================================
///
@ -91,6 +114,12 @@ public:
virtual void loadDataAndUpdate() = 0;
virtual std::vector<cvf::Vec3d> perforationLengthCenterLineCoords() const = 0;
// Fracture properties
const NonDarcyData& nonDarcyProperties() const;
void ensureValidNonDarcyProperties();
void clearCachedNonDarcyProperties();
friend class RimFractureTemplate;
@ -130,4 +159,6 @@ private:
caf::PdmField<cvf::Vec3d> m_anchorPosition;
cvf::ref<RivWellFracturePartMgr> m_fracturePartMgr;
NonDarcyData m_cachedFractureProperties;
};

View File

@ -147,11 +147,13 @@ RimFractureTemplate::RimFractureTemplate()
CAF_PDM_InitField(&m_relativeGasDensity, "RelativeGasDensity", 0.8, "<html>Relative Gas Density (&gamma;)</html>", "", "Relative density of gas at surface conditions with respect to air at STP", "");
CAF_PDM_InitField(&m_gasViscosity, "GasViscosity", 0.02, "<html>Gas Viscosity (&mu;)</html> [cP]", "", "Gas viscosity at bottom hole pressure", "");
/*
CAF_PDM_InitFieldNoDefault(&m_dFactorDisplayField, "dFactorDisplayField", "D Factor", "", "", "");
m_dFactorDisplayField.registerGetMethod(this, &RimFractureTemplate::dFactor);
m_dFactorDisplayField.uiCapability()->setUiEditorTypeName(caf::PdmUiDoubleValueEditor::uiEditorTypeName());
m_dFactorDisplayField.uiCapability()->setUiReadOnly(true);
m_dFactorDisplayField.xmlCapability()->disableIO();
*/
CAF_PDM_InitFieldNoDefault(&m_dFactorSummaryText, "dFactorSummaryText", "D Factor Summary", "", "", "");
m_dFactorSummaryText.registerGetMethod(this, &RimFractureTemplate::dFactorSummary);
@ -244,35 +246,22 @@ void RimFractureTemplate::fieldChangedByUi(const caf::PdmFieldHandle* changedFie
bool createDisplayModelAndRedraw = false;
if (changedField == &m_azimuthAngle || changedField == &m_orientationType)
{
// Changes to one of these parameters should change all fractures with this fracture template attached.
RimProject* proj;
this->firstAncestorOrThisOfType(proj);
if (proj)
for (RimFracture* fracture : fracturesUsingThisTemplate())
{
// Regenerate geometry
std::vector<RimFracture*> fractures;
proj->descendantsIncludingThisOfType(fractures);
for (RimFracture* fracture : fractures)
if (changedField == &m_azimuthAngle && (fabs(oldValue.toDouble() - fracture->m_azimuth()) < 1e-5))
{
if (fracture->fractureTemplate() == this)
{
if (changedField == &m_azimuthAngle && (fabs(oldValue.toDouble() - fracture->m_azimuth()) < 1e-5))
{
fracture->m_azimuth = m_azimuthAngle;
}
fracture->m_azimuth = m_azimuthAngle;
}
if (changedField == &m_orientationType)
{
if (newValue == AZIMUTH)
{
fracture->m_azimuth = m_azimuthAngle;
}
else
{
fracture->updateAzimuthBasedOnWellAzimuthAngle();
}
}
if (changedField == &m_orientationType)
{
if (newValue == AZIMUTH)
{
fracture->m_azimuth = m_azimuthAngle;
}
else
{
fracture->updateAzimuthBasedOnWellAzimuthAngle();
}
}
@ -282,33 +271,29 @@ void RimFractureTemplate::fieldChangedByUi(const caf::PdmFieldHandle* changedFie
if (changedField == &m_perforationLength || changedField == &m_perforationEfficiency || changedField == &m_wellDiameter)
{
RimProject* proj;
this->firstAncestorOrThisOfType(proj);
if (!proj) return;
std::vector<RimFracture*> fractures;
proj->descendantsIncludingThisOfType(fractures);
for (RimFracture* fracture : fractures)
for (RimFracture* fracture : fracturesUsingThisTemplate())
{
if (fracture->fractureTemplate() == this)
if (changedField == &m_perforationLength && (fabs(oldValue.toDouble() - fracture->m_perforationLength()) < 1e-5))
{
if (changedField == &m_perforationLength && (fabs(oldValue.toDouble() - fracture->m_perforationLength()) < 1e-5))
{
fracture->m_perforationLength = m_perforationLength;
}
if (changedField == &m_perforationEfficiency &&
(fabs(oldValue.toDouble() - fracture->m_perforationEfficiency()) < 1e-5))
{
fracture->m_perforationEfficiency = m_perforationEfficiency;
}
if (changedField == &m_wellDiameter && (fabs(oldValue.toDouble() - fracture->m_wellDiameter()) < 1e-5))
{
fracture->m_wellDiameter = m_wellDiameter;
}
fracture->m_perforationLength = m_perforationLength;
}
if (changedField == &m_perforationEfficiency &&
(fabs(oldValue.toDouble() - fracture->m_perforationEfficiency()) < 1e-5))
{
fracture->m_perforationEfficiency = m_perforationEfficiency;
}
if (changedField == &m_wellDiameter && (fabs(oldValue.toDouble() - fracture->m_wellDiameter()) < 1e-5))
{
fracture->m_wellDiameter = m_wellDiameter;
}
}
}
for (RimFracture* fracture : fracturesUsingThisTemplate())
{
fracture->clearCachedNonDarcyProperties();
}
if (changedField == &m_perforationLength)
{
createDisplayModelAndRedraw = true;
@ -370,7 +355,7 @@ void RimFractureTemplate::defineUiOrdering(QString uiConfigName, caf::PdmUiOrder
nonDarcyFlowGroup->add(&m_relativeGasDensity);
nonDarcyFlowGroup->add(&m_gasViscosity);
nonDarcyFlowGroup->add(&m_dFactorDisplayField);
// nonDarcyFlowGroup->add(&m_dFactorDisplayField);
{
auto group = nonDarcyFlowGroup->addNewGroup("D Factor Details");
@ -434,16 +419,7 @@ QList<caf::PdmOptionItemInfo> RimFractureTemplate::calculateValueOptions(const c
if (fieldNeedingOptions == &m_fractureWidthType)
{
options.push_back(caf::PdmOptionItemInfo(caf::AppEnum<WidthEnum>::uiText(USER_DEFINED_WIDTH), USER_DEFINED_WIDTH));
auto widthAndCond = widthAndConductivityAtWellPathIntersection();
if (widthAndCond.isWidthAndPermeabilityDefined())
{
options.push_back(caf::PdmOptionItemInfo(caf::AppEnum<WidthEnum>::uiText(WIDTH_FROM_FRACTURE), WIDTH_FROM_FRACTURE));
}
else
{
m_fractureWidthType = USER_DEFINED_WIDTH;
}
options.push_back(caf::PdmOptionItemInfo(caf::AppEnum<WidthEnum>::uiText(WIDTH_FROM_FRACTURE), WIDTH_FROM_FRACTURE));
}
return options;
@ -499,20 +475,12 @@ void RimFractureTemplate::prepareFieldsForUiDisplay()
// Non Darcy Flow
auto values = widthAndConductivityAtWellPathIntersection();
if (!values.isWidthAndPermeabilityDefined())
{
m_fractureWidthType = RimFractureTemplate::USER_DEFINED_WIDTH;
}
if (m_fractureWidthType == RimFractureTemplate::USER_DEFINED_WIDTH)
{
m_fractureWidth.uiCapability()->setUiReadOnly(false);
}
else
{
m_fractureWidth = values.m_width;
m_fractureWidth.uiCapability()->setUiReadOnly(true);
}
@ -528,6 +496,11 @@ void RimFractureTemplate::prepareFieldsForUiDisplay()
}
}
QString indentedText(const QString& text)
{
return QString("<pre> %1</pre>").arg(text);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@ -535,37 +508,39 @@ QString RimFractureTemplate::dFactorSummary() const
{
QString text;
auto val = dFactor();
text += QString("D-factor : %1").arg(val);
auto fractures = fracturesUsingThisTemplate();
for (auto f : fractures)
{
f->ensureValidNonDarcyProperties();
text += "<br>";
text += "<br>";
auto alpha = RiaDefines::nonDarcyFlowAlpha(m_fractureTemplateUnit());
text += QString("&alpha; : %1").arg(alpha);
text += QString("Fracture name : %1").arg(f->name());
text += "<br>";
auto beta = m_inertialCoefficient;
text += QString("&beta; : %1").arg(beta);
auto val = f->nonDarcyProperties().dFactor;
text += indentedText(QString("D-factor : %1").arg(val));
text += "<br>";
double effPerm = effectivePermeability();
text += QString("Ke : %1").arg(effPerm);
auto alpha = RiaDefines::nonDarcyFlowAlpha(m_fractureTemplateUnit());
text += indentedText(QString("&alpha; : %1").arg(alpha));
text += "<br>";
double gamma = m_relativeGasDensity;
text += QString("&gamma; : %1").arg(gamma);
auto beta = m_inertialCoefficient;
text += indentedText(QString("&beta; : %1").arg(beta));
text += "<br>";
auto h = fractureWidth();
text += QString("h : %1").arg(h);
double effPerm = f->nonDarcyProperties().effectivePermeability;
text += indentedText(QString("Ke : %1").arg(effPerm));
text += "<br>";
auto wellRadius = m_wellDiameter / 2.0;
text += QString("rw : %1").arg(wellRadius);
double gamma = m_relativeGasDensity;
text += indentedText(QString("&gamma; : %1").arg(gamma));
text += "<br>";
auto mu = m_gasViscosity;
text += QString("&mu; : %1").arg(mu);
auto h = f->nonDarcyProperties().width;
text += indentedText(QString("h : %1").arg(h));
auto wellRadius = m_wellDiameter / 2.0;
text += indentedText(QString("rw : %1").arg(wellRadius));
auto mu = m_gasViscosity;
text += indentedText(QString("&mu; : %1").arg(mu));
text += "<br>";
}
return text;
}
@ -573,7 +548,7 @@ QString RimFractureTemplate::dFactorSummary() const
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RimFractureTemplate::effectivePermeability() const
double RimFractureTemplate::computeEffectivePermeability(const RimFracture* fractureInstance) const
{
if (m_permeabilityType() == RimFractureTemplate::USER_DEFINED_PERMEABILITY)
{
@ -582,7 +557,7 @@ double RimFractureTemplate::effectivePermeability() const
else
{
double fracPermeability = 0.0;
auto values = widthAndConductivityAtWellPathIntersection();
auto values = widthAndConductivityAtWellPathIntersection(fractureInstance);
if (values.isWidthAndPermeabilityDefined())
{
fracPermeability = values.m_permeability;
@ -590,7 +565,7 @@ double RimFractureTemplate::effectivePermeability() const
else
{
auto conductivity = values.m_conductivity;
auto width = fractureWidth();
auto width = computeFractureWidth(fractureInstance);
if (fabs(width) < 1e-10) return HUGE_VAL;
@ -604,7 +579,7 @@ double RimFractureTemplate::effectivePermeability() const
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RimFractureTemplate::dFactor() const
double RimFractureTemplate::computeDFactor(const RimFracture* fractureInstance) const
{
double d;
@ -614,14 +589,26 @@ double RimFractureTemplate::dFactor() const
}
else
{
double radius = 0.0;
if (m_orientationType == ALONG_WELL_PATH && fractureInstance)
{
auto perforationLength = fractureInstance->perforationLength();
radius = perforationLength / cvf::PI_D;
}
else
{
radius = m_wellDiameter / 2.0;
}
auto alpha = RiaDefines::nonDarcyFlowAlpha(m_fractureTemplateUnit());
auto beta = m_inertialCoefficient;
auto effPerm = effectivePermeability();
auto effPerm = computeEffectivePermeability(fractureInstance);
auto gamma = m_relativeGasDensity;
auto radius = m_wellDiameter / 2.0;
auto mu = m_gasViscosity;
auto h = fractureWidth();
auto mu = m_gasViscosity;
auto h = computeFractureWidth(fractureInstance);
double numerator = alpha * beta * effPerm * gamma;
double denumerator = h * radius * mu;
@ -629,6 +616,14 @@ double RimFractureTemplate::dFactor() const
if (denumerator < 1e-10) return HUGE_VAL;
d = numerator / denumerator;
if (m_orientationType == ALONG_WELL_PATH)
{
// Correction for linear inflow into the well
// Dlinear = cgeometric * Dradial
// Dlinear = 1.2 * Dradial
d *= 1.2;
}
}
return d * m_dFactorScaleFactor;
@ -637,19 +632,19 @@ double RimFractureTemplate::dFactor() const
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RimFractureTemplate::kh() const
double RimFractureTemplate::computeKh(const RimFracture* fractureInstance) const
{
// kh = permeability * h
// conductivity = permeability * h
auto values = widthAndConductivityAtWellPathIntersection();
auto values = widthAndConductivityAtWellPathIntersection(fractureInstance);
if (values.isConductivityDefined())
{
// If conductivity is found in stim plan file, use this directly
return values.m_conductivity;
}
return effectivePermeability() * fractureWidth();
return computeEffectivePermeability(fractureInstance) * computeFractureWidth(fractureInstance);
}
//--------------------------------------------------------------------------------------------------
@ -679,12 +674,8 @@ void RimFractureTemplate::disconnectAllFracturesAndRedrawViews() const
// The unit has changed. Disconnect all fractures referencing this fracture template to avoid mix of units between fracture
// and template
std::vector<caf::PdmObjectHandle*> referringObjects;
this->objectsWithReferringPtrFields(referringObjects);
for (auto objHandle : referringObjects)
for (auto fracture : fracturesUsingThisTemplate())
{
RimFracture* fracture = dynamic_cast<RimFracture*>(objHandle);
if (fracture)
{
fracture->setFractureTemplate(nullptr);
@ -716,6 +707,11 @@ void RimFractureTemplate::setScaleFactors(double widthScale, double heightScale,
m_heightScaleFactor = heightScale;
m_dFactorScaleFactor = dFactorScale;
m_conductivityScaleFactor = conductivityScale;
for (RimFracture* fracture : fracturesUsingThisTemplate())
{
fracture->clearCachedNonDarcyProperties();
}
}
//--------------------------------------------------------------------------------------------------
@ -753,11 +749,11 @@ void RimFractureTemplate::setContainmentBaseKLayer(int baseKLayer)
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RimFractureTemplate::fractureWidth() const
double RimFractureTemplate::computeFractureWidth(const RimFracture* fractureInstance) const
{
if (m_fractureWidthType == RimFractureTemplate::WIDTH_FROM_FRACTURE)
{
auto values = widthAndConductivityAtWellPathIntersection();
auto values = widthAndConductivityAtWellPathIntersection(fractureInstance);
return values.m_width;
}
@ -765,6 +761,27 @@ double RimFractureTemplate::fractureWidth() const
return m_fractureWidth;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimFracture*> RimFractureTemplate::fracturesUsingThisTemplate() const
{
std::vector<RimFracture*> fractures;
std::vector<caf::PdmObjectHandle*> objects;
this->objectsWithReferringPtrFields(objects);
for (auto object : objects)
{
auto f = dynamic_cast<RimFracture*>(object);
if (f)
{
fractures.push_back(f);
}
}
return fractures;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------

View File

@ -38,6 +38,7 @@ class RigFractureGrid;
class RimFractureContainment;
class MinMaxAccumulator;
class PosNegAccumulator;
class RimFracture;
class FractureWidthAndConductivity
{
@ -146,8 +147,6 @@ public:
void setDefaultWellDiameterFromUnit();
bool isNonDarcyFlowEnabled() const;
double dFactor() const;
double kh() const;
virtual void convertToUnitSystem(RiaEclipseUnitTools::UnitSystem neededUnit);
@ -162,6 +161,11 @@ public:
void setContainmentTopKLayer(int topKLayer);
void setContainmentBaseKLayer(int baseKLayer);
double computeDFactor(const RimFracture* fractureInstance) const;
double computeKh(const RimFracture* fractureInstance) const;
double computeEffectivePermeability(const RimFracture* fractureInstance) const;
double computeFractureWidth(const RimFracture* fractureInstance) const;
protected:
virtual caf::PdmFieldHandle* userDescriptionField() override;
virtual void fieldChangedByUi(const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue) override;
@ -169,14 +173,13 @@ protected:
virtual void defineEditorAttribute(const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute) override;
virtual QList<caf::PdmOptionItemInfo> calculateValueOptions(const caf::PdmFieldHandle* fieldNeedingOptions, bool* useOptionsOnly) override;
std::vector<RimFracture*> fracturesUsingThisTemplate() const;
private:
void prepareFieldsForUiDisplay();
virtual FractureWidthAndConductivity widthAndConductivityAtWellPathIntersection() const = 0;
virtual FractureWidthAndConductivity widthAndConductivityAtWellPathIntersection(const RimFracture* fractureInstance) const = 0;
QString dFactorSummary() const;
double effectivePermeability() const;
double fractureWidth() const;
protected:
caf::PdmField<int> m_id;
@ -206,7 +209,7 @@ protected:
caf::PdmField<double> m_relativeGasDensity;
caf::PdmField<double> m_gasViscosity;
caf::PdmProxyValueField<double> m_dFactorDisplayField;
//caf::PdmProxyValueField<double> m_dFactorDisplayField;
caf::PdmProxyValueField<QString> m_dFactorSummaryText;
caf::PdmField<double> m_heightScaleFactor;

View File

@ -26,6 +26,7 @@
#include "RigFractureGrid.h"
#include "RigStimPlanFractureDefinition.h"
#include "RigWellPathStimplanIntersector.h"
#include "RigFractureCell.h"
#include "RimEclipseView.h"
@ -42,6 +43,7 @@
#include "cafPdmUiDoubleSliderEditor.h"
#include "cafPdmUiFilePathEditor.h"
#include "cvfMath.h"
#include "cvfVector3.h"
#include <QFileInfo>
@ -279,6 +281,11 @@ void RimStimPlanFractureTemplate::loadDataAndUpdate()
updateFractureGrid();
for (RimFracture* fracture : fracturesUsingThisTemplate())
{
fracture->clearCachedNonDarcyProperties();
}
// Todo: Must update all views using this fracture template
RimEclipseView* activeView = dynamic_cast<RimEclipseView*>(RiaApplication::instance()->activeReservoirView());
if (activeView) activeView->fractureColors()->loadDataAndUpdate();
@ -405,47 +412,134 @@ QString RimStimPlanFractureTemplate::getUnitForStimPlanParameter(QString paramet
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
FractureWidthAndConductivity RimStimPlanFractureTemplate::widthAndConductivityAtWellPathIntersection() const
std::vector<double>
RimStimPlanFractureTemplate::fractureGridResultsForUnitSystem(const QString& resultName,
const QString& unitName,
size_t timeStepIndex,
RiaEclipseUnitTools::UnitSystem requiredUnitSystem) const
{
auto resultValues = m_stimPlanFractureDefinitionData->fractureGridResults(resultName, unitName, m_activeTimeStepIndex);
if (fractureTemplateUnit() == RiaEclipseUnitTools::UNITS_METRIC)
{
for (auto& v : resultValues)
{
v = RiaEclipseUnitTools::convertToMeter(v, unitName);
}
}
else if (fractureTemplateUnit() == RiaEclipseUnitTools::UNITS_FIELD)
{
for (auto& v : resultValues)
{
v = RiaEclipseUnitTools::convertToFeet(v, unitName);
}
}
return resultValues;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
FractureWidthAndConductivity
RimStimPlanFractureTemplate::widthAndConductivityAtWellPathIntersection(const RimFracture* fractureInstance) const
{
FractureWidthAndConductivity values;
if (m_fractureGrid.notNull())
{
std::pair<size_t, size_t> wellCellIJ = m_fractureGrid->fractureCellAtWellCenter();
size_t wellCellIndex = m_fractureGrid->getGlobalIndexFromIJ(wellCellIJ.first, wellCellIJ.second);
const RigFractureCell& wellCell = m_fractureGrid->cellFromIndex(wellCellIndex);
double conductivity = wellCell.getConductivityValue();
values.m_conductivity = conductivity;
auto nameUnit = widthParameterNameAndUnit();
if (!nameUnit.first.isEmpty())
if (orientationType() == ALONG_WELL_PATH)
{
double widthInRequiredUnit = HUGE_VAL;
CVF_ASSERT(fractureInstance);
RimWellPath* rimWellPath = nullptr;
fractureInstance->firstAncestorOrThisOfType(rimWellPath);
if (rimWellPath && rimWellPath->wellPathGeometry())
{
auto resultValues =
m_stimPlanFractureDefinitionData->fractureGridResults(nameUnit.first, nameUnit.second, m_activeTimeStepIndex);
RigWellPathStimplanIntersector intersector(rimWellPath->wellPathGeometry(), fractureInstance);
double widthInFileUnitSystem = resultValues[wellCellIndex];
if (fractureTemplateUnit() == RiaEclipseUnitTools::UNITS_METRIC)
double totalLength = 0.0;
for (const auto& v : intersector.intersections())
{
QString unitText = nameUnit.second;
widthInRequiredUnit = RiaEclipseUnitTools::convertToMeter(widthInFileUnitSystem, unitText);
totalLength += v.second.computeLength();
}
else if (fractureTemplateUnit() == RiaEclipseUnitTools::UNITS_FIELD)
{
QString unitText = nameUnit.second;
widthInRequiredUnit = RiaEclipseUnitTools::convertToFeet(widthInFileUnitSystem, unitText);
std::vector<double> widthResultValues;
{
auto nameUnit = widthParameterNameAndUnit();
widthResultValues = fractureGridResultsForUnitSystem(
nameUnit.first, nameUnit.second, m_activeTimeStepIndex, fractureTemplateUnit());
}
std::vector<double> conductivityResultValues;
{
auto nameUnit = conductivityParameterNameAndUnit();
conductivityResultValues = fractureGridResultsForUnitSystem(
nameUnit.first, nameUnit.second, m_activeTimeStepIndex, fractureTemplateUnit());
}
double weightedConductivity = 0.0;
double weightedWidth = 0.0;
for (const auto& v : intersector.intersections())
{
size_t fractureGlobalCellIndex = v.first;
if (fractureGlobalCellIndex < widthResultValues.size())
{
weightedWidth += widthResultValues[fractureGlobalCellIndex] * v.second.computeLength();
}
if (fractureGlobalCellIndex < conductivityResultValues.size())
{
weightedConductivity += conductivityResultValues[fractureGlobalCellIndex] * v.second.computeLength();
}
}
if (totalLength > 1e-7)
{
weightedWidth /= totalLength;
weightedConductivity /= totalLength;
values.m_width = weightedWidth;
values.m_conductivity = weightedConductivity;
}
if (weightedWidth > 1e-7)
{
values.m_permeability = weightedConductivity / weightedWidth;
}
}
}
else
{
std::pair<size_t, size_t> wellCellIJ = m_fractureGrid->fractureCellAtWellCenter();
size_t wellCellIndex = m_fractureGrid->getGlobalIndexFromIJ(wellCellIJ.first, wellCellIJ.second);
const RigFractureCell& wellCell = m_fractureGrid->cellFromIndex(wellCellIndex);
if (widthInRequiredUnit != HUGE_VAL && fabs(widthInRequiredUnit) > 1e-20)
double conductivity = wellCell.getConductivityValue();
values.m_conductivity = conductivity;
auto nameUnit = widthParameterNameAndUnit();
if (!nameUnit.first.isEmpty())
{
values.m_width = widthInRequiredUnit;
values.m_permeability = conductivity / widthInRequiredUnit;
double widthInRequiredUnit = HUGE_VAL;
{
auto resultValues = fractureGridResultsForUnitSystem(
nameUnit.first, nameUnit.second, m_activeTimeStepIndex, fractureTemplateUnit());
if (wellCellIndex < resultValues.size())
{
widthInRequiredUnit = resultValues[wellCellIndex];
}
}
if (widthInRequiredUnit != HUGE_VAL && fabs(widthInRequiredUnit) > 1e-20)
{
values.m_width = widthInRequiredUnit;
values.m_permeability = conductivity / widthInRequiredUnit;
}
}
}
}
@ -480,6 +574,28 @@ std::pair<QString, QString> RimStimPlanFractureTemplate::widthParameterNameAndUn
return std::pair<QString, QString>();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<QString, QString> RimStimPlanFractureTemplate::conductivityParameterNameAndUnit() const
{
if (m_stimPlanFractureDefinitionData.notNull())
{
std::vector<std::pair<QString, QString>> propertyNamesUnitsOnFile =
m_stimPlanFractureDefinitionData->getStimPlanPropertyNamesUnits();
for (const auto& nameUnit : propertyNamesUnitsOnFile)
{
if (nameUnit.first.contains(m_conductivityResultNameOnFile, Qt::CaseInsensitive))
{
return nameUnit;
}
}
}
return std::pair<QString, QString>();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@ -786,8 +902,8 @@ void RimStimPlanFractureTemplate::updateFractureGrid()
auto nameUnit = widthParameterNameAndUnit();
if (!nameUnit.first.isEmpty())
{
auto resultValues =
m_stimPlanFractureDefinitionData->fractureGridResults(nameUnit.first, nameUnit.second, m_activeTimeStepIndex);
auto resultValues = fractureGridResultsForUnitSystem(
nameUnit.first, nameUnit.second, m_activeTimeStepIndex, fractureTemplateUnit());
for (size_t i = 0; i < areaPerCell.size(); i++)
{

View File

@ -109,10 +109,12 @@ private:
void computePerforationLength();
QString getUnitForStimPlanParameter(QString parameterName);
std::vector<double> fractureGridResultsForUnitSystem(const QString& resultName, const QString& unitName, size_t timeStepIndex, RiaEclipseUnitTools::UnitSystem requiredUnitSystem) const;
virtual FractureWidthAndConductivity widthAndConductivityAtWellPathIntersection() const override;
virtual FractureWidthAndConductivity widthAndConductivityAtWellPathIntersection(const RimFracture* fractureInstance) const override;
std::pair<QString, QString> widthParameterNameAndUnit() const;
std::pair<QString, QString> conductivityParameterNameAndUnit() const;
private:
caf::PdmField<int> m_activeTimeStepIndex;

View File

@ -41,6 +41,11 @@ public:
double hlength;
double vlength;
int endpointCount;
double computeLength() const
{
return cvf::Math::sqrt(hlength * hlength + vlength * vlength);
}
};
RigWellPathStimplanIntersector(const RigWellPath* wellpathGeom, const RimFracture* rimFracture);