Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
20b501c
add functions to collect included XML files
kdrienCG Mar 20, 2026
3db8b1a
add archiveInputDeck function
kdrienCG Mar 20, 2026
8afead3
call archiveInputDeck() in ProblemManager
kdrienCG Mar 20, 2026
25c325b
modify archive directory name
kdrienCG Mar 20, 2026
1cf33d5
add tests for collectIncluded functions
kdrienCG Mar 24, 2026
a4b35d0
fix typo in archiveInputDeck documentation
kdrienCG Mar 24, 2026
c873915
fix typo in collectIncludedRecursive documentation
kdrienCG Mar 24, 2026
6efbe14
add missing header includes
kdrienCG Mar 24, 2026
140aa51
add filter on collectIncluded iteration
kdrienCG Mar 25, 2026
9ed7c03
add MPI rank 0 condition for archiveInputDeck call
kdrienCG Mar 25, 2026
5c30635
add output directory invariant for archiveInputDeck
kdrienCG Mar 25, 2026
0c5fb2d
add tests for archiveInputDeck
kdrienCG Mar 25, 2026
bf1ca66
modify archive's logic to flatten inputs
kdrienCG Apr 10, 2026
6b35d1b
strip metadata attributes from the archived XML
kdrienCG Apr 10, 2026
a61d8c7
sort XML attributes in the archived XML
kdrienCG Apr 10, 2026
8738084
copy schema.xsd to the archive
kdrienCG Apr 10, 2026
93e2806
uncrustify
kdrienCG Apr 10, 2026
dbfa0fb
relocate archiveInputDeck call to generate the XSD schema
kdrienCG Apr 15, 2026
5a7c196
add command line option to trigger the archiving
kdrienCG Apr 16, 2026
d745bf6
add levels to archiving command line option
kdrienCG Apr 16, 2026
f8acdb4
Merge branch 'develop' into feature/kdrienCG/archiveInputDeck
kdrienCG Apr 16, 2026
c97394d
remove surrounding characters in a comment
kdrienCG Apr 16, 2026
aa5a851
set default archive strategy level to 1
kdrienCG Apr 17, 2026
7041f73
remove XSD schema generation
kdrienCG Apr 17, 2026
135e3a9
relocate archiving in the ProblemManager
kdrienCG Apr 17, 2026
377ebdf
log archive's creation
kdrienCG Apr 17, 2026
d498048
add option to copy the XSD schema
kdrienCG May 6, 2026
6e229b8
modify order of schema candidates
kdrienCG May 12, 2026
c388276
add missing comma
kdrienCG May 19, 2026
81084b8
remove collectIncluded* methods
kdrienCG May 19, 2026
4eb17c7
add archive command line parameter to quick start example
kdrienCG May 19, 2026
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
3 changes: 3 additions & 0 deletions src/cmake/GeosxConfig.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ endif()

set( GEOS_CMAKE_BUILD_TYPE "\"${CMAKE_BUILD_TYPE}\"" )

set( GEOS_SCHEMA_INSTALL_PATH "${CMAKE_INSTALL_FULL_DATAROOTDIR}/${CMAKE_PROJECT_NAME}/schema/schema.xsd" )
set( GEOS_SCHEMA_SOURCE_PATH "${CMAKE_SOURCE_DIR}/coreComponents/schema/schema.xsd" )

configure_file( ${CMAKE_SOURCE_DIR}/coreComponents/common/GeosxConfig.hpp.in
${CMAKE_BINARY_DIR}/include/common/GeosxConfig.hpp )

Expand Down
6 changes: 6 additions & 0 deletions src/coreComponents/common/GeosxConfig.hpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@
/// CMake option CMAKE_BUILD_TYPE
#cmakedefine GEOS_CMAKE_BUILD_TYPE @GEOS_CMAKE_BUILD_TYPE@

