Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .integrated_tests.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
baselines:
bucket: geosx
baseline: integratedTests/baseline_integratedTests-pr4040-16993-1393f80
baseline: integratedTests/baseline_integratedTests-pr4077-17009-64cf777

allow_fail:
all: ''
Expand Down
3 changes: 3 additions & 0 deletions BASELINE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ This file is designed to track changes to the integrated test baselines.
Any developer who updates the baseline ID in the .integrated_tests.yaml file is expected to create an entry in this file with the pull request number, date, and their justification for rebaselining.
These notes should be in reverse-chronological order, and use the following time format: (YYYY-MM-DD).

PR #4077 (2026-06-19) <https://storage.googleapis.com/geosx/integratedTests/baseline_integratedTests-pr4077-17009-64cf777.tar.gz>
Add input mesh parameter to output Pvd results

PR #4040 (2026-06-16) <https://storage.googleapis.com/geosx/integratedTests/baseline_integratedTests-pr4040-16993-1393f80.tar.gz>
Move relperm driver to use new constitutive driver framework

Expand Down
4 changes: 3 additions & 1 deletion src/coreComponents/mesh/CellElementSubRegion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ void CellElementSubRegion::copyFromCellBlock( CellBlockABC const & cellBlock )
{
using ArrayType = camp::first< decltype( tupleOfTypes ) >;
auto const src = Wrapper< ArrayType >::cast( wrapper ).reference().toViewConst();
ArrayType & dst = this->registerWrapper( wrapper.getName(), std::make_unique< ArrayType >() ).reference();
ArrayType & dst = this->registerWrapper( wrapper.getName(), std::make_unique< ArrayType >() )
.setPlotLevel( wrapper.getPlotLevel() )
.reference();
// This is a hack since Array's copy ctor does not accept ArrayView source
dst.resize( ArrayType::NDIM, src.dims() );
std::copy( src.data(), src.data() + src.size(), dst.data() );
Expand Down
1 change: 1 addition & 0 deletions src/coreComponents/mesh/ElementSubRegionBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ ElementSubRegionBase::ElementSubRegionBase( string const & name, Group * const p

registerWrapper( viewKeyStruct::elementVolumeString(), &m_elementVolume ).
setPlotLevel( PlotLevel::LEVEL_1 );

}

ElementSubRegionBase::~ElementSubRegionBase()
Expand Down
16 changes: 15 additions & 1 deletion src/coreComponents/mesh/FaceElementSubRegion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include "FaceElementSubRegion.hpp"
#include "common/GEOS_RAJA_Interface.hpp"
#include "common/TypeDispatch.hpp"

#include "NodeManager.hpp"
#include "MeshLevel.hpp"
Expand Down Expand Up @@ -214,7 +215,20 @@ void FaceElementSubRegion::copyFromCellBlock( FaceBlockABC const & faceBlock )
m_newFaceElements.insert( i );
}

// TODO We still need to be able to import fields on the FaceElementSubRegion.
faceBlock.forExternalProperties( [&]( WrapperBase const & wrapper )
{
types::dispatch( types::ListofTypeList< types::StandardArrays >{}, [&]( auto tupleOfTypes )
{
using ArrayType = camp::first< decltype( tupleOfTypes ) >;
auto const src = Wrapper< ArrayType >::cast( wrapper ).reference().toViewConst();
ArrayType & dst = this->registerWrapper( wrapper.getName(), std::make_unique< ArrayType >() )
.setPlotLevel( wrapper.getPlotLevel() )
.reference();
// This is a hack since Array's copy ctor does not accept ArrayView source
dst.resize( ArrayType::NDIM, src.dims() );
std::copy( src.data(), src.data() + src.size(), dst.data() );
}, wrapper );
} );
}

void FaceElementSubRegion::setupRelatedObjectsInRelations( MeshLevel const & mesh )
Expand Down
26 changes: 26 additions & 0 deletions src/coreComponents/mesh/generators/FaceBlock.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,37 @@ class FaceBlock : public FaceBlockABC
*/
void set2dElemsToCollocatedNodesBuckets( ArrayOfArrays< array1d< globalIndex > > && collocatedNodesBuckets );

