mirror of
https://github.com/OPM/ResInsight.git
synced 2026-08-27 05:37:21 -05:00
RimEnsembleFileSetCollection, RimJobCollection, RimQuickAccessCollection, RimRefinementRegionCollection and RimFractureTemplateCollection now inherit from the templated caf::PdmObjectCollection<T> base, reusing the shared add/insert/delete/items plumbing introduced for RimValveTemplateCollection. XML field keywords are preserved on every collection so existing project files continue to load.
266 lines
11 KiB
C++
266 lines
11 KiB
C++
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (C) 2026 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 "RimRefinementRegionCollection.h"
|
|
|
|
#include "RigNoRefinement.h"
|
|
#include "RigNonUniformRefinement.h"
|
|
|
|
#include "Rim3dView.h"
|
|
#include "RimEclipseCase.h"
|
|
#include "RimRefinementRegion.h"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
|
|
CAF_PDM_SOURCE_INIT( RimRefinementRegionCollection, "RefinementRegionCollection" );
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimRefinementRegionCollection::RimRefinementRegionCollection()
|
|
{
|
|
CAF_PDM_InitObject( "Refinement Regions", ":/CellFilter_Range.png" );
|
|
|
|
CAF_PDM_InitField( &m_isActive, "IsActive", true, "Show Regions in 3D View" );
|
|
m_isActive.uiCapability()->setUiHidden( true );
|
|
|
|
CAF_PDM_InitFieldNoDefault( &m_items, "Regions", "Regions" );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimRefinementRegionCollection::isActive() const
|
|
{
|
|
return m_isActive();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
bool RimRefinementRegionCollection::shouldBeVisibleInTree() const
|
|
{
|
|
return !isEmpty();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<RimRefinementRegion*> RimRefinementRegionCollection::regions() const
|
|
{
|
|
return items();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::vector<RimRefinementRegion*> RimRefinementRegionCollection::activeRegions() const
|
|
{
|
|
std::vector<RimRefinementRegion*> result;
|
|
for ( auto r : m_items )
|
|
{
|
|
if ( r && r->isActive() ) result.push_back( r );
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimRefinementRegion* RimRefinementRegionCollection::addNewRegion( RimEclipseCase* eclipseCase )
|
|
{
|
|
auto* region = new RimRefinementRegion();
|
|
region->setRegionName( QString( "Region %1" ).arg( count() + 1 ) );
|
|
addItem( region );
|
|
|
|
// Defaults depend on the case's grid; set after the region is inserted into the tree so
|
|
// that view/case ancestor lookups work inside the region.
|
|
region->setDefaultsFromCase( eclipseCase );
|
|
|
|
// Visibility of this collection in the parent view's tree depends on m_items being non-empty,
|
|
// so refresh the parent so the folder appears when the first region is added.
|
|
if ( auto* view = firstAncestorOrThisOfType<Rim3dView>() ) view->updateConnectedEditors();
|
|
return region;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimRefinementRegionCollection::addRegion( RimRefinementRegion* region )
|
|
{
|
|
addItem( region );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimRefinementRegionCollection::removeRegion( RimRefinementRegion* region )
|
|
{
|
|
if ( !region ) return;
|
|
deleteItem( region );
|
|
if ( auto* view = firstAncestorOrThisOfType<Rim3dView>() ) view->updateConnectedEditors();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
/// Refresh the parent view's tree so the collection folder disappears once the last region is
|
|
/// removed via the generic delete-item action.
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimRefinementRegionCollection::onChildDeleted( caf::PdmChildArrayFieldHandle* childArray,
|
|
std::vector<caf::PdmObjectHandle*>& referringObjects )
|
|
{
|
|
if ( auto* view = firstAncestorOrThisOfType<Rim3dView>() ) view->updateConnectedEditors();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
caf::PdmFieldHandle* RimRefinementRegionCollection::objectToggleField()
|
|
{
|
|
return &m_isActive;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
void RimRefinementRegionCollection::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
|
|
{
|
|
if ( auto view = firstAncestorOrThisOfType<Rim3dView>() )
|
|
{
|
|
view->scheduleCreateDisplayModelAndRedraw();
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::expected<std::unique_ptr<RigRefinement>, QString>
|
|
RimRefinementRegionCollection::combineRefinements( const caf::VecIjk0& sectorMin,
|
|
const caf::VecIjk0& sectorMax,
|
|
const std::vector<RimRefinementRegion*>& regions )
|
|
{
|
|
cvf::Vec3st sectorSize( sectorMax.x() - sectorMin.x() + 1, sectorMax.y() - sectorMin.y() + 1, sectorMax.z() - sectorMin.z() + 1 );
|
|
|
|
if ( regions.empty() )
|
|
{
|
|
return std::unique_ptr<RigRefinement>( std::make_unique<RigNoRefinement>( sectorSize ) );
|
|
}
|
|
|
|
// Sector bounds, 1-based, for error messages and validation
|
|
int sectorMinI = static_cast<int>( sectorMin.x() ) + 1;
|
|
int sectorMinJ = static_cast<int>( sectorMin.y() ) + 1;
|
|
int sectorMinK = static_cast<int>( sectorMin.z() ) + 1;
|
|
int sectorMaxI = static_cast<int>( sectorMax.x() ) + 1;
|
|
int sectorMaxJ = static_cast<int>( sectorMax.y() ) + 1;
|
|
int sectorMaxK = static_cast<int>( sectorMax.z() ) + 1;
|
|
|
|
for ( auto* r : regions )
|
|
{
|
|
if ( !r ) continue;
|
|
auto err = r->validateWithinSector( sectorMinI, sectorMinJ, sectorMinK, sectorMaxI, sectorMaxJ, sectorMaxK );
|
|
if ( !err.isEmpty() ) return std::unexpected( err );
|
|
}
|
|
|
|
auto result = std::make_unique<RigNonUniformRefinement>( sectorSize );
|
|
|
|
auto fractionsEqual = []( const std::vector<double>& a, const std::vector<double>& b )
|
|
{
|
|
if ( a.size() != b.size() ) return false;
|
|
for ( size_t i = 0; i < a.size(); ++i )
|
|
{
|
|
if ( std::abs( a[i] - b[i] ) > 1e-9 ) return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
const char* dimLabel[3] = { "I", "J", "K" };
|
|
const size_t sectorMin0[3] = { sectorMin.x(), sectorMin.y(), sectorMin.z() };
|
|
|
|
// For each dimension, track which region "owns" each sector index so we can give
|
|
// meaningful error messages on overlap.
|
|
std::vector<RimRefinementRegion*> ownerI( sectorSize.x(), nullptr );
|
|
std::vector<RimRefinementRegion*> ownerJ( sectorSize.y(), nullptr );
|
|
std::vector<RimRefinementRegion*> ownerK( sectorSize.z(), nullptr );
|
|
std::vector<RimRefinementRegion*>* owners[3] = { &ownerI, &ownerJ, &ownerK };
|
|
|
|
size_t regionStarts[3];
|
|
size_t regionEnds[3];
|
|
|
|
for ( auto* region : regions )
|
|
{
|
|
if ( !region ) continue;
|
|
auto regionRefinement = region->effectiveRefinement();
|
|
if ( !regionRefinement ) continue;
|
|
|
|
auto regMin = region->ijkMin();
|
|
auto regMax = region->ijkMax();
|
|
|
|
regionStarts[0] = regMin.x();
|
|
regionStarts[1] = regMin.y();
|
|
regionStarts[2] = regMin.z();
|
|
regionEnds[0] = regMax.x();
|
|
regionEnds[1] = regMax.y();
|
|
regionEnds[2] = regMax.z();
|
|
|
|
for ( size_t d = 0; d < 3; ++d )
|
|
{
|
|
auto dim = static_cast<RigRefinement::Dimension>( d );
|
|
|
|
for ( size_t absIdx = regionStarts[d]; absIdx <= regionEnds[d]; ++absIdx )
|
|
{
|
|
size_t sectorRelIdx = absIdx - sectorMin0[d];
|
|
size_t regionRelIdx = absIdx - regionStarts[d];
|
|
|
|
const auto& newFracs = regionRefinement->cumulativeFractions( dim, regionRelIdx );
|
|
|
|
// If this cell is still default identity {1.0}, accept the region's fractions unconditionally.
|
|
const auto& existing = result->cumulativeFractions( dim, sectorRelIdx );
|
|
bool existingIsIdentity = ( existing.size() == 1 && std::abs( existing[0] - 1.0 ) < 1e-12 );
|
|
|
|
if ( existingIsIdentity )
|
|
{
|
|
result->setCumulativeFractions( dim, sectorRelIdx, newFracs );
|
|
( *owners[d] )[sectorRelIdx] = region;
|
|
}
|
|
else
|
|
{
|
|
// Already claimed by another region. Allow if the refinement matches exactly.
|
|
if ( !fractionsEqual( existing, newFracs ) )
|
|
{
|
|
auto* prevOwner = ( *owners[d] )[sectorRelIdx];
|
|
return std::unexpected( QString( "Refinement regions '%1' and '%2' both refine %3-index %4 with "
|
|
"different subdivisions. Regions must have disjoint projections on each "
|
|
"axis (or identical refinement on shared indices)." )
|
|
.arg( prevOwner ? prevOwner->regionName() : QString( "<unknown>" ) )
|
|
.arg( region->regionName() )
|
|
.arg( dimLabel[d] )
|
|
.arg( static_cast<int>( absIdx ) + 1 ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !result->hasRefinement() )
|
|
{
|
|
return std::unique_ptr<RigRefinement>( std::make_unique<RigNoRefinement>( sectorSize ) );
|
|
}
|
|
|
|
return std::unique_ptr<RigRefinement>( std::move( result ) );
|
|
}
|