/// Absolute path to the XSD schema in the install tree
#cmakedefine GEOS_SCHEMA_INSTALL_PATH "@GEOS_SCHEMA_INSTALL_PATH@"

/// Absolute path to the XSD schema in the source tree (development builds)
#cmakedefine GEOS_SCHEMA_SOURCE_PATH "@GEOS_SCHEMA_SOURCE_PATH@"

/// The type that localIndex will be aliased to.
#define GEOS_LOCALINDEX_TYPE @GEOS_LOCALINDEX_TYPE@

Expand Down
3 changes: 3 additions & 0 deletions src/coreComponents/common/initializeEnvironment.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ struct CommandLineOptions

/// Print memory usage in data repository
real64 printMemoryUsage = -1.0;

/// Set the archiving level
integer archiveInputDeck = 1;
Copy link
Copy Markdown
Contributor

@bd713 bd713 May 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feature will be incredibly useful in our production environment. That said, I would personally keep the existing GEOS default behavior (no archiving) unchanged for regular usage, CI, etc.
Same for example as CSV output, which isn't on by default.

};

/**
Expand Down
2 changes: 2 additions & 0 deletions src/coreComponents/fileIO/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Contains:
#
set( fileIO_headers
LogLevelsInfo.hpp
Outputs/ArchiveInputDeck.hpp
Outputs/BlueprintOutput.hpp
Outputs/MemoryStatsOutput.hpp
Outputs/OutputBase.hpp
Expand All @@ -43,6 +44,7 @@ set( fileIO_headers
# Specify all sources
#
set( fileIO_sources
Outputs/ArchiveInputDeck.cpp
Outputs/BlueprintOutput.cpp
Outputs/MemoryStatsOutput.cpp
Outputs/OutputBase.cpp
Expand Down
252 changes: 252 additions & 0 deletions src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
/*
* ------------------------------------------------------------------------------------------------------------
* SPDX-License-Identifier: LGPL-2.1-only
*
* Copyright (c) 2016-2024 Lawrence Livermore National Security LLC
* Copyright (c) 2018-2024 TotalEnergies
* Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University
* Copyright (c) 2023-2024 Chevron
* Copyright (c) 2019- GEOS/GEOSX Contributors
* All rights reserved
*
* See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details.
* ------------------------------------------------------------------------------------------------------------
*/

/**
* @file ArchiveInputDeck.cpp
*/

#include "ArchiveInputDeck.hpp"

#include "common/GeosxConfig.hpp"
#include "common/Path.hpp"
#include "common/format/Format.hpp"
#include "common/logger/Logger.hpp"
#include "dataRepository/xmlWrapper.hpp"

#include <algorithm>
#include <chrono>
#include <filesystem>
#include <system_error>