/**
* @brief Add a property to the FaceBlock.
* @tparam T type of the property
* @param[in] propertyName the name of the property
* @return a non-const reference to the property
*/
template< typename T >
T & addProperty( string const & propertyName )
{
m_externalPropertyNames.emplace_back( propertyName );
return this->registerWrapper< T >( propertyName ).reference();
}

private:

std::list< dataRepository::WrapperBase const * > getExternalProperties() const override
{
std::list< dataRepository::WrapperBase const * > result;
for( string const & externalPropertyName : m_externalPropertyNames )
{
result.push_back( &this->getWrapperBase( externalPropertyName ) );
}
return result;
}

localIndex m_num2dElements;
localIndex m_num2dFaces;

/// Names of the properties registered from an external mesh
string_array m_externalPropertyNames;

ArrayOfArrays< localIndex > m_2dElemToNodes;
ArrayOfArrays< localIndex > m_2dElemToEdges;
ArrayOfArrays< localIndex > m_2dElemToFaces;
Expand Down
27 changes: 27 additions & 0 deletions src/coreComponents/mesh/generators/FaceBlockABC.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include "dataRepository/Group.hpp"
#include "common/DataTypes.hpp"

#include <list>

namespace geos
{

Expand Down Expand Up @@ -170,6 +172,31 @@ class FaceBlockABC : public dataRepository::Group
* @return The mapping relationship as an array.
*/
virtual array1d< globalIndex > localToGlobalMap() const = 0;

/**
* @brief Apply a lambda to each external (passthrough) property registered on this face block.
* @tparam LAMBDA type of the lambda
* @param lambda the lambda to apply; receives a @p dataRepository::WrapperBase const &
*/
template< typename LAMBDA >
void forExternalProperties( LAMBDA && lambda ) const
{
for( auto * wrapperBase : this->getExternalProperties() )
{
lambda( *wrapperBase );
}
}

private:

/**
* @brief Returns the list of external (passthrough) property wrappers.
* @return List of pointers to external property wrappers.
*/
virtual std::list< dataRepository::WrapperBase const * > getExternalProperties() const
{
return {};
}
};

}
Expand Down
119 changes: 118 additions & 1 deletion src/coreComponents/mesh/generators/VTKFaceBlockUtilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "mesh/generators/VTKUtilities.hpp"
#include "mesh/generators/CollocatedNodes.hpp"

#include "common/MpiWrapper.hpp"
#include "dataRepository/Group.hpp"

#include <vtkCell.h>
Expand Down Expand Up @@ -624,10 +625,116 @@ array1d< globalIndex > buildLocalToGlobal( vtkIdTypeArray const * faceMeshCellGl
}


