mirror of
https://github.com/OPM/ResInsight.git
synced 2025-02-25 18:55:39 -06:00
Streamline improvement (#7435)
* Use updated generator. Switch to using priority list for seeds. Fix phase reporting and sign issues. Fix step size when growing. * Reduce memory footprint by simplifying viz. code and filter out unused tracers early * Remove unused viz. code.
This commit is contained in:
parent
562ec1a398
commit
766ea6aab2
@ -96,7 +96,6 @@ void RivStreamlinesPartMgr::appendDynamicGeometryPartsToModel( cvf::ModelBasicLi
|
||||
{
|
||||
streamline.appendTracerPoint( tracer.tracerPoints()[i].position() );
|
||||
streamline.appendAbsVelocity( tracer.tracerPoints()[i].absValue() );
|
||||
streamline.appendDirection( tracer.tracerPoints()[i].direction() );
|
||||
streamline.appendPhase( tracer.tracerPoints()[i].phaseType() );
|
||||
}
|
||||
m_streamlines.push_back( streamline );
|
||||
@ -111,10 +110,6 @@ void RivStreamlinesPartMgr::appendDynamicGeometryPartsToModel( cvf::ModelBasicLi
|
||||
model->addPart( createPart( *streamlineCollection, streamline ).p() );
|
||||
}
|
||||
}
|
||||
else if ( streamlineCollection->visualizationMode() == RimStreamlineInViewCollection::VisualizationMode::VECTORS )
|
||||
{
|
||||
model->addPart( createVectorPart( *streamlineCollection, streamline ).p() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,10 +217,6 @@ cvf::ref<cvf::Part> RivStreamlinesPartMgr::createPart( const RimStreamlineInView
|
||||
|
||||
drawable->setTextureCoordArray( lineTexCoords.p() );
|
||||
|
||||
// caf::MeshEffectGenerator effgen( cvf::Color3f( 0.0, 0.0, 0.95 ) );
|
||||
// effgen.setLineWidth( 2 );
|
||||
// cvf::ref<cvf::Effect> effect = effgen.generateCachedEffect();
|
||||
|
||||
cvf::ref<cvf::Part> part = new cvf::Part;
|
||||
part->setDrawable( drawable.p() );
|
||||
part->setEffect( effect.p() );
|
||||
@ -246,13 +237,9 @@ void RivStreamlinesPartMgr::createResultColorTextureCoords( cvf::Vec2fArray*
|
||||
CVF_ASSERT( mapper );
|
||||
|
||||
RimStreamlineInViewCollection* streamlineCollection = m_rimReservoirView->streamlineCollection();
|
||||
CVF_ASSERT( streamlineCollection != nullptr );
|
||||
|
||||
size_t vertexCount = streamline.countTracerPoints() * 2 - 2;
|
||||
if ( streamlineCollection &&
|
||||
streamlineCollection->visualizationMode() == RimStreamlineInViewCollection::VisualizationMode::VECTORS )
|
||||
{
|
||||
vertexCount = streamline.countTracerPoints() * 7;
|
||||
}
|
||||
if ( textureCoords->capacity() != vertexCount ) textureCoords->reserve( vertexCount );
|
||||
|
||||
for ( size_t i = 0; i < streamline.countTracerPoints(); i++ )
|
||||
@ -279,172 +266,14 @@ void RivStreamlinesPartMgr::createResultColorTextureCoords( cvf::Vec2fArray*
|
||||
texCoord.x() = phaseValue;
|
||||
}
|
||||
|
||||
if ( streamlineCollection &&
|
||||
streamlineCollection->visualizationMode() == RimStreamlineInViewCollection::VisualizationMode::VECTORS )
|
||||
{
|
||||
for ( size_t vxIdx = 0; vxIdx < 7; ++vxIdx )
|
||||
{
|
||||
textureCoords->add( texCoord );
|
||||
}
|
||||
}
|
||||
else
|
||||
textureCoords->add( texCoord );
|
||||
if ( i > 0 && i < streamline.countTracerPoints() - 1 )
|
||||
{
|
||||
textureCoords->add( texCoord );
|
||||
if ( i > 0 && i < streamline.countTracerPoints() - 1 )
|
||||
{
|
||||
textureCoords->add( texCoord );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::ref<cvf::Part> RivStreamlinesPartMgr::createVectorPart( const RimStreamlineInViewCollection& streamlineCollection,
|
||||
Streamline& streamline )
|
||||
{
|
||||
cvf::ref<caf::DisplayCoordTransform> displayCordXf = m_rimReservoirView->displayCoordTransform();
|
||||
|
||||
std::vector<uint> shaftIndices;
|
||||
shaftIndices.reserve( 2 * streamline.countTracerPoints() );
|
||||
|
||||
std::vector<uint> headIndices;
|
||||
headIndices.reserve( 6 * streamline.countTracerPoints() );
|
||||
|
||||
std::vector<cvf::Vec3f> vertices;
|
||||
vertices.reserve( 7 * streamline.countTracerPoints() );
|
||||
|
||||
for ( size_t i = 0; i < streamline.countTracerPoints(); i++ )
|
||||
{
|
||||
cvf::Vec3f anchorPoint = cvf::Vec3f( displayCordXf->transformToDisplayCoord( streamline.getTracerPoint( i ) ) );
|
||||
cvf::Vec3f direction = cvf::Vec3f( streamline.getDirection( i ) ) * streamlineCollection.scaleFactor();
|
||||
|
||||
for ( const cvf::Vec3f& vertex : createArrowVertices( anchorPoint, direction ) )
|
||||
{
|
||||
vertices.push_back( vertex );
|
||||
}
|
||||
|
||||
for ( const uint& index : createArrowShaftIndices( 0 ) )
|
||||
{
|
||||
shaftIndices.push_back( index );
|
||||
}
|
||||
|
||||
for ( const uint& index : createArrowHeadIndices( 0 ) )
|
||||
{
|
||||
headIndices.push_back( index );
|
||||
}
|
||||
}
|
||||
|
||||
cvf::ref<cvf::PrimitiveSetIndexedUInt> indexedUIntShaft =
|
||||
new cvf::PrimitiveSetIndexedUInt( cvf::PrimitiveType::PT_LINES );
|
||||
cvf::ref<cvf::UIntArray> indexArrayShaft = new cvf::UIntArray( shaftIndices );
|
||||
|
||||
cvf::ref<cvf::PrimitiveSetIndexedUInt> indexedUIntHead =
|
||||
new cvf::PrimitiveSetIndexedUInt( cvf::PrimitiveType::PT_TRIANGLES );
|
||||
cvf::ref<cvf::UIntArray> indexArrayHead = new cvf::UIntArray( headIndices );
|
||||
|
||||
cvf::ref<cvf::DrawableGeo> drawable = new cvf::DrawableGeo();
|
||||
|
||||
indexedUIntShaft->setIndices( indexArrayShaft.p() );
|
||||
drawable->addPrimitiveSet( indexedUIntShaft.p() );
|
||||
|
||||
indexedUIntHead->setIndices( indexArrayHead.p() );
|
||||
drawable->addPrimitiveSet( indexedUIntHead.p() );
|
||||
|
||||
cvf::ref<cvf::Vec3fArray> vertexArray = new cvf::Vec3fArray( vertices );
|
||||
drawable->setVertexArray( vertexArray.p() );
|
||||
|
||||
cvf::ref<cvf::Vec2fArray> lineTexCoords = const_cast<cvf::Vec2fArray*>( drawable->textureCoordArray() );
|
||||
|
||||
if ( lineTexCoords.isNull() )
|
||||
{
|
||||
lineTexCoords = new cvf::Vec2fArray;
|
||||
}
|
||||
|
||||
cvf::ref<cvf::Effect> effect;
|
||||
|
||||
const cvf::ScalarMapper* activeScalarMapper = streamlineCollection.legendConfig()->scalarMapper();
|
||||
createResultColorTextureCoords( lineTexCoords.p(), streamline, activeScalarMapper );
|
||||
|
||||
caf::ScalarMapperMeshEffectGenerator meshEffGen( activeScalarMapper );
|
||||
effect = meshEffGen.generateCachedEffect();
|
||||
|
||||
drawable->setTextureCoordArray( lineTexCoords.p() );
|
||||
|
||||
cvf::ref<cvf::Part> part = new cvf::Part;
|
||||
part->setDrawable( drawable.p() );
|
||||
part->setEffect( effect.p() );
|
||||
part->updateBoundingBox();
|
||||
return part;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::array<cvf::Vec3f, 7> RivStreamlinesPartMgr::createArrowVertices( const cvf::Vec3f anchorPoint,
|
||||
const cvf::Vec3f direction ) const
|
||||
{
|
||||
std::array<cvf::Vec3f, 7> vertices;
|
||||
cvf::Vec3f headTop = anchorPoint + direction;
|
||||
|
||||
RimEclipseCase* eclipseCase = m_rimReservoirView->eclipseCase();
|
||||
if ( !eclipseCase ) return vertices;
|
||||
|
||||
float headLength =
|
||||
std::min<float>( eclipseCase->characteristicCellSize() / 3.0f, ( headTop - anchorPoint ).length() / 2.0 );
|
||||
|
||||
// A fixed size is preferred here
|
||||
cvf::Vec3f headBottom = headTop - ( headTop - anchorPoint ).getNormalized() * headLength;
|
||||
|
||||
float arrowWidth = headLength / 2.0f;
|
||||
|
||||
cvf::Vec3f headBottomDirection1 = direction ^ anchorPoint;
|
||||
cvf::Vec3f headBottomDirection2 = headBottomDirection1 ^ direction;
|
||||
cvf::Vec3f arrowBottomSegment1 = headBottomDirection1.getNormalized() * arrowWidth;
|
||||
cvf::Vec3f arrowBottomSegment2 = headBottomDirection2.getNormalized() * arrowWidth;
|
||||
|
||||
vertices[0] = anchorPoint;
|
||||
vertices[1] = headBottom;
|
||||
vertices[2] = headBottom + arrowBottomSegment1;
|
||||
vertices[3] = headBottom - arrowBottomSegment1;
|
||||
vertices[4] = headTop;
|
||||
vertices[5] = headBottom + arrowBottomSegment2;
|
||||
vertices[6] = headBottom - arrowBottomSegment2;
|
||||
|
||||
return vertices;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::array<uint, 2> RivStreamlinesPartMgr::createArrowShaftIndices( uint startIndex ) const
|
||||
{
|
||||
std::array<uint, 2> indices;
|
||||
|
||||
indices[0] = startIndex;
|
||||
indices[1] = startIndex + 1;
|
||||
|
||||
return indices;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::array<uint, 6> RivStreamlinesPartMgr::createArrowHeadIndices( uint startIndex ) const
|
||||
{
|
||||
std::array<uint, 6> indices;
|
||||
|
||||
indices[0] = startIndex + 2;
|
||||
indices[1] = startIndex + 3;
|
||||
indices[2] = startIndex + 4;
|
||||
|
||||
indices[3] = startIndex + 5;
|
||||
indices[4] = startIndex + 6;
|
||||
indices[5] = startIndex + 4;
|
||||
return indices;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@ -459,227 +288,6 @@ void RivStreamlinesPartMgr::setAlpha( cvf::ref<cvf::Part> part, float alpha )
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RivStreamlinesPartMgr::StreamlineSegment::computeSegments()
|
||||
{
|
||||
a = startPoint;
|
||||
b = startDirection;
|
||||
c = 3.0 * ( endPoint - startPoint ) - 2.0 * startDirection - endDirection;
|
||||
d = 2.0 * ( startPoint - endPoint ) + endDirection + startDirection;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Vec3d RivStreamlinesPartMgr::StreamlineSegment::getPointAt( double t ) const
|
||||
{
|
||||
return a + b * t + c * t * t + d * t * t * t;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Vec3d RivStreamlinesPartMgr::StreamlineSegment::getDirectionAt( double t ) const
|
||||
{
|
||||
return b + c * t + d * t * t;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RivStreamlinesPartMgr::StreamlineSegment::getVelocityAt( double localT ) const
|
||||
{
|
||||
if ( localT == 0 )
|
||||
{
|
||||
return startVelocity;
|
||||
}
|
||||
return startVelocity + ( endVelocity - startVelocity ) / localT;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RivStreamlinesPartMgr::StreamlineSegment::getChordLength() const
|
||||
{
|
||||
return startPoint.pointDistance( endPoint );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RivStreamlinesPartMgr::StreamlineVisualization::computeTValues()
|
||||
{
|
||||
double totalLength = getApproximatedTotalLength();
|
||||
double currentLength = 0.0;
|
||||
for ( StreamlineSegment& segment : segments )
|
||||
{
|
||||
segment.globalTStart = currentLength / totalLength;
|
||||
currentLength += segment.getChordLength();
|
||||
segment.globalTEnd = currentLength / totalLength;
|
||||
}
|
||||
areTValuesComputed = true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RivStreamlinesPartMgr::StreamlineVisualization::appendSegment( StreamlineSegment segment )
|
||||
{
|
||||
segments.push_back( segment );
|
||||
areTValuesComputed = false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RivStreamlinesPartMgr::StreamlineVisualization::prependSegment( StreamlineSegment segment )
|
||||
{
|
||||
segments.push_front( segment );
|
||||
areTValuesComputed = false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RivStreamlinesPartMgr::StreamlineVisualization::appendPart( cvf::ref<cvf::Part> part, double globalT )
|
||||
{
|
||||
parts.push_back( part.p() );
|
||||
partTValues.push_back( globalT );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
size_t RivStreamlinesPartMgr::StreamlineVisualization::segmentsSize() const
|
||||
{
|
||||
return segments.size();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::list<RivStreamlinesPartMgr::StreamlineSegment> RivStreamlinesPartMgr::StreamlineVisualization::getSegments()
|
||||
{
|
||||
return segments;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RivStreamlinesPartMgr::StreamlineVisualization::clear()
|
||||
{
|
||||
segments.clear();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RivStreamlinesPartMgr::StreamlineVisualization::updateAnimationGlobalT( double timeMs )
|
||||
{
|
||||
double totalLength = getApproximatedTotalLength(); // m
|
||||
double velocity = getVelocityAt( currentAnimationGlobalT ); // m/s
|
||||
currentAnimationGlobalT += velocity * timeMs / 1000.0 / totalLength;
|
||||
|
||||
if ( currentAnimationGlobalT > 1.0 )
|
||||
{
|
||||
currentAnimationGlobalT = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RivStreamlinesPartMgr::StreamlineVisualization::getApproximatedTotalLength()
|
||||
{
|
||||
if ( areTValuesComputed ) return approximatedTotalLength;
|
||||
double totalLength = 0.0;
|
||||
for ( auto& segment : segments )
|
||||
{
|
||||
totalLength += segment.getChordLength();
|
||||
}
|
||||
approximatedTotalLength = totalLength;
|
||||
return approximatedTotalLength;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Vec3d RivStreamlinesPartMgr::StreamlineVisualization::getPointAt( double globalT ) const
|
||||
{
|
||||
CVF_ASSERT( areTValuesComputed );
|
||||
for ( std::list<StreamlineSegment>::const_iterator it = segments.begin(); it != segments.end(); ++it )
|
||||
{
|
||||
if ( it->globalTStart <= globalT && it->globalTEnd >= globalT )
|
||||
{
|
||||
double localT = ( globalT - it->globalTStart ) / ( it->globalTEnd - it->globalTStart );
|
||||
return it->getPointAt( localT );
|
||||
}
|
||||
}
|
||||
return cvf::Vec3d();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Vec3d RivStreamlinesPartMgr::StreamlineVisualization::getDirectionAt( double globalT ) const
|
||||
{
|
||||
CVF_ASSERT( areTValuesComputed );
|
||||
for ( std::list<StreamlineSegment>::const_iterator it = segments.begin(); it != segments.end(); ++it )
|
||||
{
|
||||
if ( it->globalTStart <= globalT && it->globalTEnd >= globalT )
|
||||
{
|
||||
double localT = ( globalT - it->globalTStart ) / ( it->globalTEnd - it->globalTStart );
|
||||
return it->getDirectionAt( localT );
|
||||
}
|
||||
}
|
||||
return cvf::Vec3d();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RivStreamlinesPartMgr::StreamlineVisualization::getVelocityAt( double globalT ) const
|
||||
{
|
||||
CVF_ASSERT( areTValuesComputed );
|
||||
for ( std::list<StreamlineSegment>::const_iterator it = segments.begin(); it != segments.end(); ++it )
|
||||
{
|
||||
if ( it->globalTStart <= globalT && it->globalTEnd >= globalT )
|
||||
{
|
||||
double localT = ( globalT - it->globalTStart ) / ( it->globalTEnd - it->globalTStart );
|
||||
return it->getVelocityAt( localT );
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Collection<cvf::Part> RivStreamlinesPartMgr::StreamlineVisualization::getParts()
|
||||
{
|
||||
return parts;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::ref<cvf::Part> RivStreamlinesPartMgr::StreamlineVisualization::getPartAtGlobalT( double globalT ) const
|
||||
{
|
||||
CVF_ASSERT( areTValuesComputed );
|
||||
double t = 0.0;
|
||||
for ( size_t index = 0; index < parts.size(); index++ )
|
||||
{
|
||||
t = partTValues[index];
|
||||
if ( t >= globalT )
|
||||
{
|
||||
return parts[index];
|
||||
}
|
||||
}
|
||||
return cvf::ref<cvf::Part>( nullptr );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@ -696,14 +304,6 @@ void RivStreamlinesPartMgr::Streamline::appendAbsVelocity( double velocity )
|
||||
absVelocities.push_back( velocity );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RivStreamlinesPartMgr::Streamline::appendDirection( cvf::Vec3d direction )
|
||||
{
|
||||
directions.push_back( direction );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@ -719,7 +319,6 @@ void RivStreamlinesPartMgr::Streamline::clear()
|
||||
{
|
||||
tracerPoints.clear();
|
||||
absVelocities.clear();
|
||||
directions.clear();
|
||||
dominantPhases.clear();
|
||||
delete part.p();
|
||||
}
|
||||
@ -748,14 +347,6 @@ double RivStreamlinesPartMgr::Streamline::getAbsVelocity( size_t index ) const
|
||||
return absVelocities[index];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
cvf::Vec3d RivStreamlinesPartMgr::Streamline::getDirection( size_t index ) const
|
||||
{
|
||||
return directions[index];
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
@ -51,98 +51,17 @@ public:
|
||||
void updateAnimation();
|
||||
|
||||
private:
|
||||
struct StreamlineSegment
|
||||
{
|
||||
/*
|
||||
x_ij(t) = a_ij + b_ij * t + c_ij * t^2 + d_ij * t^3
|
||||
*/
|
||||
|
||||
StreamlineSegment(){};
|
||||
StreamlineSegment( cvf::Vec3d startPoint,
|
||||
cvf::Vec3d endPoint,
|
||||
cvf::Vec3d startDirection,
|
||||
cvf::Vec3d endDirection,
|
||||
double startVelocity,
|
||||
double endVelocity )
|
||||
: startPoint( startPoint )
|
||||
, endPoint( endPoint )
|
||||
, startDirection( startDirection )
|
||||
, endDirection( endDirection )
|
||||
, startVelocity( startVelocity )
|
||||
, endVelocity( endVelocity )
|
||||
{
|
||||
computeSegments();
|
||||
};
|
||||
|
||||
void computeSegments();
|
||||
|
||||
cvf::Vec3d getPointAt( double localT ) const;
|
||||
cvf::Vec3d getDirectionAt( double localT ) const;
|
||||
double getVelocityAt( double localT ) const;
|
||||
double getChordLength() const;
|
||||
|
||||
cvf::Vec3d startPoint;
|
||||
cvf::Vec3d endPoint;
|
||||
cvf::Vec3d startDirection;
|
||||
cvf::Vec3d endDirection;
|
||||
double globalTStart;
|
||||
double globalTEnd;
|
||||
double startVelocity;
|
||||
double endVelocity;
|
||||
|
||||
private:
|
||||
cvf::Vec3d a;
|
||||
cvf::Vec3d b;
|
||||
cvf::Vec3d c;
|
||||
cvf::Vec3d d;
|
||||
};
|
||||
struct StreamlineVisualization
|
||||
{
|
||||
StreamlineVisualization()
|
||||
{
|
||||
areTValuesComputed = false;
|
||||
currentAnimationGlobalT = 0.0;
|
||||
};
|
||||
|
||||
void computeTValues();
|
||||
void appendSegment( StreamlineSegment segment );
|
||||
void prependSegment( StreamlineSegment segment );
|
||||
void appendPart( cvf::ref<cvf::Part> part, double globalT );
|
||||
size_t segmentsSize() const;
|
||||
std::list<RivStreamlinesPartMgr::StreamlineSegment> getSegments();
|
||||
void clear();
|
||||
void updateAnimationGlobalT( double timeMs );
|
||||
double getApproximatedTotalLength();
|
||||
|
||||
cvf::Vec3d getPointAt( double globalT ) const;
|
||||
cvf::Vec3d getDirectionAt( double globalT ) const;
|
||||
double getVelocityAt( double globalT ) const;
|
||||
cvf::Collection<cvf::Part> getParts();
|
||||
cvf::ref<cvf::Part> getPartAtGlobalT( double globalT ) const;
|
||||
|
||||
double currentAnimationGlobalT;
|
||||
|
||||
private:
|
||||
bool areTValuesComputed;
|
||||
double approximatedTotalLength;
|
||||
std::list<StreamlineSegment> segments;
|
||||
cvf::Collection<cvf::Part> parts;
|
||||
std::vector<double> partTValues;
|
||||
};
|
||||
|
||||
struct Streamline
|
||||
{
|
||||
Streamline() { animIndex = 0; };
|
||||
|
||||
void appendTracerPoint( cvf::Vec3d point );
|
||||
void appendAbsVelocity( double velocity );
|
||||
void appendDirection( cvf::Vec3d direction );
|
||||
void appendPhase( RiaDefines::PhaseType phase );
|
||||
void clear();
|
||||
cvf::ref<cvf::Part> getPart();
|
||||
cvf::Vec3d getTracerPoint( size_t index ) const;
|
||||
double getAbsVelocity( size_t index ) const;
|
||||
cvf::Vec3d getDirection( size_t index ) const;
|
||||
RiaDefines::PhaseType getPhase( size_t index ) const;
|
||||
|
||||
size_t countTracerPoints() const;
|
||||
@ -154,7 +73,6 @@ private:
|
||||
private:
|
||||
std::vector<cvf::Vec3d> tracerPoints;
|
||||
std::vector<double> absVelocities;
|
||||
std::vector<cvf::Vec3d> directions;
|
||||
std::vector<RiaDefines::PhaseType> dominantPhases;
|
||||
|
||||
cvf::ref<cvf::Part> part;
|
||||
@ -169,12 +87,7 @@ private:
|
||||
const Streamline& streamline,
|
||||
const cvf::ScalarMapper* mapper );
|
||||
|
||||
cvf::ref<cvf::Part> createVectorPart( const RimStreamlineInViewCollection& streamlineCollection, Streamline& segment );
|
||||
|
||||
std::array<cvf::Vec3f, 7> createArrowVertices( const cvf::Vec3f anchorPoint, const cvf::Vec3f direction ) const;
|
||||
std::array<uint, 2> createArrowShaftIndices( uint startIndex ) const;
|
||||
std::array<uint, 6> createArrowHeadIndices( uint startIndex ) const;
|
||||
void setAlpha( cvf::ref<cvf::Part> part, float alpha );
|
||||
void setAlpha( cvf::ref<cvf::Part> part, float alpha );
|
||||
|
||||
private:
|
||||
std::list<Streamline> m_streamlines;
|
||||
|
@ -4,7 +4,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RimStreamline.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimStreamlineInViewCollection.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimStreamlineGeneratorBase.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimStreamlineDataAccess.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimStreamlineGenerator2.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimStreamlineGenerator.h
|
||||
)
|
||||
|
||||
set (SOURCE_GROUP_SOURCE_FILES
|
||||
@ -12,7 +12,7 @@ ${CMAKE_CURRENT_LIST_DIR}/RimStreamline.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimStreamlineInViewCollection.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimStreamlineGeneratorBase.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimStreamlineDataAccess.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimStreamlineGenerator2.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimStreamlineGenerator.cpp
|
||||
)
|
||||
|
||||
list(APPEND CODE_HEADER_FILES
|
||||
|
@ -20,11 +20,6 @@
|
||||
|
||||
#include "cafPdmFieldScriptingCapability.h"
|
||||
#include "cafPdmObjectScriptingCapability.h"
|
||||
#include "cafPdmUiLineEditor.h"
|
||||
#include "cafPdmUiTextEditor.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
CAF_PDM_ABSTRACT_SOURCE_INIT( RimStreamline, "Streamline" );
|
||||
|
||||
@ -39,12 +34,6 @@ RimStreamline::RimStreamline( QString simWellName )
|
||||
m_simWellName.uiCapability()->setUiReadOnly( true );
|
||||
m_simWellName.uiCapability()->setUiHidden( true );
|
||||
|
||||
CAF_PDM_InitScriptableFieldNoDefault( &m_propertiesTable, "PropertiesTable", "Properties Table", "", "", "" );
|
||||
m_propertiesTable.uiCapability()->setUiEditorTypeName( caf::PdmUiTextEditor::uiEditorTypeName() );
|
||||
m_propertiesTable.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN );
|
||||
m_propertiesTable.uiCapability()->setUiReadOnly( true );
|
||||
m_propertiesTable.xmlCapability()->disableIO();
|
||||
|
||||
setDeletable( false );
|
||||
}
|
||||
|
||||
@ -95,25 +84,6 @@ void RimStreamline::reverse()
|
||||
m_tracer.reverse();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimStreamline::generateStatistics()
|
||||
{
|
||||
QString stats;
|
||||
|
||||
stats += "Total distance: ";
|
||||
stats += QString::number( m_tracer.totalDistance(), 'f', 2 );
|
||||
stats += " meters\n";
|
||||
stats += "\n";
|
||||
stats += "Number of points: ";
|
||||
stats += QString::number( m_tracer.size() );
|
||||
stats += "\n";
|
||||
stats += "\n";
|
||||
|
||||
m_propertiesTable = stats;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
@ -24,10 +24,6 @@
|
||||
#include "cafPdmField.h"
|
||||
#include "cafPdmObject.h"
|
||||
|
||||
#include "cafPdmFieldCvfColor.h"
|
||||
#include "cafPdmProxyValueField.h"
|
||||
|
||||
#include "cvfObject.h"
|
||||
#include "cvfVector3.h"
|
||||
|
||||
#include <vector>
|
||||
@ -46,7 +42,6 @@ public:
|
||||
|
||||
void addTracerPoint( cvf::Vec3d position, cvf::Vec3d direction, RiaDefines::PhaseType dominantPhase );
|
||||
void reverse();
|
||||
void generateStatistics();
|
||||
|
||||
protected:
|
||||
caf::PdmFieldHandle* userDescriptionField() override;
|
||||
@ -54,6 +49,4 @@ protected:
|
||||
private:
|
||||
RigTracer m_tracer;
|
||||
caf::PdmField<QString> m_simWellName;
|
||||
|
||||
caf::PdmField<QString> m_propertiesTable;
|
||||
};
|
||||
|
@ -149,34 +149,37 @@ QString RimStreamlineDataAccess::gridResultNameFromPhase( RiaDefines::PhaseType
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Return the face scalar value for the given cell and NEG_? face, by using the neighbor cell
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RimStreamlineDataAccess::negFaceValueDividedByArea( RigCell cell,
|
||||
cvf::StructGridInterface::FaceType faceIdx,
|
||||
RiaDefines::PhaseType phase ) const
|
||||
double RimStreamlineDataAccess::negFaceRate( RigCell cell,
|
||||
cvf::StructGridInterface::FaceType faceIdx,
|
||||
RiaDefines::PhaseType phase ) const
|
||||
{
|
||||
double retval = 0.0;
|
||||
|
||||
// NEG_? face values must be read from the neighbor cells
|
||||
RigCell neighborCell = cell.neighborCell( faceIdx );
|
||||
if ( neighborCell.isInvalid() ) return retval;
|
||||
|
||||
std::vector<cvf::ref<RigResultAccessor>> access = m_dataAccess.at( phase );
|
||||
retval = access[faceIdx]->cellScalar( neighborCell.mainGridCellIndex() );
|
||||
double area = cell.faceNormalWithAreaLength( faceIdx ).length();
|
||||
if ( area != 0.0 )
|
||||
|
||||
retval = access[faceIdx]->cellScalar( neighborCell.mainGridCellIndex() );
|
||||
double area = cell.faceNormalWithAreaLength( faceIdx ).length();
|
||||
if ( area > 1.0e-4 )
|
||||
retval /= area;
|
||||
else
|
||||
retval = 0.0;
|
||||
|
||||
if ( std::isinf( retval ) ) retval = 0.0;
|
||||
|
||||
return retval;
|
||||
// change sign to get proper rate value direction (out of one cell is into the next)
|
||||
return -1.0 * retval;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Return the face scalar value for the given cell and POS_? face
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RimStreamlineDataAccess::posFaceValueDividedByArea( RigCell cell,
|
||||
cvf::StructGridInterface::FaceType faceIdx,
|
||||
RiaDefines::PhaseType phase ) const
|
||||
double RimStreamlineDataAccess::posFaceRate( RigCell cell,
|
||||
cvf::StructGridInterface::FaceType faceIdx,
|
||||
RiaDefines::PhaseType phase ) const
|
||||
{
|
||||
std::vector<cvf::ref<RigResultAccessor>> access = m_dataAccess.at( phase );
|
||||
double retval = access[faceIdx]->cellScalar( cell.mainGridCellIndex() );
|
||||
@ -193,24 +196,25 @@ double RimStreamlineDataAccess::posFaceValueDividedByArea( RigCell
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Return the face scalar value for the given cell and face
|
||||
/// Positive values is flow out of the cell, negative values is flow into the cell
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RimStreamlineDataAccess::faceValueDividedByArea( RigCell cell,
|
||||
cvf::StructGridInterface::FaceType faceIdx,
|
||||
RiaDefines::PhaseType phase ) const
|
||||
double RimStreamlineDataAccess::faceRate( RigCell cell,
|
||||
cvf::StructGridInterface::FaceType faceIdx,
|
||||
RiaDefines::PhaseType phase ) const
|
||||
{
|
||||
if ( faceIdx % 2 == 0 ) return posFaceValueDividedByArea( cell, faceIdx, phase );
|
||||
|
||||
// NEG_? face values must be read from the neighbor cells
|
||||
return negFaceValueDividedByArea( cell, faceIdx, phase );
|
||||
if ( faceIdx % 2 == 0 ) return posFaceRate( cell, faceIdx, phase );
|
||||
return negFaceRate( cell, faceIdx, phase );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Return the face scalar value for the given cell and face, by combining flow for all specified phases
|
||||
/// Positive values is flow out of the cell, negative values is flow into the cell
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
double RimStreamlineDataAccess::combinedFaceValueByArea( RigCell cell,
|
||||
cvf::StructGridInterface::FaceType faceIdx,
|
||||
std::list<RiaDefines::PhaseType> phases,
|
||||
RiaDefines::PhaseType& outDominantPhase ) const
|
||||
double RimStreamlineDataAccess::combinedFaceRate( RigCell cell,
|
||||
cvf::StructGridInterface::FaceType faceIdx,
|
||||
std::list<RiaDefines::PhaseType> phases,
|
||||
double direction,
|
||||
RiaDefines::PhaseType& outDominantPhase ) const
|
||||
{
|
||||
double retValue = 0.0;
|
||||
outDominantPhase = phases.front();
|
||||
@ -221,14 +225,16 @@ double RimStreamlineDataAccess::combinedFaceValueByArea( RigCell
|
||||
{
|
||||
double tmp = 0.0;
|
||||
if ( faceIdx % 2 == 0 )
|
||||
tmp = posFaceValueDividedByArea( cell, faceIdx, phase );
|
||||
tmp = posFaceRate( cell, faceIdx, phase );
|
||||
else
|
||||
tmp = negFaceValueDividedByArea( cell, faceIdx, phase );
|
||||
if ( abs( tmp ) > max )
|
||||
tmp = negFaceRate( cell, faceIdx, phase );
|
||||
|
||||
if ( tmp * direction > max )
|
||||
{
|
||||
outDominantPhase = phase;
|
||||
max = abs( tmp );
|
||||
max = std::abs( tmp );
|
||||
}
|
||||
|
||||
retValue += tmp;
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,11 @@ class RigGridBase;
|
||||
class RigResultAccessor;
|
||||
class RigEclipseCaseData;
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Specialized data access for streamline generation. Operates using flow rate in meters/day
|
||||
/// calculated by dividing the FLR values by the cell face area.
|
||||
/// NOTE: Positive rate values are flow out of a cell, negative values are flow into a cell
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class RimStreamlineDataAccess
|
||||
{
|
||||
public:
|
||||
@ -42,11 +47,12 @@ public:
|
||||
|
||||
bool setupDataAccess( RigMainGrid* grid, RigEclipseCaseData* data, std::list<RiaDefines::PhaseType> phases, int timeIdx );
|
||||
|
||||
double faceValueDividedByArea( RigCell cell, cvf::StructGridInterface::FaceType faceIdx, RiaDefines::PhaseType phase ) const;
|
||||
double combinedFaceValueByArea( RigCell cell,
|
||||
cvf::StructGridInterface::FaceType faceIdx,
|
||||
std::list<RiaDefines::PhaseType> phases,
|
||||
RiaDefines::PhaseType& dominantPhaseOut ) const;
|
||||
double faceRate( RigCell cell, cvf::StructGridInterface::FaceType faceIdx, RiaDefines::PhaseType phase ) const;
|
||||
double combinedFaceRate( RigCell cell,
|
||||
cvf::StructGridInterface::FaceType faceIdx,
|
||||
std::list<RiaDefines::PhaseType> phases,
|
||||
double direction,
|
||||
RiaDefines::PhaseType& dominantPhaseOut ) const;
|
||||
|
||||
const RigMainGrid* grid() const { return m_grid; }
|
||||
|
||||
@ -55,12 +61,8 @@ protected:
|
||||
getDataAccessor( cvf::StructGridInterface::FaceType faceIdx, RiaDefines::PhaseType phase, int timeIdx );
|
||||
QString gridResultNameFromPhase( RiaDefines::PhaseType phase, cvf::StructGridInterface::FaceType faceIdx ) const;
|
||||
|
||||
double posFaceValueDividedByArea( RigCell cell,
|
||||
cvf::StructGridInterface::FaceType faceIdx,
|
||||
RiaDefines::PhaseType phase ) const;
|
||||
double negFaceValueDividedByArea( RigCell cell,
|
||||
cvf::StructGridInterface::FaceType faceIdx,
|
||||
RiaDefines::PhaseType phase ) const;
|
||||
double posFaceRate( RigCell cell, cvf::StructGridInterface::FaceType faceIdx, RiaDefines::PhaseType phase ) const;
|
||||
double negFaceRate( RigCell cell, cvf::StructGridInterface::FaceType faceIdx, RiaDefines::PhaseType phase ) const;
|
||||
|
||||
private:
|
||||
RigMainGrid* m_grid;
|
||||
|
@ -0,0 +1,228 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2021 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 "RimStreamlineGenerator.h"
|
||||
|
||||
#include "RigCell.h"
|
||||
#include "RigMainGrid.h"
|
||||
|
||||
#include "RimStreamline.h"
|
||||
#include "RimStreamlineDataAccess.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Helper class for prioritizing streamline seed points
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class StreamlineSeedPoint
|
||||
{
|
||||
public:
|
||||
StreamlineSeedPoint( double rate, size_t cellIdx, cvf::StructGridInterface::FaceType faceIdx )
|
||||
: m_rate( rate )
|
||||
, m_cellIdx( cellIdx )
|
||||
, m_faceIdx( faceIdx ){};
|
||||
~StreamlineSeedPoint(){};
|
||||
|
||||
bool operator<( const StreamlineSeedPoint& other ) const { return m_rate < other.m_rate; };
|
||||
|
||||
public:
|
||||
double m_rate;
|
||||
size_t m_cellIdx;
|
||||
cvf::StructGridInterface::FaceType m_faceIdx;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimStreamlineGenerator::RimStreamlineGenerator( std::set<size_t>& wellCells )
|
||||
: RimStreamlineGeneratorBase( wellCells )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimStreamlineGenerator::~RimStreamlineGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimStreamlineGenerator::generateTracer( RigCell cell,
|
||||
double direction,
|
||||
QString simWellName,
|
||||
std::list<RimStreamline*>& outStreamlines )
|
||||
{
|
||||
RiaDefines::PhaseType dominantPhase = m_phases.front();
|
||||
|
||||
const size_t cellIdx = cell.gridLocalCellIndex();
|
||||
|
||||
// try to generate a tracer for all faces in the selected cell with positive flow (or negative flow if we are
|
||||
// backtracking from a producer)
|
||||
for ( auto faceIdx : m_allFaces )
|
||||
{
|
||||
double rate = m_dataAccess->combinedFaceRate( cell, faceIdx, m_phases, direction, dominantPhase ) * direction;
|
||||
if ( rate > m_flowThreshold )
|
||||
{
|
||||
m_seeds.push( StreamlineSeedPoint( rate, cellIdx, faceIdx ) );
|
||||
}
|
||||
}
|
||||
|
||||
while ( m_seeds.size() > 0 )
|
||||
{
|
||||
const size_t cellIdx = m_seeds.top().m_cellIdx;
|
||||
const cvf::StructGridInterface::FaceType faceIdx = m_seeds.top().m_faceIdx;
|
||||
m_seeds.pop();
|
||||
|
||||
RimStreamline* streamline = new RimStreamline( simWellName );
|
||||
|
||||
growStreamline( streamline, cellIdx, faceIdx, direction );
|
||||
|
||||
if ( direction < 0.0 ) streamline->reverse();
|
||||
|
||||
if ( streamline->tracer().totalDistance() >= m_minLength )
|
||||
outStreamlines.push_back( streamline );
|
||||
else
|
||||
delete streamline;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimStreamlineGenerator::growStreamline( RimStreamline* streamline,
|
||||
size_t cellIdx,
|
||||
cvf::StructGridInterface::FaceType faceIdx,
|
||||
double direction )
|
||||
{
|
||||
// get the cell
|
||||
RigCell cell = m_dataAccess->grid()->cell( cellIdx );
|
||||
|
||||
// get rate
|
||||
RiaDefines::PhaseType dominantPhaseOut;
|
||||
|
||||
double rate = m_dataAccess->combinedFaceRate( cell, faceIdx, m_phases, direction, dominantPhaseOut );
|
||||
|
||||
// if we go backwards from a producer, the rate needs to be flipped
|
||||
rate *= direction;
|
||||
|
||||
// grow from start cell center to face center, exiting if we reach the max length
|
||||
if ( !growStreamlineFromTo( streamline, cell.center(), cell.faceCenter( faceIdx ), rate, dominantPhaseOut ) )
|
||||
return;
|
||||
|
||||
while ( rate >= m_flowThreshold )
|
||||
{
|
||||
// find next cell and entry face
|
||||
cell = cell.neighborCell( faceIdx );
|
||||
if ( cell.isInvalid() ) break;
|
||||
faceIdx = cvf::StructGridInterface::oppositeFace( faceIdx );
|
||||
|
||||
// grow from given face center to cell center, exiting if we reach the max length
|
||||
if ( !growStreamlineFromTo( streamline, cell.faceCenter( faceIdx ), cell.center(), rate, dominantPhaseOut ) )
|
||||
break;
|
||||
|
||||
const size_t cellIdx = cell.gridLocalCellIndex();
|
||||
|
||||
if ( m_visitedCells.count( cellIdx ) > 0 ) break;
|
||||
if ( m_wellCells.count( cellIdx ) > 0 ) break;
|
||||
|
||||
m_visitedCells.insert( cellIdx );
|
||||
|
||||
// find the face with max flow where we should exit the cell
|
||||
cvf::StructGridInterface::FaceType exitFace = cvf::StructGridInterface::FaceType::NO_FACE;
|
||||
std::map<cvf::StructGridInterface::FaceType, double> rateMap;
|
||||
|
||||
double maxRate = 0.0;
|
||||
|
||||
for ( auto face : m_allFaces )
|
||||
{
|
||||
// skip the entry face
|
||||
if ( face == faceIdx ) continue;
|
||||
|
||||
RiaDefines::PhaseType tempDominantFace;
|
||||
double faceRate = m_dataAccess->combinedFaceRate( cell, face, m_phases, direction, tempDominantFace );
|
||||
|
||||
// if we go backwards from a producer, the rate needs to be flipped
|
||||
faceRate *= direction;
|
||||
|
||||
if ( faceRate > maxRate )
|
||||
{
|
||||
exitFace = face;
|
||||
maxRate = faceRate;
|
||||
dominantPhaseOut = tempDominantFace;
|
||||
}
|
||||
|
||||
rateMap[face] = faceRate;
|
||||
}
|
||||
|
||||
// did we find an exit?
|
||||
if ( exitFace == cvf::StructGridInterface::FaceType::NO_FACE ) break;
|
||||
|
||||
// add seeds for other faces with flow > threshold
|
||||
for ( auto& kvp : rateMap )
|
||||
{
|
||||
if ( kvp.first == exitFace ) continue;
|
||||
|
||||
if ( kvp.second >= m_flowThreshold )
|
||||
m_seeds.push( StreamlineSeedPoint( kvp.second, cell.gridLocalCellIndex(), kvp.first ) );
|
||||
}
|
||||
|
||||
rate = maxRate;
|
||||
|
||||
// grow from cell center to exit face center, stopping if we reach the max point limit
|
||||
if ( !growStreamlineFromTo( streamline, cell.center(), cell.faceCenter( exitFace ), rate, dominantPhaseOut ) )
|
||||
break;
|
||||
|
||||
faceIdx = exitFace;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimStreamlineGenerator::growStreamlineFromTo( RimStreamline* streamline,
|
||||
cvf::Vec3d startPos,
|
||||
cvf::Vec3d endPos,
|
||||
double rate,
|
||||
RiaDefines::PhaseType dominantPhase )
|
||||
{
|
||||
double totDistance = endPos.pointDistance( startPos );
|
||||
|
||||
if ( totDistance < 0.1 ) return true;
|
||||
if ( rate < m_flowThreshold ) return false;
|
||||
|
||||
cvf::Vec3d movementDirection = endPos - startPos;
|
||||
movementDirection.normalize();
|
||||
movementDirection *= rate * m_resolution;
|
||||
|
||||
int nSteps = (int)std::round( totDistance / ( rate * m_resolution ) );
|
||||
|
||||
cvf::Vec3d curpos = startPos;
|
||||
streamline->addTracerPoint( curpos, movementDirection, dominantPhase );
|
||||
|
||||
for ( int i = 1; i < nSteps; i++ )
|
||||
{
|
||||
curpos = curpos + movementDirection;
|
||||
streamline->addTracerPoint( curpos, movementDirection, dominantPhase );
|
||||
|
||||
if ( streamline->size() >= m_maxPoints ) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
@ -25,17 +25,18 @@
|
||||
#include <QString>
|
||||
|
||||
#include <list>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
class RigCell;
|
||||
class RimStreamline;
|
||||
class StreamlineSeedPoint;
|
||||
|
||||
class RimStreamlineGenerator2 : public RimStreamlineGeneratorBase
|
||||
class RimStreamlineGenerator : public RimStreamlineGeneratorBase
|
||||
{
|
||||
public:
|
||||
RimStreamlineGenerator2( std::set<size_t>& wellCells );
|
||||
~RimStreamlineGenerator2();
|
||||
RimStreamlineGenerator( std::set<size_t>& wellCells );
|
||||
~RimStreamlineGenerator();
|
||||
|
||||
void generateTracer( RigCell cell, double direction, QString simWellName, std::list<RimStreamline*>& outStreamlines ) override;
|
||||
|
||||
@ -48,8 +49,8 @@ protected:
|
||||
bool growStreamlineFromTo( RimStreamline* streamline,
|
||||
cvf::Vec3d startPos,
|
||||
cvf::Vec3d endpos,
|
||||
double flowVelocity,
|
||||
double rate,
|
||||
RiaDefines::PhaseType dominantPhase );
|
||||
|
||||
std::list<std::pair<size_t, cvf::StructGridInterface::FaceType>> m_seeds;
|
||||
std::priority_queue<StreamlineSeedPoint> m_seeds;
|
||||
};
|
@ -1,199 +0,0 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2021 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 "RimStreamlineGenerator2.h"
|
||||
|
||||
#include "RigCell.h"
|
||||
#include "RigMainGrid.h"
|
||||
|
||||
#include "RimStreamline.h"
|
||||
#include "RimStreamlineDataAccess.h"
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimStreamlineGenerator2::RimStreamlineGenerator2( std::set<size_t>& wellCells )
|
||||
: RimStreamlineGeneratorBase( wellCells )
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimStreamlineGenerator2::~RimStreamlineGenerator2()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimStreamlineGenerator2::generateTracer( RigCell cell,
|
||||
double direction,
|
||||
QString simWellName,
|
||||
std::list<RimStreamline*>& outStreamlines )
|
||||
{
|
||||
RiaDefines::PhaseType dominantPhase = m_phases.front();
|
||||
|
||||
const size_t cellIdx = cell.gridLocalCellIndex();
|
||||
|
||||
// try to generate a tracer for all faces in the selected cell with positive flow
|
||||
for ( auto faceIdx : m_allFaces )
|
||||
{
|
||||
double flowVelocity = m_dataAccess->combinedFaceValueByArea( cell, faceIdx, m_phases, dominantPhase ) * direction;
|
||||
if ( std::abs( flowVelocity ) > m_flowThreshold )
|
||||
{
|
||||
m_seeds.push_back( std::make_pair( cellIdx, faceIdx ) );
|
||||
}
|
||||
}
|
||||
|
||||
while ( m_seeds.size() > 0 )
|
||||
{
|
||||
const size_t cellIdx = m_seeds.front().first;
|
||||
const cvf::StructGridInterface::FaceType faceIdx = m_seeds.front().second;
|
||||
|
||||
RimStreamline* streamline = new RimStreamline( simWellName );
|
||||
|
||||
growStreamline( streamline, cellIdx, faceIdx, direction );
|
||||
|
||||
if ( direction < 0.0 ) streamline->reverse();
|
||||
|
||||
outStreamlines.push_back( streamline );
|
||||
|
||||
m_seeds.pop_front();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimStreamlineGenerator2::growStreamline( RimStreamline* streamline,
|
||||
size_t cellIdx,
|
||||
cvf::StructGridInterface::FaceType faceIdx,
|
||||
double direction )
|
||||
{
|
||||
// get the cell
|
||||
RigCell cell = m_dataAccess->grid()->cell( cellIdx );
|
||||
|
||||
// get rate
|
||||
RiaDefines::PhaseType dominantPhaseOut;
|
||||
double flowVelocity = std::abs( m_dataAccess->combinedFaceValueByArea( cell, faceIdx, m_phases, dominantPhaseOut ) );
|
||||
|
||||
while ( flowVelocity >= m_flowThreshold )
|
||||
{
|
||||
// grow from given face center to cell center, exiting if we reach the max length
|
||||
if ( !growStreamlineFromTo( streamline, cell.faceCenter( faceIdx ), cell.center(), flowVelocity, dominantPhaseOut ) )
|
||||
break;
|
||||
|
||||
// move to next cell
|
||||
RigCell neighbor = cell.neighborCell( faceIdx );
|
||||
if ( neighbor.isInvalid() ) break;
|
||||
|
||||
cvf::StructGridInterface::FaceType neighborFaceIdx = cvf::StructGridInterface::oppositeFace( faceIdx );
|
||||
|
||||
// get rate
|
||||
flowVelocity =
|
||||
std::abs( m_dataAccess->combinedFaceValueByArea( neighbor, neighborFaceIdx, m_phases, dominantPhaseOut ) );
|
||||
|
||||
// grow from face center to cell center, exiting if we reach the max point limit
|
||||
if ( !growStreamlineFromTo( streamline,
|
||||
neighbor.faceCenter( neighborFaceIdx ),
|
||||
neighbor.center(),
|
||||
flowVelocity,
|
||||
dominantPhaseOut ) )
|
||||
break;
|
||||
|
||||
// have we been here?
|
||||
if ( m_visitedCells.count( neighbor.gridLocalCellIndex() ) > 0 ) break;
|
||||
|
||||
// is this a well?
|
||||
if ( m_wellCells.count( neighbor.gridLocalCellIndex() ) > 0 ) break;
|
||||
|
||||
m_visitedCells.insert( neighbor.gridLocalCellIndex() );
|
||||
|
||||
// find the face with max flow where we should exit the cell
|
||||
cvf::StructGridInterface::FaceType exitFace = cvf::StructGridInterface::FaceType::NO_FACE;
|
||||
double maxRate = 0.0;
|
||||
|
||||
std::map<cvf::StructGridInterface::FaceType, double> rateMap;
|
||||
|
||||
for ( auto face : m_allFaces )
|
||||
{
|
||||
RiaDefines::PhaseType dummy;
|
||||
if ( face == neighborFaceIdx ) continue;
|
||||
double faceRate = m_dataAccess->combinedFaceValueByArea( neighbor, face, m_phases, dummy ) * direction;
|
||||
|
||||
if ( ( ( direction < 0.0 ) && ( faceRate < maxRate ) ) || ( ( direction > 0.0 ) && ( faceRate > maxRate ) ) )
|
||||
{
|
||||
exitFace = face;
|
||||
maxRate = faceRate;
|
||||
}
|
||||
|
||||
if ( face % 2 != 0 )
|
||||
rateMap[face] = -1.0 * faceRate * direction;
|
||||
else
|
||||
rateMap[face] = faceRate * direction;
|
||||
}
|
||||
|
||||
flowVelocity = std::abs( maxRate );
|
||||
faceIdx = exitFace;
|
||||
cell = neighbor;
|
||||
|
||||
// add seeds for other faces with flow > threshold
|
||||
for ( auto& kvp : rateMap )
|
||||
{
|
||||
if ( kvp.first == exitFace ) continue;
|
||||
if ( std::abs( kvp.second ) < m_flowThreshold ) continue;
|
||||
m_seeds.push_back( std::make_pair( neighbor.gridLocalCellIndex(), kvp.first ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimStreamlineGenerator2::growStreamlineFromTo( RimStreamline* streamline,
|
||||
cvf::Vec3d startPos,
|
||||
cvf::Vec3d endPos,
|
||||
double flowVelocity,
|
||||
RiaDefines::PhaseType dominantPhase )
|
||||
{
|
||||
double totDistance = endPos.pointDistance( startPos );
|
||||
|
||||
if ( totDistance < 0.1 ) return true;
|
||||
if ( flowVelocity < m_flowThreshold ) return false;
|
||||
|
||||
cvf::Vec3d direction = endPos - startPos;
|
||||
direction.normalize();
|
||||
direction *= flowVelocity;
|
||||
|
||||
int nSteps = (int)std::round( ( totDistance / flowVelocity ) / m_resolution );
|
||||
|
||||
cvf::Vec3d curpos = startPos;
|
||||
|
||||
for ( int i = 0; i < nSteps; i++ )
|
||||
{
|
||||
streamline->addTracerPoint( curpos, direction, dominantPhase );
|
||||
curpos = startPos + direction;
|
||||
|
||||
if ( streamline->size() > m_maxPoints ) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
@ -34,6 +34,7 @@ RimStreamlineGeneratorBase::RimStreamlineGeneratorBase( std::set<size_t>& wellCe
|
||||
, m_wellCells( wellCells )
|
||||
, m_dataAccess( nullptr )
|
||||
, m_maxPoints( 1 )
|
||||
, m_minLength( 1.0 )
|
||||
{
|
||||
}
|
||||
|
||||
@ -47,12 +48,13 @@ RimStreamlineGeneratorBase::~RimStreamlineGeneratorBase()
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimStreamlineGeneratorBase::setLimits( double flowThreshold, int maxDays, double resolutionInDays )
|
||||
void RimStreamlineGeneratorBase::setLimits( double flowThreshold, int maxDays, double resolutionInDays, double minimumLength )
|
||||
{
|
||||
m_flowThreshold = flowThreshold;
|
||||
m_maxDays = maxDays;
|
||||
m_resolution = resolutionInDays;
|
||||
m_maxPoints = maxDays / resolutionInDays;
|
||||
m_minLength = minimumLength;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
@ -41,7 +41,7 @@ public:
|
||||
RimStreamlineGeneratorBase( std::set<size_t>& wellCells );
|
||||
~RimStreamlineGeneratorBase();
|
||||
|
||||
void setLimits( double flowThreshold, int maxDays, double resolutionInDays );
|
||||
void setLimits( double flowThreshold, int maxDays, double resolutionInDays, double minimumLength );
|
||||
|
||||
void initGenerator( RimStreamlineDataAccess* dataAccess, std::list<RiaDefines::PhaseType> phases );
|
||||
|
||||
@ -52,6 +52,7 @@ protected:
|
||||
double m_flowThreshold;
|
||||
int m_maxDays;
|
||||
double m_resolution;
|
||||
double m_minLength;
|
||||
|
||||
size_t m_maxPoints;
|
||||
|
||||
|
@ -33,7 +33,7 @@
|
||||
#include "RimRegularLegendConfig.h"
|
||||
#include "RimStreamline.h"
|
||||
#include "RimStreamlineDataAccess.h"
|
||||
#include "RimStreamlineGenerator2.h"
|
||||
#include "RimStreamlineGenerator.h"
|
||||
|
||||
#include "RiaLogging.h"
|
||||
|
||||
@ -60,7 +60,6 @@ void AppEnum<RimStreamlineInViewCollection::VisualizationMode>::setUp()
|
||||
{
|
||||
addItem( RimStreamlineInViewCollection::VisualizationMode::ANIMATION, "ANIMATION", "Animation" );
|
||||
addItem( RimStreamlineInViewCollection::VisualizationMode::MANUAL, "MANUAL", "Manual control" );
|
||||
addItem( RimStreamlineInViewCollection::VisualizationMode::VECTORS, "VECTORS", "Vectors" );
|
||||
setDefault( RimStreamlineInViewCollection::VisualizationMode::ANIMATION );
|
||||
}
|
||||
|
||||
@ -407,7 +406,7 @@ void RimStreamlineInViewCollection::findStartCells( int
|
||||
{
|
||||
outInjectorCells.push_back( std::pair<QString, RigCell>( swdata->m_wellName, cell ) );
|
||||
}
|
||||
m_wellCellIds.insert( cell.mainGridCellIndex() );
|
||||
m_wellCellIds.insert( cell.gridLocalCellIndex() );
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -451,8 +450,8 @@ void RimStreamlineInViewCollection::updateStreamlines()
|
||||
if ( accessOk )
|
||||
{
|
||||
// setup the streamline generator to use
|
||||
RimStreamlineGenerator2 generator( m_wellCellIds );
|
||||
generator.setLimits( m_flowThreshold, m_maxDays, m_resolution );
|
||||
RimStreamlineGenerator generator( m_wellCellIds );
|
||||
generator.setLimits( m_flowThreshold, m_maxDays, m_resolution, m_lengthThreshold );
|
||||
generator.initGenerator( &dataAccess, phases() );
|
||||
|
||||
const int reverseDirection = -1.0;
|
||||
@ -464,7 +463,7 @@ void RimStreamlineInViewCollection::updateStreamlines()
|
||||
if ( m_useInjectors() ) seedsCount += seedCellsInjector.size();
|
||||
if ( m_useProducers() ) seedsCount += seedCellsProducer.size();
|
||||
|
||||
caf::ProgressInfo streamlineProgress( seedsCount, "Generating Streamlines" );
|
||||
caf::ProgressInfo streamlineProgress( seedsCount, "Generating streamlines, please wait..." );
|
||||
|
||||
// generate tracers for all injectors
|
||||
if ( m_useInjectors() )
|
||||
@ -492,16 +491,9 @@ void RimStreamlineInViewCollection::updateStreamlines()
|
||||
{
|
||||
if ( sline && sline->size() > 1 )
|
||||
{
|
||||
double distance = sline->tracer().totalDistance();
|
||||
|
||||
if ( distance >= m_lengthThreshold )
|
||||
{
|
||||
m_maxAnimationIndex = std::max( sline->size(), m_maxAnimationIndex );
|
||||
sline->generateStatistics();
|
||||
m_streamlines.push_back( sline );
|
||||
sline = nullptr;
|
||||
}
|
||||
if ( sline ) delete sline;
|
||||
m_maxAnimationIndex = std::max( sline->size(), m_maxAnimationIndex );
|
||||
m_streamlines.push_back( sline );
|
||||
sline = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@ -574,14 +566,10 @@ void RimStreamlineInViewCollection::defineUiOrdering( QString uiConfigName, caf:
|
||||
visualizationGroup->add( &m_animationSpeed );
|
||||
visualizationGroup->add( &m_tracerLength );
|
||||
}
|
||||
if ( m_visualizationMode() == VisualizationMode::MANUAL )
|
||||
else if ( m_visualizationMode() == VisualizationMode::MANUAL )
|
||||
{
|
||||
visualizationGroup->add( &m_animationIndex );
|
||||
}
|
||||
if ( m_visualizationMode() == VisualizationMode::VECTORS )
|
||||
{
|
||||
visualizationGroup->add( &m_scaleFactor );
|
||||
}
|
||||
|
||||
uiOrdering.skipRemainingFields();
|
||||
}
|
||||
@ -648,14 +636,8 @@ void RimStreamlineInViewCollection::fieldChangedByUi( const caf::PdmFieldHandle*
|
||||
const QVariant& oldValue,
|
||||
const QVariant& newValue )
|
||||
{
|
||||
if ( changedField == &m_animationSpeed || changedField == &m_animationIndex || changedField == &m_tracerLength )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( changedField == &m_visualizationMode &&
|
||||
qvariant_cast<int>( newValue ) != static_cast<int>( VisualizationMode::VECTORS ) &&
|
||||
qvariant_cast<int>( oldValue ) != static_cast<int>( VisualizationMode::VECTORS ) )
|
||||
if ( changedField == &m_animationSpeed || changedField == &m_animationIndex || changedField == &m_tracerLength ||
|
||||
changedField == &m_visualizationMode )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -46,8 +46,7 @@ public:
|
||||
enum class VisualizationMode
|
||||
{
|
||||
ANIMATION = 0,
|
||||
MANUAL,
|
||||
VECTORS
|
||||
MANUAL
|
||||
};
|
||||
using VisualizationModeEnum = caf::AppEnum<VisualizationMode>;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user