diff --git a/core/src/ConfiguredModule.cpp b/core/src/ConfiguredModule.cpp index 2c2dcca98..4544e1eee 100644 --- a/core/src/ConfiguredModule.cpp +++ b/core/src/ConfiguredModule.cpp @@ -8,7 +8,7 @@ #include "include/ConfiguredModule.hpp" #include "include/Configurator.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include #include @@ -17,59 +17,36 @@ namespace Nextsim { const std::string ConfiguredModule::MODULE_PREFIX = "Modules"; -ConfiguredModule::map ConfiguredModule::configuredModules; - -void ConfiguredModule::parseConfigurator() -{ - // A default string that can never be a valid C++ class name - std::string defaultStr = "+++DEFAULT+++"; - // Construct a new options map - boost::program_options::options_description opt; - - for (auto entry : configuredModules) { - std::string module = entry.first; - opt.add_options()(addPrefix(module).c_str(), - boost::program_options::value()->default_value(defaultStr), - ("Load an implementation of " + module).c_str()); - } - - boost::program_options::variables_map vm = Configurator::parse(opt); - - for (auto entry : configuredModules) { - std::string implString = vm[addPrefix(entry.first)].as(); - // Only do anything if the retrieved option is not the default value - if (implString != defaultStr) { - entry.second.first(implString); - } - } -} +// A default string that can never be a valid C++ class name +static const std::string defaultStr = "+++DEFAULT+++"; std::string ConfiguredModule::addPrefix(const std::string& moduleName) { return MODULE_PREFIX + "." + moduleName; } -void ConfiguredModule::setConfiguredModules(const map& ls) +std::string ConfiguredModule::getImpl(const std::string& module) { - map newMap(ls); - configuredModules.merge(newMap); -} + boost::program_options::options_description opt; -void ConfiguredModule::configureModule(const std::string& mod, const fn& setter, const ofn& getter) -{ - configuredModules[mod] = { setter, getter }; -} + opt.add_options()(addPrefix(module).c_str(), + boost::program_options::value()->default_value(defaultStr), + ("Load an implementation of " + module).c_str()); -std::string ConfiguredModule::getModuleConfiguration(const std::string& module) -{ - return configuredModules[module].second(); + std::string implString = Configurator::parse(opt)[addPrefix(module)].as(); + // Only do anything if the retrieved option is not the default value + if (implString != defaultStr) { + return implString; + } else { + return ""; + } } ConfigMap ConfiguredModule::getAllModuleConfigurations() { ConfigMap iiMap; - for (auto entry : configuredModules) { - iiMap[addPrefix(entry.first)] = entry.second.second(); + for (const auto& moduleImpl : Module::ImplementationNames::getAll()) { + iiMap[moduleImpl.first] = moduleImpl.second; } return iiMap; } diff --git a/core/src/DevStep.cpp b/core/src/DevStep.cpp index 4b234e72d..b6b793a3b 100644 --- a/core/src/DevStep.cpp +++ b/core/src/DevStep.cpp @@ -9,7 +9,7 @@ #include "include/ConfiguredModule.hpp" #include "include/IDiagnosticOutput.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" namespace Nextsim { DevStep::DevStep() diff --git a/core/src/Model.cpp b/core/src/Model.cpp index 56285e825..eb31554aa 100644 --- a/core/src/Model.cpp +++ b/core/src/Model.cpp @@ -12,7 +12,7 @@ #include "include/DevStep.hpp" #include "include/IDiagnosticOutput.hpp" #include "include/MissingData.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include "include/StructureFactory.hpp" #include diff --git a/core/src/ModelMetadata.cpp b/core/src/ModelMetadata.cpp index d30597ec6..65fbbb23f 100644 --- a/core/src/ModelMetadata.cpp +++ b/core/src/ModelMetadata.cpp @@ -7,7 +7,8 @@ #include "include/ModelMetadata.hpp" -#include "include/StructureModule.hpp" +#include "include/IStructure.hpp" +#include "include/NextsimModule.hpp" #include "include/gridNames.hpp" #ifdef USE_MPI diff --git a/core/src/PrognosticData.cpp b/core/src/PrognosticData.cpp index 924adcb21..8c79707dd 100644 --- a/core/src/PrognosticData.cpp +++ b/core/src/PrognosticData.cpp @@ -9,7 +9,7 @@ #include "include/PrognosticData.hpp" #include "include/ModelArrayRef.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include "include/gridNames.hpp" namespace Nextsim { diff --git a/core/src/StructureFactory.cpp b/core/src/StructureFactory.cpp index 3dd7efbcf..83b6c306e 100644 --- a/core/src/StructureFactory.cpp +++ b/core/src/StructureFactory.cpp @@ -8,7 +8,8 @@ #include "include/StructureFactory.hpp" -#include "include/StructureModule.hpp" +#include "include/IStructure.hpp" +#include "include/NextsimModule.hpp" #include "include/RectGridIO.hpp" diff --git a/core/src/include/ConfiguredModule.hpp b/core/src/include/ConfiguredModule.hpp index d0795b3e2..9c0269bdf 100644 --- a/core/src/include/ConfiguredModule.hpp +++ b/core/src/include/ConfiguredModule.hpp @@ -19,42 +19,20 @@ namespace Nextsim { /*! - * @brief A helper class to automatically load modules based on the - * configuration. + * @brief A helper class to load modules based on the configuration. */ class ConfiguredModule { public: - typedef std::function fn; - typedef std::function ofn; - typedef std::map> map; ConfiguredModule() = default; virtual ~ConfiguredModule() = default; - //! Parse the configuration for all of the modules defined in ModuleLoader. - static void parseConfigurator(); - - /*! - * Sets the names of all the modules to be configured - * @param ls a map of Module names and the locations of implementation - * setter and getter functions. - */ - static void setConfiguredModules(const map& ls); - - /*! - * Adds the name of a module to the list to be configured - * @param mod the name of the class to be configured. - * @param setter the void(std::string) function to set the implementation. - * @param getter the std::string() function to get the implementation. - */ - static void configureModule(const std::string& mod, const fn& setter, const ofn& getter); - /*! * Gets the implementation name for a named module. * Returns an empty string if there is no such module. * * @param interface the name of the module to be checked. */ - static std::string getModuleConfiguration(const std::string& interface); + static std::string getImpl(const std::string& interface); /*! * Returns a map from interface name to implementation name for all modules @@ -71,9 +49,6 @@ class ConfiguredModule { //! The configuration options section name for modules. static const std::string MODULE_PREFIX; - -private: - static map configuredModules; }; } /* namespace Nextsim */ diff --git a/core/src/include/Module.hpp b/core/src/include/Module.hpp index d21ad1039..c9994171a 100644 --- a/core/src/include/Module.hpp +++ b/core/src/include/Module.hpp @@ -1,17 +1,13 @@ /*! * @file Module.hpp * - * @date Feb 14, 2022 + * @date 23 Sep 2024 * @author Tim Spain */ #ifndef MODULE_HPP #define MODULE_HPP -#include "include/ConfigurationHelp.hpp" -#include "include/ConfiguredModule.hpp" - -#include #include #include #include @@ -20,96 +16,217 @@ namespace Module { -template std::unique_ptr getInstance(); +/*! + * A class that records the names of all Modules in this executable. + */ +class ImplementationNames { +public: + using Container = std::map; + /*! + * Returns a map between module name and current implementation name for all modules. + */ + static Container getAll() + { + Container nameMap; + for (auto entry : getNames()) { + nameMap[entry.first()] = entry.second(); + } + return nameMap; + } -template I& getImplementation(); + /*! + * Returns the implementation name of the named interface, else throws a std::out_of_range + * exception. + */ + static std::string get(const std::string& interface) + { + for (auto entry : getNames()) { + if (entry.first() == interface) { + return entry.second(); + } + } + throw std::out_of_range("No module found with name " + interface); + } -template void setImplementation(const std::string&); +private: + using fn = std::string (*)(); + using InternalContainer = std::list>; + static InternalContainer& getNames() + { + static InternalContainer cache; + return cache; + } -template std::unique_ptr newImpl() -{ - return std::unique_ptr(new Imp); -} + static void set(fn intFunctionPtr, fn impFunctionPtr) + { + getNames().push_back({ intFunctionPtr, impFunctionPtr }); + } -template I& getImplTemplate() { return M::getImplementation(); } + // Make all Module<>s a friend + template friend class Module; +}; -template void setImplTemplate(const std::string& implName) +template std::unique_ptr newImpl() { - M::setImplementation(implName); + return std::unique_ptr(new Imp); } -template std::unique_ptr getInstTemplate() { return M::getInstance(); } - -typedef std::list OptionMap; -typedef std::map HelpMap; -using ConfigType = Nextsim::ConfigurationHelp::ConfigType; - -template HelpMap& getHelpRecursive(HelpMap& map, bool getAll); - template class Module { public: - typedef std::function()> fn; - typedef std::map map; - + using fn = std::unique_ptr (*)(); + using map = std::map; + + /*! + * Sets the function to generate new instances of an implementation not included in the + * relevant module.cfg file. + * + * @param generator A pointer to a function that returns a std::unique_ptr to an instance of + * the template class. + */ static void setExternalImplementation(fn generator) { - spf = generator; - staticInstance = std::move(spf()); + setExternalImplementationInternal(generator, true); } + /*! + * Sets the implementation of this module to one of the named implementations in the module.cfg + * file. + * + * @param implName A string containing a name matching one of the implementations defined in + * module.cfg. + */ static void setImplementation(const std::string& implName) { - // setExternalImplementation() holds the common functionality - try { - setExternalImplementation(functionMap.at(implName)); - } catch (const std::out_of_range& oor) { - std::throw_with_nested(std::runtime_error( - "No implementation named " + implName + " found for Module " + moduleName())); - } + setImplementationInternal(implName, true); } - static std::unique_ptr getInstance() { return spf(); } + /*! + * Returns a std::unique_ptr to a new instance of the current implementation. + */ + static std::unique_ptr getInstance() { return getInstanceInternal(true); } + + /*! + * Returns a std::unique_ptr to the static instance of the current implementation. + */ + static std::unique_ptr& getUniqueInstance(bool suppressInit = false) + { + static std::unique_ptr staticInstance + = std::move((suppressInit) ? std::unique_ptr(nullptr) : getInstanceInternal(false)); + return staticInstance; + } - static I& getImplementation() { return *staticInstance; } + /*! + * Returns a reference to the static instance of the current implementation. + */ + static I& getImplementation() + { + if (!getUniqueInstance().get()) + throw std::logic_error("Bad reference implementation in " + moduleName()); + return *getUniqueInstance(); + } + /*! + * Returns a list of all the implementations named in the relevant module.cfg file. + */ static std::list listImplementations() { std::list keys; - for (auto entry : functionMap) { + for (const auto& entry : functionMap()) { keys.push_back(entry.first); } return keys; } + /*! + * Returns the name of the current implementation , if it is listed in the relevant module.cfg + * file. Otherwise returns an empty string. + */ static std::string implementation() { typedef std::unique_ptr(fnType)(); // The name is not cached, so find the function in the map which - // corresponds to spf. The hairy code is derived from - // https://stackoverflow.com/a/35920804 - for (auto entry : functionMap) { - if (*entry.second.template target() == *spf.template target()) { + // corresponds to getGenerationFunction. + for (const auto& entry : functionMap()) { + if (entry.second == getGenerationFunction()) { return entry.first; } } - return ""; // spf should always be an entry in functionMap + /* + * If the generation function is not found in the function map, assume an external + * implementation and return an empty string. + */ + return ""; } + //! Returns a string containing the name of the module. static std::string moduleName(); + //! Adds help information to the argument help map, according to the boolean getAll argument. + //! Implementation dependent. + static HelpMap& getHelpRecursive(HelpMap& helpMap, bool getAll); + + /*! + * Finalizes the Module by setting both pointers to nullptr. + */ + static void finalize() + { + getUniqueInstance(true) = nullptr; + getGenerationFunction() = nullptr; + } + private: - static fn spf; - static std::unique_ptr staticInstance; - static map functionMap; -}; + static std::string getDefaultImplementationName(); + static fn& getGenerationFunction() + { + static fn ptr = functionMap().at(getDefaultImplementationName()); + return ptr; + } -template void addToConfiguredModules() -{ - Nextsim::ConfiguredModule::configureModule( - Module::moduleName(), M::setImplementation, M::implementation); -} + static const map& functionMap(); -template std::string implementation() { return Module::implementation(); } + static bool& isConfigured() + { + static bool isConfiguredBool = false; + return isConfiguredBool; + } + + static std::unique_ptr getInstanceInternal(bool setStaticInstance) + { + if (!isConfigured()) { + isConfigured() = true; + std::string implName = Config::getImpl(moduleName()); + if (!implName.empty()) { + setImplementationInternal(implName, setStaticInstance); + } + } + return getGenerationFunction()(); + } + + static void setImplementationInternal(const std::string& implName, bool setStaticInstance) + { + // setExternalImplementation() holds the common functionality + try { + setExternalImplementationInternal(functionMap().at(implName), setStaticInstance); + ImplementationNames::set(moduleName, implementation); + } catch (const std::out_of_range& oor) { + std::throw_with_nested(std::runtime_error( + "No implementation named " + implName + " found for Module " + moduleName())); + } + } + + static void setExternalImplementationInternal(fn generator, bool setStaticInstance) + { + getGenerationFunction() = generator; + if (setStaticInstance) + getUniqueInstance(true) = std::move(getGenerationFunction()()); + } +}; + +template std::unique_ptr getInstance(); +template I& getImplementation(); +template void setImplementation(const std::string& impl); +template HelpMap& getHelpRecursive(HelpMap& map, bool getAll); +template std::string implementation(); } #endif /* MODULE_HPP */ diff --git a/core/src/include/NextsimModule.hpp b/core/src/include/NextsimModule.hpp new file mode 100644 index 000000000..56f99850d --- /dev/null +++ b/core/src/include/NextsimModule.hpp @@ -0,0 +1,24 @@ +/*! + * @file NextsimModule.hpp + * + * @date Sep 13, 2024 + * @author Tim Spain + */ + +#ifndef NEXTSIMMODULE_HPP +#define NEXTSIMMODULE_HPP + +#include "include/ConfigurationHelp.hpp" +#include "include/ConfiguredModule.hpp" + +// Define the types the module should use (HelpMap and Config) +namespace Module { +using OptionMap = std::list; +using HelpMap = std::map; + +using Config = Nextsim::ConfiguredModule; +} + +#include "include/Module.hpp" + +#endif /* NEXTSIMMODULE_HPP */ diff --git a/core/src/main.cpp b/core/src/main.cpp index bb94b4044..c02c2f52b 100644 --- a/core/src/main.cpp +++ b/core/src/main.cpp @@ -37,8 +37,6 @@ int main(int argc, char* argv[]) } catch (const std::exception& e) { // Do nothing. If there is no additional configuration to be parse, ignore it. } - // Parse the configuration to load those that are explicitly configured - Nextsim::ConfiguredModule::parseConfigurator(); if (!cmdLine.configHelp().empty()) { Nextsim::Model::HelpMap map; diff --git a/core/src/modules/CMakeLists.txt b/core/src/modules/CMakeLists.txt index 69f22d9fd..07f652fc7 100644 --- a/core/src/modules/CMakeLists.txt +++ b/core/src/modules/CMakeLists.txt @@ -10,14 +10,6 @@ foreach(ModuleSubDir IN LISTS ModuleSubDirs) continue() endif() - if(NOT EXISTS ("include/${ModuleSubDir}.hpp")) - # Run the header python script - execute_process( - COMMAND "${Python_EXECUTABLE}" "${ModuleHeaderScript}" - WORKING_DIRECTORY "${ModuleSubDir}" - ) - endif() - # Run the source python script execute_process( COMMAND "${Python_EXECUTABLE}" "${ModuleBuilderScript}" diff --git a/core/src/modules/DynamicsModule/include/MEVPDynamics.hpp b/core/src/modules/DynamicsModule/include/MEVPDynamics.hpp index 5b36bd4ac..d4ddbb395 100644 --- a/core/src/modules/DynamicsModule/include/MEVPDynamics.hpp +++ b/core/src/modules/DynamicsModule/include/MEVPDynamics.hpp @@ -13,7 +13,7 @@ #include "include/IDamageHealing.hpp" #include "include/IDynamics.hpp" #include "include/MEVPDynamicsKernel.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include "include/ModelArray.hpp" #include "include/ModelComponent.hpp" diff --git a/core/test/ConfigOutput_test.cpp b/core/test/ConfigOutput_test.cpp index 36a65f820..c1917c7b1 100644 --- a/core/test/ConfigOutput_test.cpp +++ b/core/test/ConfigOutput_test.cpp @@ -21,7 +21,7 @@ #include "include/ModelComponent.hpp" #include "include/ModelMetadata.hpp" #include "include/ModelState.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include "include/NZLevels.hpp" #include "include/gridNames.hpp" @@ -67,10 +67,8 @@ TEST_CASE("Test periodic output") ModelArray::setDimension(ModelArray::Dimension::Z, NZLevels::get()); #endif + Module::Module::setImplementation("Nextsim::ConfigOutput"); std::stringstream config; - config << "[Modules]" << std::endl; - config << "DiagnosticOutputModule = Nextsim::ConfigOutput" << std::endl; - config << std::endl; config << "[ConfigOutput]" << std::endl; config << "period = 3600" << std::endl; // Output every hour config << "start = 2020-01-11T00:00:00Z" << std::endl; // start after 10 days @@ -81,8 +79,6 @@ TEST_CASE("Test periodic output") std::unique_ptr pcstream(new std::stringstream(config.str())); Configurator::addStream(std::move(pcstream)); - ConfiguredModule::parseConfigurator(); - Module::setImplementation("Nextsim::ParametricGrid"); HField hice(ModelArray::Type::H); diff --git a/core/test/ConfiguredModule_test.cpp b/core/test/ConfiguredModule_test.cpp index 00331f947..449e5332d 100644 --- a/core/test/ConfiguredModule_test.cpp +++ b/core/test/ConfiguredModule_test.cpp @@ -11,119 +11,123 @@ #include #include "ArgV.hpp" -#include "moduleTestClasses.hpp" #include "include/Configurator.hpp" + +using HelpMap = int; +using Config = Nextsim::ConfiguredModule; + #include "include/Module.hpp" #include #include -// Module classes for the test classes -namespace Module { -class ITestModule : public Module -{ -}; +namespace Test { +class ITest {}; +class DefaultImpl : public ITest {}; +class Impl1 : public ITest {}; +class Impl2 : public ITest {}; + +class ITest2 {}; +class AnotherImpl : public ITest2 {}; +class DefaultImpl_2 : public ITest2 {}; + +} +// Module namespace to emulate how the names are defined with real Modules +namespace Module { +const std::string DEFAULTIMPL = "DefaultImpl"; const std::string IMPL1 = "Impl1"; const std::string IMPL2 = "Impl2"; +const std::string ANOTHERIMPL = "AnotherImpl"; template <> -Module::map Module::functionMap = { - {IMPL1, newImpl}, - {IMPL2, newImpl}, -}; -template <> Module::fn Module::spf = functionMap.at(IMPL1); -template <> std::unique_ptr Module::staticInstance = std::move(Module::spf()); -template <> std::string Module::moduleName() { return "ITest"; }; -template <> std::unique_ptr getInstance() { return getInstTemplate(); }; -template <> ITest& getImplementation() { return getImplTemplate(); }; -template <> void setImplementation(const std::string& implName) +const Module::map& Module::functionMap() { - setImplTemplate(implName); -}; + static const map theMap = { + { DEFAULTIMPL, newImpl }, + { IMPL1, newImpl }, + { IMPL2, newImpl }, + }; + return theMap; } -namespace Nextsim { - -TEST_SUITE_BEGIN("ConfiguredModule"); -TEST_CASE("Configure a module") +template <> +Module::fn& Module::getGenerationFunction() { - Configurator::clear(); - - // Create the fake command line, selecting the Impl1 implementation - ArgV argvee({"cmtest", "--Modules.ITest=Impl1"}); - - Configurator::setCommandLine(argvee.argc(), argvee()); + static fn ptr = functionMap().at(DEFAULTIMPL); + return ptr; +} - ConfiguredModule::setConfiguredModules({ - {Module::Module::moduleName(), {Module::setImplementation, Module::implementation}}, - }); - ConfiguredModule::parseConfigurator(); +template <> std::string Module::moduleName() { return "ITest"; } - ITest& impler = Module::getImplementation(); +template <> HelpMap& Module::getHelpRecursive(HelpMap& map, bool getAll) +{ + return map; +} - REQUIRE(impler() == 1); +template <> +const Module::map& Module::functionMap() +{ + static const map theMap = { + { DEFAULTIMPL, newImpl }, + { ANOTHERIMPL, newImpl }, + }; + return theMap; } -TEST_CASE("Configure a module from a stream") +template <> +Module::fn& Module::getGenerationFunction() { - Configurator::clear(); - std::stringstream config; - config << "[Modules]" << std::endl - << "ITest = Impl2" << std::endl; + static fn ptr = functionMap().at(DEFAULTIMPL); + return ptr; +} - std::unique_ptr pcstream(new std::stringstream(config.str())); - Configurator::addStream(std::move(pcstream)); +template <> std::string Module::moduleName() { return "ITest2"; } - ConfiguredModule::setConfiguredModules({ - {Module::Module::moduleName(), {Module::setImplementation, Module::implementation}}, - }); - ConfiguredModule::parseConfigurator(); +template <> HelpMap& Module::getHelpRecursive(HelpMap& map, bool getAll) +{ + return map; +} - ITest& impler = Module::getImplementation(); - REQUIRE(impler() == 2); } -TEST_CASE("Don't configure a module from a stream") +namespace Nextsim { + +TEST_SUITE_BEGIN("ConfiguredModule"); +TEST_CASE("Configure a module") { Configurator::clear(); - std::stringstream config; - config << "[Modules]" << std::endl - << "ITestNotReally = NotImpl2" << std::endl; - Module::setImplementation("Impl2"); - - std::unique_ptr pcstream(new std::stringstream(config.str())); - Configurator::addStream(std::move(pcstream)); + // Create the fake command line, selecting the Impl1 implementation + ArgV argvee({"cmtest", "--Modules.ITest=Impl1"}); - ConfiguredModule::setConfiguredModules({ - {Module::Module::moduleName(), {Module::setImplementation, Module::implementation}}, - }); - // Parse the available modules. This should not change the implementation - // to the default. - ConfiguredModule::parseConfigurator(); + Configurator::setCommandLine(argvee.argc(), argvee()); - ITest& impler = Module::getImplementation(); - REQUIRE(impler() == 2); + REQUIRE(ConfiguredModule::getImpl("ITest") == Module::IMPL1); } -TEST_CASE("Configure a module with an incorrect name") +TEST_CASE("Configure a module from a stream") { Configurator::clear(); std::stringstream config; config << "[Modules]" << std::endl - << "ITest = Optometry" << std::endl; - + << "ITest = Impl2" << std::endl + << "ITest2 = AnotherImpl" << std::endl; std::unique_ptr pcstream(new std::stringstream(config.str())); Configurator::addStream(std::move(pcstream)); - ConfiguredModule::setConfiguredModules({ - {Module::Module::moduleName(), {Module::setImplementation, Module::implementation}}, - }); - // Should throw a domain_error as "Optometry" is not a valid implementation. - REQUIRE_THROWS(ConfiguredModule::parseConfigurator()); + REQUIRE(ConfiguredModule::getImpl("ITest") == Module::IMPL2); + Module::Module::setImplementation(ConfiguredModule::getImpl("ITest")); + REQUIRE(ConfiguredModule::getImpl("ITest2") == Module::ANOTHERIMPL); + Module::Module::setImplementation(ConfiguredModule::getImpl("ITest2")); + // Tests Module::ImplementationNames, too + ConfigMap cfgMap = ConfiguredModule::getAllModuleConfigurations(); + REQUIRE(cfgMap.size() == 2); + REQUIRE(std::get(cfgMap.at("ITest")) == Module::IMPL2); + REQUIRE(std::get(cfgMap.at("ITest2")) == Module::ANOTHERIMPL); } + TEST_SUITE_END(); } diff --git a/core/test/DynamicsModuleForPDtest.cpp b/core/test/DynamicsModuleForPDtest.cpp index 5f18b876b..c4424c373 100644 --- a/core/test/DynamicsModuleForPDtest.cpp +++ b/core/test/DynamicsModuleForPDtest.cpp @@ -5,7 +5,8 @@ * @author Tim Spain */ -#include "include/DynamicsModule.hpp" +#include "include/IDynamics.hpp" +#include "include/NextsimModule.hpp" #include "include/DummyDynamics.hpp" @@ -15,35 +16,23 @@ namespace Module { const std::string DUMMYDYNAMICS = "Nextsim::DummyDynamics"; template <> -Module::map Module::functionMap = { - { DUMMYDYNAMICS, newImpl }, -}; +const Module::map& Module::functionMap() +{ + static const map theMap = { + { DUMMYDYNAMICS, newImpl }, + }; + return theMap; +} template <> -Module::fn Module::spf = functionMap.at(DUMMYDYNAMICS); -template <> -std::unique_ptr Module::staticInstance - = std::move(newImpl()); +Module::fn& Module::getGenerationFunction() +{ + static fn thePtr = functionMap().at(DUMMYDYNAMICS); + return thePtr; +} template <> std::string Module::moduleName() { return "Nextsim::IDynamics"; } template <> HelpMap& getHelpRecursive(HelpMap& map, bool getAll) { return map; } -template <> Nextsim::IDynamics& getImplementation() -{ - return getImplTemplate(); -} -template <> void setImplementation(const std::string& implName) -{ - setImplTemplate(implName); -} -template <> std::unique_ptr getInstance() -{ - return getInstTemplate(); -} -DynamicsModule::Constructor DynamicsModule::ctor; -DynamicsModule::Constructor::Constructor() -{ - addToConfiguredModules(); -} } /* namespace Module */ diff --git a/core/test/ModuleLoaderTestModules/moduleLoaderAssignments.ipp b/core/test/ModuleLoaderTestModules/moduleLoaderAssignments.ipp deleted file mode 100644 index 0e81e99df..000000000 --- a/core/test/ModuleLoaderTestModules/moduleLoaderAssignments.ipp +++ /dev/null @@ -1,11 +0,0 @@ - if (module == "ITest") { - if (impl == "Impl1") { - p_ITest = &i_Impl1; - pf_ITest = &newImpl1; - } else if (impl == "Impl2") { - p_ITest = &i_Impl2; - pf_ITest = &newImpl2; - } else { - throwup(module, impl); - } - } else { } diff --git a/core/test/ModuleLoaderTestModules/moduleLoaderFunctions.ipp b/core/test/ModuleLoaderTestModules/moduleLoaderFunctions.ipp deleted file mode 100644 index d94409c3a..000000000 --- a/core/test/ModuleLoaderTestModules/moduleLoaderFunctions.ipp +++ /dev/null @@ -1,22 +0,0 @@ -static ITest* p_ITest; -template<> -ITest& ModuleLoader::getImplementation() -{ - return *p_ITest; -} -std::unique_ptr (*pf_ITest)(); -template<> -std::unique_ptr ModuleLoader::getInstance() const -{ - return (*pf_ITest)(); -} -static Impl1 i_Impl1; -std::unique_ptr newImpl1() -{ - return std::unique_ptr(new Impl1); -} -static Impl2 i_Impl2; -std::unique_ptr newImpl2() -{ - return std::unique_ptr(new Impl2); -} diff --git a/core/test/ModuleLoaderTestModules/moduleLoaderHeaders.ipp b/core/test/ModuleLoaderTestModules/moduleLoaderHeaders.ipp deleted file mode 100644 index 4b8409326..000000000 --- a/core/test/ModuleLoaderTestModules/moduleLoaderHeaders.ipp +++ /dev/null @@ -1 +0,0 @@ -#include "../moduleTestClasses.hpp" diff --git a/core/test/ModuleLoaderTestModules/moduleLoaderNames.ipp b/core/test/ModuleLoaderTestModules/moduleLoaderNames.ipp deleted file mode 100644 index b155561c8..000000000 --- a/core/test/ModuleLoaderTestModules/moduleLoaderNames.ipp +++ /dev/null @@ -1,8 +0,0 @@ - m_availableImplementationNames = { - { - "ITest", { - "Impl1", - "Impl2" - } - }, - }; diff --git a/core/test/ModuleLoaderTestModules/modules.json b/core/test/ModuleLoaderTestModules/modules.json deleted file mode 100644 index 74e45c2ec..000000000 --- a/core/test/ModuleLoaderTestModules/modules.json +++ /dev/null @@ -1,9 +0,0 @@ -[ - { - "name": "ITest", - "implementations": [ - "Impl1", - "Impl2" - ] - } -] \ No newline at end of file diff --git a/core/test/ParaGrid_test.cpp b/core/test/ParaGrid_test.cpp index 867e5bae0..dbe2598f7 100644 --- a/core/test/ParaGrid_test.cpp +++ b/core/test/ParaGrid_test.cpp @@ -19,7 +19,8 @@ #include "include/NZLevels.hpp" #include "include/ParaGridIO.hpp" #include "include/ParametricGrid.hpp" -#include "include/StructureModule.hpp" +#include "include/IStructure.hpp" +#include "include/NextsimModule.hpp" #include "include/gridNames.hpp" #include diff --git a/core/test/PrognosticDataIO_test.cpp b/core/test/PrognosticDataIO_test.cpp index 86706b342..ca0370751 100644 --- a/core/test/PrognosticDataIO_test.cpp +++ b/core/test/PrognosticDataIO_test.cpp @@ -15,7 +15,7 @@ #include "include/ConfiguredModule.hpp" #include "include/IStructure.hpp" #include "include/ModelComponent.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include "include/StructureFactory.hpp" #include "include/UnescoFreezing.hpp" #include "include/constants.hpp" diff --git a/core/test/PrognosticData_test.cpp b/core/test/PrognosticData_test.cpp index 4fb59c03b..f2148d562 100644 --- a/core/test/PrognosticData_test.cpp +++ b/core/test/PrognosticData_test.cpp @@ -14,11 +14,12 @@ #include "include/ConfiguredModule.hpp" #include "include/ModelComponent.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include "include/UnescoFreezing.hpp" #include "include/constants.hpp" #include +#include extern template class Module::Module; @@ -32,10 +33,8 @@ TEST_CASE("PrognosticData call order test") ModelArray::setDimensions(ModelArray::Type::H, { 1, 1 }); ModelArray::setDimensions(ModelArray::Type::Z, { 1, 1, 1 }); + Module::setImplementation("Nextsim::ConfiguredAtmosphere"); std::stringstream config; - config << "[Modules]" << std::endl; - config << "AtmosphereBoundaryModule = Nextsim::ConfiguredAtmosphere" << std::endl; - config << std::endl; config << "[ConfiguredAtmosphere]" << std::endl; config << "t_air = 3" << std::endl; config << "t_dew = 2" << std::endl; @@ -49,8 +48,6 @@ TEST_CASE("PrognosticData call order test") std::unique_ptr pcstream(new std::stringstream(config.str())); Configurator::addStream(std::move(pcstream)); - ConfiguredModule::parseConfigurator(); - class OceanData : public IOceanBoundary { public: OceanData() diff --git a/core/test/XiosRead_test.cpp b/core/test/XiosRead_test.cpp index 1641902f6..1df1a7efc 100644 --- a/core/test/XiosRead_test.cpp +++ b/core/test/XiosRead_test.cpp @@ -13,7 +13,7 @@ #include "StructureModule/include/ParametricGrid.hpp" #include "include/Configurator.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include "include/Xios.hpp" #include diff --git a/core/test/XiosWrite_test.cpp b/core/test/XiosWrite_test.cpp index a5384d335..bb0be45ec 100644 --- a/core/test/XiosWrite_test.cpp +++ b/core/test/XiosWrite_test.cpp @@ -13,7 +13,7 @@ #include "StructureModule/include/ParametricGrid.hpp" #include "include/Configurator.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include "include/Xios.hpp" #include diff --git a/core/test/moduleTestClasses.hpp b/core/test/moduleTestClasses.hpp deleted file mode 100644 index 307a975bf..000000000 --- a/core/test/moduleTestClasses.hpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * @file moduleTestClasses.hpp - * - * @date Oct 21, 2021 - * @author Tim Spain - */ - -#ifndef TEST_TESTCLASSES_HPP -#define TEST_TESTCLASSES_HPP - -// Test classes -class ITest -{ -public: - virtual ~ITest() = default; - virtual int operator()() {return 0;} -}; -class Impl1 : public ITest -{ -public: - ~Impl1() = default; - int operator()() override {return 1;} -}; -class Impl2 : public ITest { -public: - ~Impl2() = default; - int operator()() override {return 2;} -}; - -#endif /* TEST_TESTCLASSES_HPP */ diff --git a/physics/src/IceGrowth.cpp b/physics/src/IceGrowth.cpp index 4e5e56556..983f98148 100644 --- a/physics/src/IceGrowth.cpp +++ b/physics/src/IceGrowth.cpp @@ -8,7 +8,7 @@ #include "include/IceGrowth.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include "include/constants.hpp" namespace Nextsim { diff --git a/physics/src/modules/AtmosphereBoundaryModule/ConfiguredAtmosphere.cpp b/physics/src/modules/AtmosphereBoundaryModule/ConfiguredAtmosphere.cpp index a0e05aa81..f9870095b 100644 --- a/physics/src/modules/AtmosphereBoundaryModule/ConfiguredAtmosphere.cpp +++ b/physics/src/modules/AtmosphereBoundaryModule/ConfiguredAtmosphere.cpp @@ -7,7 +7,7 @@ #include "include/ConfiguredAtmosphere.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" namespace Nextsim { @@ -90,10 +90,12 @@ void ConfiguredAtmosphere::configure() fluxImpl = &Module::getImplementation(); tryConfigure(fluxImpl); + } void ConfiguredAtmosphere::setData(const ModelState::DataMap& dm) { + IAtmosphereBoundary::setData(dm); tair.resize(); tdew.resize(); diff --git a/physics/src/modules/AtmosphereBoundaryModule/ERA5Atmosphere.cpp b/physics/src/modules/AtmosphereBoundaryModule/ERA5Atmosphere.cpp index 233243f66..111ac7331 100644 --- a/physics/src/modules/AtmosphereBoundaryModule/ERA5Atmosphere.cpp +++ b/physics/src/modules/AtmosphereBoundaryModule/ERA5Atmosphere.cpp @@ -7,7 +7,7 @@ #include "include/ERA5Atmosphere.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include "include/ParaGridIO.hpp" namespace Nextsim { diff --git a/physics/src/modules/AtmosphereBoundaryModule/MU71Atmosphere.cpp b/physics/src/modules/AtmosphereBoundaryModule/MU71Atmosphere.cpp index f075070d3..2018e9f73 100644 --- a/physics/src/modules/AtmosphereBoundaryModule/MU71Atmosphere.cpp +++ b/physics/src/modules/AtmosphereBoundaryModule/MU71Atmosphere.cpp @@ -3,7 +3,9 @@ // #include "include/MU71Atmosphere.hpp" -#include "include/IceAlbedoModule.hpp" + +#include "include/IIceAlbedo.hpp" +#include "include/NextsimModule.hpp" namespace Nextsim { diff --git a/physics/src/modules/CMakeLists.txt b/physics/src/modules/CMakeLists.txt index 69f22d9fd..07f652fc7 100644 --- a/physics/src/modules/CMakeLists.txt +++ b/physics/src/modules/CMakeLists.txt @@ -10,14 +10,6 @@ foreach(ModuleSubDir IN LISTS ModuleSubDirs) continue() endif() - if(NOT EXISTS ("include/${ModuleSubDir}.hpp")) - # Run the header python script - execute_process( - COMMAND "${Python_EXECUTABLE}" "${ModuleHeaderScript}" - WORKING_DIRECTORY "${ModuleSubDir}" - ) - endif() - # Run the source python script execute_process( COMMAND "${Python_EXECUTABLE}" "${ModuleBuilderScript}" diff --git a/physics/src/modules/FluxCalculationModule/FiniteElementFluxes.cpp b/physics/src/modules/FluxCalculationModule/FiniteElementFluxes.cpp index 221d07d26..aad849981 100644 --- a/physics/src/modules/FluxCalculationModule/FiniteElementFluxes.cpp +++ b/physics/src/modules/FluxCalculationModule/FiniteElementFluxes.cpp @@ -8,7 +8,8 @@ #include "include/FiniteElementFluxes.hpp" #include "include/FiniteElementSpecHum.hpp" -#include "include/IceAlbedoModule.hpp" +#include "include/IIceAlbedo.hpp" +#include "include/NextsimModule.hpp" #include "include/constants.hpp" #include diff --git a/physics/src/modules/IceThermodynamicsModule/ThermoIce0.cpp b/physics/src/modules/IceThermodynamicsModule/ThermoIce0.cpp index 4e66d017a..2cf0434e4 100644 --- a/physics/src/modules/IceThermodynamicsModule/ThermoIce0.cpp +++ b/physics/src/modules/IceThermodynamicsModule/ThermoIce0.cpp @@ -8,10 +8,11 @@ #include "include/ThermoIce0.hpp" #include "include/IceMinima.hpp" -#include "include/FreezingPointModule.hpp" +#include "include/IFreezingPoint.hpp" #include "include/IceGrowth.hpp" #include "include/IceMinima.hpp" #include "include/ModelArray.hpp" +#include "include/NextsimModule.hpp" #include "include/NZLevels.hpp" #include "include/constants.hpp" diff --git a/physics/src/modules/OceanBoundaryModule/ConfiguredOcean.cpp b/physics/src/modules/OceanBoundaryModule/ConfiguredOcean.cpp index 877c012a5..d31a7fb85 100644 --- a/physics/src/modules/OceanBoundaryModule/ConfiguredOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/ConfiguredOcean.cpp @@ -10,7 +10,7 @@ #include "include/IFreezingPoint.hpp" #include "include/IIceOceanHeatFlux.hpp" #include "include/ModelArrayRef.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include "include/constants.hpp" namespace Nextsim { diff --git a/physics/src/modules/OceanBoundaryModule/ConstantOceanBoundary.cpp b/physics/src/modules/OceanBoundaryModule/ConstantOceanBoundary.cpp index 2ba88d15d..9679a3744 100644 --- a/physics/src/modules/OceanBoundaryModule/ConstantOceanBoundary.cpp +++ b/physics/src/modules/OceanBoundaryModule/ConstantOceanBoundary.cpp @@ -7,7 +7,7 @@ #include "include/ConstantOceanBoundary.hpp" #include "include/IIceOceanHeatFlux.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include "include/constants.hpp" namespace Nextsim { diff --git a/physics/src/modules/OceanBoundaryModule/FluxConfiguredOcean.cpp b/physics/src/modules/OceanBoundaryModule/FluxConfiguredOcean.cpp index 2f3a37f60..339e8bc05 100644 --- a/physics/src/modules/OceanBoundaryModule/FluxConfiguredOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/FluxConfiguredOcean.cpp @@ -8,7 +8,7 @@ #include "include/FluxConfiguredOcean.hpp" #include "include/IFreezingPoint.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include "include/constants.hpp" namespace Nextsim { diff --git a/physics/src/modules/OceanBoundaryModule/TOPAZOcean.cpp b/physics/src/modules/OceanBoundaryModule/TOPAZOcean.cpp index 62c9e22a1..239728302 100644 --- a/physics/src/modules/OceanBoundaryModule/TOPAZOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/TOPAZOcean.cpp @@ -9,7 +9,7 @@ #include "include/IIceOceanHeatFlux.hpp" #include "include/IFreezingPoint.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include "include/ParaGridIO.hpp" #include "include/constants.hpp" diff --git a/physics/src/modules/OceanBoundaryModule/UniformOcean.cpp b/physics/src/modules/OceanBoundaryModule/UniformOcean.cpp index 1da427d91..ff76d773a 100644 --- a/physics/src/modules/OceanBoundaryModule/UniformOcean.cpp +++ b/physics/src/modules/OceanBoundaryModule/UniformOcean.cpp @@ -8,7 +8,7 @@ #include "include/UniformOcean.hpp" #include "include/IFreezingPoint.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include "include/constants.hpp" namespace Nextsim { diff --git a/physics/test/BasicIceOceanFlux_test.cpp b/physics/test/BasicIceOceanFlux_test.cpp index 93227c4f0..759b7f5e4 100644 --- a/physics/test/BasicIceOceanFlux_test.cpp +++ b/physics/test/BasicIceOceanFlux_test.cpp @@ -16,7 +16,7 @@ #include "include/IFreezingPoint.hpp" #include "include/ModelArray.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include "include/Time.hpp" #include "include/UnescoFreezing.hpp" #include "include/UniformOcean.hpp" diff --git a/physics/test/ConfiguredAtmosphere_test.cpp b/physics/test/ConfiguredAtmosphere_test.cpp index a98ba9397..253e4f9e5 100644 --- a/physics/test/ConfiguredAtmosphere_test.cpp +++ b/physics/test/ConfiguredAtmosphere_test.cpp @@ -13,8 +13,10 @@ #include "include/Configurator.hpp" #include "include/ConfiguredModule.hpp" +#include "include/IFluxCalculation.hpp" #include "include/IFreezingPoint.hpp" -#include "include/Module.hpp" +#include "include/IIceAlbedo.hpp" +#include "include/NextsimModule.hpp" #include "include/UnescoFreezing.hpp" #include "include/UniformOcean.hpp" #include "include/constants.hpp" @@ -27,12 +29,10 @@ TEST_CASE("ConfiguredAtmosphere melting test") ModelArray::setDimensions(ModelArray::Type::H, { 1, 1 }); ModelArray::setDimensions(ModelArray::Type::Z, { 1, 1, 1 }); + Module::Module::setImplementation("Nextsim::UnescoFreezing") + Module::Module::setImplementation("Nextsim::CCSMIceAlbedo") + Module::Module::setImplementation("Nextsim::FiniteElementFluxes") std::stringstream config; - config << "[Modules]" << std::endl; - config << "Nextsim::IFreezingPoint = Nextsim::UnescoFreezing" << std::endl; - config << "Nextsim::IIceAlbedo = Nextsim::CCSMIceAlbedo" << std::endl; - config << "Nextsim::IFluxCalculation = Nextsim::FiniteElementFluxes" << std::endl; - config << std::endl; config << "[CCSMIceAlbedo]" << std::endl; config << "iceAlbedo = 0.63" << std::endl; config << "snowAlbedo = 0.88" << std::endl; diff --git a/physics/test/DamageHealing_test.cpp b/physics/test/DamageHealing_test.cpp index ca6da0127..e8e5efbdd 100644 --- a/physics/test/DamageHealing_test.cpp +++ b/physics/test/DamageHealing_test.cpp @@ -8,7 +8,7 @@ #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include "include/IDamageHealing.hpp" extern template class Module::Module; @@ -28,19 +28,15 @@ TEST_CASE("Thermodynamic healing") ModelArray::setDimensions(ModelArray::Type::H, { 1, 1 }); ModelArray::setDimensions(ModelArray::Type::Z, { 1, 1, 1 }); + Module::Module::setImplementation("Nextsim::ConstantHealing"); std::stringstream config; - config << "[Modules]" << std::endl; - config << "DamageHealingModule = Nextsim::ConstantHealing" << std::endl; - config << std::endl; config << "[ConstantHealing]" << std::endl; config << "td = 20" << std::endl; std::unique_ptr pcstream(new std::stringstream(config.str())); Configurator::addStream(std::move(pcstream)); - ConfiguredModule::parseConfigurator(); - class PrognosticData : public ModelComponent { public: PrognosticData() @@ -169,4 +165,4 @@ TEST_CASE("New ice formation") iHealing->update(tst); REQUIRE(iceState.damage[0] == doctest::Approx(0.55).epsilon(prec)); } -} \ No newline at end of file +} diff --git a/physics/test/ERA5Atm_test.cpp b/physics/test/ERA5Atm_test.cpp index d36dc5dff..42fb53b9d 100644 --- a/physics/test/ERA5Atm_test.cpp +++ b/physics/test/ERA5Atm_test.cpp @@ -16,7 +16,7 @@ #include "include/IFluxCalculation.hpp" #include "include/ModelArrayRef.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include "include/Time.hpp" #include @@ -30,6 +30,22 @@ extern template class Module::Module; namespace Nextsim { +// Null flux calculation +class NullFlux : public IFluxCalculation { +public: + NullFlux() + : IFluxCalculation() + { + } + void update(const TimestepTime&) override { } + +} nullFlux; + +std::unique_ptr setNullFlux() +{ + return std::make_unique(); +} + TEST_SUITE_BEGIN("ERA5Atmosphere"); #ifdef USE_MPI MPI_TEST_CASE("ERA5Atmosphere construction test", 1) @@ -61,19 +77,7 @@ TEST_CASE("ERA5Atmosphere construction test") ERA5Atmosphere e5; - // Null flux calculation - class NullFlux : public IFluxCalculation { - public: - NullFlux() - : IFluxCalculation() - { - } - void update(const TimestepTime&) override { } - - } nullFlux; - - Module::Module::setExternalImplementation( - []() { return std::make_unique(); }); + Module::Module::setExternalImplementation(setNullFlux); e5.configure(); e5.setFilePath(filePath); diff --git a/physics/test/FiniteElementFluxes_test.cpp b/physics/test/FiniteElementFluxes_test.cpp index 58b5db425..4b5cc98b6 100644 --- a/physics/test/FiniteElementFluxes_test.cpp +++ b/physics/test/FiniteElementFluxes_test.cpp @@ -17,7 +17,7 @@ #include "include/ModelArray.hpp" #include "include/ModelArrayRef.hpp" #include "include/ModelComponent.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include "include/Time.hpp" #include "include/UnescoFreezing.hpp" #include "include/UniformOcean.hpp" @@ -31,11 +31,10 @@ TEST_CASE("Melting conditions") ModelArray::setDimensions(ModelArray::Type::H, { 1, 1 }); ModelArray::setDimensions(ModelArray::Type::Z, { 1, 1, 1 }); + Module::Module::setImplementation("Nextsim::UnescoFreezing"); + Module::Module::setImplementation("Nextsim::CCSMIceAlbedo"); + std::stringstream config; - config << "[Modules]" << std::endl; - config << "FreezingPointModule = Nextsim::UnescoFreezing" << std::endl; - config << "IceAlbedoModule = Nextsim::CCSMIceAlbedo" << std::endl; - config << std::endl; config << "[CCSMIceAlbedo]" << std::endl; config << "iceAlbedo = 0.63" << std::endl; config << "snowAlbedo = 0.88" << std::endl; @@ -43,8 +42,6 @@ TEST_CASE("Melting conditions") std::unique_ptr pcstream(new std::stringstream(config.str())); Configurator::addStream(std::move(pcstream)); - ConfiguredModule::parseConfigurator(); - Module::setImplementation("Nextsim::UnescoFreezing"); UniformOcean ocnBdy(-1., 32., 10.25); @@ -168,11 +165,9 @@ TEST_CASE("Freezing conditions") ModelArray::setDimensions(ModelArray::Type::H, { 1, 1 }); ModelArray::setDimensions(ModelArray::Type::Z, { 1, 1, 1 }); + Module::Module::setImplementation("Nextsim::UnescoFreezing"); + Module::Module::setImplementation("Nextsim::CCSMIceAlbedo"); std::stringstream config; - config << "[Modules]" << std::endl; - config << "FreezingPointModule = Nextsim::UnescoFreezing" << std::endl; - config << "IceAlbedoModule = Nextsim::CCSMIceAlbedo" << std::endl; - config << std::endl; config << "[CCSMIceAlbedo]" << std::endl; config << "iceAlbedo = 0.63" << std::endl; config << "snowAlbedo = 0.88" << std::endl; @@ -180,8 +175,6 @@ TEST_CASE("Freezing conditions") std::unique_ptr pcstream(new std::stringstream(config.str())); Configurator::addStream(std::move(pcstream)); - ConfiguredModule::parseConfigurator(); - Module::setImplementation("Nextsim::UnescoFreezing"); UniformOcean ocnBdy(-1.75, 32., 10.25); diff --git a/physics/test/IceGrowth_test.cpp b/physics/test/IceGrowth_test.cpp index 8ae4a1d91..2aedeb70d 100644 --- a/physics/test/IceGrowth_test.cpp +++ b/physics/test/IceGrowth_test.cpp @@ -12,13 +12,12 @@ #include "include/IceGrowth.hpp" #include "include/Configurator.hpp" -#include "include/ConfiguredModule.hpp" #include "include/IAtmosphereBoundary.hpp" #include "include/IFreezingPoint.hpp" #include "include/ModelArray.hpp" #include "include/ModelArrayRef.hpp" #include "include/ModelComponent.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include "include/Time.hpp" #include "include/UnescoFreezing.hpp" #include "include/UniformOcean.hpp" @@ -33,15 +32,9 @@ TEST_CASE("New ice formation") ModelArray::setDimensions(ModelArray::Type::H, { 1, 1 }); ModelArray::setDimensions(ModelArray::Type::Z, { 1, 1, 1 }); - std::stringstream config; - config << "[Modules]" << std::endl; - config << "LateralIceSpreadModule = Nextsim::HiblerSpread" << std::endl; - config << "IceThermodynamicsModule = Nextsim::ThermoIce0" << std::endl; - - std::unique_ptr pcstream(new std::stringstream(config.str())); - Configurator::addStream(std::move(pcstream)); - - ConfiguredModule::parseConfigurator(); + Module::Module::setImplementation("Nextsim::HiblerSpread"); + Module::Module::setImplementation("Nextsim::ThermoIce0"); + Module::setImplementation("Nextsim::UnescoFreezing"); class AtmosphereBoundary : public IAtmosphereBoundary { public: @@ -95,12 +88,14 @@ TEST_CASE("New ice formation") } proData; proData.setData(ModelState().data); - Module::setImplementation("Nextsim::UnescoFreezing"); - UniformOcean ocnBdy(-1.5, 32., 10.25); ocnBdy.setQio(124.689); ocnBdy.setData(ModelState().data); + HField damage(ModelArray::Type::H); + damage = 1; + ModelComponent::getStore().registerArray(Shared::DAMAGE, &damage, RW); + TimestepTime tst = { TimePoint("2000-001"), Duration("P0-1") }; IceGrowth ig; ig.configure(); @@ -119,15 +114,8 @@ TEST_CASE("Melting conditions") ModelArray::setDimensions(ModelArray::Type::H, { 1, 1 }); ModelArray::setDimensions(ModelArray::Type::Z, { 1, 1, 1 }); - std::stringstream config; - config << "[Modules]" << std::endl; - config << "LateralIceSpreadModule = Nextsim::HiblerSpread" << std::endl; - config << "IceThermodynamicsModule = Nextsim::ThermoIce0" << std::endl; - - std::unique_ptr pcstream(new std::stringstream(config.str())); - Configurator::addStream(std::move(pcstream)); - - ConfiguredModule::parseConfigurator(); + Module::Module::setImplementation("Nextsim::HiblerSpread"); + Module::Module::setImplementation("Nextsim::ThermoIce0"); class AtmosphericBoundary : public IAtmosphereBoundary { public: @@ -188,6 +176,10 @@ TEST_CASE("Melting conditions") ocnBdy.setQio(53717.8); ocnBdy.setData(ModelState().data); + HField damage(ModelArray::Type::H); + damage = 1; + ModelComponent::getStore().registerArray(Shared::DAMAGE, &damage, RW); + TimestepTime tst = { TimePoint("2000-001"), Duration("P0-0T0:10:0") }; IceGrowth ig; ig.configure(); @@ -215,15 +207,8 @@ TEST_CASE("Freezing conditions") ModelArray::setDimensions(ModelArray::Type::H, { 1, 1 }); ModelArray::setDimensions(ModelArray::Type::Z, { 1, 1, 1 }); - std::stringstream config; - config << "[Modules]" << std::endl; - config << "LateralIceSpreadModule = Nextsim::HiblerSpread" << std::endl; - config << "IceThermodynamicsModule = Nextsim::ThermoIce0" << std::endl; - - std::unique_ptr pcstream(new std::stringstream(config.str())); - Configurator::addStream(std::move(pcstream)); - - ConfiguredModule::parseConfigurator(); + Module::Module::setImplementation("Nextsim::HiblerSpread"); + Module::Module::setImplementation("Nextsim::ThermoIce0"); class AtmosphereBoundary : public IAtmosphereBoundary { public: @@ -283,6 +268,10 @@ TEST_CASE("Freezing conditions") ocnBdy.setQio(73.9465); ocnBdy.setData(ModelState().data); + HField damage(ModelArray::Type::H); + damage = 1; + ModelComponent::getStore().registerArray(Shared::DAMAGE, &damage, RW); + TimestepTime tst = { TimePoint("2000-001"), Duration("P0-0T0:10:0") }; IceGrowth ig; ig.configure(); @@ -376,6 +365,10 @@ TEST_CASE("Dummy ice") ocnBdy.setQio(0.); ocnBdy.setData(ModelState().data); + HField damage(ModelArray::Type::H); + damage = 1; + ModelComponent::getStore().registerArray(Shared::DAMAGE, &damage, RW); + TimestepTime tst = { TimePoint("2000-001"), Duration("P0-0T0:10:0") }; IceGrowth ig; @@ -410,15 +403,8 @@ TEST_CASE("Zero thickness") ModelArray::setDimensions(ModelArray::Type::H, { 1, 1 }); ModelArray::setDimensions(ModelArray::Type::Z, { 1, 1, 1 }); - std::stringstream config; - config << "[Modules]" << std::endl; - config << "LateralIceSpreadModule = Nextsim::HiblerSpread" << std::endl; - config << "IceThermodynamicsModule = Nextsim::ThermoIce0" << std::endl; - - std::unique_ptr pcstream(new std::stringstream(config.str())); - Configurator::addStream(std::move(pcstream)); - - ConfiguredModule::parseConfigurator(); + Module::Module::setImplementation("Nextsim::HiblerSpread"); + Module::Module::setImplementation("Nextsim::ThermoIce0"); class AtmosphericBoundary : public IAtmosphereBoundary { public: @@ -477,6 +463,10 @@ TEST_CASE("Zero thickness") ocnBdy.setQio(53717.8); // 57 kW m⁻² to go from -1 to -1.75 over the whole mixed layer in 600 s ocnBdy.setData(ModelState().data); + HField damage(ModelArray::Type::H); + damage = 1; + ModelComponent::getStore().registerArray(Shared::DAMAGE, &damage, RW); + class ZeroThicknessIce : public IIceThermodynamics { void setData(const ModelState::DataMap&) override { } void update(const TimestepTime& tsTime) override @@ -514,19 +504,15 @@ TEST_CASE("Turn off thermo") ModelArray::setDimensions(ModelArray::Type::H, { 1, 1 }); ModelArray::setDimensions(ModelArray::Type::Z, { 1, 1, 1 }); + Module::Module::setImplementation("Nextsim::HiblerSpread"); + Module::Module::setImplementation("Nextsim::ThermoIce0"); std::stringstream config; - config << "[Modules]" << std::endl; - config << "LateralIceSpreadModule = Nextsim::HiblerSpread" << std::endl; - config << "IceThermodynamicsModule = Nextsim::ThermoIce0" << std::endl; - config << std::endl; config << "[nextsim_thermo]" << std::endl; config << "use_thermo_forcing = false" << std::endl; std::unique_ptr pcstream(new std::stringstream(config.str())); Configurator::addStream(std::move(pcstream)); - ConfiguredModule::parseConfigurator(); - class AtmosphereBoundary : public IAtmosphereBoundary { public: AtmosphereBoundary() @@ -604,6 +590,10 @@ TEST_CASE("Turn off thermo") } ocnBdy; ocnBdy.setData(ModelState().data); + HField damage(ModelArray::Type::H); + damage = 1; + ModelComponent::getStore().registerArray(Shared::DAMAGE, &damage, RW); + TimestepTime tst = { TimePoint("2000-001"), Duration("P0-0T0:10:0") }; IceGrowth ig; ig.configure(); diff --git a/physics/test/ThermoIce0Temperature_test.cpp b/physics/test/ThermoIce0Temperature_test.cpp index d3f4adc8e..33ef30ca0 100644 --- a/physics/test/ThermoIce0Temperature_test.cpp +++ b/physics/test/ThermoIce0Temperature_test.cpp @@ -13,13 +13,13 @@ #include "include/ThermoIce0.hpp" #include "include/Configurator.hpp" -#include "include/ConfiguredModule.hpp" #include "include/constants.hpp" #include "include/IFreezingPoint.hpp" +#include "include/IIceAlbedo.hpp" #include "include/ModelArray.hpp" #include "include/ModelArrayRef.hpp" #include "include/ModelComponent.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include "include/Time.hpp" #include "include/UniformOcean.hpp" @@ -31,11 +31,9 @@ TEST_CASE("Melting conditions") ModelArray::setDimensions(ModelArray::Type::H, { 1 }); ModelArray::setDimensions(ModelArray::Type::Z, { 1, 1 }); + Module::Module::setImplementation("Nextsim::UnescoFreezing"); + Module::Module::setImplementation("Nextsim::CCSMIceAlbedo"); std::stringstream config; - config << "[Modules]" << std::endl; - config << "FreezingPointModule = Nextsim::UnescoFreezing" << std::endl; - config << "IceAlbedoModule = Nextsim::CCSMIceAlbedo" << std::endl; - config << std::endl; config << "[CCSMIceAlbedo]" << std::endl; config << "iceAlbedo = 0.63" << std::endl; config << "snowAlbedo = 0.88" << std::endl; @@ -123,11 +121,9 @@ TEST_CASE("Freezing conditions") ModelArray::setDimensions(ModelArray::Type::H, { 1 }); ModelArray::setDimensions(ModelArray::Type::Z, { 1, 1 }); + Module::Module::setImplementation("Nextsim::UnescoFreezing"); + Module::Module::setImplementation("Nextsim::CCSMIceAlbedo"); std::stringstream config; - config << "[Modules]" << std::endl; - config << "FreezingPointModule = Nextsim::UnescoFreezing" << std::endl; - config << "IceAlbedoModule = Nextsim::CCSMIceAlbedo" << std::endl; - config << std::endl; config << "[CCSMIceAlbedo]" << std::endl; config << "iceAlbedo = 0.63" << std::endl; config << "snowAlbedo = 0.88" << std::endl; diff --git a/physics/test/ThermoIce0_test.cpp b/physics/test/ThermoIce0_test.cpp index ac8c68fa1..ef9e1dc11 100644 --- a/physics/test/ThermoIce0_test.cpp +++ b/physics/test/ThermoIce0_test.cpp @@ -14,10 +14,11 @@ #include "include/Configurator.hpp" #include "include/ConfiguredModule.hpp" -#include "include/FreezingPointModule.hpp" +#include "include/IFreezingPoint.hpp" #include "include/ModelArray.hpp" #include "include/ModelArrayRef.hpp" #include "include/ModelComponent.hpp" +#include "include/NextsimModule.hpp" #include "include/Time.hpp" namespace Nextsim { diff --git a/physics/test/ThermoWintonTemperature_test.cpp b/physics/test/ThermoWintonTemperature_test.cpp index 5093e4d1f..051655e80 100644 --- a/physics/test/ThermoWintonTemperature_test.cpp +++ b/physics/test/ThermoWintonTemperature_test.cpp @@ -13,14 +13,15 @@ #include "include/ThermoWinton.hpp" #include "include/Configurator.hpp" -#include "include/ConfiguredModule.hpp" #include "include/constants.hpp" #include "include/IAtmosphereBoundary.hpp" -#include "include/FreezingPointModule.hpp" +#include "include/IFreezingPoint.hpp" +#include "include/IIceAlbedo.hpp" #include "include/IOceanBoundary.hpp" #include "include/ModelArray.hpp" #include "include/ModelArrayRef.hpp" #include "include/ModelComponent.hpp" +#include "include/NextsimModule.hpp" #include "include/Time.hpp" #include "include/UniformOcean.hpp" @@ -32,11 +33,9 @@ TEST_CASE("Melting conditions") ModelArray::setDimensions(ModelArray::Type::H, { 1, 1 }); ModelArray::setDimensions(ModelArray::Type::Z, { 1, 1, 3 }); + Module::Module::setImplementation("Nextsim::UnescoFreezing"); + Module::Module::setImplementation("Nextsim::CCSMIceAlbedo"); std::stringstream config; - config << "[Modules]" << std::endl; - config << "FreezingPointModule = Nextsim::UnescoFreezing" << std::endl; - config << "IceAlbedoModule = Nextsim::CCSMIceAlbedo" << std::endl; - config << std::endl; config << "[CCSMIceAlbedo]" << std::endl; config << "iceAlbedo = 0.63" << std::endl; config << "snowAlbedo = 0.88" << std::endl; @@ -44,8 +43,6 @@ TEST_CASE("Melting conditions") std::unique_ptr pcstream(new std::stringstream(config.str())); Configurator::addStream(std::move(pcstream)); - ConfiguredModule::parseConfigurator(); - ThermoWinton twin; class IceTemperatureData : public ModelComponent { public: @@ -136,11 +133,9 @@ TEST_CASE("Freezing conditions") ModelArray::setDimensions(ModelArray::Type::H, { 1, 1 }); ModelArray::setDimensions(ModelArray::Type::Z, { 1, 1, 3 }); + Module::Module::setImplementation("Nextsim::UnescoFreezing"); + Module::Module::setImplementation("Nextsim::CCSMIceAlbedo"); std::stringstream config; - config << "[Modules]" << std::endl; - config << "FreezingPointModule = Nextsim::UnescoFreezing" << std::endl; - config << "IceAlbedoModule = Nextsim::CCSMIceAlbedo" << std::endl; - config << std::endl; config << "[CCSMIceAlbedo]" << std::endl; config << "iceAlbedo = 0.63" << std::endl; config << "snowAlbedo = 0.88" << std::endl; @@ -148,8 +143,6 @@ TEST_CASE("Freezing conditions") std::unique_ptr pcstream(new std::stringstream(config.str())); Configurator::addStream(std::move(pcstream)); - ConfiguredModule::parseConfigurator(); - ThermoWinton twin; class IceTemperatureData : public ModelComponent { public: @@ -243,11 +236,9 @@ TEST_CASE("No ice do nothing") ModelArray::setDimensions(ModelArray::Type::H, { 1, 1 }); ModelArray::setDimensions(ModelArray::Type::Z, { 1, 1, 3 }); + Module::Module::setImplementation("Nextsim::UnescoFreezing"); + Module::Module::setImplementation("Nextsim::CCSMIceAlbedo"); std::stringstream config; - config << "[Modules]" << std::endl; - config << "FreezingPointModule = Nextsim::UnescoFreezing" << std::endl; - config << "IceAlbedoModule = Nextsim::CCSMIceAlbedo" << std::endl; - config << std::endl; config << "[CCSMIceAlbedo]" << std::endl; config << "iceAlbedo = 0.63" << std::endl; config << "snowAlbedo = 0.88" << std::endl; @@ -255,8 +246,6 @@ TEST_CASE("No ice do nothing") std::unique_ptr pcstream(new std::stringstream(config.str())); Configurator::addStream(std::move(pcstream)); - ConfiguredModule::parseConfigurator(); - ThermoWinton twin; class IceTemperatureData : public ModelComponent { public: diff --git a/physics/test/UniformOcean_test.cpp b/physics/test/UniformOcean_test.cpp index 36e8c9690..0ab10e9a8 100644 --- a/physics/test/UniformOcean_test.cpp +++ b/physics/test/UniformOcean_test.cpp @@ -12,7 +12,7 @@ #include "include/IFreezingPoint.hpp" #include "include/ModelComponent.hpp" -#include "include/Module.hpp" +#include "include/NextsimModule.hpp" #include "include/constants.hpp" namespace Nextsim { diff --git a/scripts/module_builder.py b/scripts/module_builder.py index 9ad600a1d..8d0ae7a2c 100644 --- a/scripts/module_builder.py +++ b/scripts/module_builder.py @@ -37,45 +37,61 @@ def write_source_file(source, config, strings): default_impl = section module_templ = f"Module<{strings[class_name]}>" - + h_class = "HelpMap" + c_class = "Nextsim::ConfigurationMap" + full_template_args = f"{strings[class_name]}, {c_class}, {h_class}" # Use the provided path to the Module header file - source.write(f"#include \"{strings[header_file_path_str]}\"\n") - source.write("\n") +# source.write(f"#include \"{strings[header_file_path_str]}\"\n") +# source.write("\n") + module_file = f"NextsimModule.{header_suffix}" + source.write(f"#include \"{os.path.join(strings[internal_header_dir], strings[interface_prefix_str])}{strings[file_prefix_str]}.{header_suffix}\"\n") + source.write(f"#include \"{os.path.join(strings[internal_header_dir], module_file)}\"\n") for section in valid_impl_sections: source.write(f"#include \"{os.path.join(strings[internal_header_dir], config[section][file_prefix_str])}.{header_suffix}\"\n") source.write(""" #include namespace Module { +""") + source.write(f""" +namespace {strings[module_class_name]} {{ """) impl_strings = {} for section in valid_impl_sections: impl_strings[section] = config[section][file_prefix_str].upper() source.write(f"const std::string {impl_strings[section]} = \"{section}\";\n") + source.write("}\n") # End of the module-specific namespace + mapVarName = "theMap" source.write(f""" template <> -{module_templ}::map {module_templ}::functionMap = {{ +const {module_templ}::map& {module_templ}::functionMap() +{{ + static const map {mapVarName} = {{ """) for section in valid_impl_sections: - source.write(f" {{ {impl_strings[section]}, newImpl<{strings[class_name]}, {section}> }},\n") - source.write(f"""}}; + source.write(f" {{ {strings[module_class_name]}::{impl_strings[section]}, newImpl<{strings[class_name]}, {section}> }},\n") + source.write(f""" }}; + return {mapVarName}; +}} template <> -{module_templ}::fn {module_templ}::spf = functionMap.at({impl_strings[default_impl]}); -template <> -std::unique_ptr<{strings[class_name]}> {module_templ}::staticInstance - = std::move({module_templ}::spf()); +std::string {module_templ}::getDefaultImplementationName() +{{ + return {strings[module_class_name]}::{impl_strings[default_impl]}; +}} template <> std::string {module_templ}::moduleName() {{ return \"{strings[module_class_name]}\"; }} -template <> HelpMap& getHelpRecursive<{strings[class_name]}>(HelpMap& map, bool getAll) +using ConfigType = Nextsim::ConfigurationHelp::ConfigType; + +template <> HelpMap& {module_templ}::getHelpRecursive(HelpMap& map, bool getAll) {{ const std::string& pfx = Nextsim::ConfiguredModule::MODULE_PREFIX; map[pfx].push_back({{ pfx + "." + {module_templ}::moduleName(), ConfigType::MODULE, {{ """) for section in valid_impl_sections: - source.write(f"{impl_strings[section]}, ") - source.write(f"}}, {impl_strings[default_impl]}, \"\",\n") + source.write(f"{strings[module_class_name]}::{impl_strings[section]}, ") + source.write(f"}}, {strings[module_class_name]}::{impl_strings[default_impl]}, \"\",\n") source.write(f" \"{config[module_section_str][description_str]}\" }});\n") for section in valid_impl_sections: if (has_help_str in config[section]) and (config[section][has_help_str] == true_str): @@ -83,23 +99,30 @@ def write_source_file(source, config, strings): source.write(f""" return map; }} + +template <> std::unique_ptr<{strings[class_name]}> getInstance<{strings[class_name]}>() +{{ + return {module_templ}::getInstance(); +}} + template <> {strings[class_name]}& getImplementation<{strings[class_name]}>() {{ - return getImplTemplate<{strings[class_name]}, {strings[module_class_name]}>(); + return {module_templ}::getImplementation(); }} -template <> void setImplementation<{strings[class_name]}>(const std::string& implName) + +template <> void setImplementation<{strings[class_name]}>(const std::string& impl) {{ - setImplTemplate<{strings[module_class_name]}>(implName); + {module_templ}::setImplementation(impl); }} -template <> std::unique_ptr<{strings[class_name]}> getInstance() + +template <> {h_class}& getHelpRecursive<{strings[class_name]}>({h_class}& map, bool getAll) {{ - return getInstTemplate<{strings[class_name]}, {strings[module_class_name]}>(); + return {module_templ}::getHelpRecursive(map, getAll); }} -{strings[module_class_name]}::Constructor {strings[module_class_name]}::ctor; -{strings[module_class_name]}::Constructor::Constructor() +template <> std::string implementation<{strings[class_name]}>() {{ - addToConfiguredModules<{strings[class_name]}, {strings[module_class_name]}>(); + return {module_templ}::implementation(); }} template class {module_templ}; diff --git a/scripts/module_header.py b/scripts/module_header.py deleted file mode 100644 index 7f77f5f62..000000000 --- a/scripts/module_header.py +++ /dev/null @@ -1,74 +0,0 @@ -# -# module_builder.py -# -# 20 Nov 2023 -# author: Tim Spain - -import os - -from module_strings import * - -# A file and generator header for each file -def write_file_header(stream, file_name): - stream.write( - f"""/*! - * @file {file_name} - * - * Generated by {os.path.basename(__file__)} - */ -""" - ) - -# Write all of the header file between the include guards -def write_header_file(header, strings): - guard_macro = (strings[file_prefix_str]+"MODULE_"+header_suffix).upper() - header.write( - f""" -#ifndef {guard_macro} -#define {guard_macro} - -#include "include/ConfiguredModule.hpp" -#include "include/Module.hpp" - -#include "{strings[interface_prefix_str]}{strings[file_prefix_str]}.{header_suffix}" - -namespace Module {{ - -template <> Module<{strings[class_name]}>::map Module<{strings[class_name]}>::functionMap; -class {strings[module_class_name]} : public Module<{strings[class_name]}> {{ - struct Constructor {{ - Constructor(); - }}; - static Constructor ctor; -}}; - -}} /* namespace Module */ - -#endif /* {guard_macro} */ -""" -) - -''' -Main program - -Parse the module.cfg file in a directory and generate the corresponding -module header file. -''' -def main(): - config = get_config_with_defaults() - - config_status = check_config_errors(config) - if config_status > 0: - return config_status - - # Create a dictionary of common strings - strings = common_strings(config) - - header = open(strings[header_file_path_str], "w", encoding=file_encoding) - - write_file_header(header, strings[header_file_name_str]) - write_header_file(header, strings) - header.close() - -if __name__ == "__main__": - main()