From 20b501cce2ca2e297e4cd4280216c6b73ae28dc1 Mon Sep 17 00:00:00 2001 From: kdrienCG Date: Fri, 20 Mar 2026 09:23:42 +0100 Subject: [PATCH 01/20] add functions to collect included XML files --- .../dataRepository/xmlWrapper.cpp | 83 +++++++++++++++++++ .../dataRepository/xmlWrapper.hpp | 44 ++++++++++ 2 files changed, 127 insertions(+) diff --git a/src/coreComponents/dataRepository/xmlWrapper.cpp b/src/coreComponents/dataRepository/xmlWrapper.cpp index 0c5a469a570..29be35414e7 100644 --- a/src/coreComponents/dataRepository/xmlWrapper.cpp +++ b/src/coreComponents/dataRepository/xmlWrapper.cpp @@ -24,6 +24,7 @@ #include "common/format/StringUtilities.hpp" #include "common/MpiWrapper.hpp" #include "dataRepository/KeyNames.hpp" +#include "common/Path.hpp" namespace geos { @@ -268,6 +269,88 @@ string buildMultipleInputXML( string_array const & inputFileList, return inputFileName; } +void collectIncluded( string const & filePath, + std::set< string > & collection ) +{ + xmlDocument doc; + xmlResult result = doc.loadFile( filePath ); + GEOS_THROW_IF( !result, + GEOS_FMT( "Could not load XML file '{}': {}", filePath, result.description() ), + InputError ); + xmlNode rootNode = doc.getFirstChild(); + + string const currentDir = splitPath( filePath ).first; + + for ( auto & includedNode : rootNode.children( includedListTag ) ) + { + for ( auto & fileNode : includedNode.children() ) + { + string const fileName = fileNode.attribute( "name" ).value(); + + GEOS_THROW_IF( fileName.empty(), + GEOS_FMT( "An included file entry in '{}' has an empty or missing 'name' attribute.", filePath ), + InputError ); + + string absolutePath = isAbsolutePath( fileName ) + ? getAbsolutePath( fileName ) + : getAbsolutePath( joinPath( currentDir, fileName ) ); + collection.insert( absolutePath ); + } + } +} + +std::set< string > collectIncluded( string const & filePath ) +{ + std::set< string > collection; + collectIncluded( filePath, collection ); + return collection; +} + +void collectIncludedRecursive( string const & filePath, + std::set< string > & collection ) +{ + // We want absolute paths + string const absFilePath = getAbsolutePath( filePath ); + + if ( collection.count( absFilePath ) > 0 ) + { + return; + } + collection.insert( absFilePath ); + + xmlDocument doc; + xmlResult result = doc.loadFile( absFilePath ); + GEOS_THROW_IF( !result, + GEOS_FMT( "Could not load XML file '{}': {}", filePath, result.description() ), + InputError ); + xmlNode rootNode = doc.getFirstChild(); + + string const currentDir = splitPath( filePath ).first; + + for ( auto & includedNode : rootNode.children( includedListTag ) ) + { + for ( auto & fileNode : includedNode.children( includedFileTag ) ) + { + string const includedFilePath = fileNode.attribute( "name" ).value(); + + if ( includedFilePath.empty() ) { continue; } + + string includedAbsPath = isAbsolutePath( includedFilePath ) + ? getAbsolutePath( includedFilePath ) + : getAbsolutePath( joinPath(currentDir, includedFilePath) ); + collectIncludedRecursive( includedAbsPath, + collection ); + } + } +} + +std::set< string > collectIncludedRecursive( string const & filePath ) +{ + std::set< string > collection; + collectIncludedRecursive( filePath, collection ); + return collection; +} + bool isFileMetadataAttribute( string const & name ) { static const std::set< string > fileMetadataAttributes { diff --git a/src/coreComponents/dataRepository/xmlWrapper.hpp b/src/coreComponents/dataRepository/xmlWrapper.hpp index 494eb2db119..998074a38ba 100644 --- a/src/coreComponents/dataRepository/xmlWrapper.hpp +++ b/src/coreComponents/dataRepository/xmlWrapper.hpp @@ -301,6 +301,50 @@ constexpr char const includedFileTag[] = "File"; string buildMultipleInputXML( string_array const & inputFileList, string const & outputDir = {} ); +/** + * @brief Collect the absolute paths of XML files directly included + * by a given xml file + * @param[in] filePath absolute path of the xml file to inspect + * @param[inout] collection collection to append with absolute file paths + * + * Only one level of inclusion is collected (files included by the included + * files are not added). See collectIncludedRecursive if you want this behavior. + * Duplicate entries are not inserted in @p collection + */ +void collectIncluded( string const & filePath, + std::set< string > & collection ); + +/** + * @brief Collect the absolute paths of XML files directly included + * by a given xml file + * @param[in] filePath absolute path of the xml file to inspect + * @return a collection of absolute paths + * + * Only one level of inclusion is collected (files included by the included + * files are not added). See collectIncludedRecursive if you want this behavior. + * Duplicate entries are not inserted in @p collection + */ +std::set< string > collectIncluded( string const & filePath ); + +/** + * @brief Recursively collect the absolute paths of an XML file and all XML + * files it includes + * @param[in] filePath absolute path of the root XML file + * @param[inout] collection collection to append with absolute file paths + * of every visisted file (including @p filePath itself) + */ +void collectIncludedRecursive( string const & filePath, + std::set< string > & collection ); + +/** + * @brief Recursively collect the absolute paths of an XML file and all XML + * files it includes + * @param[in] filePath absolute path of the root XML file + * @return a collection of absolute paths of every visited file (including + * @p filePath itself) + */ +std::set< string > collectIncludedRecursive( string const & filePath ); + /** * @return true if the attribute with the specified name declares metadata relative to the xml * @param name the name of an attribute From 3db8b1a1bb97076fc100cde2f97a3bfb50a61d0a Mon Sep 17 00:00:00 2001 From: kdrienCG Date: Fri, 20 Mar 2026 09:41:38 +0100 Subject: [PATCH 02/20] add archiveInputDeck function --- src/coreComponents/fileIO/CMakeLists.txt | 2 + .../fileIO/Outputs/ArchiveInputDeck.cpp | 122 ++++++++++++++++++ .../fileIO/Outputs/ArchiveInputDeck.hpp | 53 ++++++++ 3 files changed, 177 insertions(+) create mode 100644 src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp create mode 100644 src/coreComponents/fileIO/Outputs/ArchiveInputDeck.hpp diff --git a/src/coreComponents/fileIO/CMakeLists.txt b/src/coreComponents/fileIO/CMakeLists.txt index eedca99b65c..88177d8a2fe 100644 --- a/src/coreComponents/fileIO/CMakeLists.txt +++ b/src/coreComponents/fileIO/CMakeLists.txt @@ -24,6 +24,7 @@ Contains: # set( fileIO_headers LogLevelsInfo.hpp + Outputs/ArchiveInputDeck.hpp Outputs/BlueprintOutput.hpp Outputs/MemoryStatsOutput.hpp Outputs/OutputBase.hpp @@ -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 diff --git a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp new file mode 100644 index 00000000000..bff4742ef31 --- /dev/null +++ b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp @@ -0,0 +1,122 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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/Path.hpp" +#include "dataRepository/xmlWrapper.hpp" + +#include +#include + + +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(); +} + +std::set< string > collectAbsFilePaths( string_array const & fileNames ) +{ + std::set< string > collection; + for ( string const & fileName : fileNames ) + { + xmlWrapper::collectIncludedRecursive( fileName, collection ); + } + return collection; +} + +/// @brief Prefixes a file path string if it is located "behind" the +/// specified directory +/// @param absFilePath The absolute path to the file +/// @param absDirPath The absolute path to the directory +/// @return A relative path of the file prefixed with "__" for every "../" +/// from the directory location +/// +/// Example: +/// @code +/// std::string foo = prefixBackwardPath( "/usr/foo/file.txt", "/usr/bar/buzz" ) +/// assert( foo == "____foo/file.txt" ) +/// @endcode +string prefixBackwardPath( string const & absFilePath, string const & absDirPath ) +{ + string relPath = std::filesystem::relative( std::filesystem::path( absFilePath ), + std::filesystem::path( absDirPath ) ); + + string prefix; + while( relPath.size() >= 3 && relPath.substr( 0, 3 ) == "../" ) + { + prefix += "__"; + relPath = relPath.substr( 3 ); + } + + return prefix + relPath; +} + +} + +void archiveInputDeck( string_array const & inputFileNames, + string const & outputDirectory ) +{ + if ( inputFileNames.empty() ) + { + return; + } + + string const timestamp = makeTimestamp(); + string const archiveDir = joinPath( outputDirectory, "inputFiles", timestamp ); + makeDirsForPath( archiveDir + "/" ); + + string const baseDir = splitPath( getAbsolutePath(inputFileNames[0]) ).first; + std::set< string > absFilePaths = collectAbsFilePaths( inputFileNames ); + + for ( string const & absFilePath : absFilePaths ) + { + string const destPath = joinPath( archiveDir, prefixBackwardPath( absFilePath, baseDir ) ); + makeDirsForPath( splitPath( destPath ).first + "/" ); + + std::error_code ec; + bool copied = std::filesystem::copy_file( absFilePath, + destPath, + std::filesystem::copy_options::overwrite_existing, + ec ); + GEOS_LOG_IF( !copied, + GEOS_FMT( "Failed to copy archive file '{}' into '{}': {}", + absFilePath, destPath, ec.message() ) ); + } + +} + +} /* namespace archiveInputDeck */ + +} /* namespace geos */ diff --git a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.hpp b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.hpp new file mode 100644 index 00000000000..520a2ca410a --- /dev/null +++ b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.hpp @@ -0,0 +1,53 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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 Copy XML input files into the output directory, preserving the + * folder structure + * @param inputFileNames Container of XML file names to start the copy from + * @param outputDirectory The output directory to copy files into + * + * Copy XML input files and every included files they contain (specified in + * the tag. This function creates a somewhat similar folder + * structure to the actual structure in the disk. + * + * Note: XML files that are located "behind" the callpoint (the path to + * the first input file given as the -i paramater) will be prefixed + * with "__" for every "../" in the relative path from the callpoint. + */ +void archiveInputDeck( string_array const & inputFileNames, + string const & outputDirectory ); + +} /* namespace archiveInputDeck */ + +} /* namespace geos */ + + +#endif // GEOS_FILEIO_OUTPUTS_ARCHIVEINPUTDECK_HPP_ From 8afead32f162cc9e8b7ca0251c71e62a45fc0db4 Mon Sep 17 00:00:00 2001 From: kdrienCG Date: Fri, 20 Mar 2026 11:50:30 +0100 Subject: [PATCH 03/20] call archiveInputDeck() in ProblemManager --- src/coreComponents/mainInterface/ProblemManager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/coreComponents/mainInterface/ProblemManager.cpp b/src/coreComponents/mainInterface/ProblemManager.cpp index 3c2718b1955..880ddfc63b5 100644 --- a/src/coreComponents/mainInterface/ProblemManager.cpp +++ b/src/coreComponents/mainInterface/ProblemManager.cpp @@ -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" @@ -221,6 +222,8 @@ void ProblemManager::parseCommandLineInput() GEOS_LOG_RANK_0( "Opened XML file: " << absPath ); } + archiveInputDeck::archiveInputDeck( opts.inputFileNames, outputDirectory ); + inputFileName = xmlWrapper::buildMultipleInputXML( opts.inputFileNames, outputDirectory ); string & schemaName = commandLine.getReference< string >( viewKeys.schemaFileName ); From 25c325bb15f7c4a2fc034ee8611b014c1a867527 Mon Sep 17 00:00:00 2001 From: kdrienCG Date: Fri, 20 Mar 2026 11:55:19 +0100 Subject: [PATCH 04/20] modify archive directory name Rename the archive output directory "archive_inputFiles" instead of "inputFiles". This prevents the archived XML files to unintentionally overwrite the standard "inputFiles" in GEOS/ when running with `-o .` where '.' is GEOS/ location --- src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp index bff4742ef31..62ac7874206 100644 --- a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp +++ b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp @@ -94,7 +94,7 @@ void archiveInputDeck( string_array const & inputFileNames, } string const timestamp = makeTimestamp(); - string const archiveDir = joinPath( outputDirectory, "inputFiles", timestamp ); + string const archiveDir = joinPath( outputDirectory, "archive_inputFiles", timestamp ); makeDirsForPath( archiveDir + "/" ); string const baseDir = splitPath( getAbsolutePath(inputFileNames[0]) ).first; From 1cf33d5fb2ed1b42707abe4ec8b2e5bb6a25811f Mon Sep 17 00:00:00 2001 From: kdrienCG Date: Tue, 24 Mar 2026 16:49:29 +0100 Subject: [PATCH 05/20] add tests for collectIncluded functions --- .../unitTests/testXmlWrapper.cpp | 211 ++++++++++++++++++ 1 file changed, 211 insertions(+) diff --git a/src/coreComponents/dataRepository/unitTests/testXmlWrapper.cpp b/src/coreComponents/dataRepository/unitTests/testXmlWrapper.cpp index 21c80c890a8..74a1bc06081 100644 --- a/src/coreComponents/dataRepository/unitTests/testXmlWrapper.cpp +++ b/src/coreComponents/dataRepository/unitTests/testXmlWrapper.cpp @@ -18,6 +18,9 @@ #include "dataRepository/xmlWrapper.hpp" #include "common/format/EnumStrings.hpp" +#include +#include + using namespace geos; TEST( testXmlWrapper, array3d_errors ) @@ -501,6 +504,214 @@ TEST( testXmlWrapper, testGroupNamesArrayFormats ) } } +class CollectIncludedTest : public ::testing::Test +{ +protected: + std::filesystem::path m_tempDir; + + void SetUp() override + { + m_tempDir = std::filesystem::temp_directory_path() / "geos_collectIncluded_test"; + std::filesystem::create_directories( m_tempDir ); + } + + void TearDown() override + { + std::filesystem::remove_all( m_tempDir ); + } + + string filePath( string const & filename ) + { + return ( m_tempDir / filename ).string(); + } + + void writeXML( string const & filename, string const & content ) + { + std::ofstream f( filePath( filename ) ); + f << content; + } +}; + + +TEST_F( CollectIncludedTest, collectIncluded_noIncludes ) +{ + writeXML( "base.xml", "" + "" ); + + auto result = xmlWrapper::collectIncluded( filePath( "base.xml" ) ); + + EXPECT_TRUE( result.empty() ); +} + +TEST_F( CollectIncludedTest, collectIncluded_singleInclude ) +{ + writeXML( "child.xml", "" + "" ); + writeXML( "base.xml", "" + " " + " " + " " + "" ); + + auto result = xmlWrapper::collectIncluded( filePath( "base.xml" ) ); + + EXPECT_NE( result.find( filePath( "child.xml" ) ), result.end() ); +} + +TEST_F( CollectIncludedTest, collectIncluded_multipleIncludes ) +{ + writeXML( "child1.xml", "" + "" ); + writeXML( "child2.xml", "" + "" ); + writeXML( "base.xml", "" + " " + " " + " " + " " + "" ); + + auto result = xmlWrapper::collectIncluded( filePath( "base.xml" ) ); + + EXPECT_NE( result.find( filePath( "child1.xml" ) ), result.end() ); + EXPECT_NE( result.find( filePath( "child2.xml" ) ), result.end() ); +} + +TEST_F( CollectIncludedTest, collectIncluded_emptyNameAttribute ) +{ + writeXML( "base.xml", "" + " " + " " + " " + "" ); + + std::set< string > result; + + EXPECT_ANY_THROW( xmlWrapper::collectIncluded( filePath( "base.xml" ), result ) ); + + EXPECT_TRUE( result.empty() ); +} + +TEST_F( CollectIncludedTest, collectIncluded_existingEntriesKept ) +{ + std::set< string > existingCollection; + existingCollection.insert( "/somewhere/thereisanalreadyexistingxmlfile.xml" ); + writeXML( "base.xml", "" + "" ); + + xmlWrapper::collectIncluded( filePath( "base.xml" ), existingCollection ); + + EXPECT_NE( existingCollection.find( "/somewhere/thereisanalreadyexistingxmlfile.xml" ), + existingCollection.end() ); +} + + +TEST_F( CollectIncludedTest, collectIncludedRecursive_noIncludes ) +{ + writeXML( "base.xml", "" + "" ); + + auto result = xmlWrapper::collectIncludedRecursive( filePath( "base.xml" ) ); + + EXPECT_EQ( result.size(), 1 ); // size 1 because collectIncludedRecursive collects the base file +} + +TEST_F( CollectIncludedTest, collectIncludedRecursive_singleInclude ) +{ + writeXML( "child.xml", "" + "" ); + writeXML( "base.xml", "" + " " + " " + " " + "" ); + + auto result = xmlWrapper::collectIncludedRecursive( filePath( "base.xml" ) ); + + EXPECT_NE( result.find( filePath( "child.xml" ) ), result.end() ); +} + +TEST_F( CollectIncludedTest, collectIncludedRecursive_multipleIncludes ) +{ + writeXML( "child1.xml", "" + "" ); + writeXML( "child2.xml", "" + "" ); + writeXML( "base.xml", "" + " " + " " + " " + " " + "" ); + + auto result = xmlWrapper::collectIncludedRecursive( filePath( "base.xml" ) ); + + EXPECT_NE( result.find( filePath( "child1.xml" ) ), result.end() ); + EXPECT_NE( result.find( filePath( "child2.xml" ) ), result.end() ); +} + +TEST_F( CollectIncludedTest, collectIncludedRecursive_simpleRecursive ) +{ + writeXML( "child.xml", "" + "" ); + + writeXML( "middle.xml", "" + " " + " " + " " + "" ); + + writeXML( "base.xml", "" + " " + " " + " " + "" ); + + auto result = xmlWrapper::collectIncludedRecursive( filePath( "base.xml" ) ); + + EXPECT_NE( result.find( filePath( "middle.xml" ) ), result.end() ); + EXPECT_NE( result.find( filePath( "child.xml" ) ), result.end() ); +} + +TEST_F( CollectIncludedTest, collectIncludedRecursive_cyclePrevention ) +{ + writeXML( "cycle.xml", "" + " " + " " + " " + "" ); + + auto result = xmlWrapper::collectIncludedRecursive( filePath( "cycle.xml" ) ); + + EXPECT_NE( result.find( filePath( "cycle.xml" ) ), result.end() ); +} + +TEST_F( CollectIncludedTest, collectIncludedRecursive_noDuplicates ) +{ + writeXML( "base.xml", "" + " " + " " + " " + "" ); + + auto result = xmlWrapper::collectIncludedRecursive( filePath( "base.xml" ) ); + + EXPECT_EQ( result.size(), 1 ); // collectIncludedRecursive collects the base file +} + +TEST_F( CollectIncludedTest, collectIncludedRecursive_existingEntriesKept ) +{ + std::set< string > existingCollection; + existingCollection.insert( "/somewhere/thereisanalreadyexistingxmlfile.xml" ); + writeXML( "base.xml", "" + "" ); + + xmlWrapper::collectIncludedRecursive( filePath( "base.xml" ), existingCollection ); + + EXPECT_NE( existingCollection.find( "/somewhere/thereisanalreadyexistingxmlfile.xml" ), + existingCollection.end() ); +} + int main( int argc, char * argv[] ) { From a4b35d032e63f4b95ba56694696bc57a93f939cc Mon Sep 17 00:00:00 2001 From: kdrienCG Date: Tue, 24 Mar 2026 17:18:24 +0100 Subject: [PATCH 06/20] fix typo in archiveInputDeck documentation Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/coreComponents/fileIO/Outputs/ArchiveInputDeck.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.hpp b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.hpp index 520a2ca410a..4d7a814c54a 100644 --- a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.hpp +++ b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.hpp @@ -39,7 +39,7 @@ namespace archiveInputDeck * structure to the actual structure in the disk. * * Note: XML files that are located "behind" the callpoint (the path to - * the first input file given as the -i paramater) will be prefixed + * the first input file given as the -i parameter) will be prefixed * with "__" for every "../" in the relative path from the callpoint. */ void archiveInputDeck( string_array const & inputFileNames, From c8739150b31c04384769547f53e90e079223cce4 Mon Sep 17 00:00:00 2001 From: kdrienCG Date: Tue, 24 Mar 2026 17:19:58 +0100 Subject: [PATCH 07/20] fix typo in collectIncludedRecursive documentation Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/coreComponents/dataRepository/xmlWrapper.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreComponents/dataRepository/xmlWrapper.hpp b/src/coreComponents/dataRepository/xmlWrapper.hpp index 998074a38ba..0925f2b1c58 100644 --- a/src/coreComponents/dataRepository/xmlWrapper.hpp +++ b/src/coreComponents/dataRepository/xmlWrapper.hpp @@ -331,7 +331,7 @@ std::set< string > collectIncluded( string const & filePath ); * files it includes * @param[in] filePath absolute path of the root XML file * @param[inout] collection collection to append with absolute file paths - * of every visisted file (including @p filePath itself) + * of every visited file (including @p filePath itself) */ void collectIncludedRecursive( string const & filePath, std::set< string > & collection ); From 6efbe14b8c14509c23cbe59901a68260ea36ff31 Mon Sep 17 00:00:00 2001 From: kdrienCG Date: Tue, 24 Mar 2026 17:28:38 +0100 Subject: [PATCH 08/20] add missing header includes --- src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp index 62ac7874206..32dec80ab7d 100644 --- a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp +++ b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp @@ -20,6 +20,8 @@ #include "ArchiveInputDeck.hpp" #include "common/Path.hpp" +#include "common/format/Format.hpp" +#include "common/logger/Logger.hpp" #include "dataRepository/xmlWrapper.hpp" #include From 140aa51331da5daea6aa391347ff2b3f688e6083 Mon Sep 17 00:00:00 2001 From: kdrienCG Date: Wed, 25 Mar 2026 08:50:47 +0100 Subject: [PATCH 09/20] add filter on collectIncluded iteration --- src/coreComponents/dataRepository/xmlWrapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreComponents/dataRepository/xmlWrapper.cpp b/src/coreComponents/dataRepository/xmlWrapper.cpp index 29be35414e7..06a939f6084 100644 --- a/src/coreComponents/dataRepository/xmlWrapper.cpp +++ b/src/coreComponents/dataRepository/xmlWrapper.cpp @@ -283,7 +283,7 @@ void collectIncluded( string const & filePath, for ( auto & includedNode : rootNode.children( includedListTag ) ) { - for ( auto & fileNode : includedNode.children() ) + for ( auto & fileNode : includedNode.children( includedFileTag ) ) { string const fileName = fileNode.attribute( "name" ).value(); From 9ed7c03892c4fc324821e952c45c5eeaa48eec1c Mon Sep 17 00:00:00 2001 From: kdrienCG Date: Wed, 25 Mar 2026 11:26:00 +0100 Subject: [PATCH 10/20] add MPI rank 0 condition for archiveInputDeck call --- src/coreComponents/mainInterface/ProblemManager.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/coreComponents/mainInterface/ProblemManager.cpp b/src/coreComponents/mainInterface/ProblemManager.cpp index 880ddfc63b5..919651d36c0 100644 --- a/src/coreComponents/mainInterface/ProblemManager.cpp +++ b/src/coreComponents/mainInterface/ProblemManager.cpp @@ -222,7 +222,10 @@ void ProblemManager::parseCommandLineInput() GEOS_LOG_RANK_0( "Opened XML file: " << absPath ); } - archiveInputDeck::archiveInputDeck( opts.inputFileNames, outputDirectory ); + if ( MpiWrapper::commRank() == 0 ) + { + archiveInputDeck::archiveInputDeck( opts.inputFileNames, outputDirectory ); + } inputFileName = xmlWrapper::buildMultipleInputXML( opts.inputFileNames, outputDirectory ); From 5c306354ab30990f88200e0ad6507d19892499b4 Mon Sep 17 00:00:00 2001 From: kdrienCG Date: Wed, 25 Mar 2026 11:26:57 +0100 Subject: [PATCH 11/20] add output directory invariant for archiveInputDeck --- src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp index 32dec80ab7d..85f37ae91e9 100644 --- a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp +++ b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp @@ -95,6 +95,12 @@ void archiveInputDeck( string_array const & inputFileNames, return; } + if ( outputDirectory.empty() ) + { + return; + } + + string const timestamp = makeTimestamp(); string const archiveDir = joinPath( outputDirectory, "archive_inputFiles", timestamp ); makeDirsForPath( archiveDir + "/" ); From 0c5fb2df2d16a3fc8dceb43b79ed95f34d050cf7 Mon Sep 17 00:00:00 2001 From: kdrienCG Date: Wed, 25 Mar 2026 11:27:43 +0100 Subject: [PATCH 12/20] add tests for archiveInputDeck --- .../fileIO/Outputs/unitTests/CMakeLists.txt | 1 + .../unitTests/testArchiveInputDeck.cpp | 178 ++++++++++++++++++ 2 files changed, 179 insertions(+) create mode 100644 src/coreComponents/fileIO/Outputs/unitTests/testArchiveInputDeck.cpp diff --git a/src/coreComponents/fileIO/Outputs/unitTests/CMakeLists.txt b/src/coreComponents/fileIO/Outputs/unitTests/CMakeLists.txt index 04134af6c4a..cd7abf8d724 100644 --- a/src/coreComponents/fileIO/Outputs/unitTests/CMakeLists.txt +++ b/src/coreComponents/fileIO/Outputs/unitTests/CMakeLists.txt @@ -1,6 +1,7 @@ # Unit tests for fileIO/Outputs set( gtest_outputs_tests + testArchiveInputDeck.cpp testMemoryStats.cpp ) # Link against mainInterface to resolve basicSetup and ProblemManager symbols diff --git a/src/coreComponents/fileIO/Outputs/unitTests/testArchiveInputDeck.cpp b/src/coreComponents/fileIO/Outputs/unitTests/testArchiveInputDeck.cpp new file mode 100644 index 00000000000..3fce2caaf26 --- /dev/null +++ b/src/coreComponents/fileIO/Outputs/unitTests/testArchiveInputDeck.cpp @@ -0,0 +1,178 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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. + * ------------------------------------------------------------------------------------------------------------ + */ + +#include "fileIO/Outputs/ArchiveInputDeck.hpp" + +#include +#include +#include + +using namespace geos; + +namespace fs = std::filesystem; + +class ArchiveInputDeckTest : public ::testing::Test +{ +protected: + fs::path m_inputDir; + fs::path m_outputDir; + + void SetUp() override + { + m_inputDir = fs::temp_directory_path() / "geos_archiveInputDeck_test_input"; + m_outputDir = fs::temp_directory_path() / "geos_archiveInputDeck_test_output"; + fs::create_directories( m_inputDir ); + fs::create_directories( m_outputDir ); + } + + void TearDown() override + { + fs::remove_all( m_inputDir ); + fs::remove_all( m_outputDir ); + } + + string inputPath( string const & filename ) + { + return ( m_inputDir / filename ).string(); + } + + void writeXML( string const & filename, string const & content ) + { + fs::path const fullPath = m_inputDir / filename; + fs::create_directories( fullPath.parent_path() ); + std::ofstream f( fullPath ); + f << content; + } + + /// Return the single timestamped subdir in inputFiles/ + /// or an empty path + fs::path findArchiveDir() + { + fs::path const inputFilesDir = m_outputDir / "archive_inputFiles"; + if ( !fs::exists( inputFilesDir ) ) + { + return {}; + } + for ( auto const & entry : fs::directory_iterator( inputFilesDir ) ) + { + if ( entry.is_directory() ) + { + return entry.path(); + } + } + return {}; + } + + std::set< string > collectArchiveFiles( fs::path const & archiveDir ) + { + std::set< string > files; + + for ( auto const & entry : fs::recursive_directory_iterator( archiveDir ) ) + { + if ( entry.is_regular_file() ) + { + files.insert( fs::relative( entry.path(), archiveDir ).string() ); + } + } + + return files; + } +}; + + +TEST_F( ArchiveInputDeckTest, singleFile ) +{ + writeXML( "base.xml", "" + "" ); + string_array inputFiles { inputPath( "base.xml" ) }; + + archiveInputDeck::archiveInputDeck( inputFiles, m_outputDir.string() ); + + fs::path const archiveDir = findArchiveDir(); + EXPECT_FALSE( archiveDir.empty() ) << "Archive directory was not created"; + + auto const files = collectArchiveFiles( archiveDir ); + EXPECT_NE( files.find( "base.xml" ), files.end() ); +} + +TEST_F( ArchiveInputDeckTest, fileWithSubdirInclude ) +{ + writeXML( "subdir/child.xml", "" + "" ); + writeXML( "base.xml", "" + " " + " " + " " + "" ); + string_array inputFiles { inputPath( "base.xml" ) }; + + archiveInputDeck::archiveInputDeck( inputFiles, m_outputDir.string() ); + + fs::path const archiveDir = findArchiveDir(); + EXPECT_FALSE( archiveDir.empty() ) << "Archive directory was not created"; + + auto const files = collectArchiveFiles( archiveDir ); + EXPECT_NE( files.find( "subdir/child.xml" ), files.end() ); +} + +TEST_F( ArchiveInputDeckTest, fileOneDirectoryAbove ) +{ + writeXML( "other.xml", "" + "" ); + writeXML( "subdir/base.xml", "" + " " + " " + " " + "" ); + string_array inputFiles { inputPath( "subdir/base.xml" ) }; + + archiveInputDeck::archiveInputDeck( inputFiles, m_outputDir.string() ); + + fs::path const archiveDir = findArchiveDir(); + EXPECT_FALSE( archiveDir.empty() ) << "Archive directory was not created"; + + auto const files = collectArchiveFiles( archiveDir ); + EXPECT_NE( files.find( "__other.xml" ), files.end() ); +} + +TEST_F( ArchiveInputDeckTest, fileTwoDirectoriesAbove ) +{ + writeXML( "other.xml", "" + "" ); + writeXML( "subdir/subdir/base.xml", "" + " " + " " + " " + "" ); + string_array inputFiles { inputPath( "subdir/subdir/base.xml" ) }; + + archiveInputDeck::archiveInputDeck( inputFiles, m_outputDir.string() ); + + fs::path const archiveDir = findArchiveDir(); + EXPECT_FALSE( archiveDir.empty() ) << "Archive directory was not created"; + + auto const files = collectArchiveFiles( archiveDir ); + EXPECT_NE( files.find( "____other.xml" ), files.end() ); +} + + +int main( int argc, char * * argv ) +{ + ::testing::InitGoogleTest( &argc, argv ); + + int const result = RUN_ALL_TESTS(); + + return result; +} From bf1ca665331eaf49e88d8af7c3bdf32c36b99fcb Mon Sep 17 00:00:00 2001 From: kdrienCG Date: Fri, 10 Apr 2026 16:41:48 +0200 Subject: [PATCH 13/20] modify archive's logic to flatten inputs --- .../fileIO/Outputs/ArchiveInputDeck.cpp | 112 ++++++----- .../fileIO/Outputs/ArchiveInputDeck.hpp | 16 +- .../fileIO/Outputs/unitTests/CMakeLists.txt | 1 - .../unitTests/testArchiveInputDeck.cpp | 178 ------------------ .../mainInterface/ProblemManager.cpp | 6 +- 5 files changed, 78 insertions(+), 235 deletions(-) delete mode 100644 src/coreComponents/fileIO/Outputs/unitTests/testArchiveInputDeck.cpp diff --git a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp index 85f37ae91e9..84625ebd61e 100644 --- a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp +++ b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp @@ -24,10 +24,10 @@ #include "common/logger/Logger.hpp" #include "dataRepository/xmlWrapper.hpp" +#include #include #include - namespace geos { @@ -48,38 +48,51 @@ string makeTimestamp() return timestampStream.str(); } -std::set< string > collectAbsFilePaths( string_array const & fileNames ) +void reorderTags( xmlWrapper::xmlNode rootNode, string_array const & tagOrder ) { - std::set< string > collection; - for ( string const & fileName : fileNames ) + xmlWrapper::xmlNode lastInserted; + for( string const & tagName : tagOrder ) { - xmlWrapper::collectIncludedRecursive( fileName, collection ); + xmlWrapper::xmlNode tag = rootNode.child( tagName.c_str() ); + if( !tag ) + { + continue; + } + + lastInserted ? rootNode.insert_move_after( tag, lastInserted ) + : rootNode.append_move( tag ); + + lastInserted = tag; } - return collection; -} -/// @brief Prefixes a file path string if it is located "behind" the -/// specified directory -/// @param absFilePath The absolute path to the file -/// @param absDirPath The absolute path to the directory -/// @return A relative path of the file prefixed with "__" for every "../" -/// from the directory location -/// -/// Example: -/// @code -/// std::string foo = prefixBackwardPath( "/usr/foo/file.txt", "/usr/bar/buzz" ) -/// assert( foo == "____foo/file.txt" ) -/// @endcode -string prefixBackwardPath( string const & absFilePath, string const & absDirPath ) -{ - string relPath = std::filesystem::relative( std::filesystem::path( absFilePath ), - std::filesystem::path( absDirPath ) ); + // 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() ); - string prefix; - while( relPath.size() >= 3 && relPath.substr( 0, 3 ) == "../" ) + if( tag ) { - prefix += "__"; - relPath = relPath.substr( 3 ); + rootNode.append_move( tag ); + } + } +} + } return prefix + relPath; @@ -95,34 +108,45 @@ void archiveInputDeck( string_array const & inputFileNames, return; } - if ( outputDirectory.empty() ) + +string archiveInputDeck( string_array const & inputFileNames, + string const & outputDirectory, + string_array const & xmlTagOrder ) +{ + if( inputFileNames.empty() || outputDirectory.empty() ) { - return; + return {}; } - string const timestamp = makeTimestamp(); string const archiveDir = joinPath( outputDirectory, "archive_inputFiles", timestamp ); makeDirsForPath( archiveDir + "/" ); - string const baseDir = splitPath( getAbsolutePath(inputFileNames[0]) ).first; - std::set< string > absFilePaths = collectAbsFilePaths( inputFileNames ); + xmlWrapper::xmlDocument flatDoc; + xmlWrapper::xmlNode root = flatDoc.appendChild( "Problem" ); - for ( string const & absFilePath : absFilePaths ) + for( string const & fileName : inputFileNames ) { - string const destPath = joinPath( archiveDir, prefixBackwardPath( absFilePath, baseDir ) ); - makeDirsForPath( splitPath( destPath ).first + "/" ); - - std::error_code ec; - bool copied = std::filesystem::copy_file( absFilePath, - destPath, - std::filesystem::copy_options::overwrite_existing, - ec ); - GEOS_LOG_IF( !copied, - GEOS_FMT( "Failed to copy archive file '{}' into '{}': {}", - absFilePath, destPath, ec.message() ) ); + 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 ); + } } + reorderTags( root, xmlTagOrder ); + + flatDoc.saveFile( joinPath( archiveDir, "input.xml" ) ); + + return archiveDir; } } /* namespace archiveInputDeck */ diff --git a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.hpp b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.hpp index 4d7a814c54a..39a190428c0 100644 --- a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.hpp +++ b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.hpp @@ -29,21 +29,17 @@ namespace archiveInputDeck { /** - * @brief Copy XML input files into the output directory, preserving the - * folder structure + * @brief Copy XML input files as a flat XML file 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 * * Copy XML input files and every included files they contain (specified in - * the tag. This function creates a somewhat similar folder - * structure to the actual structure in the disk. - * - * Note: XML files that are located "behind" the callpoint (the path to - * the first input file given as the -i parameter) will be prefixed - * with "__" for every "../" in the relative path from the callpoint. + * the tag) into a single flat file. */ -void archiveInputDeck( string_array const & inputFileNames, - string const & outputDirectory ); +string archiveInputDeck( string_array const & inputFileNames, + string const & outputDirectory, + string_array const & xmlTagOrder ); } /* namespace archiveInputDeck */ diff --git a/src/coreComponents/fileIO/Outputs/unitTests/CMakeLists.txt b/src/coreComponents/fileIO/Outputs/unitTests/CMakeLists.txt index cd7abf8d724..04134af6c4a 100644 --- a/src/coreComponents/fileIO/Outputs/unitTests/CMakeLists.txt +++ b/src/coreComponents/fileIO/Outputs/unitTests/CMakeLists.txt @@ -1,7 +1,6 @@ # Unit tests for fileIO/Outputs set( gtest_outputs_tests - testArchiveInputDeck.cpp testMemoryStats.cpp ) # Link against mainInterface to resolve basicSetup and ProblemManager symbols diff --git a/src/coreComponents/fileIO/Outputs/unitTests/testArchiveInputDeck.cpp b/src/coreComponents/fileIO/Outputs/unitTests/testArchiveInputDeck.cpp deleted file mode 100644 index 3fce2caaf26..00000000000 --- a/src/coreComponents/fileIO/Outputs/unitTests/testArchiveInputDeck.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/* - * ------------------------------------------------------------------------------------------------------------ - * 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. - * ------------------------------------------------------------------------------------------------------------ - */ - -#include "fileIO/Outputs/ArchiveInputDeck.hpp" - -#include -#include -#include - -using namespace geos; - -namespace fs = std::filesystem; - -class ArchiveInputDeckTest : public ::testing::Test -{ -protected: - fs::path m_inputDir; - fs::path m_outputDir; - - void SetUp() override - { - m_inputDir = fs::temp_directory_path() / "geos_archiveInputDeck_test_input"; - m_outputDir = fs::temp_directory_path() / "geos_archiveInputDeck_test_output"; - fs::create_directories( m_inputDir ); - fs::create_directories( m_outputDir ); - } - - void TearDown() override - { - fs::remove_all( m_inputDir ); - fs::remove_all( m_outputDir ); - } - - string inputPath( string const & filename ) - { - return ( m_inputDir / filename ).string(); - } - - void writeXML( string const & filename, string const & content ) - { - fs::path const fullPath = m_inputDir / filename; - fs::create_directories( fullPath.parent_path() ); - std::ofstream f( fullPath ); - f << content; - } - - /// Return the single timestamped subdir in inputFiles/ - /// or an empty path - fs::path findArchiveDir() - { - fs::path const inputFilesDir = m_outputDir / "archive_inputFiles"; - if ( !fs::exists( inputFilesDir ) ) - { - return {}; - } - for ( auto const & entry : fs::directory_iterator( inputFilesDir ) ) - { - if ( entry.is_directory() ) - { - return entry.path(); - } - } - return {}; - } - - std::set< string > collectArchiveFiles( fs::path const & archiveDir ) - { - std::set< string > files; - - for ( auto const & entry : fs::recursive_directory_iterator( archiveDir ) ) - { - if ( entry.is_regular_file() ) - { - files.insert( fs::relative( entry.path(), archiveDir ).string() ); - } - } - - return files; - } -}; - - -TEST_F( ArchiveInputDeckTest, singleFile ) -{ - writeXML( "base.xml", "" - "" ); - string_array inputFiles { inputPath( "base.xml" ) }; - - archiveInputDeck::archiveInputDeck( inputFiles, m_outputDir.string() ); - - fs::path const archiveDir = findArchiveDir(); - EXPECT_FALSE( archiveDir.empty() ) << "Archive directory was not created"; - - auto const files = collectArchiveFiles( archiveDir ); - EXPECT_NE( files.find( "base.xml" ), files.end() ); -} - -TEST_F( ArchiveInputDeckTest, fileWithSubdirInclude ) -{ - writeXML( "subdir/child.xml", "" - "" ); - writeXML( "base.xml", "" - " " - " " - " " - "" ); - string_array inputFiles { inputPath( "base.xml" ) }; - - archiveInputDeck::archiveInputDeck( inputFiles, m_outputDir.string() ); - - fs::path const archiveDir = findArchiveDir(); - EXPECT_FALSE( archiveDir.empty() ) << "Archive directory was not created"; - - auto const files = collectArchiveFiles( archiveDir ); - EXPECT_NE( files.find( "subdir/child.xml" ), files.end() ); -} - -TEST_F( ArchiveInputDeckTest, fileOneDirectoryAbove ) -{ - writeXML( "other.xml", "" - "" ); - writeXML( "subdir/base.xml", "" - " " - " " - " " - "" ); - string_array inputFiles { inputPath( "subdir/base.xml" ) }; - - archiveInputDeck::archiveInputDeck( inputFiles, m_outputDir.string() ); - - fs::path const archiveDir = findArchiveDir(); - EXPECT_FALSE( archiveDir.empty() ) << "Archive directory was not created"; - - auto const files = collectArchiveFiles( archiveDir ); - EXPECT_NE( files.find( "__other.xml" ), files.end() ); -} - -TEST_F( ArchiveInputDeckTest, fileTwoDirectoriesAbove ) -{ - writeXML( "other.xml", "" - "" ); - writeXML( "subdir/subdir/base.xml", "" - " " - " " - " " - "" ); - string_array inputFiles { inputPath( "subdir/subdir/base.xml" ) }; - - archiveInputDeck::archiveInputDeck( inputFiles, m_outputDir.string() ); - - fs::path const archiveDir = findArchiveDir(); - EXPECT_FALSE( archiveDir.empty() ) << "Archive directory was not created"; - - auto const files = collectArchiveFiles( archiveDir ); - EXPECT_NE( files.find( "____other.xml" ), files.end() ); -} - - -int main( int argc, char * * argv ) -{ - ::testing::InitGoogleTest( &argc, argv ); - - int const result = RUN_ALL_TESTS(); - - return result; -} diff --git a/src/coreComponents/mainInterface/ProblemManager.cpp b/src/coreComponents/mainInterface/ProblemManager.cpp index 919651d36c0..2c152a65fb1 100644 --- a/src/coreComponents/mainInterface/ProblemManager.cpp +++ b/src/coreComponents/mainInterface/ProblemManager.cpp @@ -222,9 +222,11 @@ void ProblemManager::parseCommandLineInput() GEOS_LOG_RANK_0( "Opened XML file: " << absPath ); } - if ( MpiWrapper::commRank() == 0 ) + if( MpiWrapper::commRank() == 0 ) { - archiveInputDeck::archiveInputDeck( opts.inputFileNames, outputDirectory ); + string_array xmlTagOrder; + initializationOrder( xmlTagOrder ); + archiveInputDeck::archiveInputDeck( opts.inputFileNames, outputDirectory, xmlTagOrder ); } inputFileName = xmlWrapper::buildMultipleInputXML( opts.inputFileNames, outputDirectory ); From 6b35d1b3c659e3f78e3f440407986e86eb26ff8c Mon Sep 17 00:00:00 2001 From: kdrienCG Date: Fri, 10 Apr 2026 16:42:36 +0200 Subject: [PATCH 14/20] strip metadata attributes from the archived XML --- .../fileIO/Outputs/ArchiveInputDeck.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp index 84625ebd61e..ce475ce10d1 100644 --- a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp +++ b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp @@ -48,6 +48,17 @@ string makeTimestamp() 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; @@ -142,6 +153,7 @@ string archiveInputDeck( string_array const & inputFileNames, } } + stripMetadataAttributes( root ); reorderTags( root, xmlTagOrder ); flatDoc.saveFile( joinPath( archiveDir, "input.xml" ) ); From a61d8c7b3e909571e4f3125e42e8c7923ecae7c6 Mon Sep 17 00:00:00 2001 From: kdrienCG Date: Fri, 10 Apr 2026 16:43:56 +0200 Subject: [PATCH 15/20] sort XML attributes in the archived XML --- .../fileIO/Outputs/ArchiveInputDeck.cpp | 44 ++++++++++++++++--- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp index ce475ce10d1..1c94a8868bc 100644 --- a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp +++ b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp @@ -104,17 +104,48 @@ void reorderTags( xmlWrapper::xmlNode rootNode, string_array const & tagOrder ) } } +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; } - return prefix + relPath; -} + // 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 ); + } } -void archiveInputDeck( string_array const & inputFileNames, - string const & outputDirectory ) -{ - if ( inputFileNames.empty() ) { return; } @@ -155,6 +186,7 @@ string archiveInputDeck( string_array const & inputFileNames, stripMetadataAttributes( root ); reorderTags( root, xmlTagOrder ); + sortAttributes( root ); flatDoc.saveFile( joinPath( archiveDir, "input.xml" ) ); From 87380842d992c3037228e3eaf3b984c7f6112389 Mon Sep 17 00:00:00 2001 From: kdrienCG Date: Fri, 10 Apr 2026 16:44:24 +0200 Subject: [PATCH 16/20] copy schema.xsd to the archive --- .../fileIO/Outputs/ArchiveInputDeck.cpp | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp index 1c94a8868bc..c392c510b97 100644 --- a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp +++ b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp @@ -146,10 +146,37 @@ void sortAttributes( xmlWrapper::xmlNode node ) } } +void copySchemaToArchive( string const & archiveDir ) +{ + std::error_code ec; + std::filesystem::path const exeDir = std::filesystem::read_symlink( "/proc/self/exe", ec ).parent_path(); + if( ec ) { return; } + std::filesystem::path const candidates[] = { + exeDir / "../share/geosx/schema/schema.xsd", + exeDir / "schema.xsd" + }; + + for( auto const & schemaSource : candidates ) + { + if( std::filesystem::is_regular_file( schemaSource ) ) + { + std::filesystem::path const schemaDest = std::filesystem::path( archiveDir ) / "schema.xsd"; + std::filesystem::copy_file( schemaSource, + schemaDest, + std::filesystem::copy_options::overwrite_existing, + ec ); + GEOS_LOG_IF( ec, GEOS_FMT( "Failed to copy schema to archive: {}", ec.message() ) ); + break; + } + } +} + +} + string archiveInputDeck( string_array const & inputFileNames, string const & outputDirectory, @@ -190,6 +217,8 @@ string archiveInputDeck( string_array const & inputFileNames, flatDoc.saveFile( joinPath( archiveDir, "input.xml" ) ); + copySchemaToArchive( archiveDir ); + return archiveDir; } From 93e28067cbf052e2d14d6723c0bfa1e08ff2662e Mon Sep 17 00:00:00 2001 From: kdrienCG Date: Fri, 10 Apr 2026 16:45:01 +0200 Subject: [PATCH 17/20] uncrustify --- .../unitTests/testXmlWrapper.cpp | 6 ++--- .../dataRepository/xmlWrapper.cpp | 23 +++++++++++-------- .../dataRepository/xmlWrapper.hpp | 10 ++++---- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/coreComponents/dataRepository/unitTests/testXmlWrapper.cpp b/src/coreComponents/dataRepository/unitTests/testXmlWrapper.cpp index 74a1bc06081..e0e6aef1b63 100644 --- a/src/coreComponents/dataRepository/unitTests/testXmlWrapper.cpp +++ b/src/coreComponents/dataRepository/unitTests/testXmlWrapper.cpp @@ -601,7 +601,7 @@ TEST_F( CollectIncludedTest, collectIncluded_existingEntriesKept ) xmlWrapper::collectIncluded( filePath( "base.xml" ), existingCollection ); - EXPECT_NE( existingCollection.find( "/somewhere/thereisanalreadyexistingxmlfile.xml" ), + EXPECT_NE( existingCollection.find( "/somewhere/thereisanalreadyexistingxmlfile.xml" ), existingCollection.end() ); } @@ -670,7 +670,7 @@ TEST_F( CollectIncludedTest, collectIncludedRecursive_simpleRecursive ) auto result = xmlWrapper::collectIncludedRecursive( filePath( "base.xml" ) ); EXPECT_NE( result.find( filePath( "middle.xml" ) ), result.end() ); - EXPECT_NE( result.find( filePath( "child.xml" ) ), result.end() ); + EXPECT_NE( result.find( filePath( "child.xml" ) ), result.end() ); } TEST_F( CollectIncludedTest, collectIncludedRecursive_cyclePrevention ) @@ -708,7 +708,7 @@ TEST_F( CollectIncludedTest, collectIncludedRecursive_existingEntriesKept ) xmlWrapper::collectIncludedRecursive( filePath( "base.xml" ), existingCollection ); - EXPECT_NE( existingCollection.find( "/somewhere/thereisanalreadyexistingxmlfile.xml" ), + EXPECT_NE( existingCollection.find( "/somewhere/thereisanalreadyexistingxmlfile.xml" ), existingCollection.end() ); } diff --git a/src/coreComponents/dataRepository/xmlWrapper.cpp b/src/coreComponents/dataRepository/xmlWrapper.cpp index 06a939f6084..46e502a90cf 100644 --- a/src/coreComponents/dataRepository/xmlWrapper.cpp +++ b/src/coreComponents/dataRepository/xmlWrapper.cpp @@ -281,9 +281,9 @@ void collectIncluded( string const & filePath, string const currentDir = splitPath( filePath ).first; - for ( auto & includedNode : rootNode.children( includedListTag ) ) + for( auto & includedNode : rootNode.children( includedListTag ) ) { - for ( auto & fileNode : includedNode.children( includedFileTag ) ) + for( auto & fileNode : includedNode.children( includedFileTag ) ) { string const fileName = fileNode.attribute( "name" ).value(); @@ -291,9 +291,9 @@ void collectIncluded( string const & filePath, GEOS_FMT( "An included file entry in '{}' has an empty or missing 'name' attribute.", filePath ), InputError ); - string absolutePath = isAbsolutePath( fileName ) + string absolutePath = isAbsolutePath( fileName ) ? getAbsolutePath( fileName ) - : getAbsolutePath( joinPath( currentDir, fileName ) ); + : getAbsolutePath( joinPath( currentDir, fileName ) ); collection.insert( absolutePath ); } } @@ -312,7 +312,7 @@ void collectIncludedRecursive( string const & filePath, // We want absolute paths string const absFilePath = getAbsolutePath( filePath ); - if ( collection.count( absFilePath ) > 0 ) + if( collection.count( absFilePath ) > 0 ) { return; } @@ -327,17 +327,20 @@ void collectIncludedRecursive( string const & filePath, string const currentDir = splitPath( filePath ).first; - for ( auto & includedNode : rootNode.children( includedListTag ) ) + for( auto & includedNode : rootNode.children( includedListTag ) ) { - for ( auto & fileNode : includedNode.children( includedFileTag ) ) + for( auto & fileNode : includedNode.children( includedFileTag ) ) { string const includedFilePath = fileNode.attribute( "name" ).value(); - if ( includedFilePath.empty() ) { continue; } + if( includedFilePath.empty() ) + { + continue; + } - string includedAbsPath = isAbsolutePath( includedFilePath ) + string includedAbsPath = isAbsolutePath( includedFilePath ) ? getAbsolutePath( includedFilePath ) - : getAbsolutePath( joinPath(currentDir, includedFilePath) ); + : getAbsolutePath( joinPath( currentDir, includedFilePath ) ); collectIncludedRecursive( includedAbsPath, collection ); } diff --git a/src/coreComponents/dataRepository/xmlWrapper.hpp b/src/coreComponents/dataRepository/xmlWrapper.hpp index 0925f2b1c58..cbd04536b5d 100644 --- a/src/coreComponents/dataRepository/xmlWrapper.hpp +++ b/src/coreComponents/dataRepository/xmlWrapper.hpp @@ -302,11 +302,11 @@ string buildMultipleInputXML( string_array const & inputFileList, string const & outputDir = {} ); /** - * @brief Collect the absolute paths of XML files directly included + * @brief Collect the absolute paths of XML files directly included * by a given xml file * @param[in] filePath absolute path of the xml file to inspect * @param[inout] collection collection to append with absolute file paths - * + * * Only one level of inclusion is collected (files included by the included * files are not added). See collectIncludedRecursive if you want this behavior. * Duplicate entries are not inserted in @p collection @@ -315,11 +315,11 @@ void collectIncluded( string const & filePath, std::set< string > & collection ); /** - * @brief Collect the absolute paths of XML files directly included + * @brief Collect the absolute paths of XML files directly included * by a given xml file * @param[in] filePath absolute path of the xml file to inspect * @return a collection of absolute paths - * + * * Only one level of inclusion is collected (files included by the included * files are not added). See collectIncludedRecursive if you want this behavior. * Duplicate entries are not inserted in @p collection @@ -340,7 +340,7 @@ void collectIncludedRecursive( string const & filePath, * @brief Recursively collect the absolute paths of an XML file and all XML * files it includes * @param[in] filePath absolute path of the root XML file - * @return a collection of absolute paths of every visited file (including + * @return a collection of absolute paths of every visited file (including * @p filePath itself) */ std::set< string > collectIncludedRecursive( string const & filePath ); From dbfa0fb3aa7645d4d8853755bbb233a221b7b5f3 Mon Sep 17 00:00:00 2001 From: kdrienCG Date: Wed, 15 Apr 2026 16:17:38 +0200 Subject: [PATCH 18/20] relocate archiveInputDeck call to generate the XSD schema ProblemManager::generateDocumentation() used to create the XSD schema has undesired side-effects on the Problem. To mitigate this, the archiving has been moved after the basicSetup() in the main.cpp, and creates an isolated ProblemManager (like the tests) that will not propagate the side-effects, and make us able to copy the XSD schema to the archive. --- .../fileIO/Outputs/ArchiveInputDeck.cpp | 97 ++++++++++--------- .../fileIO/Outputs/ArchiveInputDeck.hpp | 13 ++- .../mainInterface/ProblemManager.cpp | 8 -- src/main/main.cpp | 3 + 4 files changed, 60 insertions(+), 61 deletions(-) diff --git a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp index c392c510b97..82192554e8e 100644 --- a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp +++ b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp @@ -19,14 +19,21 @@ #include "ArchiveInputDeck.hpp" +#include "common/MpiWrapper.hpp" #include "common/Path.hpp" #include "common/format/Format.hpp" +#include "common/initializeEnvironment.hpp" #include "common/logger/Logger.hpp" +#include "dataRepository/Group.hpp" #include "dataRepository/xmlWrapper.hpp" +#include "mainInterface/ProblemManager.hpp" + +#include #include #include #include +#include namespace geos { @@ -146,70 +153,61 @@ void sortAttributes( xmlWrapper::xmlNode node ) } } -void copySchemaToArchive( string const & archiveDir ) +xmlWrapper::xmlDocument flattenXMLs( string_array const & fileNames ) { - std::error_code ec; - std::filesystem::path const exeDir = std::filesystem::read_symlink( "/proc/self/exe", ec ).parent_path(); - if( ec ) + xmlWrapper::xmlDocument flatDoc; + xmlWrapper::xmlNode root = flatDoc.appendChild( "Problem" ); + + for( string const & fileName : fileNames ) { - return; - } + 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(); - std::filesystem::path const candidates[] = { - exeDir / "../share/geosx/schema/schema.xsd", - exeDir / "schema.xsd" - }; + doc.addIncludedXML( docRoot ); - for( auto const & schemaSource : candidates ) - { - if( std::filesystem::is_regular_file( schemaSource ) ) + for( xmlWrapper::xmlNode & node : docRoot.children() ) { - std::filesystem::path const schemaDest = std::filesystem::path( archiveDir ) / "schema.xsd"; - std::filesystem::copy_file( schemaSource, - schemaDest, - std::filesystem::copy_options::overwrite_existing, - ec ); - GEOS_LOG_IF( ec, GEOS_FMT( "Failed to copy schema to archive: {}", ec.message() ) ); - break; + root.append_copy( node ); } } + + return flatDoc; } + } -string archiveInputDeck( string_array const & inputFileNames, - string const & outputDirectory, - string_array const & xmlTagOrder ) +void archiveInputDeck( CommandLineOptions const & opts ) { - if( inputFileNames.empty() || outputDirectory.empty() ) + if( opts.inputFileNames.empty() || opts.outputDirectory.empty() ) { - return {}; + return; } - string const timestamp = makeTimestamp(); - string const archiveDir = joinPath( outputDirectory, "archive_inputFiles", timestamp ); - makeDirsForPath( archiveDir + "/" ); + if( MpiWrapper::commRank() != 0 ) + { + return; + } - xmlWrapper::xmlDocument flatDoc; - xmlWrapper::xmlNode root = flatDoc.appendChild( "Problem" ); + // Creates a temporary and isolated ProblemManager to generate the schema.xsd + // because ProblemManager::generateDocumentation() has unwanted side effects + conduit::Node tempRoot; + ProblemManager tempPM( tempRoot ); - for( string const & fileName : inputFileNames ) - { - 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(); + string_array xmlTagOrder; + tempPM.initializationOrder( xmlTagOrder ); - doc.addIncludedXML( docRoot ); + string const timestamp = makeTimestamp(); + string const archiveDir = joinPath( opts.outputDirectory, "archive_inputFiles", timestamp ); + makeDirsForPath( archiveDir + "/" ); - for( xmlWrapper::xmlNode & node : docRoot.children() ) - { - root.append_copy( node ); - } - } + xmlWrapper::xmlDocument flatDoc = flattenXMLs( opts.inputFileNames ); + xmlWrapper::xmlNode root = flatDoc.getFirstChild(); stripMetadataAttributes( root ); reorderTags( root, xmlTagOrder ); @@ -217,11 +215,18 @@ string archiveInputDeck( string_array const & inputFileNames, flatDoc.saveFile( joinPath( archiveDir, "input.xml" ) ); - copySchemaToArchive( archiveDir ); + string const schemaPath = joinPath( archiveDir, "schema.xsd" ); + + dataRepository::Group & commandLine = tempPM.getGroup< dataRepository::Group >( tempPM.groupKeys.commandLine ); + commandLine.getReference< string >( tempPM.viewKeys.schemaFileName ) = schemaPath; + + tempPM.generateDocumentation(); - return archiveDir; + std::error_code ec; + std::filesystem::remove( std::filesystem::path( schemaPath + ".other" ), ec ); } + } /* namespace archiveInputDeck */ } /* namespace geos */ diff --git a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.hpp b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.hpp index 39a190428c0..a106e66558f 100644 --- a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.hpp +++ b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.hpp @@ -25,21 +25,20 @@ namespace geos { +struct CommandLineOptions; + namespace archiveInputDeck { /** - * @brief Copy XML input files as a flat XML file 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 + * @brief Copy the XML input files as a flat XML file into the output directory + * @param opts A reference to the command line options, used to retrieve the input + * file names and the output directory to store the archive * * Copy XML input files and every included files they contain (specified in * the tag) into a single flat file. */ -string archiveInputDeck( string_array const & inputFileNames, - string const & outputDirectory, - string_array const & xmlTagOrder ); +void archiveInputDeck( CommandLineOptions const & opts ); } /* namespace archiveInputDeck */ diff --git a/src/coreComponents/mainInterface/ProblemManager.cpp b/src/coreComponents/mainInterface/ProblemManager.cpp index 2c152a65fb1..3c2718b1955 100644 --- a/src/coreComponents/mainInterface/ProblemManager.cpp +++ b/src/coreComponents/mainInterface/ProblemManager.cpp @@ -37,7 +37,6 @@ #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" @@ -222,13 +221,6 @@ void ProblemManager::parseCommandLineInput() GEOS_LOG_RANK_0( "Opened XML file: " << absPath ); } - if( MpiWrapper::commRank() == 0 ) - { - string_array xmlTagOrder; - initializationOrder( xmlTagOrder ); - archiveInputDeck::archiveInputDeck( opts.inputFileNames, outputDirectory, xmlTagOrder ); - } - inputFileName = xmlWrapper::buildMultipleInputXML( opts.inputFileNames, outputDirectory ); string & schemaName = commandLine.getReference< string >( viewKeys.schemaFileName ); diff --git a/src/main/main.cpp b/src/main/main.cpp index 7bfa23a2b02..2374defed87 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -18,6 +18,7 @@ #include "common/logger/Logger.hpp" #include "common/TimingMacros.hpp" #include "common/Units.hpp" +#include "fileIO/Outputs/ArchiveInputDeck.hpp" #include "mainInterface/initialization.hpp" #include "mainInterface/ProblemManager.hpp" #include "mainInterface/GeosxState.hpp" @@ -37,6 +38,8 @@ int main( int argc, char *argv[] ) outputVersionInfo(); + archiveInputDeck::archiveInputDeck( *commandLineOptions ); + GEOS_LOG_RANK_0( GEOS_FMT( "Started at {:%Y-%m-%d %H:%M:%S}", startTime ) ); std::chrono::system_clock::duration initTime; From 5a7c196176d14ffb25be197fc5d64e8ea4023dcb Mon Sep 17 00:00:00 2001 From: kdrienCG Date: Thu, 16 Apr 2026 10:39:07 +0200 Subject: [PATCH 19/20] add command line option to trigger the archiving --- src/coreComponents/common/initializeEnvironment.hpp | 3 +++ src/coreComponents/mainInterface/initialization.cpp | 7 +++++++ src/main/main.cpp | 5 ++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/coreComponents/common/initializeEnvironment.hpp b/src/coreComponents/common/initializeEnvironment.hpp index 1072e8147a0..44a04c0e2e8 100644 --- a/src/coreComponents/common/initializeEnvironment.hpp +++ b/src/coreComponents/common/initializeEnvironment.hpp @@ -93,6 +93,9 @@ struct CommandLineOptions /// Print memory usage in data repository real64 printMemoryUsage = -1.0; + + /// Archive the input deck and the XSD schema + bool archiveInputDeck = false; }; /** diff --git a/src/coreComponents/mainInterface/initialization.cpp b/src/coreComponents/mainInterface/initialization.cpp index 39367cbb263..9a72b5f9f64 100644 --- a/src/coreComponents/mainInterface/initialization.cpp +++ b/src/coreComponents/mainInterface/initialization.cpp @@ -106,6 +106,7 @@ std::unique_ptr< CommandLineOptions > parseCommandLineOptions( int argc, char * MEMORY_USAGE, PAUSE_FOR, ERRORSOUTPUT, + ARCHIVE, }; const option::Descriptor usage[] = @@ -130,6 +131,7 @@ std::unique_ptr< CommandLineOptions > parseCommandLineOptions( int argc, char * { MEMORY_USAGE, 0, "m", "memory-usage", Arg::nonEmpty, "\t-m, --memory-usage, \t Minimum threshold for printing out memory allocations in a member of the data repository." }, { PAUSE_FOR, 0, "", "pause-for", Arg::numeric, "\t--pause-for, \t Pause geosx for a given number of seconds before starting execution" }, { ERRORSOUTPUT, 0, "e", "errorsOutput", Arg::nonEmpty, "\t-e, --errors-output, \t Output path for the errors file (\".yaml\" supported)" }, + { ARCHIVE, 0, "a", "archive", Arg::None, "\t-a, --archive, \t Archive the input deck and generate the XSD schema" }, { 0, 0, nullptr, nullptr, nullptr, nullptr } }; @@ -266,6 +268,11 @@ std::unique_ptr< CommandLineOptions > parseCommandLineOptions( int argc, char * ErrorLogger::global().createFile(); } break; + case ARCHIVE: + { + commandLineOptions->archiveInputDeck = true; + } + break; } } diff --git a/src/main/main.cpp b/src/main/main.cpp index 2374defed87..081ea6203fe 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -38,7 +38,10 @@ int main( int argc, char *argv[] ) outputVersionInfo(); - archiveInputDeck::archiveInputDeck( *commandLineOptions ); + if( commandLineOptions->archiveInputDeck ) + { + archiveInputDeck::archiveInputDeck( *commandLineOptions ); + } GEOS_LOG_RANK_0( GEOS_FMT( "Started at {:%Y-%m-%d %H:%M:%S}", startTime ) ); From d745bf64bf3479aff26a878b2e884305f3b213d6 Mon Sep 17 00:00:00 2001 From: kdrienCG Date: Thu, 16 Apr 2026 10:39:51 +0200 Subject: [PATCH 20/20] add levels to archiving command line option --- src/coreComponents/common/initializeEnvironment.hpp | 4 ++-- src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp | 5 ++++- src/coreComponents/mainInterface/initialization.cpp | 5 +++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/coreComponents/common/initializeEnvironment.hpp b/src/coreComponents/common/initializeEnvironment.hpp index 44a04c0e2e8..93261ac5694 100644 --- a/src/coreComponents/common/initializeEnvironment.hpp +++ b/src/coreComponents/common/initializeEnvironment.hpp @@ -94,8 +94,8 @@ struct CommandLineOptions /// Print memory usage in data repository real64 printMemoryUsage = -1.0; - /// Archive the input deck and the XSD schema - bool archiveInputDeck = false; + /// Set the archiving level + integer archiveInputDeck = 0; }; /** diff --git a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp index 82192554e8e..2fd952aee49 100644 --- a/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp +++ b/src/coreComponents/fileIO/Outputs/ArchiveInputDeck.cpp @@ -220,7 +220,10 @@ void archiveInputDeck( CommandLineOptions const & opts ) dataRepository::Group & commandLine = tempPM.getGroup< dataRepository::Group >( tempPM.groupKeys.commandLine ); commandLine.getReference< string >( tempPM.viewKeys.schemaFileName ) = schemaPath; - tempPM.generateDocumentation(); + if( opts.archiveInputDeck >= 2 ) + { + tempPM.generateDocumentation(); + } std::error_code ec; std::filesystem::remove( std::filesystem::path( schemaPath + ".other" ), ec ); diff --git a/src/coreComponents/mainInterface/initialization.cpp b/src/coreComponents/mainInterface/initialization.cpp index 9a72b5f9f64..e5ea6cf519f 100644 --- a/src/coreComponents/mainInterface/initialization.cpp +++ b/src/coreComponents/mainInterface/initialization.cpp @@ -131,7 +131,7 @@ std::unique_ptr< CommandLineOptions > parseCommandLineOptions( int argc, char * { MEMORY_USAGE, 0, "m", "memory-usage", Arg::nonEmpty, "\t-m, --memory-usage, \t Minimum threshold for printing out memory allocations in a member of the data repository." }, { PAUSE_FOR, 0, "", "pause-for", Arg::numeric, "\t--pause-for, \t Pause geosx for a given number of seconds before starting execution" }, { ERRORSOUTPUT, 0, "e", "errorsOutput", Arg::nonEmpty, "\t-e, --errors-output, \t Output path for the errors file (\".yaml\" supported)" }, - { ARCHIVE, 0, "a", "archive", Arg::None, "\t-a, --archive, \t Archive the input deck and generate the XSD schema" }, + { ARCHIVE, 0, "a", "archive", Arg::numeric, "\t-a, --archive, \t Set the archiving strategy level (0 = no archiving, 1 = only XML inputs, 2 = XML inputs and the XSD schema)" }, { 0, 0, nullptr, nullptr, nullptr, nullptr } }; @@ -270,7 +270,8 @@ std::unique_ptr< CommandLineOptions > parseCommandLineOptions( int argc, char * break; case ARCHIVE: { - commandLineOptions->archiveInputDeck = true; + integer const level = std::stoi( opt.arg ); + commandLineOptions->archiveInputDeck = level; } break; }