/**
* @brief Register a passthrough field on a FaceBlock, ensuring MPI consistency.
*
* On ranks where the VTK array is absent (e.g. empty fracture partition), an empty
* wrapper of the correct type is still registered so all ranks have the same wrappers
* during ghost communication.
*/
static void writeFacePassthroughField( vtkDataSet & faceMesh,
localIndex const numElems,
string const & fieldName,
FaceBlock & faceBlock )
{
vtkDataArray * srcArray = faceMesh.GetCellData()->GetArray( fieldName.c_str() );

// Collectively determine type: isReal (1/0) and numComp, or -1 if absent on this rank.
int localIsReal = ( srcArray != nullptr )
? ( ( srcArray->GetDataType() == VTK_FLOAT || srcArray->GetDataType() == VTK_DOUBLE ) ? 1 : 0 )
: -1;
int localNumComp = ( srcArray != nullptr ) ? srcArray->GetNumberOfComponents() : -1;

int const globalIsReal = MpiWrapper::allReduce( localIsReal, MpiWrapper::Reduction::Max, MPI_COMM_GEOS );
int const globalNumComp = MpiWrapper::allReduce( localNumComp, MpiWrapper::Reduction::Max, MPI_COMM_GEOS );

if( globalNumComp == -1 )
{
// Field absent on every rank — not a passthrough field of this mesh.
GEOS_WARNING( GEOS_FMT( "Passthrough field '{}' not found in any fracture VTK CellData; skipping.", fieldName ) );
return;
}

if( srcArray == nullptr )
{
// This rank has no data for this field (empty partition). Register an empty
// wrapper so ghost communication finds the same wrapper on all ranks.
if( globalIsReal == 1 )
{
if( globalNumComp == 1 )
{
faceBlock.addProperty< array1d< real64 > >( fieldName ).resize( numElems );
}
else
{
faceBlock.addProperty< array2d< real64 > >( fieldName ).resize( numElems, globalNumComp );
}
}
else
{
if( globalNumComp == 1 )
{
faceBlock.addProperty< array1d< integer > >( fieldName ).resize( numElems );
}
else
{
faceBlock.addProperty< array2d< integer > >( fieldName ).resize( numElems, globalNumComp );
}
}
faceBlock.getWrapperBase( fieldName ).setPlotLevel( dataRepository::PlotLevel::LEVEL_1 );
return;
}

// This rank has the data — fill it.
bool const isReal = ( globalIsReal == 1 );
if( isReal )
{
if( globalNumComp == 1 )
{
array1d< real64 > & dst = faceBlock.addProperty< array1d< real64 > >( fieldName );
dst.resize( numElems );
faceBlock.getWrapperBase( fieldName ).setPlotLevel( dataRepository::PlotLevel::LEVEL_1 );
for( localIndex i = 0; i < numElems; ++i )
dst[i] = static_cast< real64 >( srcArray->GetTuple1( i ) );
}
else
{
array2d< real64 > & dst = faceBlock.addProperty< array2d< real64 > >( fieldName );
dst.resize( numElems, globalNumComp );
faceBlock.getWrapperBase( fieldName ).setPlotLevel( dataRepository::PlotLevel::LEVEL_1 );
for( localIndex i = 0; i < numElems; ++i )
for( int c = 0; c < globalNumComp; ++c )
dst( i, c ) = static_cast< real64 >( srcArray->GetComponent( i, c ) );
}
}
else
{
if( globalNumComp == 1 )
{
array1d< integer > & dst = faceBlock.addProperty< array1d< integer > >( fieldName );
dst.resize( numElems );
faceBlock.getWrapperBase( fieldName ).setPlotLevel( dataRepository::PlotLevel::LEVEL_1 );
for( localIndex i = 0; i < numElems; ++i )
dst[i] = static_cast< integer >( srcArray->GetTuple1( i ) );
}
else
{
array2d< integer > & dst = faceBlock.addProperty< array2d< integer > >( fieldName );
dst.resize( numElems, globalNumComp );
faceBlock.getWrapperBase( fieldName ).setPlotLevel( dataRepository::PlotLevel::LEVEL_1 );
for( localIndex i = 0; i < numElems; ++i )
for( int c = 0; c < globalNumComp; ++c )
dst( i, c ) = static_cast< integer >( srcArray->GetComponent( i, c ) );
}
}
}


void importFractureNetwork( string const & faceBlockName,
vtkSmartPointer< vtkDataSet > faceMesh,
vtkSmartPointer< vtkDataSet > mesh,
CellBlockManager & cellBlockManager )
CellBlockManager & cellBlockManager,
Span< string const > passthroughFieldNames )
{
ArrayOfArrays< localIndex > const faceToNodes = cellBlockManager.getFaceToNodes();
vtk::internal::ElementToFace const elemToFaces( cellBlockManager.getCellBlocks() );
Expand Down Expand Up @@ -686,6 +793,16 @@ void importFractureNetwork( string const & faceBlockName,
buildCollocatedNodesBucketsOf2dElemsMap( build2dElemTo2dNodes( faceMesh ),
buildCollocatedNodesMap( collocatedNodes ) )
);

// Import passthrough fields from the face mesh CellData.
// writeFacePassthroughField performs an MPI_Allreduce to determine the field type
// collectively, ensuring all ranks register the same wrappers even when some ranks
// have an empty fracture partition (needed for ghost communication consistency).
localIndex const numElems = LvArray::integerConversion< localIndex >( num2dElements );
for( string const & fieldName : passthroughFieldNames )
{
writeFacePassthroughField( *faceMesh, numElems, fieldName, faceBlock );
}
}

} // end of namespace
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "CellBlockManager.hpp"

#include "common/DataTypes.hpp"
#include "common/Span.hpp"

