diff --git a/CMakeLists.txt b/CMakeLists.txt index ece5d139e4..ada2e6b4c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,8 +148,6 @@ set(BOUT_SOURCES ./include/bout/operatorstencil.hxx ./include/bout/options.hxx ./include/bout/options_io.hxx - ./include/bout/options_netcdf.hxx - ./include/bout/options_adios.hxx ./include/bout/optionsreader.hxx ./include/bout/output.hxx ./include/bout/output_bout_types.hxx @@ -344,7 +342,9 @@ set(BOUT_SOURCES ./src/sys/options/options_ini.hxx ./src/sys/options/options_io.cxx ./src/sys/options/options_netcdf.cxx + ./src/sys/options/options_netcdf.hxx ./src/sys/options/options_adios.cxx + ./src/sys/options/options_adios.hxx ./src/sys/optionsreader.cxx ./src/sys/output.cxx ./src/sys/petsclib.cxx diff --git a/include/bout/bout.hxx b/include/bout/bout.hxx index 5e718dde33..d929a19c2f 100644 --- a/include/bout/bout.hxx +++ b/include/bout/bout.hxx @@ -2,8 +2,6 @@ * * @mainpage BOUT++ * - * @version 3.0 - * * @par Description * Framework for the solution of partial differential * equations, in particular fluid models in plasma physics. @@ -33,8 +31,8 @@ * **************************************************************************/ -#ifndef __BOUT_H__ -#define __BOUT_H__ +#ifndef BOUT_H +#define BOUT_H #include "bout/build_config.hxx" @@ -44,7 +42,7 @@ #include "bout/field3d.hxx" #include "bout/globals.hxx" #include "bout/mesh.hxx" -#include "bout/options_netcdf.hxx" +#include "bout/options_io.hxx" #include "bout/output.hxx" #include "bout/smoothing.hxx" // Smoothing functions #include "bout/solver.hxx" @@ -206,4 +204,4 @@ private: */ int BoutFinalise(bool write_settings = true); -#endif // __BOUT_H__ +#endif // BOUT_H diff --git a/include/bout/generic_factory.hxx b/include/bout/generic_factory.hxx index 3a2a63c94c..f7a0692af8 100644 --- a/include/bout/generic_factory.hxx +++ b/include/bout/generic_factory.hxx @@ -1,8 +1,8 @@ /// Base type for factories #pragma once -#ifndef __BOUT_GENERIC_FACTORY_H__ -#define __BOUT_GENERIC_FACTORY_H__ +#ifndef BOUT_GENERIC_FACTORY_H +#define BOUT_GENERIC_FACTORY_H #include "bout/boutexception.hxx" #include "bout/options.hxx" @@ -259,4 +259,4 @@ public: }; }; -#endif // __BOUT_GENERIC_FACTORY_H__ +#endif // BOUT_GENERIC_FACTORY_H diff --git a/include/bout/options_adios.hxx b/include/bout/options_adios.hxx deleted file mode 100644 index 7bbe421247..0000000000 --- a/include/bout/options_adios.hxx +++ /dev/null @@ -1,95 +0,0 @@ - -#pragma once - -#ifndef __OPTIONS_ADIOS_H__ -#define __OPTIONS_ADIOS_H__ - -#include "bout/build_config.hxx" -#include "bout/options.hxx" -#include "bout/options_io.hxx" - -#if !BOUT_HAS_ADIOS - -#include - -#include "bout/boutexception.hxx" - -namespace bout { - -class OptionsADIOS : public OptionsIO { -public: - OptionsADIOS() {} - explicit OptionsADIOS(const std::string& filename [[maybe_unused]], - [[maybe_unused]] bout::OptionsIO::FileMode mode = - bout::OptionsIO::FileMode::replace, - [[maybe_unused]] bool singleWriteFile = false) {} - OptionsADIOS(const OptionsADIOS&) = delete; - OptionsADIOS(OptionsADIOS&&) noexcept = default; - ~OptionsADIOS() = default; - - OptionsADIOS& operator=(const OptionsADIOS&) = delete; - OptionsADIOS& operator=(OptionsADIOS&&) noexcept = default; - - /// Read options from file - Options read() override { throw BoutException("OptionsADIOS not available\n"); } - - /// Write options to file - void write([[maybe_unused]] const Options& options, - [[maybe_unused]] const std::string& time_dim) override { - throw BoutException("OptionsADIOS not available\n"); - } - - void verifyTimesteps() const override { - throw BoutException("OptionsADIOS not available\n"); - } -}; - -} // namespace bout - -#else - -#include -#include - -namespace bout { - -/// Forward declare ADIOS file type so we don't need to depend -/// directly on ADIOS -struct ADIOSStream; - -class OptionsADIOS : public OptionsIO { -public: - // Constructors need to be defined in implementation due to forward - // declaration of ADIOSStream - OptionsADIOS() {} - OptionsADIOS(std::string filename, - bout::OptionsIO::FileMode mode = bout::OptionsIO::FileMode::replace, - bool singleWriteFile = false) - : OptionsIO(filename, mode, singleWriteFile) {} - OptionsADIOS(const OptionsADIOS&) = delete; - OptionsADIOS(OptionsADIOS&&) noexcept = default; - ~OptionsADIOS() = default; - - OptionsADIOS& operator=(const OptionsADIOS&) = delete; - OptionsADIOS& operator=(OptionsADIOS&&) noexcept = default; - - /// Read options from file - Options read() override; - - /// Write options to file - void write(const Options& options) override { write(options, "t"); } - void write(const Options& options, const std::string& time_dim) override; - - /// Check that all variables with the same time dimension have the - /// same size in that dimension. Throws BoutException if there are - /// any differences, otherwise is silent - void verifyTimesteps() const override; - -private: -}; - -} // namespace bout - -#endif // BOUT_HAS_ADIOS - -#endif // __OPTIONS_ADIOS_H__ diff --git a/include/bout/options_io.hxx b/include/bout/options_io.hxx index d8f26dc8ef..0139609417 100644 --- a/include/bout/options_io.hxx +++ b/include/bout/options_io.hxx @@ -6,40 +6,29 @@ #define OPTIONS_IO_H #include "bout/build_config.hxx" +#include "bout/generic_factory.hxx" +#include "bout/options.hxx" #include #include -#include "bout/options.hxx" - namespace bout { class OptionsIO { public: - enum class FileMode { - replace, ///< Overwrite file when writing - append ///< Append to file when writing - }; - - enum class Library { ADIOS, NetCDF, Invalid }; - - static const Library defaultIOLibrary = -#if BOUT_HAS_ADIOS - Library::NetCDF; -#elif BOUT_HAS_NETCDF - Library::NetCDF; -#else - Library::Invalid; -#endif + /// No default constructor, as settings are required + OptionsIO() = delete; + + /// Constructor specifies the kind of file, and options to control + /// the name of file, mode of operation etc. + OptionsIO(Options&) {} + + virtual ~OptionsIO() = default; - OptionsIO(); - OptionsIO(std::string filename, FileMode mode = FileMode::replace, - bool singleWriteFile = false); - virtual ~OptionsIO(); OptionsIO(const OptionsIO&) = delete; - OptionsIO(OptionsIO&&) noexcept; + OptionsIO(OptionsIO&&) noexcept = default; OptionsIO& operator=(const OptionsIO&) = delete; - OptionsIO& operator=(OptionsIO&&) noexcept; + OptionsIO& operator=(OptionsIO&&) noexcept = default; /// Read options from file virtual Options read() = 0; @@ -54,46 +43,72 @@ public: /// ADIOS: Indicate completion of an output step. virtual void verifyTimesteps() const = 0; - /// ADIOS: close file at the end of write(). NetCDF: no effect. - /// restart file must have this true if using ADIOS - //void setSingleWriteFile(const bool flag) { singleWriteFile = flag; }; + static std::unique_ptr create(const std::string& file); +}; + +class OptionsIOFactory : public Factory { +public: + static constexpr auto type_name = "OptionsIO"; + static constexpr auto section_name = "io"; + static constexpr auto option_name = "type"; + static constexpr auto default_type = +#if BOUT_HAS_NETCDF + "netcdf"; +#elif BOUT_HAS_ADIOS + "adios"; +#else + "invalid"; +#endif -protected: - /// Name of the file on disk - std::string filename; - /// How to open the file for writing - FileMode file_mode{FileMode::replace}; - bool singleWriteFile = false; + /// Create a restart file, configured with options (if given), + /// or root "restart_files" section. + /// + /// Options: + /// - "type" The type of file e.g "netcdf" or "adios" + /// - "file" Name of the file. Default is /.[type-dependent] + /// - "path" Path to restart files. Default is root "datadir" option, + /// that defaults to "data" + /// - "prefix" Default is "BOUT.restart" + ReturnType createRestart(Options* optionsptr = nullptr) const; + + /// Create an output file for writing time history. + /// Configure with options (if given), or root "output" section. + /// + /// Options: + /// - "type" The type of file e.g "netcdf" or "adios" + /// - "file" Name of the file. Default is /.[type] + /// - "path" Path to output files. Default is root "datadir" option, + /// that defaults to "data" + /// - "prefix" Default is "BOUT.dmp" + /// - "append" Append to existing file? Default is root "append" option, + /// that defaults to false. + ReturnType createOutput(Options* optionsptr = nullptr) const; + + /// Create a single file (e.g. mesh file) of the default type + ReturnType createFile(const std::string& file) const; }; -std::shared_ptr -OptionsIOFactory(std::string filename, - OptionsIO::FileMode mode = OptionsIO::FileMode::replace, - const OptionsIO::Library library = OptionsIO::defaultIOLibrary, - const bool singleWriteFile = false); - -OptionsIO::Library getIOLibrary(Options& options); - -/// Name of the directory for restart files -std::string getRestartDirectoryName(Options& options); -/// Name of the restart file on this rank -std::string getRestartFilename(Options& options, const OptionsIO::Library library); -/// Name of the restart file on \p rank -std::string getRestartFilename(Options& options, int rank, - const OptionsIO::Library library); -/// Name of the main output file on this rank -std::string getOutputFilename(Options& options, const OptionsIO::Library library); -/// Name of the main output file on \p rank -std::string getOutputFilename(Options& options, int rank, - const OptionsIO::Library library); -/// Write `Options::root()` to the main output file, overwriting any -/// existing files -void writeDefaultOutputFile( - const OptionsIO::Library library = OptionsIO::defaultIOLibrary); -/// Write \p options to the main output file, overwriting any existing -/// files -void writeDefaultOutputFile( - Options& options, const OptionsIO::Library library = OptionsIO::defaultIOLibrary); +/// Simpler name for Factory registration helper class +/// +/// Usage: +/// +/// #include +/// namespace { +/// RegisterOptionsIO registeroptionsiomine("myoptionsio"); +/// } +template +using RegisterOptionsIO = OptionsIOFactory::RegisterInFactory; + +/// Simpler name for indicating that an OptionsIO implementation +/// is unavailable. +/// +/// Usage: +/// +/// namespace { +/// RegisterUnavailableOptionsIO +/// unavailablemyoptionsio("myoptiosio", "BOUT++ was not configured with MyOptionsIO"); +/// } +using RegisterUnavailableOptionsIO = OptionsIOFactory::RegisterUnavailableInFactory; } // namespace bout diff --git a/include/bout/options_netcdf.hxx b/include/bout/options_netcdf.hxx deleted file mode 100644 index 5cbbf7e5fc..0000000000 --- a/include/bout/options_netcdf.hxx +++ /dev/null @@ -1,91 +0,0 @@ - -#pragma once - -#ifndef __OPTIONS_NETCDF_H__ -#define __OPTIONS_NETCDF_H__ - -#include "bout/build_config.hxx" - -#include "bout/options.hxx" -#include "bout/options_io.hxx" - -#if !BOUT_HAS_NETCDF || BOUT_HAS_LEGACY_NETCDF - -#include - -#include "bout/boutexception.hxx" -#include "bout/options.hxx" - -namespace bout { - -class OptionsNetCDF : public OptionsIO { -public: - OptionsNetCDF(const std::string& filename, - bout::OptionsIO::FileMode mode = bout::OptionsIO::FileMode::replace, - bool singleWriteFile = false) {} - OptionsNetCDF(const OptionsNetCDF&) = default; - OptionsNetCDF(OptionsNetCDF&&) = default; - OptionsNetCDF& operator=(const OptionsNetCDF&) = default; - OptionsNetCDF& operator=(OptionsNetCDF&&) = default; - - /// Read options from file - Options read() { throw BoutException("OptionsNetCDF not available\n"); } - - /// Write options to file - void write(const Options& options, const std::string& time_dim) { - throw BoutException("OptionsNetCDF not available\n"); - } - - void verifyTimesteps() const { throw BoutException("OptionsADIOS not available\n"); } -}; - -} // namespace bout - -#else - -#include -#include - -/// Forward declare netCDF file type so we don't need to depend -/// directly on netCDF -namespace netCDF { -class NcFile; -} - -namespace bout { - -class OptionsNetCDF : public OptionsIO { -public: - // Constructors need to be defined in implementation due to forward - // declaration of NcFile - OptionsNetCDF(); - OptionsNetCDF(std::string filename, FileMode mode = FileMode::replace, - bool singleWriteFile = false); - ~OptionsNetCDF(); - OptionsNetCDF(const OptionsNetCDF&) = delete; - OptionsNetCDF(OptionsNetCDF&&) noexcept; - OptionsNetCDF& operator=(const OptionsNetCDF&) = delete; - OptionsNetCDF& operator=(OptionsNetCDF&&) noexcept; - - /// Read options from file - Options read(); - - /// Write options to file - void write(const Options& options) { write(options, "t"); } - void write(const Options& options, const std::string& time_dim); - - /// Check that all variables with the same time dimension have the - /// same size in that dimension. Throws BoutException if there are - /// any differences, otherwise is silent - void verifyTimesteps() const; - -private: - /// Pointer to netCDF file so we don't introduce direct dependence - std::unique_ptr data_file; -}; - -} // namespace bout - -#endif - -#endif // __OPTIONS_NETCDF_H__ diff --git a/include/bout/physicsmodel.hxx b/include/bout/physicsmodel.hxx index eb1bae8ae1..e0f046eb1f 100644 --- a/include/bout/physicsmodel.hxx +++ b/include/bout/physicsmodel.hxx @@ -88,9 +88,6 @@ public: void add(Vector2D* value, const std::string& name, bool save_repeat = false); void add(Vector3D* value, const std::string& name, bool save_repeat = false); - /// Write stored data to file immediately - bool write(); - private: /// Helper struct to save enough information so that we can save an /// object to file later @@ -148,7 +145,7 @@ public: bout::DataFileFacade restart{}; /*! - * Initialse the model, calling the init() and postInit() methods + * Initialise the model, calling the init() and postInit() methods * * Note: this is usually only called by the Solver */ @@ -383,13 +380,13 @@ private: /// State for outputs Options output_options; /// File to write the outputs to - std::shared_ptr output_file; + std::unique_ptr output_file; /// Should we write output files bool output_enabled{true}; /// Stores the state for restarting Options restart_options; /// File to write the restart-state to - std::shared_ptr restart_file; + std::unique_ptr restart_file; /// Should we write restart files bool restart_enabled{true}; /// Split operator model? diff --git a/include/bout/solver.hxx b/include/bout/solver.hxx index 8a3f07c27a..ef7cbe63eb 100644 --- a/include/bout/solver.hxx +++ b/include/bout/solver.hxx @@ -33,8 +33,8 @@ * **************************************************************************/ -#ifndef __SOLVER_H__ -#define __SOLVER_H__ +#ifndef SOLVER_H +#define SOLVER_H #include "bout/build_config.hxx" @@ -597,4 +597,4 @@ private: BoutReal output_timestep; }; -#endif // __SOLVER_H__ +#endif // SOLVER_H diff --git a/src/bout++.cxx b/src/bout++.cxx index a42b0dcff8..1dc93dc76d 100644 --- a/src/bout++.cxx +++ b/src/bout++.cxx @@ -4,9 +4,9 @@ * Adapted from the BOUT code by B.Dudson, University of York, Oct 2007 * ************************************************************************** - * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Copyright 2010-2023 BOUT++ contributors * - * Contact Ben Dudson, bd512@york.ac.uk + * Contact Ben Dudson, dudson2@llnl.gov * * This file is part of BOUT++. * diff --git a/src/mesh/data/gridfromfile.cxx b/src/mesh/data/gridfromfile.cxx index 7e875bd109..aa66b145c7 100644 --- a/src/mesh/data/gridfromfile.cxx +++ b/src/mesh/data/gridfromfile.cxx @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include #include @@ -14,7 +14,7 @@ #include GridFile::GridFile(std::string gridfilename) - : GridDataSource(true), data(bout::OptionsNetCDF(gridfilename).read()), + : GridDataSource(true), data(bout::OptionsIO::create(gridfilename)->read()), filename(std::move(gridfilename)) { TRACE("GridFile constructor"); diff --git a/src/physics/physicsmodel.cxx b/src/physics/physicsmodel.cxx index 538851f4cb..9f538895ed 100644 --- a/src/physics/physicsmodel.cxx +++ b/src/physics/physicsmodel.cxx @@ -58,18 +58,6 @@ void DataFileFacade::add(Vector3D* value, const std::string& name, bool save_rep add(value->y, name_prefix + "y"s, save_repeat); add(value->z, name_prefix + "z"s, save_repeat); } - -bool DataFileFacade::write() { - for (const auto& item : data) { - bout::utils::visit(bout::OptionsConversionVisitor{Options::root(), item.name}, - item.value); - if (item.repeat) { - Options::root()[item.name].attributes["time_dimension"] = "t"; - } - } - writeDefaultOutputFile(); - return true; -} } // namespace bout PhysicsModel::PhysicsModel() @@ -81,21 +69,12 @@ PhysicsModel::PhysicsModel() .withDefault(true)) { - bout::OptionsIO::Library iolibrary = bout::getIOLibrary(Options::root()); if (output_enabled) { - std::string outputFileName = bout::getOutputFilename(Options::root(), iolibrary); - auto mode = Options::root()["append"] - .doc("Add output data to existing (dump) files?") - .withDefault(false) - ? bout::OptionsIO::FileMode::append - : bout::OptionsIO::FileMode::replace; - output_file = bout::OptionsIOFactory(outputFileName, mode, iolibrary); + output_file = bout::OptionsIOFactory::getInstance().createOutput(); } if (restart_enabled) { - std::string restartFileName = bout::getRestartFilename(Options::root(), iolibrary); - restart_file = bout::OptionsIOFactory( - restartFileName, bout::OptionsIO::FileMode::replace, iolibrary, true); + restart_file = bout::OptionsIOFactory::getInstance().createRestart(); } } diff --git a/src/sys/options/options_adios.cxx b/src/sys/options/options_adios.cxx index a032fd1629..fd8e978091 100644 --- a/src/sys/options/options_adios.cxx +++ b/src/sys/options/options_adios.cxx @@ -2,8 +2,8 @@ #if BOUT_HAS_ADIOS +#include "options_adios.hxx" #include "bout/adios_object.hxx" -#include "bout/options_adios.hxx" #include "bout/bout.hxx" #include "bout/globals.hxx" @@ -19,6 +19,22 @@ namespace bout { /// Name of the attribute used to track individual variable's time indices constexpr auto current_time_index_name = "current_time_index"; +OptionsADIOS::OptionsADIOS(Options& options) { + if (options["file"].doc("File name. Defaults to /.pb").isSet()) { + filename = options["file"].as(); + } else { + // Both path and prefix must be set + filename = fmt::format("{}/{}.bp", options["path"].as(), + options["prefix"].as()); + } + + file_mode = (options["append"].doc("Append to existing file?").withDefault(false)) + ? FileMode::append + : FileMode::replace; + + singleWriteFile = options["singleWriteFile"].withDefault(false); +} + template bool readVariable(adios2::Engine& reader, adios2::IO& io, const std::string& name, const std::string& type, Options& result) { diff --git a/src/sys/options/options_adios.hxx b/src/sys/options/options_adios.hxx new file mode 100644 index 0000000000..2460dfd10c --- /dev/null +++ b/src/sys/options/options_adios.hxx @@ -0,0 +1,84 @@ + +#pragma once + +#ifndef OPTIONS_ADIOS_H +#define OPTIONS_ADIOS_H + +#include "bout/build_config.hxx" +#include "bout/options.hxx" +#include "bout/options_io.hxx" + +#if !BOUT_HAS_ADIOS + +namespace { +bout::RegisterUnavailableOptionsIO + registerunavailableoptionsadios("adios", "BOUT++ was not configured with ADIOS2"); +} + +#else + +#include +#include + +namespace bout { + +/// Forward declare ADIOS file type so we don't need to depend +/// directly on ADIOS +struct ADIOSStream; + +class OptionsADIOS : public OptionsIO { +public: + // Constructors need to be defined in implementation due to forward + // declaration of ADIOSStream + OptionsADIOS() = delete; + + /// Create an OptionsADIOS + /// + /// Options: + /// - "file" The name of the file + /// If not set then "path" and "prefix" must be set, + /// and file is set to {path}/{prefix}.bp + /// - "append" + /// - "singleWriteFile" + OptionsADIOS(Options& options); + + OptionsADIOS(const OptionsADIOS&) = delete; + OptionsADIOS(OptionsADIOS&&) noexcept = default; + ~OptionsADIOS() = default; + + OptionsADIOS& operator=(const OptionsADIOS&) = delete; + OptionsADIOS& operator=(OptionsADIOS&&) noexcept = default; + + /// Read options from file + Options read() override; + + /// Write options to file + void write(const Options& options) override { write(options, "t"); } + void write(const Options& options, const std::string& time_dim) override; + + /// Check that all variables with the same time dimension have the + /// same size in that dimension. Throws BoutException if there are + /// any differences, otherwise is silent + void verifyTimesteps() const override; + +private: + enum class FileMode { + replace, ///< Overwrite file when writing + append ///< Append to file when writing + }; + + /// Name of the file on disk + std::string filename; + /// How to open the file for writing + FileMode file_mode{FileMode::replace}; + bool singleWriteFile = false; +}; + +namespace { +RegisterOptionsIO registeroptionsadios("adios"); +} + +} // namespace bout + +#endif // BOUT_HAS_ADIOS +#endif // OPTIONS_ADIOS_H diff --git a/src/sys/options/options_io.cxx b/src/sys/options/options_io.cxx index 514307ec59..0d25591db7 100644 --- a/src/sys/options/options_io.cxx +++ b/src/sys/options/options_io.cxx @@ -1,118 +1,41 @@ -#include "bout/build_config.hxx" - #include "bout/options_io.hxx" -#include "bout/bout.hxx" -#include "bout/globals.hxx" -#include "bout/mesh.hxx" -#include "bout/sys/timer.hxx" - -#include -#include -#include - -#include "bout/options_adios.hxx" -#include "bout/options_netcdf.hxx" +#include "options_adios.hxx" +#include "options_netcdf.hxx" namespace bout { - -OptionsIO::OptionsIO() {} - -OptionsIO::OptionsIO(std::string filename, FileMode mode, bool singleWriteFile) - : filename(std::move(filename)), file_mode(mode), singleWriteFile(singleWriteFile) {} - -OptionsIO::~OptionsIO() = default; -OptionsIO::OptionsIO(OptionsIO&&) noexcept = default; -OptionsIO& OptionsIO::operator=(OptionsIO&&) noexcept = default; - -OptionsIO::Library getIOLibrary(Options& options) { - if (options["iolibrary"].isSet()) { - // Solver-specific IO library - std::string iolib = options["iolibrary"]; - std::transform(iolib.begin(), iolib.end(), iolib.begin(), ::tolower); - if (iolib == "adios") - return OptionsIO::Library::ADIOS; - else if (iolib == "netcdf") - return OptionsIO::Library::NetCDF; - else - return OptionsIO::Library::Invalid; - } else { - return OptionsIO::defaultIOLibrary; - } +std::unique_ptr OptionsIO::create(const std::string& file) { + return OptionsIOFactory::getInstance().createFile(file); } -std::shared_ptr OptionsIOFactory(std::string filename, - OptionsIO::FileMode mode, - const OptionsIO::Library library, - const bool singleWriteFile) { - if (library == OptionsIO::Library::ADIOS) { - return std::make_shared(OptionsADIOS(filename, mode, singleWriteFile)); - } else if (library == OptionsIO::Library::NetCDF) { - return std::make_shared( - OptionsNetCDF(filename, mode, singleWriteFile)); - } else { - return nullptr; - } -} +OptionsIOFactory::ReturnType OptionsIOFactory::createRestart(Options* optionsptr) const { + Options& options = optionsptr ? *optionsptr : Options::root()["restart_files"]; -std::string getRestartDirectoryName(Options& options) { - if (options["restartdir"].isSet()) { - // Solver-specific restart directory - return options["restartdir"].withDefault("data"); - } - // Use the root data directory - return options["datadir"].withDefault("data"); + // Set defaults + options["path"].overrideDefault( + Options::root()["datadir"].withDefault("data")); + options["prefix"].overrideDefault("BOUT.restart"); + options["append"].overrideDefault(false); + options["singleWriteFile"].overrideDefault(true); + return create(getType(&options), options); } -std::string getRestartFilename(Options& options, const OptionsIO::Library library) { - return getRestartFilename(options, BoutComm::rank(), library); -} - -std::string getRestartFilename(Options& options, int rank, - const OptionsIO::Library library) { - if (library == OptionsIO::Library::ADIOS) - return fmt::format("{}/BOUT.restart.bp", bout::getRestartDirectoryName(options)); - else if (library == OptionsIO::Library::NetCDF) - return fmt::format("{}/BOUT.restart.{}.nc", bout::getRestartDirectoryName(options), - rank); - else - return fmt::format("{}/BOUT.restart.{}.data", bout::getRestartDirectoryName(options), - rank); -} - -std::string getOutputFilename(Options& options, const OptionsIO::Library library) { - return getOutputFilename(options, BoutComm::rank(), library); -} - -std::string getOutputFilename(Options& options, int rank, - const OptionsIO::Library library) { - if (library == OptionsIO::Library::ADIOS) - return fmt::format("{}/BOUT.dmp.bp", - options["datadir"].withDefault("data")); - else if (library == OptionsIO::Library::NetCDF) - return fmt::format("{}/BOUT.dmp.{}.nc", - options["datadir"].withDefault("data"), rank); - else - return fmt::format("{}/BOUT.dmp.{}.data", - options["datadir"].withDefault("data"), rank); -} +OptionsIOFactory::ReturnType OptionsIOFactory::createOutput(Options* optionsptr) const { + Options& options = optionsptr ? *optionsptr : Options::root()["output"]; -void writeDefaultOutputFile(const OptionsIO::Library library) { - writeDefaultOutputFile(Options::root(), library); + // Set defaults + options["path"].overrideDefault( + Options::root()["datadir"].withDefault("data")); + options["prefix"].overrideDefault("BOUT.dmp"); + options["append"].overrideDefault(Options::root()["append"] + .doc("Add output data to existing (dump) files?") + .withDefault(false)); + return create(getType(&options), options); } -void writeDefaultOutputFile(Options& options, const OptionsIO::Library library) { - bout::experimental::addBuildFlagsToOptions(options); - bout::globals::mesh->outputVars(options); - auto mode = options["append"] - .doc("Add output data to existing (dump) files?") - .withDefault(false) - ? bout::OptionsIO::FileMode::append - : bout::OptionsIO::FileMode::replace; - // Note: `options` contains the data to write. - // Get the output file from the `Options::root()` input settings. - auto io = OptionsIOFactory(getOutputFilename(Options::root(), library), mode, library); - io->write(options); +OptionsIOFactory::ReturnType OptionsIOFactory::createFile(const std::string& file) const { + Options options{{"file", file}}; + return create(getDefaultType(), options); } } // namespace bout diff --git a/src/sys/options/options_netcdf.cxx b/src/sys/options/options_netcdf.cxx index 0262e0cbbd..eda2b0bfbd 100644 --- a/src/sys/options/options_netcdf.cxx +++ b/src/sys/options/options_netcdf.cxx @@ -2,7 +2,7 @@ #if BOUT_HAS_NETCDF && !BOUT_HAS_LEGACY_NETCDF -#include "bout/options_netcdf.hxx" +#include "options_netcdf.hxx" #include "bout/bout.hxx" #include "bout/globals.hxx" @@ -643,14 +643,19 @@ std::vector verifyTimesteps(const NcGroup& group) { namespace bout { -OptionsNetCDF::OptionsNetCDF() : data_file(nullptr) {} - -OptionsNetCDF::OptionsNetCDF(std::string filename, FileMode mode, bool singleWriteFile) - : OptionsIO(filename, mode, singleWriteFile), data_file(nullptr) {} +OptionsNetCDF::OptionsNetCDF(Options& options) : OptionsIO(options) { + if (options["file"].doc("File name. Defaults to /..nc").isSet()) { + filename = options["file"].as(); + } else { + // Both path and prefix must be set + filename = fmt::format("{}/{}.{}.nc", options["path"].as(), + options["prefix"].as(), BoutComm::rank()); + } -OptionsNetCDF::~OptionsNetCDF() = default; -OptionsNetCDF::OptionsNetCDF(OptionsNetCDF&&) noexcept = default; -OptionsNetCDF& OptionsNetCDF::operator=(OptionsNetCDF&&) noexcept = default; + file_mode = (options["append"].doc("Append to existing file?").withDefault(false)) + ? FileMode::append + : FileMode::replace; +} void OptionsNetCDF::verifyTimesteps() const { NcFile dataFile(filename, NcFile::read); diff --git a/src/sys/options/options_netcdf.hxx b/src/sys/options/options_netcdf.hxx new file mode 100644 index 0000000000..8f195c9d92 --- /dev/null +++ b/src/sys/options/options_netcdf.hxx @@ -0,0 +1,84 @@ + +#pragma once + +#ifndef OPTIONS_NETCDF_H +#define OPTIONS_NETCDF_H + +#include "bout/build_config.hxx" + +#include "bout/options.hxx" +#include "bout/options_io.hxx" + +#if !BOUT_HAS_NETCDF || BOUT_HAS_LEGACY_NETCDF + +namespace { +RegisterUnavailableOptionsIO + registerunavailableoptionsnetcdf("netcdf", "BOUT++ was not configured with NetCDF"); +} + +#else + +#include +#include +#include + +namespace bout { + +class OptionsNetCDF : public OptionsIO { +public: + // Constructors need to be defined in implementation due to forward + // declaration of NcFile + OptionsNetCDF() = delete; + + /// Create an OptionsNetCDF + /// + /// Options: + /// - "file" The name of the file + /// If not set then "path" and "prefix" options must be set, + /// and file is set to {path}/{prefix}.{rank}.nc + /// - "append" File mode, default is false + OptionsNetCDF(Options& options); + + ~OptionsNetCDF() {} + + OptionsNetCDF(const OptionsNetCDF&) = delete; + OptionsNetCDF(OptionsNetCDF&&) noexcept = default; + OptionsNetCDF& operator=(const OptionsNetCDF&) = delete; + OptionsNetCDF& operator=(OptionsNetCDF&&) noexcept = default; + + /// Read options from file + Options read(); + + /// Write options to file + void write(const Options& options) { write(options, "t"); } + void write(const Options& options, const std::string& time_dim); + + /// Check that all variables with the same time dimension have the + /// same size in that dimension. Throws BoutException if there are + /// any differences, otherwise is silent + void verifyTimesteps() const; + +private: + enum class FileMode { + replace, ///< Overwrite file when writing + append ///< Append to file when writing + }; + + /// Pointer to netCDF file so we don't introduce direct dependence + std::unique_ptr data_file = nullptr; + + /// Name of the file on disk + std::string filename; + /// How to open the file for writing + FileMode file_mode{FileMode::replace}; +}; + +namespace { +RegisterOptionsIO registeroptionsnetcdf("netcdf"); +} + +} // namespace bout + +#endif + +#endif // OPTIONS_NETCDF_H diff --git a/tools/pylib/_boutpp_build/bout_options.pxd b/tools/pylib/_boutpp_build/bout_options.pxd index ba5e64c8e3..550bfc01e1 100644 --- a/tools/pylib/_boutpp_build/bout_options.pxd +++ b/tools/pylib/_boutpp_build/bout_options.pxd @@ -8,20 +8,10 @@ cdef extern from "boutexception_helper.hxx": cdef void raise_bout_py_error() -cdef extern from "bout/options_netcdf.hxx" namespace "bout": - cdef void writeDefaultOutputFile(); - cdef void writeDefaultOutputFile(Options& options); - cppclass OptionsNetCDF: - enum FileMode: - replace - append - OptionsNetCDF() except +raise_bout_py_error - OptionsNetCDF(string filename) except +raise_bout_py_error - OptionsNetCDF(string filename, FileMode mode) except +raise_bout_py_error - OptionsNetCDF(const OptionsNetCDF&); - OptionsNetCDF(OptionsNetCDF&&); - OptionsNetCDF& operator=(const OptionsNetCDF&); - OptionsNetCDF& operator=(OptionsNetCDF&&); +cdef extern from "bout/options_io.hxx" namespace "bout": + cppclass OptionsIO: + @staticmethod + OptionsIO * create(string filename) Options read(); void write(const Options& options); void write(const Options& options, string time_dim); diff --git a/tools/pylib/_boutpp_build/boutcpp.pxd.jinja b/tools/pylib/_boutpp_build/boutcpp.pxd.jinja index c94fd14a17..28633cad95 100644 --- a/tools/pylib/_boutpp_build/boutcpp.pxd.jinja +++ b/tools/pylib/_boutpp_build/boutcpp.pxd.jinja @@ -5,7 +5,7 @@ from libcpp.memory cimport unique_ptr from libcpp.string cimport string cimport resolve_enum as benum -from bout_options cimport Options, OptionsReader, OptionsNetCDF, writeDefaultOutputFile +from bout_options cimport Options, OptionsReader, OptionsIO cdef extern from "boutexception_helper.hxx": cdef void raise_bout_py_error() diff --git a/tools/pylib/_boutpp_build/boutpp.pyx.jinja b/tools/pylib/_boutpp_build/boutpp.pyx.jinja index 657e2f28c1..a8c027475a 100644 --- a/tools/pylib/_boutpp_build/boutpp.pyx.jinja +++ b/tools/pylib/_boutpp_build/boutpp.pyx.jinja @@ -1723,10 +1723,6 @@ cdef class Options: del self.cobj self.cobj = NULL - -def writeDefaultOutputFile(options: Options): - c.writeDefaultOutputFile(deref(options.cobj)) - def print(*args, sep=" ", end="\n"): _print(sep.join([str(a) for a in args]) + end)