namespace geos
{

using namespace dataRepository;

namespace archiveInputDeck
{

namespace
{

string makeTimestamp()
{
auto const now = std::chrono::system_clock::now();
auto const time_t_now = std::chrono::system_clock::to_time_t( now );
std::ostringstream timestampStream;
timestampStream << std::put_time( std::localtime( &time_t_now ), "%Y%m%d_%H%M%S" );
return timestampStream.str();
}

void stripMetadataAttributes( xmlWrapper::xmlNode node )
{
node.remove_attribute( xmlWrapper::filePathString );
node.remove_attribute( xmlWrapper::charOffsetString );

for( xmlWrapper::xmlNode child : node.children() )
{
stripMetadataAttributes( child );
}
}

void reorderTags( xmlWrapper::xmlNode rootNode, string_array const & tagOrder )
{
xmlWrapper::xmlNode lastInserted;
for( string const & tagName : tagOrder )
{
xmlWrapper::xmlNode tag = rootNode.child( tagName.c_str() );
if( !tag )
{
continue;
}

lastInserted ? rootNode.insert_move_after( tag, lastInserted )
: rootNode.append_move( tag );

lastInserted = tag;
}

// ProblemManager's order list doesn't provide every XML tags available in GEOS
// so we put the missing ones below the ones it provides.
// And sort them alphabetically
stdVector< string > missingTags;

for( xmlWrapper::xmlNode const & tag : rootNode.children() )
{
string const & tagName = tag.name();

if( std::find( tagOrder.begin(), tagOrder.end(), tag.name() ) == tagOrder.end() )
{
missingTags.push_back( tagName );
}
}

std::sort( missingTags.begin(), missingTags.end() );

for( string const & tagName : missingTags )
{
xmlWrapper::xmlNode tag = rootNode.child( tagName.c_str() );

if( tag )
{
rootNode.append_move( tag );
}
}
}

void sortAttributes( xmlWrapper::xmlNode node )
{
stdVector< std::pair< string, string > > attributes;
for( xmlWrapper::xmlAttribute attr = node.first_attribute();
attr;
attr = attr.next_attribute() )
{
attributes.emplace_back( attr.name(), attr.value() );
}

std::sort( attributes.begin(),
attributes.end(),
[]( std::pair< string, string > const & a,
std::pair< string, string > const & b )
{
// name attribute should be the first attribute, and not sorted alphabetically
bool const aIsName = ( a.first == "name" );
bool const bIsName = ( b.first == "name" );
if( aIsName != bIsName )
{
return aIsName;
}

// other attributes are sorted alphabetically
return a.first < b.first;
} );

// pugi doesn't have any move_attribute method yet, so we have to
// copy and remove attributes
while( node.remove_attribute( node.first_attribute() ) )
{}
for( auto const & attr : attributes )
{
node.append_attribute( attr.first.c_str() ).set_value( attr.second.c_str() );
}

for( xmlWrapper::xmlNode child : node.children() )
{
sortAttributes( child );
}
}

xmlWrapper::xmlDocument flattenXMLs( string_array const & fileNames )
{
xmlWrapper::xmlDocument flatDoc;
xmlWrapper::xmlNode root = flatDoc.appendChild( "Problem" );

for( string const & fileName : fileNames )
{
xmlWrapper::xmlDocument doc;
xmlWrapper::xmlResult const result = doc.loadFile( fileName, true );
GEOS_THROW_IF( !result,
GEOS_FMT( "Could not load XML file '{}': {}", fileName, result.description() ),
InputError );
xmlWrapper::xmlNode docRoot = doc.getFirstChild();

doc.addIncludedXML( docRoot );

for( xmlWrapper::xmlNode & node : docRoot.children() )
{
root.append_copy( node );
}
}

return flatDoc;
}

void copySchemaToArchive( string const & archiveDir )
{
std::filesystem::path const candidates[] = {
GEOS_SCHEMA_SOURCE_PATH,
GEOS_SCHEMA_INSTALL_PATH
};

std::error_code ec;
for( std::filesystem::path const & source : candidates )
{
if( source.empty() || !std::filesystem::is_regular_file( source, ec ) )
{
continue;
}

std::filesystem::path const destination = std::filesystem::path( archiveDir ) / "schema.xsd";
std::filesystem::copy_file( source,
destination,
ec );

if( ec )
{
GEOS_WARNING( GEOS_FMT( "Failed to copy XSD schema to archive '{}': {}",
destination.string(), ec.message() ) );
return;
}

GEOS_LOG_RANK_0( GEOS_FMT( "Archived XSD schema: {}",
getAbsolutePath( destination.string() ) ) );

return;
}

GEOS_WARNING( "Could not locate the XSD schema for archiving" );
}


}


void archiveInputDeck( string_array const & inputFileNames,
string const & outputDirectory,
string_array const & xmlTagOrder,
integer const level )
{
if( level == 0 || inputFileNames.empty() || outputDirectory.empty() )
{
return;
}

string const timestamp = makeTimestamp();
string const archiveDir = joinPath( outputDirectory, "archive_inputFiles", timestamp );
makeDirsForPath( archiveDir + "/" );

xmlWrapper::xmlDocument flatDoc = flattenXMLs( inputFileNames );
xmlWrapper::xmlNode root = flatDoc.getFirstChild();

stripMetadataAttributes( root );
reorderTags( root, xmlTagOrder );
sortAttributes( root );

string const inputArchiveFile = joinPath( archiveDir, "input.xml" );
flatDoc.saveFile( inputArchiveFile );

GEOS_LOG_RANK_0( GEOS_FMT( "Archived XML inputs: {}",
getAbsolutePath( inputArchiveFile ) ) );

if( level >= 2 )
{
copySchemaToArchive( archiveDir );
}
}


} /* namespace archiveInputDeck */

} /* namespace geos */
56 changes: 56 additions & 0 deletions src/coreComponents/fileIO/Outputs/ArchiveInputDeck.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* ------------------------------------------------------------------------------------------------------------
* SPDX-License-Identifier: LGPL-2.1-only
*
* Copyright (c) 2016-2024 Lawrence Livermore National Security LLC
* Copyright (c) 2018-2024 TotalEnergies
* Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University
* Copyright (c) 2023-2024 Chevron
* Copyright (c) 2019- GEOS/GEOSX Contributors
* All rights reserved
*
* See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details.
* ------------------------------------------------------------------------------------------------------------
*/