#include <vtkDataSet.h>
#include <vtkSmartPointer.h>
Expand All @@ -32,11 +33,13 @@ namespace geos::vtk
* @param faceMesh[in] The vtk mesh for the face block.
* @param mesh[in] The vtk volumic mesh.
* @param cellBlockManager[inout] The cell block manager that will receive the face block information.
* @param passthroughFieldNames[in] Names of VTK CellData arrays to import and pass through to output as-is.
*/
void importFractureNetwork( string const & faceBlockName,
vtkSmartPointer< vtkDataSet > faceMesh,
vtkSmartPointer< vtkDataSet > mesh,
CellBlockManager & cellBlockManager );
CellBlockManager & cellBlockManager,
Span< string const > passthroughFieldNames = {} );
}

#endif // include guard
12 changes: 10 additions & 2 deletions src/coreComponents/mesh/generators/VTKMeshGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "VTKMeshGenerator.hpp"

#include "common/DataTypes.hpp"
#include "common/Span.hpp"
#include "mesh/ExternalDataSourceManager.hpp"
#include "mesh/LogLevelsInfo.hpp"
#include "mesh/generators/VTKFaceBlockUtilities.hpp"
Expand Down Expand Up @@ -98,6 +99,11 @@ VTKMeshGenerator::VTKMeshGenerator( string const & name,
setInputFlag( InputFlags::OPTIONAL ).
setDescription( "Name of the VTK data source" );

registerWrapper( viewKeyStruct::passthroughFieldsString(), &m_passthroughFieldNames ).
setRTTypeName( rtTypes::CustomTypes::groupNameRefArray ).
setInputFlag( InputFlags::OPTIONAL ).
setDescription( "Names of VTK CellData arrays to import and pass through to output as-is, without mapping to a GEOS field." );

addLogLevel< logInfo::VTKSteps >();
}

Expand Down Expand Up @@ -234,7 +240,8 @@ void VTKMeshGenerator::fillCellBlockManager( CellBlockManager & cellBlockManager
writeNodes( getLogLevel(), *m_vtkMesh, m_nodesetNames, cellBlockManager, m_translate, m_scale );

GEOS_LOG_LEVEL_RANK_0( logInfo::VTKSteps, GEOS_FMT( "{} '{}': writing cells...", catalogName(), getName() ) );
writeCells( getLogLevel(), *m_vtkMesh, m_cellMap, m_structuredIndexAttributeName, cellBlockManager );
writeCells( getLogLevel(), *m_vtkMesh, m_cellMap, m_structuredIndexAttributeName, cellBlockManager,
Span< string const >( m_passthroughFieldNames.begin(), m_passthroughFieldNames.end() ) );

GEOS_LOG_LEVEL_RANK_0( logInfo::VTKSteps, GEOS_FMT( "{} '{}': writing surfaces...", catalogName(), getName() ) );
writeSurfaces( getLogLevel(), *m_vtkMesh, m_cellMap, cellBlockManager );
Expand All @@ -248,7 +255,8 @@ void VTKMeshGenerator::fillCellBlockManager( CellBlockManager & cellBlockManager

for( auto const & [name, mesh]: m_faceBlockMeshes )
{
vtk::importFractureNetwork( name, mesh, m_vtkMesh, cellBlockManager );
vtk::importFractureNetwork( name, mesh, m_vtkMesh, cellBlockManager,
Span< string const >( m_passthroughFieldNames.begin(), m_passthroughFieldNames.end() ) );
}

GEOS_LOG_LEVEL_RANK_0( logInfo::VTKSteps, GEOS_FMT( "{} '{}': done!", catalogName(), getName() ) );
Expand Down
4 changes: 4 additions & 0 deletions src/coreComponents/mesh/generators/VTKMeshGenerator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ class VTKMeshGenerator : public ExternalMeshGeneratorBase
constexpr static char const * partitionFractureWeightString() { return "partitionFractureWeight"; }
constexpr static char const * useGlobalIdsString() { return "useGlobalIds"; }
constexpr static char const * dataSourceString() { return "dataSourceName"; }
constexpr static char const * passthroughFieldsString() { return "passThroughFields"; }
};

struct groupKeyStruct
Expand Down Expand Up @@ -180,6 +181,9 @@ class VTKMeshGenerator : public ExternalMeshGeneratorBase
/// Repository of VTK objects
VTKHierarchicalDataSource * m_dataSource;

/// Names of VTK CellData arrays to import and pass through to output as-is
string_array m_passthroughFieldNames;

};

} // namespace geos
Expand Down
Loading