diff --git a/.github/workflows/DeployStaticExecutables.yaml b/.github/workflows/DeployStaticExecutables.yaml index c355790ff855..3e5ba5326a6f 100644 --- a/.github/workflows/DeployStaticExecutables.yaml +++ b/.github/workflows/DeployStaticExecutables.yaml @@ -53,35 +53,50 @@ jobs: gdown \ "https://drive.google.com/uc?id=1uaESVdylNmcHwpZwogX4TVrNs8JbWNF1" gdown \ - "https://drive.google.com/uc?id=1rfzjtXtrP9pQE2az3935uN0z_5aSF1hy" + "https://drive.google.com/uc?id=1yYMM4PVUec9pIjKTxI4aCpo0Umqconr8" - name: Copy CCE executables from the container run: > - mkdir CceExecutables + mkdir CceExecutables; + mkdir ./CceExecutables/ReduceCceWorldtube; + mkdir ./CceExecutables/Tests; cp ./tests/InputFiles/Cce/CharacteristicExtract.yaml ./CceExecutables/CharacteristicExtract.yaml - for i in CharacteristicExtract ReduceCceWorldtube; do - docker cp static-execs:/work/spectre/build/bin/$i ./CceExecutables; - done + cp ./tests/InputFiles/ReduceCceWorldtube/ReduceCceWorldtube.yaml + ./CceExecutables/ReduceCceWorldtube/ReduceCceWorldtube.yaml + + docker cp + static-execs:/work/spectre/build/bin/CharacteristicExtract + ./CceExecutables/ + + docker cp static-execs:/work/spectre/build/bin/ReduceCceWorldtube + ./CceExecutables/ReduceCceWorldtube/ - name: Test CCE executable outside of container run: | - sed -i 's/CceR0257/BondiSachsCceR0200/g' \ + mv BondiSachsCceR0200.h5 ./CceExecutables/Tests/ + mv CheckCceOutput.py ./CceExecutables/Tests/ + mv CharacteristicExtractReduction_Expected.h5 \ + ./CceExecutables/Tests/ + + sed -i 's/CceR0257/Tests\/BondiSachsCceR0200/g' \ ./CceExecutables/CharacteristicExtract.yaml sed -i 's/H5IsBondiData: False/H5IsBondiData: True/g' \ ./CceExecutables/CharacteristicExtract.yaml - ./CceExecutables/CharacteristicExtract \ - --input-file ./CceExecutables/CharacteristicExtract.yaml - python ./CheckCceOutput.py + + cd ./CceExecutables/ + + ./CharacteristicExtract \ + --input-file ./CharacteristicExtract.yaml + python ./Tests/CheckCceOutput.py rm CharacteristicExtractReduction.h5 + + cd ../ - name: Create CCE executables release asset # Note: We use xz compression since it's much better than gzip, even # though it's a decent bit slower. Specifically, xz is two thirds the # size of gzip. run: | - mv BondiSachsCceR0200.h5 ./CceExecutables/ - mv CharacteristicExtractReduction_Expected.h5 ./CceExecutables/ - mv CheckCceOutput.py ./CceExecutables/ tar cJf CceExecutables.tar.xz CceExecutables - name: Upload to release uses: softprops/action-gh-release@v2 diff --git a/cmake/AddInputFileTests.cmake b/cmake/AddInputFileTests.cmake index 08ac193d7ae1..91b1a464a54d 100644 --- a/cmake/AddInputFileTests.cmake +++ b/cmake/AddInputFileTests.cmake @@ -109,13 +109,20 @@ function(add_single_input_file_test INPUT_FILE EXECUTABLE COMMAND_LINE_ARGS endfunction() # Searches the directory INPUT_FILE_DIR for .yaml files and adds a test for each -# one. See `WritingTests.md` for details on controlling input file tests. -function(add_input_file_tests INPUT_FILE_DIR) +# one. See `WritingTests.md` for details on controlling input file tests. Add +# input files to the whitelist at the bottom of this file to ignore those tests +function(add_input_file_tests INPUT_FILE_DIR INPUT_FILE_WHITELIST) set(INPUT_FILE_LIST "") file(GLOB_RECURSE INPUT_FILE_LIST ${INPUT_FILE_DIR} "${INPUT_FILE_DIR}*.yaml") set(TIMEOUT 2) + list(TRANSFORM INPUT_FILE_WHITELIST PREPEND ${INPUT_FILE_DIR}) foreach(INPUT_FILE ${INPUT_FILE_LIST}) + # Only parse the input file if we are allowed to + if (${INPUT_FILE} IN_LIST INPUT_FILE_WHITELIST) + continue() + endif() + file(READ ${INPUT_FILE} INPUT_FILE_CONTENTS) # Read the priority of the test specified in input file, empty is accepted. @@ -239,4 +246,9 @@ configure_file( ${PROJECT_BINARY_DIR}/tmp/RunInputFileTest.sh @ONLY) -add_input_file_tests("${CMAKE_SOURCE_DIR}/tests/InputFiles/") +# These paths should be relative to the input file directory passed to +# `add_input_file_tests` +set(INPUT_FILE_WHITELIST + "ReduceCceWorldtube/ReduceCceWorldtube.yaml") + +add_input_file_tests("${CMAKE_SOURCE_DIR}/tests/InputFiles/" ${INPUT_FILE_WHITELIST}) diff --git a/docs/DevGuide/WritingTests.md b/docs/DevGuide/WritingTests.md index 0a0b8ae6752f..9ec7b9dc7911 100644 --- a/docs/DevGuide/WritingTests.md +++ b/docs/DevGuide/WritingTests.md @@ -259,7 +259,9 @@ namespace. We have a suite of input file tests in addition to unit tests. Every input file in the `tests/InputFiles/` directory is added to the test suite automatically. -The input file must specify the `Executable` it should run with in the input +If you don't want your input file tested at all, add the relative input file +path to the whitelist in `cmake/AddInputFileTests.cmake`. If the input file is +being tested, it must specify the `Executable` it should run with in the input file metadata (above the `---` marker in the input file). Properties of the test are controlled by the `Testing` section in the input file metadata. The following properties are available: diff --git a/docs/Tutorials/CCE.md b/docs/Tutorials/CCE.md index b0dc08ca4d1f..0b92832804e5 100644 --- a/docs/Tutorials/CCE.md +++ b/docs/Tutorials/CCE.md @@ -20,12 +20,16 @@ of the release (there may be a lot of text detailing what's been updated in this release). Inside this tarball is - the CCE executable `CharacteristicExtract` -- an example set of Bondi-Sachs worldtube data (see - [Input worldtube data formats](#input_worldtube_data_formats) section) - an example YAML input file -- example output from CCE -- a `ReduceCceWorldtube` executable for converting between - [worldtube data formats](#input_worldtube_data_formats) +- an example set of Bondi-Sachs worldtube data in the `Tests/` directory (see + [Input worldtube data formats](#input_worldtube_data_formats) section) +- example output from CCE in the `Tests/` directory +- a `ReduceCceWorldtube` executable and YAML file for converting between + [worldtube data formats](#input_worldtube_data_formats) in the + `ReduceCceWorldtube/` diretory +- a python script `CheckCceOutput.py` (meant to be run from the root of the + tarball and after you run the example YAML input file also in the root of the + tarball) that will check if the example output is correct \note The tarball is `.xz` so use `tar -xf TarName.tar.xz` to extract. The `-z` flag to use gzip will cause an error. @@ -198,18 +202,22 @@ SpECTRE provides a separate executable for converting from the The `ReduceCceWorldtube` executable should be run on a [cartesian_metric](#cartesian_metric_and_derivatives) worldtube file, and will produce a corresponding 'reduced' Bondi-Sachs worldtube file. -The basic command-line arguments for the executable are: +This executable works similarly to our other executables by accepting a YAML +input file: ``` -ReduceCceWorldtube --input-file CceR0050.h5 --output-file BondiCceR0050.h5\ - --lmax_factor 3 +ReduceCceWorldtube --input-file ReduceCceWorldtube.yaml ``` -The argument `--lmax_factor` determines the factor by which the resolution of +with a YAML file + +\snippet ReduceCceWorldtube.yaml reduce_cce_worldtube_yaml_doxygen_example + +The option `LMaxFactor` determines the factor by which the resolution of the boundary computation that is run will exceed the resolution of the input and output files. -Empirically, we have found that `lmax_factor` of 3 is sufficient to achieve -roundoff precision in all boundary data we have attempted, and an `lmax_factor` +Empirically, we have found that `LMaxFactor` of 3 is sufficient to achieve +roundoff precision in all boundary data we have attempted, and an `LMaxFactor` of 2 is usually sufficient to vastly exceed the precision of the simulation that provided the boundary dataset. diff --git a/src/Executables/ReduceCceWorldtube/CMakeLists.txt b/src/Executables/ReduceCceWorldtube/CMakeLists.txt index d6302bb327c4..5fd6a312f648 100644 --- a/src/Executables/ReduceCceWorldtube/CMakeLists.txt +++ b/src/Executables/ReduceCceWorldtube/CMakeLists.txt @@ -17,6 +17,8 @@ target_link_libraries( Cce GeneralRelativity Informer + Options + Parallel Printf Spectral SpinWeightedSphericalHarmonics diff --git a/src/Executables/ReduceCceWorldtube/ReduceCceWorldtube.cpp b/src/Executables/ReduceCceWorldtube/ReduceCceWorldtube.cpp index 90cf316597b4..9203fb1dba55 100644 --- a/src/Executables/ReduceCceWorldtube/ReduceCceWorldtube.cpp +++ b/src/Executables/ReduceCceWorldtube/ReduceCceWorldtube.cpp @@ -3,10 +3,12 @@ #include #include +#include #include #include "DataStructures/ComplexModalVector.hpp" #include "DataStructures/DataBox/DataBox.hpp" +#include "DataStructures/DataBox/Tag.hpp" #include "DataStructures/DataVector.hpp" #include "DataStructures/SpinWeighted.hpp" #include "DataStructures/Variables.hpp" @@ -18,9 +20,14 @@ #include "Evolution/Systems/Cce/WorldtubeBufferUpdater.hpp" #include "NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp" #include "NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp" +#include "Options/Auto.hpp" +#include "Options/ParseOptions.hpp" +#include "Options/String.hpp" +#include "Parallel/CreateFromOptions.hpp" #include "Parallel/Printf/Printf.hpp" #include "Utilities/Gsl.hpp" #include "Utilities/TMPL.hpp" +#include "Utilities/TaggedTuple.hpp" // Charm looks for this function but since we build without a main function or // main module we just have it be empty @@ -275,6 +282,107 @@ void perform_cce_worldtube_reduction( } Parallel::printf("\n"); } + +namespace OptionTags { +struct InputH5File { + using type = std::string; + static constexpr Options::String help = + "Name of the H5 worldtube file. A '.h5' extension will be added if " + "needed."; +}; + +struct OutputH5File { + using type = std::string; + static constexpr Options::String help = + "Name of output H5 file. A '.h5' extension will be added if needed."; +}; + +struct FixSpecNormalization { + using type = bool; + static constexpr Options::String help = + "Apply corrections associated with documented SpEC worldtube file " + "errors. If you are using worldtube data from SpECTRE or from another NR " + "code but in the SpECTRE format, then this option must be 'False'"; +}; + +struct BufferDepth { + using type = Options::Auto; + static constexpr Options::String help = + "Number of time steps to load during each call to the file-accessing " + "routines. Higher values mean fewer, larger loads from file into RAM. " + "Set to 'Auto' to use a default value (2000)."; +}; + +struct LMaxFactor { + using type = Options::Auto; + static constexpr Options::String help = + "The boundary computations will be performed at a resolution that is " + "'LMaxFactor' times the input file LMax to avoid aliasing. Set to 'Auto' " + "to use a default value (2)."; +}; +} // namespace OptionTags + +using option_tags = + tmpl::list; +using OptionTuple = tuples::tagged_tuple_from_typelist; + +namespace ReduceCceTags { +struct InputH5File : db::SimpleTag { + using type = std::string; + using option_tags = tmpl::list; + static constexpr bool pass_metavariables = false; + static type create_from_options(std::string option) { + if (not option.ends_with(".h5")) { + option += ".h5"; + } + return option; + } +}; + +struct OutputH5File : db::SimpleTag { + using type = std::string; + using option_tags = tmpl::list; + static constexpr bool pass_metavariables = false; + static type create_from_options(std::string option) { + if (not option.ends_with(".h5")) { + option += ".h5"; + } + return option; + } +}; + +struct FixSpecNormalization : db::SimpleTag { + using type = bool; + using option_tags = tmpl::list; + static constexpr bool pass_metavariables = false; + static type create_from_options(const bool option) { return option; } +}; + +struct BufferDepth : db::SimpleTag { + using type = size_t; + using option_tags = tmpl::list; + static constexpr bool pass_metavariables = false; + static type create_from_options(const std::optional& option) { + return option.value_or(2000); + } +}; + +struct LMaxFactor : db::SimpleTag { + using type = size_t; + using option_tags = tmpl::list; + static constexpr bool pass_metavariables = false; + static type create_from_options(const std::optional& option) { + return option.value_or(2); + } +}; +} // namespace ReduceCceTags + +using tags = tmpl::list; +using TagsTuple = tuples::tagged_tuple_from_typelist; } // namespace /* @@ -283,44 +391,60 @@ void perform_cce_worldtube_reduction( * storing the worldtube scalars that are required as input for CCE. */ int main(int argc, char** argv) { - boost::program_options::positional_options_description pos_desc; - pos_desc.add("old_spec_cce_file", 1).add("output_file", 1); - + // Boost options for the input yaml and --help flag boost::program_options::options_description desc("Options"); desc.add_options()("help,h,", "show this help message")( - "input_file", boost::program_options::value()->required(), - "name of old CCE data file")( - "output_file", boost::program_options::value()->required(), - "output filename")( - "fix_spec_normalization", - "Apply corrections associated with documented SpEC " - "worldtube file errors")( - "buffer_depth", - boost::program_options::value()->default_value(2000), - "number of time steps to load during each call to the file-accessing " - "routines. Higher values mean fewer, larger loads from file into RAM.")( - "lmax_factor", boost::program_options::value()->default_value(2), - "the boundary computations will be performed at a resolution that is " - "lmax_factor times the input file lmax to avoid aliasing"); + "input-file", boost::program_options::value()->required(), + "Name of YAML input file to use."); boost::program_options::variables_map vars; - boost::program_options::store( boost::program_options::command_line_parser(argc, argv) - .positional(pos_desc) .options(desc) .run(), vars); - if (vars.count("help") != 0u or vars.count("input_file") == 0u or - vars.count("output_file") == 0u) { - Parallel::printf("%s\n", desc); + // Option parser for all the actual options + Options::Parser parser{ + "This executable is used for converting the unnecessarily large metric " + "worldtube data format into a smaller representation (roughly a factor " + "of 4) just storing the worldtube scalars that are required as " + "input for CCE."}; + + // Help is a successful return + if (vars.contains("help")) { + Parallel::printf("%s\n%s", desc, parser.help()); return 0; } - perform_cce_worldtube_reduction(vars["input_file"].as(), - vars["output_file"].as(), - vars["buffer_depth"].as(), - vars["lmax_factor"].as(), - vars.count("fix_spec_normalization") != 0u); + // Not specifying an input file is an error + if (not vars.contains("input-file")) { + Parallel::printf("Missing input file. Pass '--input-file'"); + return 1; + } + + // Wrap in try-catch to print nice errors and terminate gracefully + try { + const std::string input_yaml = vars["input-file"].as(); + + // Actually parse the yaml. This does a check if it exists. + parser.parse_file(input_yaml); + + // First create option tags, and then actual tags. + const OptionTuple options = parser.template apply( + [](auto... args) { return OptionTuple(std::move(args)...); }); + const TagsTuple inputs = + Parallel::create_from_options(options, tags{}); + + // Do the reduction + perform_cce_worldtube_reduction( + tuples::get(inputs), + tuples::get(inputs), + tuples::get(inputs), + tuples::get(inputs), + tuples::get(inputs)); + } catch (const std::exception& exception) { + Parallel::printf("%s\n", exception.what()); + return 1; + } } diff --git a/tests/InputFiles/ReduceCceWorldtube/ReduceCceWorldtube.yaml b/tests/InputFiles/ReduceCceWorldtube/ReduceCceWorldtube.yaml new file mode 100644 index 000000000000..535cf996fd5d --- /dev/null +++ b/tests/InputFiles/ReduceCceWorldtube/ReduceCceWorldtube.yaml @@ -0,0 +1,12 @@ +# Distributed under the MIT License. +# See LICENSE.txt for details. + +# [reduce_cce_worldtube_yaml_doxygen_example] + +InputH5File: InputFilenameR0292.h5 +OutputH5File: ReducedWorldtubeR0292.h5 +FixSpecNormalization: False +BufferDepth: Auto +LMaxFactor: 3 + +# [reduce_cce_worldtube_yaml_doxygen_example]