/**
* @file ArchiveInputDeck.hpp
*/

#ifndef GEOS_FILEIO_OUTPUTS_ARCHIVEINPUTDECK_HPP_
#define GEOS_FILEIO_OUTPUTS_ARCHIVEINPUTDECK_HPP_

#include "common/DataTypes.hpp"

namespace geos
{

namespace archiveInputDeck
{

/**
* @brief Archive the XML input deck (and optionally the XSD schema) into the
* output directory.
* @param inputFileNames Container of XML file names to start the copy from
* @param outputDirectory The output directory to copy files into
* @param xmlTagOrder The order of the XML tags in the XML archive file
* @param level Archiving strategy level:
* - 0: no archiving (returns immediately)
* - 1: XML inputs only (flattened into a single file)
* - 2: XML inputs + the XSD schema
*
* Copy XML input files and every included files they contain (specified in
* the Included tag) into a single flat file. When @p level is at least 2, the
* XSD schema is also copied next to the flattened input.
*/
void archiveInputDeck( string_array const & inputFileNames,
string const & outputDirectory,
string_array const & xmlTagOrder,
integer level );

} /* namespace archiveInputDeck */

} /* namespace geos */


#endif // GEOS_FILEIO_OUTPUTS_ARCHIVEINPUTDECK_HPP_
11 changes: 11 additions & 0 deletions src/coreComponents/mainInterface/ProblemManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "finiteVolume/FluxApproximationBase.hpp"
#include "finiteVolume/HybridMimeticDiscretization.hpp"
#include "fieldSpecification/FieldSpecificationManager.hpp"
#include "fileIO/Outputs/ArchiveInputDeck.hpp"
#include "fileIO/Outputs/OutputBase.hpp"
#include "fileIO/Outputs/OutputManager.hpp"
#include "functions/FunctionManager.hpp"
Expand Down Expand Up @@ -323,6 +324,16 @@ void ProblemManager::parseCommandLineInput()
GEOS_LOG_RANK_0( "Opened XML file: " << absPath );
}

if( opts.archiveInputDeck && MpiWrapper::commRank() == 0 )
{
string_array xmlTagOrder;
initializationOrder( xmlTagOrder );
archiveInputDeck::archiveInputDeck( opts.inputFileNames,
outputDirectory,
xmlTagOrder,
opts.archiveInputDeck );
}

inputFileName = xmlWrapper::buildMultipleInputXML( opts.inputFileNames, outputDirectory );

string & schemaName = commandLine.getReference< string >( viewKeys.schemaFileName );
Expand Down
Loading
Loading