diff --git a/src/coreComponents/mesh/WellElementRegion.cpp b/src/coreComponents/mesh/WellElementRegion.cpp index f9f787433ca..0ded06aed11 100644 --- a/src/coreComponents/mesh/WellElementRegion.cpp +++ b/src/coreComponents/mesh/WellElementRegion.cpp @@ -71,18 +71,21 @@ void WellElementRegion::generateWell( MeshLevel & mesh, // 1) select the local perforations based on connectivity to the local reservoir elements subRegion.connectPerforationsToMeshElements( mesh, lineBlock, geomTol ); globalIndex const matchedPerforations = MpiWrapper::sum( perforationData->size() ); - //should we do this check in the well generator? Here we don't have the wellGeneratorDataContext - GEOS_THROW_IF( matchedPerforations != numPerforationsGlobal, - GEOS_FMT( "Invalid mapping perforation-to-element in {} {}. This happens when GEOSX cannot match " - "a perforation with a reservoir element. There are two common reasons for this error:\n" - " 1- The most common reason for this error is that a perforation is on a section of " - " the well polyline located outside the domain.\n" - " 2- This error can also happen if a perforation falls on a mesh face or a mesh vertex." - " Please try to move the perforation slightly (to the interior of the perforated cell) " - "to see if it fixes the problem.", - InternalWellGenerator::catalogName(), - getWellGeneratorName() ), - InputError, getDataContext() ); + // TODO: should we do this check in the well generator? Here we don't have the wellGeneratorDataContext + GEOS_THROW_IF_LT_MSG( matchedPerforations, numPerforationsGlobal, + GEOS_FMT( "Invalid mapping in {} {}: {} perforation(s) not found in domain. Verify perforations are " + "within the domain boundaries.", + InternalWellGenerator::catalogName(), + getWellGeneratorName(), + numPerforationsGlobal - matchedPerforations ), + InputError, getDataContext() ); + + GEOS_THROW_IF_GT_MSG( matchedPerforations, numPerforationsGlobal, + GEOS_FMT( "Invalid mapping in {} {}: Perforations falsely matched to multiple cells, likely " + "indicating bad mesh quality.", + InternalWellGenerator::catalogName(), + getWellGeneratorName() ), + InputError, getDataContext() ); // 2) classify well elements based on connectivity to local mesh partition array1d< integer > elemStatusGlobal; diff --git a/src/coreComponents/mesh/WellElementSubRegion.cpp b/src/coreComponents/mesh/WellElementSubRegion.cpp index 401eb4b239c..6b2699487b6 100644 --- a/src/coreComponents/mesh/WellElementSubRegion.cpp +++ b/src/coreComponents/mesh/WellElementSubRegion.cpp @@ -915,6 +915,8 @@ void WellElementSubRegion::connectPerforationsToMeshElements( MeshLevel & mesh, lineBlock.getName(), iperfGlobal, location[0], location[1], location[2] ) ); + bool perfFound = false; + localIndex erStart = -1, erEnd = -1; localIndex const targetRegionIndex = elemManager.getRegions().getIndex( perfTargetRegionGlobal[iperfGlobal] ); @@ -936,14 +938,28 @@ void WellElementSubRegion::connectPerforationsToMeshElements( MeshLevel & mesh, localIndex esrMatched = -1; localIndex eiMatched = -1; globalIndex giMatched = -1; - integer const resElemFound = searchLocalElements( mesh, - location, - m_searchDepth, - er, - esrMatched, - eiMatched, - giMatched, - geomTol ); + integer resElemFound = searchLocalElements( mesh, + location, + m_searchDepth, + er, + esrMatched, + eiMatched, + giMatched, + geomTol ); + + // Multiple ranks can match the element of the well perforation occurs at the interface of 2 or multiple + // cells (face, edge or even vertex). We will arbitrarily assign the perforation to the process with the + // lowest global id + integer const matchedRanks = MpiWrapper::sum( resElemFound ? 1 : 0 ); + if( 1 < matchedRanks ) + { + globalIndex const minId = MpiWrapper::min( giMatched < 0 ? LvArray::NumericLimits< globalIndex >::max : giMatched ); + // If this process does not have the lowest Id we will say it hasn't found the element + if( minId != giMatched ) + { + resElemFound = 0; + } + } // if the element was found if( resElemFound ) @@ -968,9 +984,17 @@ void WellElementSubRegion::connectPerforationsToMeshElements( MeshLevel & mesh, } // if one rank has found the element, all ranks exit the search - if( MpiWrapper::allReduce( resElemFound, MpiWrapper::Reduction::LogicalOr )) + perfFound = perfFound || ( 0 < matchedRanks ); + if( perfFound ) break; } + + // We will issue this only as a warning to capture all failed perforations. + // If the feailure is irrecoverable, an error will be issued in WellElementRegion + GEOS_WARNING_IF( !perfFound && MpiWrapper::commRank() == 0, + GEOS_FMT( "Failed to locate perforation {} for well {} with location ({}, {}, {}). " + "This is because the perforation is on a section of the well polyline located outside the domain.", + perfName[iperfGlobal], lineBlock.getName(), location[0], location[1], location[2] )); } // set the size based on the number of perforations matched with local reservoir elements