diff --git a/cmake/base64encoder.cpp b/cmake/base64encoder.cpp index 0621bb70e..344874f85 100644 --- a/cmake/base64encoder.cpp +++ b/cmake/base64encoder.cpp @@ -25,7 +25,7 @@ int main(int pArgC, char *pArgV[]) // Convert the given file to a base64-encoded string. if (pArgC != 2) { - std::cerr << "Usage: " << pArgV[0] << " " << std::endl; + std::cerr << "Usage: " << pArgV[0] << " \n"; return 1; } @@ -37,7 +37,7 @@ int main(int pArgC, char *pArgV[]) file.read(buffer, fileSize); if (!file) { - std::cerr << "Error: the file could not be read." << std::endl; + std::cerr << "Error: the file could not be read.\n"; return 1; } @@ -48,7 +48,7 @@ int main(int pArgC, char *pArgV[]) modp_b64_encode(base64, buffer, fileSize); - std::cout << base64 << std::endl; + std::cout << base64 << "\n"; delete[] base64; delete[] buffer; diff --git a/cmake/packages.cmake b/cmake/packages.cmake index d82c042fd..4b7cd621c 100644 --- a/cmake/packages.cmake +++ b/cmake/packages.cmake @@ -193,9 +193,9 @@ function(retrieve_package PACKAGE_NAME PACKAGE_VERSION PACKAGE_REPOSITORY RELEAS set(PACKAGE_URL "https://github.com/opencor/${PACKAGE_REPOSITORY}/releases/download/${RELEASE_TAG}/${PACKAGE_FILE}") if("${PACKAGE_NAME}" STREQUAL "libCellML") - #---GRY--- THIS IS TEMPORARY UNTIL libCellML HAS AN OFFICIAL RELEASE WITH THE INTERPRETER. + #---GRY--- USE THIS VERSION OF libCellML WHICH CORRESPONDS TO PR #1256 UNTIL IT GETS MERGED IN. - set(PACKAGE_URL "https://github.com/agarny/${PACKAGE_REPOSITORY}/releases/download/707e211/${PACKAGE_FILE}") + set(PACKAGE_URL "https://github.com/agarny/${PACKAGE_REPOSITORY}/releases/download/04d8d15/${PACKAGE_FILE}") endif() set(ATTEMPT 1) diff --git a/scripts/genericclean b/scripts/genericclean index 719c3e1e9..3b6bf0326 100755 --- a/scripts/genericclean +++ b/scripts/genericclean @@ -8,11 +8,8 @@ fi appDir=$(cd $(dirname $0); pwd)/.. -for i in _skbuild build src/bindings/python/libopencor.egg-info tests/install/build; do - rm -fr $appDir/$i -done - -find $appDir -name .DS_Store -exec rm -f {} \; +find $appDir -type f -name .DS_Store -exec rm -f {} \; 2> /dev/null +find $appDir -type d \( -name .pytest_cache -o -name __pycache__ -o -name _skbuild -o -name build \) -exec rm -fr {} \; 2> /dev/null if [ "$1" = "all" ]; then rm -fr $appDir/prebuilt/* > /dev/null 2>&1 diff --git a/scripts/genericclean.bat b/scripts/genericclean.bat index dc444efc1..4199e46a4 100644 --- a/scripts/genericclean.bat +++ b/scripts/genericclean.bat @@ -11,7 +11,9 @@ IF "%1" == "all" ( SET AppDir=%~dp0..\ SET OrigDir=!CD! -FOR /D %%I IN (_skbuild build src\bindings\python\libopencor.egg-info tests\install\build) DO IF EXIST !AppDir!%%I RMDIR /S /Q !AppDir!%%I +FOR /R "%AppDir%" %%d IN (.pytest_cache __pycache__ _skbuild build) DO ( + IF EXIST "%%d" RMDIR /S /Q "%%d" +) IF "%1" == "all" ( IF EXIST !AppDir!prebuilt ( diff --git a/src/3rdparty/libCellML/CMakeLists.txt b/src/3rdparty/libCellML/CMakeLists.txt index bf8eb5e64..61ab247f6 100644 --- a/src/3rdparty/libCellML/CMakeLists.txt +++ b/src/3rdparty/libCellML/CMakeLists.txt @@ -34,49 +34,49 @@ if(LIBOPENCOR_PREBUILT_LIBCELLML OR ONLY_BUILD_JAVASCRIPT_THIRD_PARTY_LIBRARIES) if(EMSCRIPTEN) retrieve_package(${PACKAGE_NAME} ${PACKAGE_VERSION} ${PACKAGE_REPOSITORY} ${RELEASE_TAG} - e6e9bfdc883280289aa6131598b617b54672ae72) + 13a93a71d210b4ec3af336180f5d386b1e6d44c9) else() if(WIN32) if(RELEASE_MODE) if(INTEL_MODE) retrieve_package(${PACKAGE_NAME} ${PACKAGE_VERSION} ${PACKAGE_REPOSITORY} ${RELEASE_TAG} - c688813b383c5759cb4982c1d6737f0fa0d68462) + 25c2e2dd016a47e9fff6a81b610de7be6cdb6bbc) else() retrieve_package(${PACKAGE_NAME} ${PACKAGE_VERSION} ${PACKAGE_REPOSITORY} ${RELEASE_TAG} - a1cab436551c576ab8d1bd16b6156e246134b733) + bb4b2e898776d03abd0644e2662d97034725f8d7) endif() else() if(INTEL_MODE) retrieve_package(${PACKAGE_NAME} ${PACKAGE_VERSION} ${PACKAGE_REPOSITORY} ${RELEASE_TAG} - 9623a3a236f4eadd9a9e7614b66fad916f07ea1d) + 526cfc4d623af35081c37e865f37b856cd39fe0a) else() retrieve_package(${PACKAGE_NAME} ${PACKAGE_VERSION} ${PACKAGE_REPOSITORY} ${RELEASE_TAG} - d486630d09a245f8b35670304f259019afb70fc9) + 770ac1735960bf4d01564fee6a09ff33a62486ab) endif() endif() elseif(APPLE) if(INTEL_MODE) retrieve_package(${PACKAGE_NAME} ${PACKAGE_VERSION} ${PACKAGE_REPOSITORY} ${RELEASE_TAG} - 62768aa55bea80757378c706cb190043173991fb) + 836e28f4bce436fbee88f109a4f050b9e4dd68f6) else() retrieve_package(${PACKAGE_NAME} ${PACKAGE_VERSION} ${PACKAGE_REPOSITORY} ${RELEASE_TAG} - c80100db4e298acc5a1e2ea06c71c7b2766e49d4) + 555c258f6541ca7c51c54445a02386c5a619d754) endif() else() if(INTEL_MODE) retrieve_package(${PACKAGE_NAME} ${PACKAGE_VERSION} ${PACKAGE_REPOSITORY} ${RELEASE_TAG} - 1ce868ce9918deb8972f869f63817cbfbf6a411e) + 0842347a0a7abf4c6350aa44a6142ad5ba22a397) else() retrieve_package(${PACKAGE_NAME} ${PACKAGE_VERSION} ${PACKAGE_REPOSITORY} ${RELEASE_TAG} - deb06fa9adcae7a786f8150e3fa77aafa175e297) + 235fb4aa20bc3dde6ad28483775f2e41e9a093ef) endif() endif() endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0c5eb4da0..7db742ce3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -131,6 +131,7 @@ set(API_MODULE_FILE ${GIT_API_MODULE_FILE}) set(INTERNAL_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/file/filemanager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/misc/compiler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/misc/utils.cpp ${CMAKE_CURRENT_SOURCE_DIR}/solver/solver.cpp ${CMAKE_CURRENT_SOURCE_DIR}/solver/solvercvode.cpp @@ -148,12 +149,6 @@ set(INTERNAL_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/support/sedml/sedmlfile.cpp ) -if(NOT EMSCRIPTEN) - list(APPEND INTERNAL_SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/misc/compiler.cpp - ) -endif() - set(GIT_API_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/file/file.cpp ${CMAKE_CURRENT_SOURCE_DIR}/file/filemanager.cpp @@ -214,6 +209,8 @@ set(SOURCE_FILES ) set(INTERNAL_HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/misc/compiler_p.h + ${CMAKE_CURRENT_SOURCE_DIR}/misc/compiler.h ${CMAKE_CURRENT_SOURCE_DIR}/misc/utils.h ${CMAKE_CURRENT_SOURCE_DIR}/solver/solver_p.h ${CMAKE_CURRENT_SOURCE_DIR}/solver/solvercvode_p.h @@ -236,13 +233,6 @@ set(INTERNAL_HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/unittestingexport.h ) -if(NOT EMSCRIPTEN) - list(APPEND INTERNAL_HEADER_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/misc/compiler_p.h - ${CMAKE_CURRENT_SOURCE_DIR}/misc/compiler.h - ) -endif() - set(GIT_HEADER_FILES ${INTERNAL_HEADER_FILES} ${THIRD_PARTY_HEADER_FILES} @@ -281,9 +271,10 @@ if(EMSCRIPTEN) # Set the flags for our generated .js file. # Note #1: we use -O3 to get the best performance, -sALLOW_MEMORY_GROWTH to allow the memory to grow dynamically, - # -sEXPORT_ES6=1 to export ES6 classes, -sEXPORTED_FUNCTIONS to export the functions we need, - # -sINLINING_LIMIT to increase the inlining limit, -sSINGLE_FILE=1 to generate a single file, -sSTACK_SIZE - # to increase the stack size, and --bind to generate the necessary bindings. + # -sASSERTIONS=0 to disable assertions (which are not needed in production), -sEXPORT_ES6=1 to export ES6 + # classes, -sEXPORTED_FUNCTIONS to export the functions we need, -sINLINING_LIMIT to increase the inlining + # limit, -sSINGLE_FILE=1 to generate a single file, -sSTACK_SIZE to increase the stack size, and --bind to + # generate the necessary bindings. # Note #2: by default, Emscripten uses 64KB of stack, but this is not enough for "complex" models such as the Zhang # et al. model (see https://github.com/agarny/COR/blob/master/Models/zhang_SAN_model_2000_all.cellml). For # that model, we need about 108KB of stack. However, Emscripten originally used to use 5MB of stack (see @@ -301,7 +292,7 @@ if(EMSCRIPTEN) set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES COMPILE_FLAGS "-O3" - LINK_FLAGS "-O3 -sALLOW_MEMORY_GROWTH -sEXPORT_ES6=1 -sEXPORTED_FUNCTIONS=[_malloc,_free] -sEXPORTED_RUNTIME_METHODS=HEAPU8 -sINLINING_LIMIT -sSINGLE_FILE=1 -sSTACK_SIZE=4MB --bind") + LINK_FLAGS "-O3 -sALLOW_MEMORY_GROWTH -sASSERTIONS=0 -sEXPORT_ES6=1 -sEXPORTED_FUNCTIONS=[_malloc,_free] -sEXPORTED_RUNTIME_METHODS=HEAPU8 -sINLINING_LIMIT -sSINGLE_FILE=1 -sSTACK_SIZE=4MB --bind") # Create a .tgz file for our generated .js file that can be used to distribute our JavaScript bindings. diff --git a/src/api/libopencor/seddocument.h b/src/api/libopencor/seddocument.h index 1adc0d76e..a79daac22 100644 --- a/src/api/libopencor/seddocument.h +++ b/src/api/libopencor/seddocument.h @@ -320,17 +320,10 @@ class LIBOPENCOR_EXPORT SedDocument: public Logger * * Create an instance of this simulation experiment description. * - * @param pCompiled Whether the instance of this simulation experiment description is to be compiled (as opposed to - * being interpreted). - * * @return A smart pointer to a @ref SedInstance object. */ -#ifdef __EMSCRIPTEN__ SedInstancePtr instantiate(); -#else - SedInstancePtr instantiate(bool pCompiled = true); -#endif private: class Impl; /**< Forward declaration of the implementation class, @private. */ diff --git a/src/api/libopencor/sedinstance.h b/src/api/libopencor/sedinstance.h index d4026ea85..9343aee75 100644 --- a/src/api/libopencor/sedinstance.h +++ b/src/api/libopencor/sedinstance.h @@ -98,7 +98,7 @@ class LIBOPENCOR_EXPORT SedInstance: public Logger private: class Impl; /**< Forward declaration of the implementation class, @private. */ - explicit SedInstance(const SedDocumentPtr &pDocument, bool pCompiled); /**< Constructor @private. */ + explicit SedInstance(const SedDocumentPtr &pDocument); /**< Constructor @private. */ Impl *pimpl(); /**< Private implementation pointer, @private. */ const Impl *pimpl() const; /**< Constant private implementation pointer, @private. */ diff --git a/src/api/libopencor/sedinstancetask.h b/src/api/libopencor/sedinstancetask.h index d4141f168..e4e953093 100644 --- a/src/api/libopencor/sedinstancetask.h +++ b/src/api/libopencor/sedinstancetask.h @@ -335,7 +335,7 @@ class LIBOPENCOR_EXPORT SedInstanceTask: public Logger private: class Impl; /**< Forward declaration of the implementation class, @private. */ - explicit SedInstanceTask(const SedAbstractTaskPtr &pTask, bool pCompiled); /**< Constructor @private. */ + explicit SedInstanceTask(const SedAbstractTaskPtr &pTask); /**< Constructor @private. */ Impl *pimpl(); /**< Private implementation pointer, @private. */ const Impl *pimpl() const; /**< Constant private implementation pointer, @private. */ diff --git a/src/api/libopencor/solvernla.h b/src/api/libopencor/solvernla.h index 2017c74cd..a1fad8f34 100644 --- a/src/api/libopencor/solvernla.h +++ b/src/api/libopencor/solvernla.h @@ -33,7 +33,7 @@ class LIBOPENCOR_EXPORT SolverNla: public Solver friend class SedSimulation; public: - using ComputeSystem = void (*)(double *pU, double *pF, void *pUserData); /**< Signature of the method that computes the system of NLA equations. */ + using ComputeObjectiveFunction = void (*)(double *pU, double *pF, void *pUserData); /**< Signature of the method that computes the objective function of the NLA system. */ /** * Constructors, destructor, and assignment operators. @@ -54,15 +54,19 @@ class LIBOPENCOR_EXPORT SolverNla: public Solver * * Solve the NLA system. * - * @param pComputeSystem The method that computes the system of NLA equations. + * @param pComputeObjectiveFunction The method that computes the objective function of the NLA system. * @param pU The initial guess for the solution of the NLA system. * @param pN The number of variables in the NLA system. - * @param pUserData Some user data that may be passed to @p pComputeSystem. + * @param pUserData User data, if any, that are passed to @p pComputeObjectiveFunction. * * @return @c true if the NLA system could be solved, @c false otherwise. */ - bool solve(ComputeSystem pComputeSystem, double *pU, size_t pN, void *pUserData); +#ifdef __EMSCRIPTEN__ + bool solve(size_t pComputeObjectiveFunctionIndex, double *pU, size_t pN, void *pUserData); +#else + bool solve(ComputeObjectiveFunction pComputeObjectiveFunction, double *pU, size_t pN, void *pUserData); +#endif protected: class Impl; /**< Forward declaration of the implementation class, @private. */ diff --git a/src/bindings/javascript/solver.cpp b/src/bindings/javascript/solver.cpp index 527094c17..9ae61733e 100644 --- a/src/bindings/javascript/solver.cpp +++ b/src/bindings/javascript/solver.cpp @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +#include "solvernla_p.h" + #include void solverApi() @@ -47,6 +49,11 @@ void solverApi() // SolverNla API. + emscripten::function("nlaSolve", emscripten::optional_override([](uintptr_t pNlaSolverAddress, size_t pObjectiveFunctionIndex, uintptr_t pU, size_t pN, uintptr_t pData) { + libOpenCOR::nlaSolve(pNlaSolverAddress, pObjectiveFunctionIndex, + reinterpret_cast(pU), pN, reinterpret_cast(pData)); + })); + emscripten::class_>("SolverNla") .smart_ptr("SolverNla"); diff --git a/src/bindings/python/sed.cpp b/src/bindings/python/sed.cpp index 0e83f2745..2c640fc22 100644 --- a/src/bindings/python/sed.cpp +++ b/src/bindings/python/sed.cpp @@ -89,7 +89,7 @@ void sedApi(nb::module_ &m) .def("add_task", &libOpenCOR::SedDocument::addTask, "Add a task.", nb::arg("task").none()) .def("remove_task", &libOpenCOR::SedDocument::removeTask, "Remove a task.", nb::arg("task").none()) .def("remove_all_tasks", &libOpenCOR::SedDocument::removeAllTasks, "Remove all tasks.") - .def("instantiate", &libOpenCOR::SedDocument::instantiate, "Instantiate this SedDocument object.", nb::arg("compiled") = true); + .def("instantiate", &libOpenCOR::SedDocument::instantiate, "Instantiate this SedDocument object."); // SedInstance API. diff --git a/src/misc/compiler.cpp b/src/misc/compiler.cpp index ce31723f4..ff07a31ed 100644 --- a/src/misc/compiler.cpp +++ b/src/misc/compiler.cpp @@ -17,6 +17,7 @@ limitations under the License. #include "compiler_p.h" #include "clangbegin.h" +#include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CodeGenAction.h" #include "clang/Driver/Compilation.h" #include "clang/Driver/Driver.h" @@ -27,11 +28,16 @@ limitations under the License. #include "clangend.h" #include "llvmbegin.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/MC/TargetRegistry.h" #include "llvm/Support/Host.h" #include "llvm/Support/TargetSelect.h" +#include "llvm/Target/TargetMachine.h" #include "llvm-c/Core.h" #include "llvmend.h" +#include #include namespace libOpenCOR { @@ -55,22 +61,28 @@ std::string llvmClangError(llvm::Error pError) } // namespace +#ifdef __EMSCRIPTEN__ +bool Compiler::Impl::compile(const std::string &pCode, UnsignedChars &pWasmModule) +#else bool Compiler::Impl::compile(const std::string &pCode) +#endif { // Reset ourselves. +#ifndef __EMSCRIPTEN__ mLljit.reset(nullptr); +#endif removeAllIssues(); // Create a diagnostics engine. - auto diagnosticOptions = llvm::IntrusiveRefCntPtr(new clang::DiagnosticOptions {}); + auto diagnosticOptions = llvm::IntrusiveRefCntPtr(std::make_unique()); std::string diagnostics; llvm::raw_string_ostream outputStream(diagnostics); - auto diagnosticsEngine = llvm::IntrusiveRefCntPtr(new clang::DiagnosticsEngine {llvm::IntrusiveRefCntPtr(new clang::DiagnosticIDs {}), - &*diagnosticOptions, - new clang::TextDiagnosticPrinter {outputStream, &*diagnosticOptions}}); + auto diagnosticsEngine = llvm::IntrusiveRefCntPtr(std::make_unique(llvm::IntrusiveRefCntPtr(std::make_unique()), + &*diagnosticOptions, + std::make_unique(outputStream, &*diagnosticOptions).release())); diagnosticsEngine->setWarningsAsErrors(true); @@ -84,11 +96,7 @@ bool Compiler::Impl::compile(const std::string &pCode) static constexpr auto DUMMY_FILE_NAME = "dummy.c"; static const std::vector COMPILATION_ARGUMENTS = {"clang", "-fsyntax-only", -#ifdef NDEBUG "-O3", -#else - "-g", "-O0", -#endif "-fno-math-errno", "-fno-stack-protector", DUMMY_FILE_NAME}; @@ -146,17 +154,17 @@ bool Compiler::Impl::compile(const std::string &pCode) // Create a compiler instance. - clang::CompilerInstance compilerInstance; + auto compilerInstance = std::make_unique(); - compilerInstance.setDiagnostics(diagnosticsEngine.get()); - compilerInstance.setVerboseOutputStream(llvm::nulls()); + compilerInstance->setDiagnostics(diagnosticsEngine.get()); + compilerInstance->setVerboseOutputStream(llvm::nulls()); // Create a compiler invocation object. #ifndef CODE_COVERAGE_ENABLED bool res = #endif - clang::CompilerInvocation::CreateFromArgs(compilerInstance.getInvocation(), + clang::CompilerInvocation::CreateFromArgs(compilerInstance->getInvocation(), commandArguments, *diagnosticsEngine); @@ -170,8 +178,7 @@ bool Compiler::Impl::compile(const std::string &pCode) // Map our code to a memory buffer. - auto code = R"( -// Arithmetic operators. + auto code = R"(// Arithmetic operators. extern double pow(double, double); extern double sqrt(double); @@ -190,15 +197,12 @@ extern double fmod(double, double); extern double sin(double); extern double cos(double); extern double tan(double); - extern double sinh(double); extern double cosh(double); extern double tanh(double); - extern double asin(double); extern double acos(double); extern double atan(double); - extern double asinh(double); extern double acosh(double); extern double atanh(double); @@ -207,16 +211,18 @@ extern double atanh(double); #define INFINITY (__builtin_inf()) #define NAN (__builtin_nan("")) + )" + pCode; - compilerInstance.getInvocation().getPreprocessorOpts().addRemappedFile(DUMMY_FILE_NAME, - llvm::MemoryBuffer::getMemBuffer(code).release()); + compilerInstance->getInvocation().getPreprocessorOpts().addRemappedFile(DUMMY_FILE_NAME, + llvm::MemoryBuffer::getMemBuffer(code).release()); // Compile the given code, resulting in an LLVM bitcode module. - std::unique_ptr codeGenAction(new clang::EmitLLVMOnlyAction {llvm::unwrap(LLVMGetGlobalContext())}); + auto llvmContext = std::make_unique(); + auto codeGenAction = std::make_unique(llvmContext.get()); - if (!compilerInstance.ExecuteAction(*codeGenAction)) { + if (!compilerInstance->ExecuteAction(*codeGenAction)) { addError("The given code could not be compiled."); static constexpr auto ERROR = ": error: "; @@ -266,35 +272,87 @@ extern double atanh(double); return false; } - // Retrieve the LLVM bitcode module. + // Retrieve the LLVM module. auto module = codeGenAction->takeModule(); #ifndef CODE_COVERAGE_ENABLED if (module == nullptr) { - addError("The LLVM bitcode module could not be retrieved."); + addError("The LLVM module could not be retrieved."); return false; } #endif - // Initialise the native target (and its ASM printer), so not only can we then create an execution engine, but more - // importantly its data layout will match that of our target platform. + // Initialise the native target and its ASM printer. llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmPrinter(); +#ifdef __EMSCRIPTEN__ + // Look up the target. + + std::string error; + auto target = llvm::TargetRegistry::lookupTarget(module->getTargetTriple(), error); + + if (target == nullptr) { + error[0] = static_cast(tolower(error[0])); + + addError(std::string("the target (").append(module->getTargetTriple()).append(") could not be found: ").append(error)); + + return false; + } + + // Create a target machine. + + auto targetMachine = std::unique_ptr(target->createTargetMachine(module->getTargetTriple(), + "generic", "", + llvm::TargetOptions(), + llvm::Reloc::Static)); + + if (targetMachine == nullptr) { + addError("A target machine could not be created."); + + return false; + } + + // Get the target machine to emit some WebAssembly code. + + llvm::legacy::PassManager passManager; + llvm::SmallVector outputBuffer; + llvm::raw_svector_ostream output(outputBuffer); + + if (targetMachine->addPassesToEmitFile(passManager, output, nullptr, llvm::CGFT_ObjectFile)) { + addError("The target machine cannot emit some WebAssembly code."); + + return false; + } + + passManager.run(*module); + + // Retrieve the WebAssembly code. + + pWasmModule.assign(outputBuffer.begin(), outputBuffer.end()); + + if (pWasmModule.empty()) { + addError("No WebAssembly code could be generated."); + + return false; + } + + return true; +#else // Create an ORC-based JIT and keep track of it. auto lljit = llvm::orc::LLJITBuilder().create(); -#ifndef CODE_COVERAGE_ENABLED +# ifndef CODE_COVERAGE_ENABLED if (!lljit) { addError(std::string("An ORC-based JIT could not be created").append(llvmClangError(lljit.takeError())).append(".")); return false; } -#endif +# endif mLljit = std::move(*lljit); @@ -302,37 +360,38 @@ extern double atanh(double); auto dynamicLibrarySearchGenerator = llvm::orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(mLljit->getDataLayout().getGlobalPrefix()); -#ifndef CODE_COVERAGE_ENABLED +# ifndef CODE_COVERAGE_ENABLED if (!dynamicLibrarySearchGenerator) { addError(std::string("The dynamic library search generator could not be created").append(llvmClangError(dynamicLibrarySearchGenerator.takeError())).append(".")); return false; } -#endif +# endif mLljit->getMainJITDylib().addGenerator(std::move(*dynamicLibrarySearchGenerator)); // Add our LLVM bitcode module to our ORC-based JIT. - auto llvmContext = std::make_unique(); auto threadSafeModule = llvm::orc::ThreadSafeModule(std::move(module), std::move(llvmContext)); -#ifdef CODE_COVERAGE_ENABLED +# ifdef CODE_COVERAGE_ENABLED const bool res = -#else +# else res = -#endif +# endif !mLljit->addIRModule(std::move(threadSafeModule)); -#ifndef CODE_COVERAGE_ENABLED +# ifndef CODE_COVERAGE_ENABLED if (!res) { addError("The LLVM bitcode module could not be added to the ORC-based JIT."); } -#endif +# endif return res; +#endif } +#ifndef __EMSCRIPTEN__ bool Compiler::Impl::addFunction(const std::string &pName, void *pFunction) { // Add the given function to our ORC-based JIT. Note that we assume that we have a valid ORC-based JIT, function @@ -342,11 +401,11 @@ bool Compiler::Impl::addFunction(const std::string &pName, void *pFunction) {mLljit->mangleAndIntern(pName), llvm::JITEvaluatedSymbol(llvm::pointerToJITTargetAddress(pFunction), llvm::JITSymbolFlags::Exported)}, })); -#ifndef CODE_COVERAGE_ENABLED +# ifndef CODE_COVERAGE_ENABLED if (!res) { addError(std::string("The ").append(pName).append("() function could not be added to the compiler.")); } -#endif +# endif return res; } @@ -364,6 +423,7 @@ void *Compiler::Impl::function(const std::string &pName) const return {}; } +#endif Compiler::Compiler() : Logger(new Impl {}) @@ -390,6 +450,12 @@ CompilerPtr Compiler::create() return CompilerPtr {new Compiler {}}; } +#ifdef __EMSCRIPTEN__ +bool Compiler::compile(const std::string &pCode, UnsignedChars &pWasmModule) +{ + return pimpl()->compile(pCode, pWasmModule); +} +#else bool Compiler::compile(const std::string &pCode) { return pimpl()->compile(pCode); @@ -404,5 +470,6 @@ void *Compiler::function(const std::string &pName) const { return pimpl()->function(pName); } +#endif } // namespace libOpenCOR diff --git a/src/misc/compiler.h b/src/misc/compiler.h index 5916eb754..71fcff3d3 100644 --- a/src/misc/compiler.h +++ b/src/misc/compiler.h @@ -40,11 +40,15 @@ class LIBOPENCOR_UNIT_TESTING_EXPORT Compiler: public Logger static CompilerPtr create(); +#ifdef __EMSCRIPTEN__ + bool compile(const std::string &pCode, UnsignedChars &pWasmModule); +#else bool compile(const std::string &pCode); bool addFunction(const std::string &pName, void *pFunction); void *function(const std::string &pName) const; +#endif private: class Impl; diff --git a/src/misc/compiler_p.h b/src/misc/compiler_p.h index dcd08ae14..4476c5c48 100644 --- a/src/misc/compiler_p.h +++ b/src/misc/compiler_p.h @@ -20,15 +20,20 @@ limitations under the License. #include "compiler.h" -#include "llvmbegin.h" -#include "llvm/ExecutionEngine/Orc/LLJIT.h" -#include "llvmend.h" +#ifndef __EMSCRIPTEN__ +# include "llvmbegin.h" +# include "llvm/ExecutionEngine/Orc/LLJIT.h" +# include "llvmend.h" +#endif namespace libOpenCOR { class Compiler::Impl: public Logger::Impl { public: +#ifdef __EMSCRIPTEN__ + bool compile(const std::string &pCode, UnsignedChars &pWasmModule); +#else std::unique_ptr mLljit; bool compile(const std::string &pCode); @@ -36,6 +41,7 @@ class Compiler::Impl: public Logger::Impl bool addFunction(const std::string &pName, void *pFunction); void *function(const std::string &pName) const; +#endif }; } // namespace libOpenCOR diff --git a/src/misc/utils.cpp b/src/misc/utils.cpp index 77365de47..c2e3eb2bb 100644 --- a/src/misc/utils.cpp +++ b/src/misc/utils.cpp @@ -16,6 +16,8 @@ limitations under the License. #include "utils.h" +#include "libopencor/issue.h" + #ifndef __EMSCRIPTEN__ # include "curl/curl.h" #endif @@ -29,6 +31,7 @@ limitations under the License. #include #include #include +#include #include #include @@ -36,12 +39,105 @@ limitations under the License. # include #endif +#ifdef ERROR +# undef ERROR +#endif + #ifdef NAN # undef NAN #endif namespace libOpenCOR { +#ifndef CODE_COVERAGE_ENABLED +void printIssues(const LoggerPtr &pLogger) +{ + std::cout << "---[ISSUES]---[BEGIN]\n"; + + for (auto &issue : pLogger->issues()) { + std::cout << ((issue->type() == Issue::Type::ERROR) ? "ERROR" : "WARNING") << ": " << issue->description() << "\n"; + } + + std::cout << "---[ISSUES]---[END]\n"; +} + +void printHexDump(const UnsignedChars &pBytes) +{ + static constexpr auto BYTES_PER_LINE = 16; + static constexpr auto ADDRESS_WIDTH = 8; + static constexpr auto FIRST_ASCII_CHARACTER = 32; + static constexpr auto LAST_ASCII_CHARACTER = 126; + + std::cout << "---[BYTES]---[BEGIN]\n"; + + for (size_t i = 0; i < pBytes.size(); i += BYTES_PER_LINE) { + // Print the offset. + + std::cout << std::hex << std::setfill('0') << std::setw(ADDRESS_WIDTH) << i << " "; + + // Print hex bytes. + + for (size_t j = 0; j < BYTES_PER_LINE; ++j) { + if (i + j < pBytes.size()) { + std::cout << std::hex << std::setfill('0') << std::setw(2) << static_cast(pBytes[i + j]); + } else { + std::cout << " "; + } + + // Add an extra space after 8 bytes. + + if (j == ADDRESS_WIDTH - 1) { + std::cout << " "; + } else { + std::cout << " "; + } + } + + std::cout << " |"; + + // Print the ASCII representation. + + const size_t bytesOnThisLine = (pBytes.size() - i < BYTES_PER_LINE) ? (pBytes.size() - i) : BYTES_PER_LINE; + + for (size_t j = 0; j < bytesOnThisLine; ++j) { + auto byte = pBytes[i + j]; + + if (byte >= FIRST_ASCII_CHARACTER && byte <= LAST_ASCII_CHARACTER) { + std::cout << static_cast(byte); + } else { + std::cout << "."; + } + } + + for (size_t j = bytesOnThisLine; j < BYTES_PER_LINE; ++j) { + std::cout << " "; + } + + std::cout << "|\n"; + } + + std::cout << std::dec; + + std::cout << "---[BYTES]---[END]\n"; +} + +void printArray(const std::string &pName, const Doubles &pDoubles) +{ + std::cout << "---[ARRAY]---[" << pName << "]---[BEGIN]\n"; + + if (!pDoubles.empty()) { + const auto arraySize = pDoubles.size(); + const auto indexWidth = static_cast(log10(static_cast(arraySize - 1))) + 1; + + for (size_t i = 0; i < arraySize; ++i) { + std::cout << "[" << std::setfill('0') << std::setw(indexWidth) << i << "] " << pDoubles[i] << "\n"; + } + } + + std::cout << "---[ARRAY]---[" << pName << "]---[END]\n"; +} +#endif + bool fuzzyCompare(double pNb1, double pNb2) { static constexpr double ONE_TRILLION = 1000000000000.0; @@ -232,7 +328,7 @@ using TimeVal = struct namespace { -int getTimeOfDay(TimeVal &pTimeVal) +void getTimeOfDay(TimeVal &pTimeVal) { // Based off https://stackoverflow.com/a/58162122. @@ -241,8 +337,6 @@ int getTimeOfDay(TimeVal &pTimeVal) pTimeVal.seconds = static_cast(seconds.count()); pTimeVal.microeconds = static_cast(std::chrono::duration_cast(duration - seconds).count()); - - return 0; } std::filesystem::path uniqueFilePath() @@ -389,21 +483,13 @@ UnsignedChars fileContents(const std::filesystem::path &pFilePath) } #endif -char *nlaSolverAddress(SolverNla *pNlaSolver) +std::string nlaSolverAddress(SolverNla *pNlaSolver) { std::ostringstream oss; - oss << pNlaSolver; - - auto str = oss.str(); - auto len = str.size(); - auto *res = new char[len + 1]; + oss << "0x" << std::hex << reinterpret_cast(pNlaSolver); - std::ranges::copy(str, res); - - res[len] = '\0'; // NOLINT - - return res; + return oss.str(); } bool toBool(const std::string &pString) @@ -495,104 +581,6 @@ std::string toString(const UnsignedChars &pBytes) return {reinterpret_cast(pBytes.data()), pBytes.size()}; } -SolverCvode::IntegrationMethod toCvodeIntegrationMethod(const std::string &pIntegrationMethod) -{ - return (pIntegrationMethod == "BDF") ? - SolverCvode::IntegrationMethod::BDF : - SolverCvode::IntegrationMethod::ADAMS_MOULTON; -} - -std::string toString(SolverCvode::IntegrationMethod pIntegrationMethod) -{ - return (pIntegrationMethod == SolverCvode::IntegrationMethod::BDF) ? - "BDF" : - "Adams-Moulton"; -} - -SolverCvode::IterationType toCvodeIterationType(const std::string &pIterationType) -{ - return (pIterationType == "Functional") ? - SolverCvode::IterationType::FUNCTIONAL : - SolverCvode::IterationType::NEWTON; -} - -std::string toString(SolverCvode::IterationType pIterationType) -{ - return (pIterationType == SolverCvode::IterationType::FUNCTIONAL) ? - "Functional" : - "Newton"; -} - -SolverCvode::LinearSolver toCvodeLinearSolver(const std::string &pLinearSolver) -{ - return (pLinearSolver == "Dense") ? - SolverCvode::LinearSolver::DENSE : - (pLinearSolver == "Banded") ? - SolverCvode::LinearSolver::BANDED : - (pLinearSolver == "Diagonal") ? - SolverCvode::LinearSolver::DIAGONAL : - (pLinearSolver == "GMRES") ? - SolverCvode::LinearSolver::GMRES : - (pLinearSolver == "BiCGStab") ? - SolverCvode::LinearSolver::BICGSTAB : - SolverCvode::LinearSolver::TFQMR; -} - -std::string toString(SolverCvode::LinearSolver pLinearSolver) -{ - return (pLinearSolver == SolverCvode::LinearSolver::DENSE) ? - "Dense" : - (pLinearSolver == SolverCvode::LinearSolver::BANDED) ? - "Banded" : - (pLinearSolver == SolverCvode::LinearSolver::DIAGONAL) ? - "Diagonal" : - (pLinearSolver == SolverCvode::LinearSolver::GMRES) ? - "GMRES" : - (pLinearSolver == SolverCvode::LinearSolver::BICGSTAB) ? - "BiCGStab" : - "TFQMR"; -} - -SolverCvode::Preconditioner toCvodePreconditioner(const std::string &pPreconditioner) -{ - return (pPreconditioner == "No") ? - SolverCvode::Preconditioner::NO : - SolverCvode::Preconditioner::BANDED; -} - -std::string toString(SolverCvode::Preconditioner pPreconditioner) -{ - return (pPreconditioner == SolverCvode::Preconditioner::NO) ? - "No" : - "Banded"; -} - -SolverKinsol::LinearSolver toKinsolLinearSolver(const std::string &pLinearSolver) -{ - return (pLinearSolver == "Dense") ? - SolverKinsol::LinearSolver::DENSE : - (pLinearSolver == "Banded") ? - SolverKinsol::LinearSolver::BANDED : - (pLinearSolver == "GMRES") ? - SolverKinsol::LinearSolver::GMRES : - (pLinearSolver == "BiCGStab") ? - SolverKinsol::LinearSolver::BICGSTAB : - SolverKinsol::LinearSolver::TFQMR; -} - -std::string toString(SolverKinsol::LinearSolver pLinearSolver) -{ - return (pLinearSolver == SolverKinsol::LinearSolver::DENSE) ? - "Dense" : - (pLinearSolver == SolverKinsol::LinearSolver::BANDED) ? - "Banded" : - (pLinearSolver == SolverKinsol::LinearSolver::GMRES) ? - "GMRES" : - (pLinearSolver == SolverKinsol::LinearSolver::BICGSTAB) ? - "BiCGStab" : - "TFQMR"; -} - const xmlChar *toConstXmlCharPtr(const std::string &pString) { return reinterpret_cast(pString.c_str()); diff --git a/src/misc/utils.h b/src/misc/utils.h index 234760b21..f3928ea07 100644 --- a/src/misc/utils.h +++ b/src/misc/utils.h @@ -70,14 +70,20 @@ using StringStringMap = std::map; # define ASSERT_LE(x, y) assert(x <= y) #endif +#ifndef CODE_COVERAGE_ENABLED +void LIBOPENCOR_UNIT_TESTING_EXPORT printIssues(const LoggerPtr &pLogger); +void LIBOPENCOR_UNIT_TESTING_EXPORT printHexDump(const UnsignedChars &pBytes); +void LIBOPENCOR_UNIT_TESTING_EXPORT printArray(const std::string &pName, const Doubles &pDoubles); +#endif + bool LIBOPENCOR_UNIT_TESTING_EXPORT fuzzyCompare(double pNb1, double pNb2); #ifdef BUILDING_USING_MSVC std::string LIBOPENCOR_UNIT_TESTING_EXPORT forwardSlashPath(const std::string &pPath); #endif -std::filesystem::path LIBOPENCOR_UNIT_TESTING_EXPORT stringToPath(const std::string &pString); -std::string LIBOPENCOR_UNIT_TESTING_EXPORT pathToString(const std::filesystem::path &pPath); +std::filesystem::path stringToPath(const std::string &pString); +std::string pathToString(const std::filesystem::path &pPath); #ifdef BUILDING_USING_MSVC std::string LIBOPENCOR_UNIT_TESTING_EXPORT canonicalFileName(const std::string &pFileName, bool pIsRemoteFile = false); @@ -94,7 +100,7 @@ std::tuple downloadFile(const std::string &pUrl); UnsignedChars LIBOPENCOR_UNIT_TESTING_EXPORT fileContents(const std::filesystem::path &pFilePath); #endif -char LIBOPENCOR_UNIT_TESTING_EXPORT *nlaSolverAddress(SolverNla *pNlaSolver); +std::string nlaSolverAddress(SolverNla *pNlaSolver); bool toBool(const std::string &pString); std::string toString(bool pBoolean); @@ -111,21 +117,6 @@ std::string toString(double pNumber); std::string LIBOPENCOR_UNIT_TESTING_EXPORT toString(const UnsignedChars &pBytes); -SolverCvode::IntegrationMethod toCvodeIntegrationMethod(const std::string &pIntegrationMethod); -std::string toString(SolverCvode::IntegrationMethod pIntegrationMethod); - -SolverCvode::IterationType toCvodeIterationType(const std::string &pIterationType); -std::string toString(SolverCvode::IterationType pIterationType); - -SolverCvode::LinearSolver toCvodeLinearSolver(const std::string &pLinearSolver); -std::string toString(SolverCvode::LinearSolver pLinearSolver); - -SolverCvode::Preconditioner toCvodePreconditioner(const std::string &pPreconditioner); -std::string toString(SolverCvode::Preconditioner pPreconditioner); - -SolverKinsol::LinearSolver toKinsolLinearSolver(const std::string &pLinearSolver); -std::string toString(SolverKinsol::LinearSolver pLinearSolver); - const xmlChar *toConstXmlCharPtr(const std::string &pString); libcellml::ComponentPtr owningComponent(const libcellml::VariablePtr &pVariable); diff --git a/src/sed/seddocument.cpp b/src/sed/seddocument.cpp index bf7131bdd..451288e08 100644 --- a/src/sed/seddocument.cpp +++ b/src/sed/seddocument.cpp @@ -608,16 +608,9 @@ bool SedDocument::removeAllTasks() return pimpl()->removeAllTasks(); } -#ifdef __EMSCRIPTEN__ SedInstancePtr SedDocument::instantiate() { - return SedInstance::Impl::create(shared_from_this(), false); -} -#else -SedInstancePtr SedDocument::instantiate(bool pCompiled) -{ - return SedInstance::Impl::create(shared_from_this(), pCompiled); + return SedInstance::Impl::create(shared_from_this()); } -#endif } // namespace libOpenCOR diff --git a/src/sed/sedinstance.cpp b/src/sed/sedinstance.cpp index d9b2c5213..b402f3045 100644 --- a/src/sed/sedinstance.cpp +++ b/src/sed/sedinstance.cpp @@ -22,12 +22,12 @@ limitations under the License. namespace libOpenCOR { -SedInstancePtr SedInstance::Impl::create(const SedDocumentPtr &pDocument, bool pCompiled) +SedInstancePtr SedInstance::Impl::create(const SedDocumentPtr &pDocument) { - return SedInstancePtr {new SedInstance(pDocument, pCompiled)}; + return SedInstancePtr {new SedInstance(pDocument)}; } -SedInstance::Impl::Impl(const SedDocumentPtr &pDocument, bool pCompiled) +SedInstance::Impl::Impl(const SedDocumentPtr &pDocument) : Logger::Impl() { // Check whether there are some outputs that should be generated or, failing that, whether there are some tasks that @@ -58,7 +58,7 @@ SedInstance::Impl::Impl(const SedDocumentPtr &pDocument, bool pCompiled) if (tasksValid) { for (const auto &task : tasks) { - auto taskInstance = SedInstanceTask::Impl::create(task, pCompiled); + auto taskInstance = SedInstanceTask::Impl::create(task); mTasks.push_back(taskInstance); @@ -115,8 +115,8 @@ SedInstanceTaskPtr SedInstance::Impl::task(size_t pIndex) const return mTasks[pIndex]; } -SedInstance::SedInstance(const SedDocumentPtr &pDocument, bool pCompiled) - : Logger(new Impl(pDocument, pCompiled)) +SedInstance::SedInstance(const SedDocumentPtr &pDocument) + : Logger(new Impl(pDocument)) { } diff --git a/src/sed/sedinstance_p.h b/src/sed/sedinstance_p.h index 1ac8e7685..4b5dbc183 100644 --- a/src/sed/sedinstance_p.h +++ b/src/sed/sedinstance_p.h @@ -27,9 +27,9 @@ class SedInstance::Impl: public Logger::Impl public: SedInstanceTaskPtrs mTasks; - static SedInstancePtr create(const SedDocumentPtr &pDocument, bool pCompiled); + static SedInstancePtr create(const SedDocumentPtr &pDocument); - explicit Impl(const SedDocumentPtr &pDocument, bool pCompiled); + explicit Impl(const SedDocumentPtr &pDocument); double run(); diff --git a/src/sed/sedinstancetask.cpp b/src/sed/sedinstancetask.cpp index 1a3519c62..8d6f41c5b 100644 --- a/src/sed/sedinstancetask.cpp +++ b/src/sed/sedinstancetask.cpp @@ -25,19 +25,26 @@ limitations under the License. namespace libOpenCOR { -SedInstanceTaskPtr SedInstanceTask::Impl::create(const SedAbstractTaskPtr &pTask, bool pCompiled) +SedInstanceTaskPtr SedInstanceTask::Impl::create(const SedAbstractTaskPtr &pTask) { - auto res = SedInstanceTaskPtr {new SedInstanceTask {pTask, pCompiled}}; + auto res = SedInstanceTaskPtr {new SedInstanceTask {pTask}}; res->pimpl()->mOwner = res; - res->pimpl()->initialise(); + // Initialise the instance task, but only if there are no issues with it. + +#ifndef CODE_COVERAGE_ENABLED + if (!res->hasIssues()) { +#endif + res->pimpl()->initialise(); +#ifndef CODE_COVERAGE_ENABLED + } +#endif return res; } -SedInstanceTask::Impl::Impl(const SedAbstractTaskPtr &pTask, bool pCompiled) - : mCompiled(pCompiled) +SedInstanceTask::Impl::Impl(const SedAbstractTaskPtr &pTask) { //---GRY--- AT THIS STAGE, WE ONLY SUPPORT SedTask TASKS, HENCE WE ASSERT (FOR NOW) THAT pTask IS INDEED A SedTask // OBJECT. @@ -62,7 +69,7 @@ SedInstanceTask::Impl::Impl(const SedAbstractTaskPtr &pTask, bool pCompiled) mOdeSolver = (odeSolver != nullptr) ? std::dynamic_pointer_cast(odeSolver->pimpl()->duplicate()) : nullptr; mNlaSolver = (nlaSolver != nullptr) ? std::dynamic_pointer_cast(nlaSolver->pimpl()->duplicate()) : nullptr; - mRuntime = cellmlFile->runtime(mNlaSolver, mCompiled); + mRuntime = cellmlFile->runtime(mNlaSolver); #ifndef CODE_COVERAGE_ENABLED if (mRuntime->hasErrors()) { @@ -149,41 +156,39 @@ void SedInstanceTask::Impl::initialise() if (mSedUniformTimeCourse != nullptr) { mVoi = mSedUniformTimeCourse->pimpl()->mOutputStartTime; -#ifndef __EMSCRIPTEN__ - if (mCompiled) { - mRuntime->initialiseCompiledVariablesForDifferentialModel()(mStates, mRates, mConstants, mComputedConstants, mAlgebraic); - - applyChanges(); - - mRuntime->computeCompiledComputedConstants()(mConstants, mComputedConstants); - mRuntime->computeCompiledRates()(mVoi, mStates, mRates, mConstants, mComputedConstants, mAlgebraic); - mRuntime->computeCompiledVariablesForDifferentialModel()(mVoi, mStates, mRates, mConstants, mComputedConstants, mAlgebraic); - } else { +#ifdef __EMSCRIPTEN__ + mRuntime->initialiseVariablesForDifferentialModel(mStates, mRates, mConstants, mComputedConstants, mAlgebraic); +#else + mRuntime->initialiseVariablesForDifferentialModel()(mStates, mRates, mConstants, mComputedConstants, mAlgebraic); #endif - mRuntime->initialiseInterpretedVariablesForDifferentialModel()(mStates, mRates, mConstants, mComputedConstants, mAlgebraic); - - applyChanges(); - - mRuntime->computeInterpretedComputedConstants()(mConstants, mComputedConstants); - mRuntime->computeInterpretedRates()(mVoi, mStates, mRates, mConstants, mComputedConstants, mAlgebraic); - mRuntime->computeInterpretedVariablesForDifferentialModel()(mVoi, mStates, mRates, mConstants, mComputedConstants, mAlgebraic); -#ifndef __EMSCRIPTEN__ - } - } else if (mCompiled) { - mRuntime->initialiseCompiledVariablesForAlgebraicModel()(mConstants, mComputedConstants, mAlgebraic); applyChanges(); - mRuntime->computeCompiledComputedConstants()(mConstants, mComputedConstants); - mRuntime->computeCompiledVariablesForAlgebraicModel()(mConstants, mComputedConstants, mAlgebraic); +#ifdef __EMSCRIPTEN__ + mRuntime->computeComputedConstants(mConstants, mComputedConstants); + mRuntime->computeRates(mVoi, mStates, mRates, mConstants, mComputedConstants, mAlgebraic); + mRuntime->computeVariablesForDifferentialModel(mVoi, mStates, mRates, mConstants, mComputedConstants, mAlgebraic); +#else + mRuntime->computeComputedConstants()(mConstants, mComputedConstants); + mRuntime->computeRates()(mVoi, mStates, mRates, mConstants, mComputedConstants, mAlgebraic); + mRuntime->computeVariablesForDifferentialModel()(mVoi, mStates, mRates, mConstants, mComputedConstants, mAlgebraic); #endif } else { - mRuntime->initialiseInterpretedVariablesForAlgebraicModel()(mConstants, mComputedConstants, mAlgebraic); +#ifdef __EMSCRIPTEN__ + mRuntime->initialiseVariablesForAlgebraicModel(mConstants, mComputedConstants, mAlgebraic); +#else + mRuntime->initialiseVariablesForAlgebraicModel()(mConstants, mComputedConstants, mAlgebraic); +#endif applyChanges(); - mRuntime->computeInterpretedComputedConstants()(mConstants, mComputedConstants); - mRuntime->computeInterpretedVariablesForAlgebraicModel()(mConstants, mComputedConstants, mAlgebraic); +#ifdef __EMSCRIPTEN__ + mRuntime->computeComputedConstants(mConstants, mComputedConstants); + mRuntime->computeVariablesForAlgebraicModel(mConstants, mComputedConstants, mAlgebraic); +#else + mRuntime->computeComputedConstants()(mConstants, mComputedConstants); + mRuntime->computeVariablesForAlgebraicModel()(mConstants, mComputedConstants, mAlgebraic); +#endif } // Make sure that the NLA solver, should it have been used, didn't report any issues. @@ -199,7 +204,7 @@ void SedInstanceTask::Impl::initialise() if (mDifferentialModel) { if (!mOdeSolver->pimpl()->initialise(mVoi, mAnalyserModel->stateCount(), mStates, mRates, mConstants, mComputedConstants, mAlgebraic, - mRuntime->computeCompiledRates(), mRuntime->computeInterpretedRates())) { + mRuntime)) { addIssues(mOdeSolver); return; @@ -264,16 +269,12 @@ double SedInstanceTask::Impl::run() return 0.0; } -#ifndef __EMSCRIPTEN__ - if (mCompiled) { - mRuntime->computeCompiledVariablesForDifferentialModel()(mVoi, mStates, mRates, - mConstants, mComputedConstants, mAlgebraic); - } else { -#endif - mRuntime->computeInterpretedVariablesForDifferentialModel()(mVoi, mStates, mRates, - mConstants, mComputedConstants, mAlgebraic); -#ifndef __EMSCRIPTEN__ - } +#ifdef __EMSCRIPTEN__ + mRuntime->computeVariablesForDifferentialModel(mVoi, mStates, mRates, + mConstants, mComputedConstants, mAlgebraic); +#else + mRuntime->computeVariablesForDifferentialModel()(mVoi, mStates, mRates, + mConstants, mComputedConstants, mAlgebraic); #endif //---GRY--- WE NEED TO CHECK FOR POSSIBLE NLA ISSUES, BUT FOR CODE COVERAGE WE NEED A MODEL THAT WOULD @@ -498,8 +499,8 @@ std::string SedInstanceTask::Impl::algebraicUnit(size_t pIndex) const return mAnalyserModel->algebraic()[pIndex]->variable()->units()->name(); } -SedInstanceTask::SedInstanceTask(const SedAbstractTaskPtr &pTask, bool pCompiled) - : Logger(new Impl(pTask, pCompiled)) +SedInstanceTask::SedInstanceTask(const SedAbstractTaskPtr &pTask) + : Logger(new Impl(pTask)) { } diff --git a/src/sed/sedinstancetask_p.h b/src/sed/sedinstancetask_p.h index 73920fe67..ac41b223e 100644 --- a/src/sed/sedinstancetask_p.h +++ b/src/sed/sedinstancetask_p.h @@ -49,7 +49,6 @@ class SedInstanceTask::Impl: public Logger::Impl libcellml::AnalyserModelPtr mAnalyserModel; SolverOdePtr mOdeSolver; SolverNlaPtr mNlaSolver; - bool mCompiled = true; double mVoi = 0.0; double *mStates = nullptr; @@ -66,9 +65,9 @@ class SedInstanceTask::Impl: public Logger::Impl SedInstanceTaskResults mResults; - static SedInstanceTaskPtr create(const SedAbstractTaskPtr &pTask, bool pCompiled); + static SedInstanceTaskPtr create(const SedAbstractTaskPtr &pTask); - explicit Impl(const SedAbstractTaskPtr &pTask, bool pCompiled); + explicit Impl(const SedAbstractTaskPtr &pTask); void trackResults(size_t pIndex); diff --git a/src/solver/solvercvode.cpp b/src/solver/solvercvode.cpp index 1c8b08c2d..01dacfd94 100644 --- a/src/solver/solvercvode.cpp +++ b/src/solver/solvercvode.cpp @@ -37,6 +37,48 @@ limitations under the License. namespace libOpenCOR { +// Some utilities. + +namespace { + +std::string toString(SolverCvode::IntegrationMethod pIntegrationMethod) +{ + return (pIntegrationMethod == SolverCvode::IntegrationMethod::BDF) ? + "BDF" : + "Adams-Moulton"; +} + +std::string toString(SolverCvode::IterationType pIterationType) +{ + return (pIterationType == SolverCvode::IterationType::FUNCTIONAL) ? + "Functional" : + "Newton"; +} + +std::string toString(SolverCvode::LinearSolver pLinearSolver) +{ + return (pLinearSolver == SolverCvode::LinearSolver::DENSE) ? + "Dense" : + (pLinearSolver == SolverCvode::LinearSolver::BANDED) ? + "Banded" : + (pLinearSolver == SolverCvode::LinearSolver::DIAGONAL) ? + "Diagonal" : + (pLinearSolver == SolverCvode::LinearSolver::GMRES) ? + "GMRES" : + (pLinearSolver == SolverCvode::LinearSolver::BICGSTAB) ? + "BiCGStab" : + "TFQMR"; +} + +std::string toString(SolverCvode::Preconditioner pPreconditioner) +{ + return (pPreconditioner == SolverCvode::Preconditioner::NO) ? + "No" : + "Banded"; +} + +} // namespace + // Right-hand side function. namespace { @@ -64,13 +106,13 @@ int rhsFunction(double pVoi, N_Vector pStates, N_Vector pRates, void *pUserData) { auto *userData = static_cast(pUserData); - if (userData->computeCompiledRates != nullptr) { - userData->computeCompiledRates(pVoi, N_VGetArrayPointer_Serial(pStates), N_VGetArrayPointer_Serial(pRates), - userData->constants, userData->computedConstants, userData->algebraic); - } else { - userData->computeInterpretedRates(pVoi, N_VGetArrayPointer_Serial(pStates), N_VGetArrayPointer_Serial(pRates), +#ifdef __EMSCRIPTEN__ + userData->runtime->computeRates(pVoi, N_VGetArrayPointer_Serial(pStates), N_VGetArrayPointer_Serial(pRates), + userData->constants, userData->computedConstants, userData->algebraic); +#else + userData->runtime->computeRates()(pVoi, N_VGetArrayPointer_Serial(pStates), N_VGetArrayPointer_Serial(pRates), userData->constants, userData->computedConstants, userData->algebraic); - } +#endif return 0; } @@ -119,7 +161,9 @@ void SolverCvode::Impl::populate(libsedml::SedAlgorithm *pAlgorithm) value = toString(DEFAULT_INTEGRATION_METHOD); } - mIntegrationMethod = toCvodeIntegrationMethod(value); + mIntegrationMethod = (value == "BDF") ? + SolverCvode::IntegrationMethod::BDF : + SolverCvode::IntegrationMethod::ADAMS_MOULTON; } else if (kisaoId == "KISAO:0000476") { if ((value != "Functional") && (value != "Newton")) { addWarning(std::string("The iteration type ('").append(kisaoId).append("') cannot be equal to '").append(value).append("'. It must be equal to 'Functional' or 'Newton'. A ").append(toString(DEFAULT_ITERATION_TYPE)).append(" iteration type will be used instead.")); @@ -127,7 +171,9 @@ void SolverCvode::Impl::populate(libsedml::SedAlgorithm *pAlgorithm) value = toString(DEFAULT_ITERATION_TYPE); } - mIterationType = toCvodeIterationType(value); + mIterationType = (value == "Functional") ? + SolverCvode::IterationType::FUNCTIONAL : + SolverCvode::IterationType::NEWTON; } else if (kisaoId == "KISAO:0000477") { if ((value != "Dense") && (value != "Banded") && (value != "Diagonal") && (value != "GMRES") && (value != "BiCGStab") && (value != "TFQMR")) { addWarning(std::string("The linear solver ('").append(kisaoId).append("') cannot be equal to '").append(value).append("'. It must be equal to 'Dense', 'Banded', 'Diagonal', 'GMRES', 'BiCGStab', or 'TFQMR'. A ").append(toString(DEFAULT_LINEAR_SOLVER)).append(" linear solver will be used instead.")); @@ -135,7 +181,17 @@ void SolverCvode::Impl::populate(libsedml::SedAlgorithm *pAlgorithm) value = toString(DEFAULT_LINEAR_SOLVER); } - mLinearSolver = toCvodeLinearSolver(value); + mLinearSolver = (value == "Dense") ? + SolverCvode::LinearSolver::DENSE : + (value == "Banded") ? + SolverCvode::LinearSolver::BANDED : + (value == "Diagonal") ? + SolverCvode::LinearSolver::DIAGONAL : + (value == "GMRES") ? + SolverCvode::LinearSolver::GMRES : + (value == "BiCGStab") ? + SolverCvode::LinearSolver::BICGSTAB : + SolverCvode::LinearSolver::TFQMR; } else if (kisaoId == "KISAO:0000478") { if ((value != "No") && (value != "Banded")) { addWarning(std::string("The preconditioner ('").append(kisaoId).append("') cannot be equal to '").append(value).append("'. It must be equal to 'No' or 'Banded'. A ").append(toString(DEFAULT_PRECONDITIONER)).append(" preconditioner will be used instead.")); @@ -143,7 +199,9 @@ void SolverCvode::Impl::populate(libsedml::SedAlgorithm *pAlgorithm) value = toString(DEFAULT_PRECONDITIONER); } - mPreconditioner = toCvodePreconditioner(value); + mPreconditioner = (value == "No") ? + SolverCvode::Preconditioner::NO : + SolverCvode::Preconditioner::BANDED; } else if (kisaoId == "KISAO:0000479") { mUpperHalfBandwidth = toInt(value); @@ -247,8 +305,7 @@ StringStringMap SolverCvode::Impl::properties() const bool SolverCvode::Impl::initialise(double pVoi, size_t pSize, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic, - CellmlFileRuntime::ComputeCompiledRates pComputeCompiledRates, - CellmlFileRuntime::ComputeInterpretedRates pComputeInterpretedRates) + const CellmlFileRuntimePtr &pRuntime) { resetInternals(); removeAllIssues(); @@ -257,7 +314,7 @@ bool SolverCvode::Impl::initialise(double pVoi, size_t pSize, double *pStates, d SolverOde::Impl::initialise(pVoi, pSize, pStates, pRates, pConstants, pComputedConstants, pAlgebraic, - pComputeCompiledRates, pComputeInterpretedRates); + pRuntime); // Check the solver's properties. @@ -342,8 +399,7 @@ bool SolverCvode::Impl::initialise(double pVoi, size_t pSize, double *pStates, d mUserData.constants = pConstants; mUserData.computedConstants = pComputedConstants; mUserData.algebraic = pAlgebraic; - mUserData.computeCompiledRates = pComputeCompiledRates; - mUserData.computeInterpretedRates = pComputeInterpretedRates; + mUserData.runtime = pRuntime; ASSERT_EQ(CVodeSetUserData(mSolver, &mUserData), CV_SUCCESS); diff --git a/src/solver/solvercvode_p.h b/src/solver/solvercvode_p.h index ac5af0fde..365989b94 100644 --- a/src/solver/solvercvode_p.h +++ b/src/solver/solvercvode_p.h @@ -33,8 +33,7 @@ struct SolverCvodeUserData double *computedConstants = nullptr; double *algebraic = nullptr; - CellmlFileRuntime::ComputeCompiledRates computeCompiledRates = nullptr; - CellmlFileRuntime::ComputeInterpretedRates computeInterpretedRates = nullptr; + CellmlFileRuntimePtr runtime; }; class SolverCvode::Impl: public SolverOde::Impl @@ -91,8 +90,7 @@ class SolverCvode::Impl: public SolverOde::Impl bool initialise(double pVoi, size_t pSize, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic, - CellmlFileRuntime::ComputeCompiledRates pComputeCompiledRates, - CellmlFileRuntime::ComputeInterpretedRates pComputeInterpretedRates) override; + const CellmlFileRuntimePtr &pRuntime) override; /*---GRY--- TO BE UNCOMMENTED ONCE WE ACTUALLY NEED IT. bool reinitialise(double pVoi) override; */ diff --git a/src/solver/solverforwardeuler.cpp b/src/solver/solverforwardeuler.cpp index 94a6e987f..ed12d8732 100644 --- a/src/solver/solverforwardeuler.cpp +++ b/src/solver/solverforwardeuler.cpp @@ -32,8 +32,7 @@ SolverPtr SolverForwardEuler::Impl::duplicate() bool SolverForwardEuler::Impl::initialise(double pVoi, size_t pSize, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic, - CellmlFileRuntime::ComputeCompiledRates pComputeCompiledRates, - CellmlFileRuntime::ComputeInterpretedRates pComputeInterpretedRates) + const CellmlFileRuntimePtr &pRuntime) { removeAllIssues(); @@ -41,7 +40,7 @@ bool SolverForwardEuler::Impl::initialise(double pVoi, size_t pSize, double *pSt return SolverOdeFixedStep::Impl::initialise(pVoi, pSize, pStates, pRates, pConstants, pComputedConstants, pAlgebraic, - pComputeCompiledRates, pComputeInterpretedRates); + pRuntime); } bool SolverForwardEuler::Impl::solve(double &pVoi, double pVoiEnd) diff --git a/src/solver/solverforwardeuler_p.h b/src/solver/solverforwardeuler_p.h index e71c4f376..bf34215de 100644 --- a/src/solver/solverforwardeuler_p.h +++ b/src/solver/solverforwardeuler_p.h @@ -31,8 +31,7 @@ class SolverForwardEuler::Impl: public SolverOdeFixedStep::Impl bool initialise(double pVoi, size_t pSize, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic, - CellmlFileRuntime::ComputeCompiledRates pComputeCompiledRates, - CellmlFileRuntime::ComputeInterpretedRates pComputeInterpretedRates) override; + const CellmlFileRuntimePtr &pRuntime) override; bool solve(double &pVoi, double pVoiEnd) override; }; diff --git a/src/solver/solverfourthorderrungekutta.cpp b/src/solver/solverfourthorderrungekutta.cpp index 565c2ead1..138f11238 100644 --- a/src/solver/solverfourthorderrungekutta.cpp +++ b/src/solver/solverfourthorderrungekutta.cpp @@ -32,8 +32,7 @@ SolverPtr SolverFourthOrderRungeKutta::Impl::duplicate() bool SolverFourthOrderRungeKutta::Impl::initialise(double pVoi, size_t pSize, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic, - CellmlFileRuntime::ComputeCompiledRates pComputeCompiledRates, - CellmlFileRuntime::ComputeInterpretedRates pComputeInterpretedRates) + const CellmlFileRuntimePtr &pRuntime) { removeAllIssues(); @@ -41,7 +40,7 @@ bool SolverFourthOrderRungeKutta::Impl::initialise(double pVoi, size_t pSize, do if (!SolverOdeFixedStep::Impl::initialise(pVoi, pSize, pStates, pRates, pConstants, pComputedConstants, pAlgebraic, - pComputeCompiledRates, pComputeInterpretedRates)) { + pRuntime)) { return false; } diff --git a/src/solver/solverfourthorderrungekutta_p.h b/src/solver/solverfourthorderrungekutta_p.h index f9ad63ede..06c22181e 100644 --- a/src/solver/solverfourthorderrungekutta_p.h +++ b/src/solver/solverfourthorderrungekutta_p.h @@ -41,8 +41,7 @@ class SolverFourthOrderRungeKutta::Impl: public SolverOdeFixedStep::Impl bool initialise(double pVoi, size_t pSize, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic, - CellmlFileRuntime::ComputeCompiledRates pComputeCompiledRates, - CellmlFileRuntime::ComputeInterpretedRates pComputeInterpretedRates) override; + const CellmlFileRuntimePtr &pRuntime) override; bool solve(double &pVoi, double pVoiEnd) override; }; diff --git a/src/solver/solverheun.cpp b/src/solver/solverheun.cpp index 01af070fa..5748db2cd 100644 --- a/src/solver/solverheun.cpp +++ b/src/solver/solverheun.cpp @@ -32,8 +32,7 @@ SolverPtr SolverHeun::Impl::duplicate() bool SolverHeun::Impl::initialise(double pVoi, size_t pSize, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic, - CellmlFileRuntime::ComputeCompiledRates pComputeCompiledRates, - CellmlFileRuntime::ComputeInterpretedRates pComputeInterpretedRates) + const CellmlFileRuntimePtr &pRuntime) { removeAllIssues(); @@ -41,7 +40,7 @@ bool SolverHeun::Impl::initialise(double pVoi, size_t pSize, double *pStates, do if (!SolverOdeFixedStep::Impl::initialise(pVoi, pSize, pStates, pRates, pConstants, pComputedConstants, pAlgebraic, - pComputeCompiledRates, pComputeInterpretedRates)) { + pRuntime)) { return false; } diff --git a/src/solver/solverheun_p.h b/src/solver/solverheun_p.h index 3190f899e..14e6603fd 100644 --- a/src/solver/solverheun_p.h +++ b/src/solver/solverheun_p.h @@ -37,8 +37,7 @@ class SolverHeun::Impl: public SolverOdeFixedStep::Impl bool initialise(double pVoi, size_t pSize, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic, - CellmlFileRuntime::ComputeCompiledRates pComputeCompiledRates, - CellmlFileRuntime::ComputeInterpretedRates pComputeInterpretedRates) override; + const CellmlFileRuntimePtr &pRuntime) override; bool solve(double &pVoi, double pVoiEnd) override; }; diff --git a/src/solver/solverkinsol.cpp b/src/solver/solverkinsol.cpp index 66eb96193..27dccc9ee 100644 --- a/src/solver/solverkinsol.cpp +++ b/src/solver/solverkinsol.cpp @@ -34,6 +34,25 @@ limitations under the License. namespace libOpenCOR { +// Some utilities. + +namespace { + +std::string toString(SolverKinsol::LinearSolver pLinearSolver) +{ + return (pLinearSolver == SolverKinsol::LinearSolver::DENSE) ? + "Dense" : + (pLinearSolver == SolverKinsol::LinearSolver::BANDED) ? + "Banded" : + (pLinearSolver == SolverKinsol::LinearSolver::GMRES) ? + "GMRES" : + (pLinearSolver == SolverKinsol::LinearSolver::BICGSTAB) ? + "BiCGStab" : + "TFQMR"; +} + +} // namespace + // Compute system. namespace { @@ -53,18 +72,33 @@ void errorHandler(int pLine, const char *pFunction, const char *pFile, const cha } #endif +#ifdef __EMSCRIPTEN__ +static constexpr auto MAX_SIZE_T = std::numeric_limits::max(); +#endif + struct SolverKinsolUserData { - SolverNla::ComputeSystem computeSystem = nullptr; +#ifdef __EMSCRIPTEN__ + size_t computeObjectiveFunctionIndex = MAX_SIZE_T; +#else + SolverNla::ComputeObjectiveFunction computeObjectiveFunction = nullptr; +#endif void *userData = nullptr; }; -int computeSystem(N_Vector pU, N_Vector pF, void *pUserData) +int computeObjectiveFunction(N_Vector pU, N_Vector pF, void *pUserData) { auto *userData = static_cast(pUserData); - userData->computeSystem(N_VGetArrayPointer_Serial(pU), N_VGetArrayPointer_Serial(pF), userData->userData); +#ifdef __EMSCRIPTEN__ + // clang-format off + EM_ASM({ + Module.objectiveFunctions[$0]($1, $2, $3); + }, userData->computeObjectiveFunctionIndex, N_VGetArrayPointer_Serial(pU), N_VGetArrayPointer_Serial(pF), userData->userData); // clang-format on +#else + userData->computeObjectiveFunction(N_VGetArrayPointer_Serial(pU), N_VGetArrayPointer_Serial(pF), userData->userData); +#endif return 0; } @@ -100,7 +134,15 @@ void SolverKinsol::Impl::populate(libsedml::SedAlgorithm *pAlgorithm) value = toString(DEFAULT_LINEAR_SOLVER); } - mLinearSolver = toKinsolLinearSolver(value); + mLinearSolver = (value == "Dense") ? + SolverKinsol::LinearSolver::DENSE : + (value == "Banded") ? + SolverKinsol::LinearSolver::BANDED : + (value == "GMRES") ? + SolverKinsol::LinearSolver::GMRES : + (value == "BiCGStab") ? + SolverKinsol::LinearSolver::BICGSTAB : + SolverKinsol::LinearSolver::TFQMR; } else if (kisaoId == "KISAO:0000479") { mUpperHalfBandwidth = toInt(value); @@ -188,11 +230,15 @@ void SolverKinsol::Impl::setLowerHalfBandwidth(int pLowerHalfBandwidth) mLowerHalfBandwidth = pLowerHalfBandwidth; } -bool SolverKinsol::Impl::solve(ComputeSystem pComputeSystem, double *pU, size_t pN, void *pUserData) +#ifdef __EMSCRIPTEN__ +bool SolverKinsol::Impl::solve(size_t pComputeObjectiveFunctionIndex, double *pU, size_t pN, void *pUserData) +#else +bool SolverKinsol::Impl::solve(ComputeObjectiveFunction pComputeObjectiveFunction, double *pU, size_t pN, void *pUserData) +#endif { removeAllIssues(); - // We don't have any data associated with the given pComputeSystem, so get some by first making sure that the + // We don't have any data associated with the given objective function, so get some by first making sure that the // solver's properties are all valid. if (mMaximumNumberOfIterations <= 0) { @@ -252,7 +298,7 @@ bool SolverKinsol::Impl::solve(ComputeSystem pComputeSystem, double *pU, size_t N_VConst(1.0, ones); - ASSERT_EQ(KINInit(solver, computeSystem, u), KIN_SUCCESS); + ASSERT_EQ(KINInit(solver, computeObjectiveFunction, u), KIN_SUCCESS); // Set our linear solver. @@ -293,7 +339,11 @@ bool SolverKinsol::Impl::solve(ComputeSystem pComputeSystem, double *pU, size_t SolverKinsolUserData userData; - userData.computeSystem = pComputeSystem; +#ifdef __EMSCRIPTEN__ + userData.computeObjectiveFunctionIndex = pComputeObjectiveFunctionIndex; +#else + userData.computeObjectiveFunction = pComputeObjectiveFunction; +#endif userData.userData = pUserData; ASSERT_EQ(KINSetUserData(solver, &userData), KIN_SUCCESS); diff --git a/src/solver/solverkinsol_p.h b/src/solver/solverkinsol_p.h index 5ee2b42b0..19ce31e99 100644 --- a/src/solver/solverkinsol_p.h +++ b/src/solver/solverkinsol_p.h @@ -57,7 +57,11 @@ class SolverKinsol::Impl: public SolverNla::Impl int lowerHalfBandwidth() const; void setLowerHalfBandwidth(int pLowerHalfBandwidth); - bool solve(ComputeSystem pComputeSystem, double *pU, size_t pN, void *pUserData) override; +#ifdef __EMSCRIPTEN__ + bool solve(size_t pComputeObjectiveFunctionIndex, double *pU, size_t pN, void *pUserData) override; +#else + bool solve(ComputeObjectiveFunction pComputeObjectiveFunction, double *pU, size_t pN, void *pUserData) override; +#endif }; } // namespace libOpenCOR diff --git a/src/solver/solvernla.cpp b/src/solver/solvernla.cpp index ae4f28f62..c3dfd1f54 100644 --- a/src/solver/solvernla.cpp +++ b/src/solver/solvernla.cpp @@ -20,18 +20,18 @@ limitations under the License. namespace libOpenCOR { -void nlaSolve(const char *pNlaSolverAddress, void (*pObjectiveFunction)(double *, double *, void *), +#ifdef __EMSCRIPTEN__ +void nlaSolve(uintptr_t pNlaSolverAddress, size_t pObjectiveFunctionIndex, double *pU, size_t pN, void *pData) +{ + reinterpret_cast(pNlaSolverAddress)->solve(pObjectiveFunctionIndex, pU, pN, pData); +} +#else +void nlaSolve(uintptr_t pNlaSolverAddress, void (*pObjectiveFunction)(double *, double *, void *), double *pU, size_t pN, void *pData) { - std::istringstream iss(pNlaSolverAddress); - void *ptr = nullptr; - - iss >> ptr; - - ASSERT_NE(ptr, nullptr); - - reinterpret_cast(ptr)->solve(pObjectiveFunction, pU, pN, pData); + reinterpret_cast(pNlaSolverAddress)->solve(pObjectiveFunction, pU, pN, pData); // NOLINT } +#endif SolverNla::Impl::Impl(const std::string &pId, const std::string &pName) : Solver::Impl(pId, pName) @@ -58,9 +58,16 @@ Solver::Type SolverNla::type() const return Type::NLA; } -bool SolverNla::solve(ComputeSystem pComputeSystem, double *pU, size_t pN, void *pUserData) +#ifdef __EMSCRIPTEN__ +bool SolverNla::solve(size_t pComputeObjectiveFunctionIndex, double *pU, size_t pN, void *pUserData) +{ + return pimpl()->solve(pComputeObjectiveFunctionIndex, pU, pN, pUserData); +} +#else +bool SolverNla::solve(ComputeObjectiveFunction pComputeObjectiveFunction, double *pU, size_t pN, void *pUserData) { - return pimpl()->solve(pComputeSystem, pU, pN, pUserData); + return pimpl()->solve(pComputeObjectiveFunction, pU, pN, pUserData); } +#endif } // namespace libOpenCOR diff --git a/src/solver/solvernla_p.h b/src/solver/solvernla_p.h index 9a7dc76c8..cc6b7564f 100644 --- a/src/solver/solvernla_p.h +++ b/src/solver/solvernla_p.h @@ -22,16 +22,23 @@ limitations under the License. namespace libOpenCOR { -void LIBOPENCOR_UNIT_TESTING_EXPORT nlaSolve(const char *pNlaSolverAddress, - void (*pObjectiveFunction)(double *, double *, void *), - double *pU, size_t pN, void *pData); +#ifdef __EMSCRIPTEN__ +void nlaSolve(uintptr_t pNlaSolverAddress, size_t pObjectiveFunctionIndex, double *pU, size_t pN, void *pData); +#else +void nlaSolve(uintptr_t pNlaSolverAddress, void (*pObjectiveFunction)(double *, double *, void *), + double *pU, size_t pN, void *pData); +#endif class SolverNla::Impl: public Solver::Impl { public: explicit Impl(const std::string &pId, const std::string &pName); - virtual bool solve(ComputeSystem pComputeSystem, double *pU, size_t pN, void *pUserData) = 0; +#ifdef __EMSCRIPTEN__ + virtual bool solve(size_t pComputeObjectiveFunctionIndex, double *pU, size_t pN, void *pUserData) = 0; +#else + virtual bool solve(ComputeObjectiveFunction pComputeObjectiveFunction, double *pU, size_t pN, void *pUserData) = 0; +#endif }; } // namespace libOpenCOR diff --git a/src/solver/solverode.cpp b/src/solver/solverode.cpp index f9a04490c..dec2d15d5 100644 --- a/src/solver/solverode.cpp +++ b/src/solver/solverode.cpp @@ -25,8 +25,7 @@ SolverOde::Impl::Impl(const std::string &pId, const std::string &pName) bool SolverOde::Impl::initialise(double pVoi, size_t pSize, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic, - CellmlFileRuntime::ComputeCompiledRates pComputeCompiledRates, - CellmlFileRuntime::ComputeInterpretedRates pComputeInterpretedRates) + const CellmlFileRuntimePtr &pRuntime) { (void)pVoi; @@ -38,8 +37,7 @@ bool SolverOde::Impl::initialise(double pVoi, size_t pSize, double *pStates, dou mComputedConstants = pComputedConstants; mAlgebraic = pAlgebraic; - mComputeCompiledRates = pComputeCompiledRates; - mComputeInterpretedRates = std::move(pComputeInterpretedRates); + mRuntime = pRuntime; return true; } @@ -56,11 +54,11 @@ bool SolverOde::Impl::reinitialise(double pVoi) void SolverOde::Impl::computeRates(double pVoi, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic) const { - if (mComputeCompiledRates != nullptr) { - mComputeCompiledRates(pVoi, pStates, pRates, pConstants, pComputedConstants, pAlgebraic); - } else { - mComputeInterpretedRates(pVoi, pStates, pRates, pConstants, pComputedConstants, pAlgebraic); - } +#ifdef __EMSCRIPTEN__ + mRuntime->computeRates(pVoi, pStates, pRates, pConstants, pComputedConstants, pAlgebraic); +#else + mRuntime->computeRates()(pVoi, pStates, pRates, pConstants, pComputedConstants, pAlgebraic); +#endif } SolverOde::SolverOde(Impl *pPimpl) diff --git a/src/solver/solverode_p.h b/src/solver/solverode_p.h index d23ab6b2e..0725be743 100644 --- a/src/solver/solverode_p.h +++ b/src/solver/solverode_p.h @@ -35,15 +35,13 @@ class SolverOde::Impl: public Solver::Impl double *mComputedConstants = nullptr; double *mAlgebraic = nullptr; - CellmlFileRuntime::ComputeCompiledRates mComputeCompiledRates = nullptr; - CellmlFileRuntime::ComputeInterpretedRates mComputeInterpretedRates = nullptr; + CellmlFileRuntimePtr mRuntime; explicit Impl(const std::string &pId, const std::string &pName); virtual bool initialise(double pVoi, size_t pSize, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic, - CellmlFileRuntime::ComputeCompiledRates pComputeCompiledRates, - CellmlFileRuntime::ComputeInterpretedRates pComputeInterpretedRates) = 0; + const CellmlFileRuntimePtr &pRuntime) = 0; /*---GRY--- TO BE UNCOMMENTED ONCE WE ACTUALLY NEED IT. virtual bool reinitialise(double pVoi); */ diff --git a/src/solver/solverodefixedstep.cpp b/src/solver/solverodefixedstep.cpp index 443aa1291..2f1460011 100644 --- a/src/solver/solverodefixedstep.cpp +++ b/src/solver/solverodefixedstep.cpp @@ -70,14 +70,13 @@ StringStringMap SolverOdeFixedStep::Impl::properties() const bool SolverOdeFixedStep::Impl::initialise(double pVoi, size_t pSize, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic, - CellmlFileRuntime::ComputeCompiledRates pComputeCompiledRates, - CellmlFileRuntime::ComputeInterpretedRates pComputeInterpretedRates) + const CellmlFileRuntimePtr &pRuntime) { // Initialise the ODE solver itself. SolverOde::Impl::initialise(pVoi, pSize, pStates, pRates, pConstants, pComputedConstants, pAlgebraic, - pComputeCompiledRates, pComputeInterpretedRates); + pRuntime); // Check the solver's properties. diff --git a/src/solver/solverodefixedstep_p.h b/src/solver/solverodefixedstep_p.h index 251676902..0a279cd4a 100644 --- a/src/solver/solverodefixedstep_p.h +++ b/src/solver/solverodefixedstep_p.h @@ -41,8 +41,7 @@ class SolverOdeFixedStep::Impl: public SolverOde::Impl bool initialise(double pVoi, size_t pSize, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic, - CellmlFileRuntime::ComputeCompiledRates pComputeCompiledRates, - CellmlFileRuntime::ComputeInterpretedRates pComputeInterpretedRates) override; + const CellmlFileRuntimePtr &pRuntime) override; double step() const; void setStep(double pStep); diff --git a/src/solver/solversecondorderrungekutta.cpp b/src/solver/solversecondorderrungekutta.cpp index 75ec7cc9b..f1a6fb1ea 100644 --- a/src/solver/solversecondorderrungekutta.cpp +++ b/src/solver/solversecondorderrungekutta.cpp @@ -32,8 +32,7 @@ SolverPtr SolverSecondOrderRungeKutta::Impl::duplicate() bool SolverSecondOrderRungeKutta::Impl::initialise(double pVoi, size_t pSize, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic, - CellmlFileRuntime::ComputeCompiledRates pComputeCompiledRates, - CellmlFileRuntime::ComputeInterpretedRates pComputeInterpretedRates) + const CellmlFileRuntimePtr &pRuntime) { removeAllIssues(); @@ -41,7 +40,7 @@ bool SolverSecondOrderRungeKutta::Impl::initialise(double pVoi, size_t pSize, do if (!SolverOdeFixedStep::Impl::initialise(pVoi, pSize, pStates, pRates, pConstants, pComputedConstants, pAlgebraic, - pComputeCompiledRates, pComputeInterpretedRates)) { + pRuntime)) { return false; } diff --git a/src/solver/solversecondorderrungekutta_p.h b/src/solver/solversecondorderrungekutta_p.h index a22d05cf6..dfbe5d8ed 100644 --- a/src/solver/solversecondorderrungekutta_p.h +++ b/src/solver/solversecondorderrungekutta_p.h @@ -35,8 +35,7 @@ class SolverSecondOrderRungeKutta::Impl: public SolverOdeFixedStep::Impl bool initialise(double pVoi, size_t pSize, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic, - CellmlFileRuntime::ComputeCompiledRates pComputeCompiledRates, - CellmlFileRuntime::ComputeInterpretedRates pComputeInterpretedRates) override; + const CellmlFileRuntimePtr &pRuntime) override; bool solve(double &pVoi, double pVoiEnd) override; }; diff --git a/src/support/cellml/cellmlfile.cpp b/src/support/cellml/cellmlfile.cpp index 6a477cd68..57a65649f 100644 --- a/src/support/cellml/cellmlfile.cpp +++ b/src/support/cellml/cellmlfile.cpp @@ -55,16 +55,6 @@ CellmlFile::Impl::Impl(const FilePtr &pFile, const libcellml::ModelPtr &pModel, if (mAnalyser->errorCount() != 0) { addIssues(mAnalyser); } - - //---GRY--- WHEN USING OUR JavaScript BINDINGS, TEMPORARILY MAKE SURE THAT THE MODEL IS EITHER AN ALGEBRAIC MODEL OR AN ODE MODEL. - -#ifdef __EMSCRIPTEN__ - if (!hasIssues() - && (mAnalyserModel->type() != libcellml::AnalyserModel::Type::ALGEBRAIC) - && (mAnalyserModel->type() != libcellml::AnalyserModel::Type::ODE)) { - addError("Only CellML files describing either an algebraic model or an ODE model are currently supported."); - } -#endif } void CellmlFile::Impl::populateDocument(const SedDocumentPtr &pDocument) const @@ -122,10 +112,9 @@ libcellml::AnalyserModelPtr CellmlFile::Impl::analyserModel() const return mAnalyserModel; } -CellmlFileRuntimePtr CellmlFile::Impl::runtime(const CellmlFilePtr &pCellmlFile, const SolverNlaPtr &pNlaSolver, - bool pCompiled) +CellmlFileRuntimePtr CellmlFile::Impl::runtime(const CellmlFilePtr &pCellmlFile, const SolverNlaPtr &pNlaSolver) { - return CellmlFileRuntime::create(pCellmlFile, pNlaSolver, pCompiled); + return CellmlFileRuntime::create(pCellmlFile, pNlaSolver); } CellmlFile::CellmlFile(const FilePtr &pFile, const libcellml::ModelPtr &pModel, bool pStrict) @@ -200,9 +189,9 @@ libcellml::AnalyserModelPtr CellmlFile::analyserModel() const return pimpl()->analyserModel(); } -CellmlFileRuntimePtr CellmlFile::runtime(const SolverNlaPtr &pNlaSolver, bool pCompiled) +CellmlFileRuntimePtr CellmlFile::runtime(const SolverNlaPtr &pNlaSolver) { - return CellmlFile::Impl::runtime(shared_from_this(), pNlaSolver, pCompiled); + return CellmlFile::Impl::runtime(shared_from_this(), pNlaSolver); } } // namespace libOpenCOR diff --git a/src/support/cellml/cellmlfile.h b/src/support/cellml/cellmlfile.h index e2e9b7624..cbc4e5a2b 100644 --- a/src/support/cellml/cellmlfile.h +++ b/src/support/cellml/cellmlfile.h @@ -26,7 +26,6 @@ limitations under the License. #include "libcellml/generator.h" #include "libcellml/generatorprofile.h" #include "libcellml/importer.h" -#include "libcellml/interpreter.h" #include "libcellml/model.h" #include "libcellml/parser.h" #include "libcellml/units.h" @@ -64,7 +63,7 @@ class LIBOPENCOR_UNIT_TESTING_EXPORT CellmlFile: public Logger libcellml::AnalyserPtr analyser() const; libcellml::AnalyserModelPtr analyserModel() const; - CellmlFileRuntimePtr runtime(const SolverNlaPtr &pNlaSolver = {}, bool pCompiled = true); + CellmlFileRuntimePtr runtime(const SolverNlaPtr &pNlaSolver = {}); private: class Impl; diff --git a/src/support/cellml/cellmlfile_p.h b/src/support/cellml/cellmlfile_p.h index ec62556ef..34f0c3100 100644 --- a/src/support/cellml/cellmlfile_p.h +++ b/src/support/cellml/cellmlfile_p.h @@ -40,8 +40,7 @@ class CellmlFile::Impl: public Logger::Impl libcellml::AnalyserPtr analyser() const; libcellml::AnalyserModelPtr analyserModel() const; - static CellmlFileRuntimePtr runtime(const CellmlFilePtr &pCellmlFile, const SolverNlaPtr &pNlaSolver, - bool pCompiled); + static CellmlFileRuntimePtr runtime(const CellmlFilePtr &pCellmlFile, const SolverNlaPtr &pNlaSolver); }; } // namespace libOpenCOR diff --git a/src/support/cellml/cellmlfileruntime.cpp b/src/support/cellml/cellmlfileruntime.cpp index 0dd42c377..f00d1cece 100644 --- a/src/support/cellml/cellmlfileruntime.cpp +++ b/src/support/cellml/cellmlfileruntime.cpp @@ -21,7 +21,139 @@ limitations under the License. namespace libOpenCOR { -CellmlFileRuntime::Impl::Impl(const CellmlFilePtr &pCellmlFile, const SolverNlaPtr &pNlaSolver, bool pCompiled) +#ifdef __EMSCRIPTEN__ +// Some utilities. + +namespace { + +std::string exportJavaScriptName(const std::string &pName) +{ + return std::string("__attribute__((export_name(\"").append(pName).append("\")))\n"); +} + +void *instantiateWebAssemblyModule(UnsignedChars pWasmModule, bool pDifferentialModel, bool pIsOdeModel, + bool pIsAlgebraicModel, bool pHasObjectiveFunctions) +{ + // clang-format off + return EM_ASM_PTR({ + try { + // Instantiate the WebAssembly module. + + const wasmBytes = new Uint8Array(HEAPU8.subarray($0, $0 + $1)); + const wasmModule = new WebAssembly.Module(wasmBytes); + const wasmInstance = new WebAssembly.Instance(wasmModule, { + env: { + __linear_memory: wasmMemory, + __indirect_function_table: wasmTable, + + // Some standard C library functions. + + free: _free, + malloc: _malloc, + + // NLA solve function. + + nlaSolve: function(nlaSolverAddress, objectiveFunctionIndex, u, n, data) { + Module.nlaSolve(Number(nlaSolverAddress), Number(objectiveFunctionIndex), Number(u), Number(n), Number(data)); + }, + + // Arithmetic operators. + + pow: Math.pow, + // sqrt() is not needed. + // fabs() is not needed. + exp: Math.exp, + // log() is not needed. + // log10() is not needed. + // ceil() is not needed. + // floor() is not needed. + // fmin() is not needed. + // fmax() is not needed. + // fmod() is not needed. + + // Trigonometric operators. + + sin: Math.sin, + cos: Math.cos, + tan: Math.tan, + sinh: Math.sinh, + cosh: Math.cosh, + tanh: Math.tanh, + asin: Math.asin, + acos: Math.acos, + atan: Math.atan, + asinh: Math.asinh, + acosh: Math.acosh, + atanh: Math.atanh, + + // Constants. + + // INFINITY is not needed. + // NAN is not needed. + } + }); + + // Retrieve the functions needed to compute the model. + + if ($2) { + Module.initialiseVariables = wasmInstance.exports.initialiseVariables; + Module.computeComputedConstants = wasmInstance.exports.computeComputedConstants; + Module.computeRates = wasmInstance.exports.computeRates; + Module.computeVariables = wasmInstance.exports.computeVariables; + + if ((Module.initialiseVariables === undefined) + || (Module.computeComputedConstants === undefined) + || (Module.computeRates === undefined) + || (Module.computeVariables === undefined)) { + throw new Error("The functions needed to compute the " + ($3 ? "ODE" : "DAE") + " model could not be retrieved."); + } + } else { + Module.initialiseVariables = wasmInstance.exports.initialiseVariables; + Module.computeComputedConstants = wasmInstance.exports.computeComputedConstants; + Module.computeVariables = wasmInstance.exports.computeVariables; + + if ((Module.initialiseVariables === undefined) + || (Module.computeComputedConstants === undefined) + || (Module.computeVariables === undefined)) { + throw new Error("The functions needed to compute the " + ($4 ? "algebraic" : "NLA") + " model could not be retrieved."); + } + } + + // Retrieve the objective functions, if any. + + if ($5) { + Module.objectiveFunctions = {}; + + for (let name in wasmInstance.exports) { + if (name.startsWith("objectiveFunction")) { + const objectiveFunctionIndex = parseInt(name.replace("objectiveFunction", "")); + + Module.objectiveFunctions[objectiveFunctionIndex] = wasmInstance.exports[name]; + } + } + + if (Object.keys(Module.objectiveFunctions).length === 0) { + throw new Error("The objective functions could not be retrieved."); + } + } + + return null; + } catch (error) { + const errorMessage = error.toString(); + const errorMessageLength = lengthBytesUTF8(errorMessage) + 1; + const errorMessagePtr = _malloc(errorMessageLength); + + stringToUTF8(errorMessage, errorMessagePtr, errorMessageLength); + + return errorMessagePtr; + } + }, pWasmModule.data(), pWasmModule.size(), pDifferentialModel, pIsOdeModel, pIsAlgebraicModel, pHasObjectiveFunctions); // clang-format on +} + +} // namespace +#endif + +CellmlFileRuntime::Impl::Impl(const CellmlFilePtr &pCellmlFile, const SolverNlaPtr &pNlaSolver) { auto cellmlFileAnalyser = pCellmlFile->analyser(); @@ -34,223 +166,344 @@ CellmlFileRuntime::Impl::Impl(const CellmlFilePtr &pCellmlFile, const SolverNlaP mNlaSolverAddress = nlaSolverAddress(pNlaSolver.get()); } - // Get either a compiled or an interpreted version of the runtime. - -#ifndef __EMSCRIPTEN__ - if (pCompiled) { - // Determine the type of the model. - - auto cellmlFileType = pCellmlFile->type(); - auto differentialModel = (cellmlFileType == libcellml::AnalyserModel::Type::ODE) - || (cellmlFileType == libcellml::AnalyserModel::Type::DAE); - - // Generate some code for the given CellML file. - - auto generator = libcellml::Generator::create(); - auto generatorProfile = libcellml::GeneratorProfile::create(); - - generatorProfile->setOriginCommentString(""); - generatorProfile->setImplementationHeaderString(""); - generatorProfile->setImplementationVersionString(""); - generatorProfile->setImplementationStateCountString(""); - generatorProfile->setImplementationConstantCountString(""); - generatorProfile->setImplementationComputedConstantCountString(""); - generatorProfile->setImplementationAlgebraicCountString(""); - generatorProfile->setImplementationExternalCountString(""); - generatorProfile->setImplementationLibcellmlVersionString(""); - generatorProfile->setImplementationVoiInfoString(""); - generatorProfile->setImplementationStateInfoString(""); - generatorProfile->setImplementationConstantInfoString(""); - generatorProfile->setImplementationComputedConstantInfoString(""); - generatorProfile->setImplementationAlgebraicInfoString(""); - generatorProfile->setImplementationExternalInfoString(""); - generatorProfile->setImplementationCreateStatesArrayMethodString(""); - generatorProfile->setImplementationCreateConstantsArrayMethodString(""); - generatorProfile->setImplementationCreateComputedConstantsArrayMethodString(""); - generatorProfile->setImplementationCreateAlgebraicArrayMethodString(""); - generatorProfile->setImplementationCreateExternalsArrayMethodString(""); - generatorProfile->setImplementationDeleteArrayMethodString(""); - - if (pNlaSolver != nullptr) { - generatorProfile->setExternNlaSolveMethodString("typedef unsigned long long size_t;\n" - "\n" - "extern void nlaSolve(const char *, void (*objectiveFunction)(double *, double *, void *),\n" - " double *u, size_t n, void *data);\n"); - generatorProfile->setNlaSolveCallString(differentialModel, false, - std::string("nlaSolve(\"") + mNlaSolverAddress + "\", objectiveFunction[INDEX], u, [SIZE], &rfi);\n"); + // Determine the type of the model. + + auto cellmlFileType = pCellmlFile->type(); + auto differentialModel = (cellmlFileType == libcellml::AnalyserModel::Type::ODE) + || (cellmlFileType == libcellml::AnalyserModel::Type::DAE); + + // Generate some code for the given CellML file. + + auto generator = libcellml::Generator::create(); + auto generatorProfile = libcellml::GeneratorProfile::create(); + + generatorProfile->setOriginCommentString(""); + generatorProfile->setImplementationHeaderString(""); + generatorProfile->setImplementationVersionString(""); + generatorProfile->setImplementationStateCountString(""); + generatorProfile->setImplementationConstantCountString(""); + generatorProfile->setImplementationComputedConstantCountString(""); + generatorProfile->setImplementationAlgebraicCountString(""); + generatorProfile->setImplementationExternalCountString(""); + generatorProfile->setImplementationLibcellmlVersionString(""); + generatorProfile->setImplementationVoiInfoString(""); + generatorProfile->setImplementationStateInfoString(""); + generatorProfile->setImplementationConstantInfoString(""); + generatorProfile->setImplementationComputedConstantInfoString(""); + generatorProfile->setImplementationAlgebraicInfoString(""); + generatorProfile->setImplementationExternalInfoString(""); + generatorProfile->setImplementationCreateStatesArrayMethodString(""); + generatorProfile->setImplementationCreateConstantsArrayMethodString(""); + generatorProfile->setImplementationCreateComputedConstantsArrayMethodString(""); + generatorProfile->setImplementationCreateAlgebraicArrayMethodString(""); + generatorProfile->setImplementationCreateExternalsArrayMethodString(""); + generatorProfile->setImplementationDeleteArrayMethodString(""); + + static constexpr auto WITH_EXTERNAL_VARIABLES = false; + +#ifdef __EMSCRIPTEN__ + // Allocate the memory needed by our objective functions on the heap rather than on the stack. + + if (pNlaSolver != nullptr) { + if (differentialModel) { + generatorProfile->setFindRootMethodString(differentialModel, WITH_EXTERNAL_VARIABLES, + R"(void findRoot[INDEX](double voi, double *states, double *rates, double *constants, double *computedConstants, double *algebraic) +{ + RootFindingInfo *rfi = (RootFindingInfo *) malloc(sizeof(RootFindingInfo)); + double *u = (double *) malloc([SIZE] * sizeof(double)); + + rfi->voi = voi; + rfi->states = states; + rfi->rates = rates; + rfi->constants = constants; + rfi->computedConstants = computedConstants; + rfi->algebraic = algebraic; + +[CODE] + + free(u); + free(rfi); +} +)"); + } else { + generatorProfile->setFindRootMethodString(differentialModel, WITH_EXTERNAL_VARIABLES, + R"(void findRoot[INDEX](double *constants, double *computedConstants, double *algebraic) +{ + RootFindingInfo *rfi = (RootFindingInfo *) malloc(sizeof(RootFindingInfo)); + double *u = (double *) malloc([SIZE] * sizeof(double)); + + rfi->constants = constants; + rfi->computedConstants = computedConstants; + rfi->algebraic = algebraic; + +[CODE] + + free(u); + free(rfi); +} +)"); } + } + + // Export our various methods. + + generatorProfile->setImplementationInitialiseVariablesMethodString(differentialModel, + exportJavaScriptName("initialiseVariables").append(generatorProfile->implementationInitialiseVariablesMethodString(differentialModel))); + generatorProfile->setImplementationComputeComputedConstantsMethodString(exportJavaScriptName("computeComputedConstants").append(generatorProfile->implementationComputeComputedConstantsMethodString())); + generatorProfile->setImplementationComputeRatesMethodString(WITH_EXTERNAL_VARIABLES, + exportJavaScriptName("computeRates").append(generatorProfile->implementationComputeRatesMethodString(WITH_EXTERNAL_VARIABLES))); + generatorProfile->setImplementationComputeVariablesMethodString(differentialModel, WITH_EXTERNAL_VARIABLES, + exportJavaScriptName("computeVariables").append(generatorProfile->implementationComputeVariablesMethodString(differentialModel, WITH_EXTERNAL_VARIABLES))); +#endif - generator->setModel(pCellmlFile->analyserModel()); - generator->setProfile(generatorProfile); + if (pNlaSolver != nullptr) { + // Note: both uintptr_t and size_t are defined as follows: + // - Emscripten (wasm32): unsigned int (which is the same as unsigned long on 32 bits and is what we + // need to use here since malloc() expects an unsigned long); + // - Windows (64 bits): unsigned long long; and + // - Linux/macOS (64 bits): unsigned long. + +#ifdef __EMSCRIPTEN__ + generatorProfile->setExternNlaSolveMethodString(R"(typedef unsigned long uintptr_t; +typedef unsigned long size_t; + +extern void *malloc(size_t size); +extern void free(void *ptr); + +extern void nlaSolve(uintptr_t nlaSolverAddress, size_t objectiveFunctionIndex, uintptr_t u, size_t n, uintptr_t data); +)"); + generatorProfile->setNlaSolveCallString(differentialModel, WITH_EXTERNAL_VARIABLES, + std::string("nlaSolve(").append(mNlaSolverAddress).append(", [INDEX], (uintptr_t) u, [SIZE], (uintptr_t) rfi);\n")); +#else +# ifdef BUILDING_USING_MSVC + generatorProfile->setExternNlaSolveMethodString(R"(typedef unsigned long long uintptr_t; +typedef unsigned long long size_t; + +extern void nlaSolve(uintptr_t nlaSolverAddress, void (*objectiveFunction)(double *, double *, void *), + double *u, size_t n, void *data); +)"); +# else + generatorProfile->setExternNlaSolveMethodString(R"(typedef unsigned long uintptr_t; +typedef unsigned long size_t; + +extern void nlaSolve(uintptr_t nlaSolverAddress, void (*objectiveFunction)(double *, double *, void *), + double *u, size_t n, void *data); +)"); +# endif + generatorProfile->setNlaSolveCallString(differentialModel, WITH_EXTERNAL_VARIABLES, + std::string("nlaSolve(").append(mNlaSolverAddress).append(", objectiveFunction[INDEX], u, [SIZE], &rfi);\n")); +#endif + } + + generator->setModel(pCellmlFile->analyserModel()); + generator->setProfile(generatorProfile); + +#ifdef __EMSCRIPTEN__ + // Export our various objective functions. + + auto implementationCode = generator->implementationCode(); + + if (pNlaSolver != nullptr) { + std::vector handledNlaSystemIndices; - // Compile the generated code. + for (const auto &equation : pCellmlFile->analyserModel()->equations()) { + if (equation->type() == libcellml::AnalyserEquation::Type::NLA) { + auto nlaSystemIndex = equation->nlaSystemIndex(); - mCompiler = Compiler::create(); + if (std::find(handledNlaSystemIndices.begin(), handledNlaSystemIndices.end(), nlaSystemIndex) == handledNlaSystemIndices.end()) { + auto objectiveFunctionName = std::string("objectiveFunction").append(std::to_string(nlaSystemIndex)); + implementationCode.insert(implementationCode.find("void " + objectiveFunctionName), + exportJavaScriptName(objectiveFunctionName)); + + handledNlaSystemIndices.push_back(nlaSystemIndex); + } + } + } + } +#endif + + // Compile the generated code. + + mCompiler = Compiler::create(); + +#ifdef __EMSCRIPTEN__ + if (!mCompiler->compile(implementationCode, mWasmModule)) { + // The compilation failed, so add the issues it generated. + + addIssues(mCompiler); + + return; + } + + // Instantiate the WebAssembly module. + + auto errorMessagePtr = instantiateWebAssemblyModule(mWasmModule, differentialModel, + cellmlFileType == libcellml::AnalyserModel::Type::ODE, + cellmlFileType == libcellml::AnalyserModel::Type::ALGEBRAIC, + pNlaSolver != nullptr); + + if (errorMessagePtr != nullptr) { + std::string jsErrorMessage(reinterpret_cast(errorMessagePtr)); + + free(errorMessagePtr); + + addError(std::string("The WebAssembly module could not be instantiated (").append(jsErrorMessage).append(").")); + + return; + } +#else # ifdef CODE_COVERAGE_ENABLED - mCompiler->compile(generator->implementationCode()); + mCompiler->compile(generator->implementationCode()); # else - if (!mCompiler->compile(generator->implementationCode())) { - // The compilation failed, so add the issues it generated. + if (!mCompiler->compile(generator->implementationCode())) { + // The compilation failed, so add the issues it generated. - addIssues(mCompiler); + addIssues(mCompiler); - return; - } + return; + } # endif - // Make sure that our compiler knows about nlaSolve(), if needed. + // Make sure that our compiler knows about nlaSolve(), if needed. - if ((cellmlFileType == libcellml::AnalyserModel::Type::NLA) - || (cellmlFileType == libcellml::AnalyserModel::Type::DAE)) { + if ((cellmlFileType == libcellml::AnalyserModel::Type::NLA) + || (cellmlFileType == libcellml::AnalyserModel::Type::DAE)) { # ifndef CODE_COVERAGE_ENABLED - const bool functionAdded = + const bool functionAdded = # endif - mCompiler->addFunction("nlaSolve", reinterpret_cast(nlaSolve)); + mCompiler->addFunction("nlaSolve", reinterpret_cast(nlaSolve)); # ifndef CODE_COVERAGE_ENABLED - if (!functionAdded) { - addIssues(mCompiler); + if (!functionAdded) { + addIssues(mCompiler); - return; - } -# endif + return; } +# endif + } - // Retrieve our algebraic/differential functions and make sure that we managed to retrieve them. + // Retrieve our algebraic/differential functions and make sure that we managed to retrieve them. - if (differentialModel) { - mInitialiseCompiledVariablesForDifferentialModel = reinterpret_cast(mCompiler->function("initialiseVariables")); - mComputeCompiledComputedConstants = reinterpret_cast(mCompiler->function("computeComputedConstants")); - mComputeCompiledRates = reinterpret_cast(mCompiler->function("computeRates")); - mComputeCompiledVariablesForDifferentialModel = reinterpret_cast(mCompiler->function("computeVariables")); + if (differentialModel) { + mInitialiseVariablesForDifferentialModel = reinterpret_cast(mCompiler->function("initialiseVariables")); + mComputeComputedConstants = reinterpret_cast(mCompiler->function("computeComputedConstants")); + mComputeRates = reinterpret_cast(mCompiler->function("computeRates")); + mComputeVariablesForDifferentialModel = reinterpret_cast(mCompiler->function("computeVariables")); # ifndef CODE_COVERAGE_ENABLED - if ((mInitialiseCompiledVariablesForDifferentialModel == nullptr) - || (mComputeCompiledComputedConstants == nullptr) - || (mComputeCompiledRates == nullptr) - || (mComputeCompiledVariablesForDifferentialModel == nullptr)) { - if (cellmlFileType == libcellml::AnalyserModel::Type::ODE) { - addError("The functions needed to compute the ODE model could not be retrieved."); - } else { - addError("The functions needed to compute the DAE model could not be retrieved."); - } - } + if ((mInitialiseVariablesForDifferentialModel == nullptr) + || (mComputeComputedConstants == nullptr) + || (mComputeRates == nullptr) + || (mComputeVariablesForDifferentialModel == nullptr)) { + addError(std::string("The functions needed to compute the ").append((cellmlFileType == libcellml::AnalyserModel::Type::ODE) ? "ODE" : "DAE").append(" model could not be retrieved.")); + } # endif - } else { - mInitialiseCompiledVariablesForAlgebraicModel = reinterpret_cast(mCompiler->function("initialiseVariables")); - mComputeCompiledComputedConstants = reinterpret_cast(mCompiler->function("computeComputedConstants")); - mComputeCompiledVariablesForAlgebraicModel = reinterpret_cast(mCompiler->function("computeVariables")); + } else { + mInitialiseVariablesForAlgebraicModel = reinterpret_cast(mCompiler->function("initialiseVariables")); + mComputeComputedConstants = reinterpret_cast(mCompiler->function("computeComputedConstants")); + mComputeVariablesForAlgebraicModel = reinterpret_cast(mCompiler->function("computeVariables")); # ifndef CODE_COVERAGE_ENABLED - if ((mInitialiseCompiledVariablesForAlgebraicModel == nullptr) - || (mComputeCompiledComputedConstants == nullptr) - || (mComputeCompiledVariablesForAlgebraicModel == nullptr)) { - if (cellmlFileType == libcellml::AnalyserModel::Type::ALGEBRAIC) { - addError("The functions needed to compute the algebraic model could not be retrieved."); - } else { - addError("The functions needed to compute the NLA model could not be retrieved."); - } - } -# endif + if ((mInitialiseVariablesForAlgebraicModel == nullptr) + || (mComputeComputedConstants == nullptr) + || (mComputeVariablesForAlgebraicModel == nullptr)) { + addError(std::string("The functions needed to compute the ").append((cellmlFileType == libcellml::AnalyserModel::Type::ALGEBRAIC) ? "algebraic" : "NLA").append(" model could not be retrieved.")); } - } else { -#endif - auto interpreter = libcellml::Interpreter::create(); - - interpreter->setModel(pCellmlFile->analyserModel()); - - mInitialiseInterpretedVariablesForAlgebraicModel = InitialiseInterpretedVariablesForAlgebraicModel([interpreter](double *pConstants, double *pComputedConstants, double *pAlgebraic) { - interpreter->initialiseVariables(pConstants, pComputedConstants, pAlgebraic); - }); - mInitialiseInterpretedVariablesForDifferentialModel = InitialiseInterpretedVariablesForDifferentialModel([interpreter](double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic) { - interpreter->initialiseVariables(pStates, pRates, pConstants, pComputedConstants, pAlgebraic); - }); - mComputeInterpretedComputedConstants = ComputeInterpretedComputedConstants([interpreter](double *pConstants, double *pComputedConstants) { - interpreter->computeComputedConstants(pConstants, pComputedConstants); - }); - mComputeInterpretedRates = ComputeInterpretedRates([interpreter](double pVoi, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic) { - interpreter->computeRates(pVoi, pStates, pRates, pConstants, pComputedConstants, pAlgebraic); - }); - mComputeInterpretedVariablesForAlgebraicModel = ComputeInterpretedVariablesForAlgebraicModel([interpreter](double *pConstants, double *pComputedConstants, double *pAlgebraic) { - interpreter->computeVariables(pConstants, pComputedConstants, pAlgebraic); - }); - mComputeInterpretedVariablesForDifferentialModel = ComputeInterpretedVariablesForDifferentialModel([interpreter](double pVoi, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic) { - interpreter->computeVariables(pVoi, pStates, pRates, pConstants, pComputedConstants, pAlgebraic); - }); -#ifndef __EMSCRIPTEN__ +# endif } #endif } } -CellmlFileRuntime::Impl::~Impl() -{ - delete[] mNlaSolverAddress; -} +#ifdef __EMSCRIPTEN__ +EM_JS(void, jsInitialiseVariablesForAlgebraicModel, (double *pConstants, double *pComputedConstants, double *pAlgebraic), { + Module.initialiseVariables(pConstants, pComputedConstants, pAlgebraic); +}); -CellmlFileRuntime::InitialiseCompiledVariablesForAlgebraicModel CellmlFileRuntime::Impl::initialiseCompiledVariablesForAlgebraicModel() const +void CellmlFileRuntime::Impl::initialiseVariablesForAlgebraicModel(double *pConstants, double *pComputedConstants, double *pAlgebraic) const { - return mInitialiseCompiledVariablesForAlgebraicModel; + jsInitialiseVariablesForAlgebraicModel(pConstants, pComputedConstants, pAlgebraic); } -CellmlFileRuntime::InitialiseCompiledVariablesForDifferentialModel CellmlFileRuntime::Impl::initialiseCompiledVariablesForDifferentialModel() const +EM_JS(void, jsInitialiseVariablesForDifferentialModel, (double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic), { + Module.initialiseVariables(pStates, pRates, pConstants, pComputedConstants, pAlgebraic); +}); + +void CellmlFileRuntime::Impl::initialiseVariablesForDifferentialModel(double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic) const { - return mInitialiseCompiledVariablesForDifferentialModel; + jsInitialiseVariablesForDifferentialModel(pStates, pRates, pConstants, pComputedConstants, pAlgebraic); } -CellmlFileRuntime::ComputeCompiledComputedConstants CellmlFileRuntime::Impl::computeCompiledComputedConstants() const +EM_JS(void, jsComputeComputedConstants, (double *pConstants, double *pComputedConstants), { + Module.computeComputedConstants(pConstants, pComputedConstants); +}); + +void CellmlFileRuntime::Impl::computeComputedConstants(double *pConstants, double *pComputedConstants) const { - return mComputeCompiledComputedConstants; + jsComputeComputedConstants(pConstants, pComputedConstants); } -CellmlFileRuntime::ComputeCompiledRates CellmlFileRuntime::Impl::computeCompiledRates() const +EM_JS(void, jsComputeRates, (double pVoi, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic), { + Module.computeRates(pVoi, pStates, pRates, pConstants, pComputedConstants, pAlgebraic); +}); + +void CellmlFileRuntime::Impl::computeRates(double pVoi, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic) const { - return mComputeCompiledRates; + jsComputeRates(pVoi, pStates, pRates, pConstants, pComputedConstants, pAlgebraic); } -CellmlFileRuntime::ComputeCompiledVariablesForAlgebraicModel CellmlFileRuntime::Impl::computeCompiledVariablesForAlgebraicModel() const +EM_JS(void, jsComputeVariablesForAlgebraicModel, (double *pConstants, double *pComputedConstants, double *pAlgebraic), { + Module.computeVariables(pConstants, pComputedConstants, pAlgebraic); +}); + +void CellmlFileRuntime::Impl::computeVariablesForAlgebraicModel(double *pConstants, double *pComputedConstants, double *pAlgebraic) const { - return mComputeCompiledVariablesForAlgebraicModel; + jsComputeVariablesForAlgebraicModel(pConstants, pComputedConstants, pAlgebraic); } -CellmlFileRuntime::ComputeCompiledVariablesForDifferentialModel CellmlFileRuntime::Impl::computeCompiledVariablesForDifferentialModel() const +EM_JS(void, jsComputeVariablesForDifferentialModel, (double pVoi, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic), { + Module.computeVariables(pVoi, pStates, pRates, pConstants, pComputedConstants, pAlgebraic); +}); + +void CellmlFileRuntime::Impl::computeVariablesForDifferentialModel(double pVoi, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic) const { - return mComputeCompiledVariablesForDifferentialModel; + jsComputeVariablesForDifferentialModel(pVoi, pStates, pRates, pConstants, pComputedConstants, pAlgebraic); } - -CellmlFileRuntime::InitialiseInterpretedVariablesForAlgebraicModel CellmlFileRuntime::Impl::initialiseInterpretedVariablesForAlgebraicModel() const +#else +CellmlFileRuntime::InitialiseVariablesForAlgebraicModel CellmlFileRuntime::Impl::initialiseVariablesForAlgebraicModel() const { - return mInitialiseInterpretedVariablesForAlgebraicModel; + return mInitialiseVariablesForAlgebraicModel; } -CellmlFileRuntime::InitialiseInterpretedVariablesForDifferentialModel CellmlFileRuntime::Impl::initialiseInterpretedVariablesForDifferentialModel() const +CellmlFileRuntime::InitialiseVariablesForDifferentialModel CellmlFileRuntime::Impl::initialiseVariablesForDifferentialModel() const { - return mInitialiseInterpretedVariablesForDifferentialModel; + return mInitialiseVariablesForDifferentialModel; } -CellmlFileRuntime::ComputeInterpretedComputedConstants CellmlFileRuntime::Impl::computeInterpretedComputedConstants() const +CellmlFileRuntime::ComputeComputedConstants CellmlFileRuntime::Impl::computeComputedConstants() const { - return mComputeInterpretedComputedConstants; + return mComputeComputedConstants; } -CellmlFileRuntime::ComputeInterpretedRates CellmlFileRuntime::Impl::computeInterpretedRates() const +CellmlFileRuntime::ComputeRates CellmlFileRuntime::Impl::computeRates() const { - return mComputeInterpretedRates; + return mComputeRates; } -CellmlFileRuntime::ComputeInterpretedVariablesForAlgebraicModel CellmlFileRuntime::Impl::computeInterpretedVariablesForAlgebraicModel() const +CellmlFileRuntime::ComputeVariablesForAlgebraicModel CellmlFileRuntime::Impl::computeVariablesForAlgebraicModel() const { - return mComputeInterpretedVariablesForAlgebraicModel; + return mComputeVariablesForAlgebraicModel; } -CellmlFileRuntime::ComputeInterpretedVariablesForDifferentialModel CellmlFileRuntime::Impl::computeInterpretedVariablesForDifferentialModel() const +CellmlFileRuntime::ComputeVariablesForDifferentialModel CellmlFileRuntime::Impl::computeVariablesForDifferentialModel() const { - return mComputeInterpretedVariablesForDifferentialModel; + return mComputeVariablesForDifferentialModel; } +#endif -CellmlFileRuntime::CellmlFileRuntime(const CellmlFilePtr &pCellmlFile, const SolverNlaPtr &pNlaSolver, bool pCompiled) - : Logger(new Impl {pCellmlFile, pNlaSolver, pCompiled}) +CellmlFileRuntime::CellmlFileRuntime(const CellmlFilePtr &pCellmlFile, const SolverNlaPtr &pNlaSolver) + : Logger(new Impl {pCellmlFile, pNlaSolver}) { } @@ -269,70 +522,71 @@ const CellmlFileRuntime::Impl *CellmlFileRuntime::pimpl() const return static_cast(Logger::mPimpl); } -CellmlFileRuntimePtr CellmlFileRuntime::create(const CellmlFilePtr &pCellmlFile, const SolverNlaPtr &pNlaSolver, - bool pCompiled) +CellmlFileRuntimePtr CellmlFileRuntime::create(const CellmlFilePtr &pCellmlFile, const SolverNlaPtr &pNlaSolver) { - return CellmlFileRuntimePtr {new CellmlFileRuntime {pCellmlFile, pNlaSolver, pCompiled}}; + return CellmlFileRuntimePtr {new CellmlFileRuntime {pCellmlFile, pNlaSolver}}; } -CellmlFileRuntime::InitialiseCompiledVariablesForAlgebraicModel CellmlFileRuntime::initialiseCompiledVariablesForAlgebraicModel() const +#ifdef __EMSCRIPTEN__ +void CellmlFileRuntime::initialiseVariablesForAlgebraicModel(double *pConstants, double *pComputedConstants, double *pAlgebraic) const { - return pimpl()->initialiseCompiledVariablesForAlgebraicModel(); + pimpl()->initialiseVariablesForAlgebraicModel(pConstants, pComputedConstants, pAlgebraic); } -CellmlFileRuntime::InitialiseCompiledVariablesForDifferentialModel CellmlFileRuntime::initialiseCompiledVariablesForDifferentialModel() const +void CellmlFileRuntime::initialiseVariablesForDifferentialModel(double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic) const { - return pimpl()->initialiseCompiledVariablesForDifferentialModel(); + pimpl()->initialiseVariablesForDifferentialModel(pStates, pRates, pConstants, pComputedConstants, pAlgebraic); } -CellmlFileRuntime::ComputeCompiledComputedConstants CellmlFileRuntime::computeCompiledComputedConstants() const +void CellmlFileRuntime::computeComputedConstants(double *pConstants, double *pComputedConstants) const { - return pimpl()->computeCompiledComputedConstants(); + pimpl()->computeComputedConstants(pConstants, pComputedConstants); } -CellmlFileRuntime::ComputeCompiledRates CellmlFileRuntime::computeCompiledRates() const +void CellmlFileRuntime::computeRates(double pVoi, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic) const { - return pimpl()->computeCompiledRates(); + pimpl()->computeRates(pVoi, pStates, pRates, pConstants, pComputedConstants, pAlgebraic); } -CellmlFileRuntime::ComputeCompiledVariablesForAlgebraicModel CellmlFileRuntime::computeCompiledVariablesForAlgebraicModel() const +void CellmlFileRuntime::computeVariablesForAlgebraicModel(double *pConstants, double *pComputedConstants, double *pAlgebraic) const { - return pimpl()->computeCompiledVariablesForAlgebraicModel(); + pimpl()->computeVariablesForAlgebraicModel(pConstants, pComputedConstants, pAlgebraic); } -CellmlFileRuntime::ComputeCompiledVariablesForDifferentialModel CellmlFileRuntime::computeCompiledVariablesForDifferentialModel() const +void CellmlFileRuntime::computeVariablesForDifferentialModel(double pVoi, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic) const { - return pimpl()->computeCompiledVariablesForDifferentialModel(); + pimpl()->computeVariablesForDifferentialModel(pVoi, pStates, pRates, pConstants, pComputedConstants, pAlgebraic); } - -CellmlFileRuntime::InitialiseInterpretedVariablesForAlgebraicModel CellmlFileRuntime::initialiseInterpretedVariablesForAlgebraicModel() const +#else +CellmlFileRuntime::InitialiseVariablesForAlgebraicModel CellmlFileRuntime::initialiseVariablesForAlgebraicModel() const { - return pimpl()->initialiseInterpretedVariablesForAlgebraicModel(); + return pimpl()->initialiseVariablesForAlgebraicModel(); } -CellmlFileRuntime::InitialiseInterpretedVariablesForDifferentialModel CellmlFileRuntime::initialiseInterpretedVariablesForDifferentialModel() const +CellmlFileRuntime::InitialiseVariablesForDifferentialModel CellmlFileRuntime::initialiseVariablesForDifferentialModel() const { - return pimpl()->initialiseInterpretedVariablesForDifferentialModel(); + return pimpl()->initialiseVariablesForDifferentialModel(); } -CellmlFileRuntime::ComputeInterpretedComputedConstants CellmlFileRuntime::computeInterpretedComputedConstants() const +CellmlFileRuntime::ComputeComputedConstants CellmlFileRuntime::computeComputedConstants() const { - return pimpl()->computeInterpretedComputedConstants(); + return pimpl()->computeComputedConstants(); } -CellmlFileRuntime::ComputeInterpretedRates CellmlFileRuntime::computeInterpretedRates() const +CellmlFileRuntime::ComputeRates CellmlFileRuntime::computeRates() const { - return pimpl()->computeInterpretedRates(); + return pimpl()->computeRates(); } -CellmlFileRuntime::ComputeInterpretedVariablesForAlgebraicModel CellmlFileRuntime::computeInterpretedVariablesForAlgebraicModel() const +CellmlFileRuntime::ComputeVariablesForAlgebraicModel CellmlFileRuntime::computeVariablesForAlgebraicModel() const { - return pimpl()->computeInterpretedVariablesForAlgebraicModel(); + return pimpl()->computeVariablesForAlgebraicModel(); } -CellmlFileRuntime::ComputeInterpretedVariablesForDifferentialModel CellmlFileRuntime::computeInterpretedVariablesForDifferentialModel() const +CellmlFileRuntime::ComputeVariablesForDifferentialModel CellmlFileRuntime::computeVariablesForDifferentialModel() const { - return pimpl()->computeInterpretedVariablesForDifferentialModel(); + return pimpl()->computeVariablesForDifferentialModel(); } +#endif } // namespace libOpenCOR diff --git a/src/support/cellml/cellmlfileruntime.h b/src/support/cellml/cellmlfileruntime.h index 954897c47..f8372e52f 100644 --- a/src/support/cellml/cellmlfileruntime.h +++ b/src/support/cellml/cellmlfileruntime.h @@ -31,19 +31,14 @@ using CellmlFileRuntimePtr = std::shared_ptr; class CellmlFileRuntime: public Logger { public: - using InitialiseCompiledVariablesForAlgebraicModel = void (*)(double *pConstants, double *pComputedConstants, double *pAlgebraic); - using InitialiseCompiledVariablesForDifferentialModel = void (*)(double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic); - using ComputeCompiledComputedConstants = void (*)(double *pConstants, double *pComputedConstants); - using ComputeCompiledRates = void (*)(double pVoi, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic); - using ComputeCompiledVariablesForAlgebraicModel = void (*)(double *pConstants, double *pComputedConstants, double *pAlgebraic); - using ComputeCompiledVariablesForDifferentialModel = void (*)(double pVoi, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic); - - using InitialiseInterpretedVariablesForAlgebraicModel = std::function; - using InitialiseInterpretedVariablesForDifferentialModel = std::function; - using ComputeInterpretedComputedConstants = std::function; - using ComputeInterpretedRates = std::function; - using ComputeInterpretedVariablesForAlgebraicModel = std::function; - using ComputeInterpretedVariablesForDifferentialModel = std::function; +#ifndef __EMSCRIPTEN__ + using InitialiseVariablesForAlgebraicModel = void (*)(double *pConstants, double *pComputedConstants, double *pAlgebraic); + using InitialiseVariablesForDifferentialModel = void (*)(double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic); + using ComputeComputedConstants = void (*)(double *pConstants, double *pComputedConstants); + using ComputeRates = void (*)(double pVoi, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic); + using ComputeVariablesForAlgebraicModel = void (*)(double *pConstants, double *pComputedConstants, double *pAlgebraic); + using ComputeVariablesForDifferentialModel = void (*)(double pVoi, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic); +#endif CellmlFileRuntime() = delete; ~CellmlFileRuntime() override; @@ -54,27 +49,28 @@ class CellmlFileRuntime: public Logger CellmlFileRuntime &operator=(const CellmlFileRuntime &pRhs) = delete; CellmlFileRuntime &operator=(CellmlFileRuntime &&pRhs) noexcept = delete; - static CellmlFileRuntimePtr create(const CellmlFilePtr &pCellmlFile, const SolverNlaPtr &pNlaSolver, - bool pCompiled); - - InitialiseCompiledVariablesForAlgebraicModel initialiseCompiledVariablesForAlgebraicModel() const; - InitialiseCompiledVariablesForDifferentialModel initialiseCompiledVariablesForDifferentialModel() const; - ComputeCompiledComputedConstants computeCompiledComputedConstants() const; - ComputeCompiledRates computeCompiledRates() const; - ComputeCompiledVariablesForAlgebraicModel computeCompiledVariablesForAlgebraicModel() const; - ComputeCompiledVariablesForDifferentialModel computeCompiledVariablesForDifferentialModel() const; - - InitialiseInterpretedVariablesForAlgebraicModel initialiseInterpretedVariablesForAlgebraicModel() const; - InitialiseInterpretedVariablesForDifferentialModel initialiseInterpretedVariablesForDifferentialModel() const; - ComputeInterpretedComputedConstants computeInterpretedComputedConstants() const; - ComputeInterpretedRates computeInterpretedRates() const; - ComputeInterpretedVariablesForAlgebraicModel computeInterpretedVariablesForAlgebraicModel() const; - ComputeInterpretedVariablesForDifferentialModel computeInterpretedVariablesForDifferentialModel() const; + static CellmlFileRuntimePtr create(const CellmlFilePtr &pCellmlFile, const SolverNlaPtr &pNlaSolver); + +#ifdef __EMSCRIPTEN__ + void initialiseVariablesForAlgebraicModel(double *pConstants, double *pComputedConstants, double *pAlgebraic) const; + void initialiseVariablesForDifferentialModel(double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic) const; + void computeComputedConstants(double *pConstants, double *pComputedConstants) const; + void computeRates(double pVoi, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic) const; + void computeVariablesForAlgebraicModel(double *pConstants, double *pComputedConstants, double *pAlgebraic) const; + void computeVariablesForDifferentialModel(double pVoi, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic) const; +#else + InitialiseVariablesForAlgebraicModel initialiseVariablesForAlgebraicModel() const; + InitialiseVariablesForDifferentialModel initialiseVariablesForDifferentialModel() const; + ComputeComputedConstants computeComputedConstants() const; + ComputeRates computeRates() const; + ComputeVariablesForAlgebraicModel computeVariablesForAlgebraicModel() const; + ComputeVariablesForDifferentialModel computeVariablesForDifferentialModel() const; +#endif private: class Impl; - explicit CellmlFileRuntime(const CellmlFilePtr &pCellmlFile, const SolverNlaPtr &pNlaSolver, bool pCompiled); + explicit CellmlFileRuntime(const CellmlFilePtr &pCellmlFile, const SolverNlaPtr &pNlaSolver); Impl *pimpl(); const Impl *pimpl() const; diff --git a/src/support/cellml/cellmlfileruntime_p.h b/src/support/cellml/cellmlfileruntime_p.h index bc69869b6..7cda231c3 100644 --- a/src/support/cellml/cellmlfileruntime_p.h +++ b/src/support/cellml/cellmlfileruntime_p.h @@ -26,42 +26,38 @@ namespace libOpenCOR { class CellmlFileRuntime::Impl: public Logger::Impl { public: -#ifndef __EMSCRIPTEN__ CompilerPtr mCompiler = nullptr; + std::string mNlaSolverAddress; +#ifdef __EMSCRIPTEN__ + UnsignedChars mWasmModule; #endif - char *mNlaSolverAddress = nullptr; - - InitialiseCompiledVariablesForAlgebraicModel mInitialiseCompiledVariablesForAlgebraicModel = nullptr; - InitialiseCompiledVariablesForDifferentialModel mInitialiseCompiledVariablesForDifferentialModel = nullptr; - ComputeCompiledComputedConstants mComputeCompiledComputedConstants = nullptr; - ComputeCompiledRates mComputeCompiledRates = nullptr; - ComputeCompiledVariablesForAlgebraicModel mComputeCompiledVariablesForAlgebraicModel = nullptr; - ComputeCompiledVariablesForDifferentialModel mComputeCompiledVariablesForDifferentialModel = nullptr; - - InitialiseInterpretedVariablesForAlgebraicModel mInitialiseInterpretedVariablesForAlgebraicModel = nullptr; - InitialiseInterpretedVariablesForDifferentialModel mInitialiseInterpretedVariablesForDifferentialModel = nullptr; - ComputeInterpretedComputedConstants mComputeInterpretedComputedConstants = nullptr; - ComputeInterpretedRates mComputeInterpretedRates = nullptr; - ComputeInterpretedVariablesForAlgebraicModel mComputeInterpretedVariablesForAlgebraicModel = nullptr; - ComputeInterpretedVariablesForDifferentialModel mComputeInterpretedVariablesForDifferentialModel = nullptr; - - explicit Impl(const CellmlFilePtr &pCellmlFile, const SolverNlaPtr &pNlaSolver, bool pCompiled); - ~Impl() override; - - CellmlFileRuntime::InitialiseCompiledVariablesForAlgebraicModel initialiseCompiledVariablesForAlgebraicModel() const; - CellmlFileRuntime::InitialiseCompiledVariablesForDifferentialModel initialiseCompiledVariablesForDifferentialModel() const; - CellmlFileRuntime::ComputeCompiledComputedConstants computeCompiledComputedConstants() const; - CellmlFileRuntime::ComputeCompiledRates computeCompiledRates() const; - CellmlFileRuntime::ComputeCompiledVariablesForAlgebraicModel computeCompiledVariablesForAlgebraicModel() const; - CellmlFileRuntime::ComputeCompiledVariablesForDifferentialModel computeCompiledVariablesForDifferentialModel() const; +#ifndef __EMSCRIPTEN__ + InitialiseVariablesForAlgebraicModel mInitialiseVariablesForAlgebraicModel = nullptr; + InitialiseVariablesForDifferentialModel mInitialiseVariablesForDifferentialModel = nullptr; + ComputeComputedConstants mComputeComputedConstants = nullptr; + ComputeRates mComputeRates = nullptr; + ComputeVariablesForAlgebraicModel mComputeVariablesForAlgebraicModel = nullptr; + ComputeVariablesForDifferentialModel mComputeVariablesForDifferentialModel = nullptr; +#endif - CellmlFileRuntime::InitialiseInterpretedVariablesForAlgebraicModel initialiseInterpretedVariablesForAlgebraicModel() const; - CellmlFileRuntime::InitialiseInterpretedVariablesForDifferentialModel initialiseInterpretedVariablesForDifferentialModel() const; - CellmlFileRuntime::ComputeInterpretedComputedConstants computeInterpretedComputedConstants() const; - CellmlFileRuntime::ComputeInterpretedRates computeInterpretedRates() const; - CellmlFileRuntime::ComputeInterpretedVariablesForAlgebraicModel computeInterpretedVariablesForAlgebraicModel() const; - CellmlFileRuntime::ComputeInterpretedVariablesForDifferentialModel computeInterpretedVariablesForDifferentialModel() const; + explicit Impl(const CellmlFilePtr &pCellmlFile, const SolverNlaPtr &pNlaSolver); + +#ifdef __EMSCRIPTEN__ + void initialiseVariablesForAlgebraicModel(double *pConstants, double *pComputedConstants, double *pAlgebraic) const; + void initialiseVariablesForDifferentialModel(double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic) const; + void computeComputedConstants(double *pConstants, double *pComputedConstants) const; + void computeRates(double pVoi, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic) const; + void computeVariablesForAlgebraicModel(double *pConstants, double *pComputedConstants, double *pAlgebraic) const; + void computeVariablesForDifferentialModel(double pVoi, double *pStates, double *pRates, double *pConstants, double *pComputedConstants, double *pAlgebraic) const; +#else + CellmlFileRuntime::InitialiseVariablesForAlgebraicModel initialiseVariablesForAlgebraicModel() const; + CellmlFileRuntime::InitialiseVariablesForDifferentialModel initialiseVariablesForDifferentialModel() const; + CellmlFileRuntime::ComputeComputedConstants computeComputedConstants() const; + CellmlFileRuntime::ComputeRates computeRates() const; + CellmlFileRuntime::ComputeVariablesForAlgebraicModel computeVariablesForAlgebraicModel() const; + CellmlFileRuntime::ComputeVariablesForDifferentialModel computeVariablesForDifferentialModel() const; +#endif }; } // namespace libOpenCOR diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8eac5a613..dd9226ba5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -123,6 +123,7 @@ string(REPLACE "\"" "\\\"" SOLVER_NLA2_CONTENTS "${SOLVER_NLA2_CONTENTS}") file(READ res/api/sed/algebraic.cellml ALGEBRAIC_CONTENTS) file(READ res/api/sed/dae.cellml DAE_CONTENTS) +file(READ res/api/sed/math.cellml MATH_CONTENTS) file(READ res/api/sed/nla.cellml NLA_CONTENTS) file(READ res/api/sed/overconstrained.cellml OVERCONSTRAINED_CONTENTS) file(READ res/api/sed/underconstrained.cellml UNDERCONSTRAINED_CONTENTS) @@ -130,6 +131,7 @@ file(READ res/api/sed/unsuitably_constrained.cellml UNSUITABLY_CONSTRAINED_CONTE string(REPLACE "\"" "\\\"" ALGEBRAIC_CONTENTS "${ALGEBRAIC_CONTENTS}") string(REPLACE "\"" "\\\"" DAE_CONTENTS "${DAE_CONTENTS}") +string(REPLACE "\"" "\\\"" MATH_CONTENTS "${MATH_CONTENTS}") string(REPLACE "\"" "\\\"" NLA_CONTENTS "${NLA_CONTENTS}") string(REPLACE "\"" "\\\"" OVERCONSTRAINED_CONTENTS "${OVERCONSTRAINED_CONTENTS}") string(REPLACE "\"" "\\\"" UNDERCONSTRAINED_CONTENTS "${UNDERCONSTRAINED_CONTENTS}") diff --git a/tests/api/sed/coveragetests.cpp b/tests/api/sed/coveragetests.cpp index 408aab0df..39f829c17 100644 --- a/tests/api/sed/coveragetests.cpp +++ b/tests/api/sed/coveragetests.cpp @@ -20,6 +20,8 @@ limitations under the License. #include +static const auto NoDoubles = std::vector {}; + TEST(CoverageSedTest, initialise) { static const std::string expectedSerialisation = R"( @@ -409,8 +411,6 @@ TEST(CoverageSedTest, sedUniformTimeCourse) EXPECT_EQ(simulation->numberOfSteps(), NUMBER_OF_STEPS); } -static const auto NoDoubles = std::vector {}; - TEST(CoverageSedTest, sedInstanceAndSedInstanceTaskDifferentialModel) { static const libOpenCOR::ExpectedIssues EXPECTED_ISSUES = { @@ -556,3 +556,33 @@ TEST(CoverageSedTest, solver) EXPECT_FALSE(instance->hasIssues()); } + +TEST(CoverageSedTest, math) +{ + auto file = libOpenCOR::File::create(libOpenCOR::resourcePath("api/sed/math.cellml")); + auto document = libOpenCOR::SedDocument::create(file); + auto instance = document->instantiate(); + auto instanceTask = instance->tasks()[0]; + + EXPECT_EQ(instanceTask->constantCount(), 0); + EXPECT_EQ(instanceTask->computedConstantCount(), 37); + EXPECT_EQ(instanceTask->algebraicCount(), 0); + + instance->run(); + + static const auto COMPUTED_CONSTANT_VALUES = std::vector({243.0, 3.0, 7.0, 20.085536923187668, 1.0986122886681098, 0.47712125471966244, 4.0, 3.0, 3.0, 5.0, + 3.0, 0.14112000805986721, -0.98999249660044542, -0.1425465430742778, -1.0101086659079939, + 7.0861673957371867, -7.0152525514345339, 10.017874927409903, 10.067661995777765, + 0.99505475368673046, 0.099327927419433207, 0.099821569668822732, 1.0049698233136892, + 0.30469265401539747, 1.266103672779499, 1.2490457723982544, 1.2309594173407747, + 0.33983690945412193, 0.32175055439664219, 1.8184464592320668, 1.7627471740390861, + 0.30951960420311175, 1.8738202425274144, 0.32745015023725843, 0.34657359027997264, + std::numeric_limits::infinity(), std::numeric_limits::quiet_NaN()}); + static const auto COMPUTED_CONSTANT_ABS_TOLS = std::vector({0.0000001, 0.0000001, 0.0000001, 0.0000001, 0.0000001, 0.0000001, 0.0000001, 0.0000001, + 0.0000001, 0.0000001, 0.0000001, 0.0000001, 0.0000001, 0.0000001, 0.0000001, 0.0000001, + 0.0000001, 0.0000001, 0.0000001, 0.0000001, 0.0000001, 0.0000001, 0.0000001, 0.0000001, + 0.0000001, 0.0000001, 0.0000001, 0.0000001, 0.0000001, 0.0000001, 0.0000001, 0.0000001, + 0.0000001, 0.0000001, 0.0000001, 0.0000001, 0.0000001}); + + EXPECT_EQ_VALUES(instanceTask, 0, {}, {}, {}, {}, {}, {}, COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, {}, {}); +} diff --git a/tests/api/sed/instancetests.cpp b/tests/api/sed/instancetests.cpp index d37842d10..f08f1c537 100644 --- a/tests/api/sed/instancetests.cpp +++ b/tests/api/sed/instancetests.cpp @@ -87,49 +87,28 @@ TEST(InstanceSedTest, unsuitablyConstrainedCellmlFile) EXPECT_EQ_ISSUES(instance, EXPECTED_ISSUES); } -namespace { - -void runAlgebraicModel(bool pCompiled) +TEST(InstanceSedTest, algebraicModel) { auto file = libOpenCOR::File::create(libOpenCOR::resourcePath("api/sed/algebraic.cellml")); auto document = libOpenCOR::SedDocument::create(file); - auto instance = document->instantiate(pCompiled); + auto instance = document->instantiate(); instance->run(); EXPECT_FALSE(instance->hasIssues()); } -} // namespace - -TEST(InstanceSedTest, compiledAlgebraicModel) -{ - runAlgebraicModel(true); -} - -TEST(InstanceSedTest, interpretedAlgebraicModel) -{ - runAlgebraicModel(false); -} - -namespace { - -void runOdeModel(bool pCompiled) +TEST(InstanceSedTest, odeModel) { const libOpenCOR::ExpectedIssues EXPECTED_ISSUES = { #ifdef BUILDING_ON_INTEL {libOpenCOR::Issue::Type::ERROR, "At t = 0.00140013827899996, mxstep steps taken before reaching tout."}, #else - { - libOpenCOR::Issue::Type::ERROR, + {libOpenCOR::Issue::Type::ERROR, # ifdef BUILDING_ON_WINDOWS - pCompiled ? - "At t = 0.00140013827899821, mxstep steps taken before reaching tout." : - "At t = 0.00140013827899996, mxstep steps taken before reaching tout.", + "At t = 0.00140013827899821, mxstep steps taken before reaching tout." # else - pCompiled ? - "At t = 0.00140013827899707, mxstep steps taken before reaching tout." : - "At t = 0.00140013827900052, mxstep steps taken before reaching tout.", + "At t = 0.00140013827899707, mxstep steps taken before reaching tout." # endif }, #endif @@ -145,7 +124,7 @@ void runOdeModel(bool pCompiled) cvode->setMaximumNumberOfSteps(NOK_MAXIMUM_NUMBER_OF_STEPS); - auto instance = document->instantiate(pCompiled); + auto instance = document->instantiate(); EXPECT_FALSE(instance->hasIssues()); @@ -157,25 +136,13 @@ void runOdeModel(bool pCompiled) cvode->setMaximumNumberOfSteps(OK_MAXIMUM_NUMBER_OF_STEPS); - instance = document->instantiate(pCompiled); + instance = document->instantiate(); instance->run(); EXPECT_FALSE(instance->hasIssues()); } -} // namespace - -TEST(InstanceSedTest, compiledOdeModel) -{ - runOdeModel(true); -} - -TEST(InstanceSedTest, interpretedOdeModel) -{ - runOdeModel(false); -} - TEST(InstanceSedTest, odeModelWithNoOdeSolver) { static const libOpenCOR::ExpectedIssues EXPECTED_ISSUES = { @@ -194,8 +161,6 @@ TEST(InstanceSedTest, odeModelWithNoOdeSolver) TEST(InstanceSedTest, nlaModel) { - //---GRY--- AS FOR THE ALGEBRAIC AND ODE MODELS, WE WILL NEED TO ADD AN INTERPRETED VERSION OF THIS TEST. - static const libOpenCOR::ExpectedIssues EXPECTED_ISSUES = { {libOpenCOR::Issue::Type::ERROR, "The upper half-bandwidth cannot be equal to -1. It must be between 0 and 0."}, }; @@ -237,8 +202,6 @@ TEST(InstanceSedTest, nlaModelWithNoNlaSolver) TEST(InstanceSedTest, daeModel) { - //---GRY--- AS FOR THE ALGEBRAIC AND ODE MODELS, WE WILL NEED TO ADD AN INTERPRETED VERSION OF THIS TEST. - static const libOpenCOR::ExpectedIssues EXPECTED_ISSUES = { {libOpenCOR::Issue::Type::ERROR, "The upper half-bandwidth cannot be equal to -1. It must be between 0 and 0."}, }; diff --git a/tests/api/solver/cvodetests.cpp b/tests/api/solver/cvodetests.cpp index a21150875..0e75258b0 100644 --- a/tests/api/solver/cvodetests.cpp +++ b/tests/api/solver/cvodetests.cpp @@ -172,30 +172,7 @@ TEST(CvodeSolverTest, absoluteToleranceValueWithInvalidNumber) EXPECT_EQ_ISSUES(instance, EXPECTED_ISSUES); } -namespace { - -void cvodeSolve(const libOpenCOR::Doubles &pStateValues, const libOpenCOR::Doubles &pStateAbsTols, - const libOpenCOR::Doubles &pRateValues, const libOpenCOR::Doubles &pRateAbsTols, - const libOpenCOR::Doubles &pConstantValues, const libOpenCOR::Doubles &pConstantAbsTols, - const libOpenCOR::Doubles &pComputedConstantValues, const libOpenCOR::Doubles &pComputedConstantAbsTols, - const libOpenCOR::Doubles &pAlgebraicValues, const libOpenCOR::Doubles &pAlgebraicAbsTols, - bool pCompiled) -{ - auto file = libOpenCOR::File::create(libOpenCOR::resourcePath("api/solver/ode.cellml")); - auto document = libOpenCOR::SedDocument::create(file); - - OdeModel::run(document, - pStateValues, pStateAbsTols, - pRateValues, pRateAbsTols, - pConstantValues, pConstantAbsTols, - pComputedConstantValues, pComputedConstantAbsTols, - pAlgebraicValues, pAlgebraicAbsTols, - pCompiled); -} - -} // namespace - -TEST(CvodeSolverTest, compiledSolve) +TEST(CvodeSolverTest, solve) { static const auto STATE_VALUES = std::vector({-63.886, 0.135007, 0.984333, 0.740973}); static const auto STATE_ABS_TOLS = std::vector({0.001, 0.000001, 0.000001, 0.000001}); @@ -208,33 +185,15 @@ TEST(CvodeSolverTest, compiledSolve) static const auto ALGEBRAIC_VALUES = std::vector({0.0, -15.9819, -823.517, 789.779, 3.9699, 0.11499, 0.00287, 0.96735, 0.54133, 0.056246}); static const auto ALGEBRAIC_ABS_TOLS = std::vector({0.0, 0.0001, 0.001, 0.001, 0.0001, 0.00001, 0.00001, 0.00001, 0.00001, 0.000001}); - cvodeSolve(STATE_VALUES, STATE_ABS_TOLS, - RATE_VALUES, RATE_ABS_TOLS, - CONSTANT_VALUES, CONSTANT_ABS_TOLS, - COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, - ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS, - true); -} + auto file = libOpenCOR::File::create(libOpenCOR::resourcePath("api/solver/ode.cellml")); + auto document = libOpenCOR::SedDocument::create(file); -TEST(CvodeSolverTest, interpretedSolve) -{ - static const auto STATE_VALUES = std::vector({-63.886, 0.135008, 0.984333, 0.740972}); - static const auto STATE_ABS_TOLS = std::vector({0.001, 0.000001, 0.000001, 0.000001}); - static const auto RATE_VALUES = std::vector({49.725, -0.128193, -0.05090, 0.09865}); - static const auto RATE_ABS_TOLS = std::vector({0.001, 0.000001, 0.00001, 0.00001}); - static const auto CONSTANT_VALUES = std::vector({1.0, 0.0, 0.3, 120.0, 36.0}); - static const auto CONSTANT_ABS_TOLS = std::vector({0.0, 0.0, 0.0, 0.0, 0.0}); - static const auto COMPUTED_CONSTANT_VALUES = std::vector({-10.613, -115.0, 12.0}); - static const auto COMPUTED_CONSTANT_ABS_TOLS = std::vector({0.0, 0.0, 0.0}); - static const auto ALGEBRAIC_VALUES = std::vector({0.0, -15.9819, -823.517, 789.779, 3.9699, 0.11499, 0.00287, 0.967347, 0.54133, 0.056246}); - static const auto ALGEBRAIC_ABS_TOLS = std::vector({0.0, 0.0001, 0.001, 0.001, 0.0001, 0.00001, 0.00001, 0.000001, 0.00001, 0.000001}); - - cvodeSolve(STATE_VALUES, STATE_ABS_TOLS, - RATE_VALUES, RATE_ABS_TOLS, - CONSTANT_VALUES, CONSTANT_ABS_TOLS, - COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, - ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS, - false); + OdeModel::run(document, + STATE_VALUES, STATE_ABS_TOLS, + RATE_VALUES, RATE_ABS_TOLS, + CONSTANT_VALUES, CONSTANT_ABS_TOLS, + COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, + ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS); } TEST(CvodeSolverTest, solveWithoutInterpolateSolution) @@ -262,8 +221,7 @@ TEST(CvodeSolverTest, solveWithoutInterpolateSolution) RATE_VALUES, RATE_ABS_TOLS, CONSTANT_VALUES, CONSTANT_ABS_TOLS, COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, - ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS, - true); + ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS); } TEST(CvodeSolverTest, solveWithAdamsMoultonIntegrationMethod) @@ -291,8 +249,7 @@ TEST(CvodeSolverTest, solveWithAdamsMoultonIntegrationMethod) RATE_VALUES, RATE_ABS_TOLS, CONSTANT_VALUES, CONSTANT_ABS_TOLS, COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, - ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS, - true); + ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS); } TEST(CvodeSolverTest, solveWithFunctionalIterationType) @@ -320,8 +277,7 @@ TEST(CvodeSolverTest, solveWithFunctionalIterationType) RATE_VALUES, RATE_ABS_TOLS, CONSTANT_VALUES, CONSTANT_ABS_TOLS, COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, - ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS, - true); + ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS); } TEST(CvodeSolverTest, solveWithBandedLinearSolver) @@ -349,8 +305,7 @@ TEST(CvodeSolverTest, solveWithBandedLinearSolver) RATE_VALUES, RATE_ABS_TOLS, CONSTANT_VALUES, CONSTANT_ABS_TOLS, COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, - ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS, - true); + ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS); } TEST(CvodeSolverTest, solveWithDiagonalLinearSolver) @@ -378,8 +333,7 @@ TEST(CvodeSolverTest, solveWithDiagonalLinearSolver) RATE_VALUES, RATE_ABS_TOLS, CONSTANT_VALUES, CONSTANT_ABS_TOLS, COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, - ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS, - true); + ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS); } TEST(CvodeSolverTest, solveWithGmresLinearSolver) @@ -407,8 +361,7 @@ TEST(CvodeSolverTest, solveWithGmresLinearSolver) RATE_VALUES, RATE_ABS_TOLS, CONSTANT_VALUES, CONSTANT_ABS_TOLS, COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, - ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS, - true); + ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS); } TEST(CvodeSolverTest, solveWithBicgstabLinearSolver) @@ -436,8 +389,7 @@ TEST(CvodeSolverTest, solveWithBicgstabLinearSolver) RATE_VALUES, RATE_ABS_TOLS, CONSTANT_VALUES, CONSTANT_ABS_TOLS, COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, - ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS, - true); + ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS); } TEST(CvodeSolverTest, solveWithTfqmrLinearSolver) @@ -465,8 +417,7 @@ TEST(CvodeSolverTest, solveWithTfqmrLinearSolver) RATE_VALUES, RATE_ABS_TOLS, CONSTANT_VALUES, CONSTANT_ABS_TOLS, COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, - ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS, - true); + ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS); } TEST(CvodeSolverTest, solveWithGmresLinearSolverAndNoPreconditioner) @@ -495,8 +446,7 @@ TEST(CvodeSolverTest, solveWithGmresLinearSolverAndNoPreconditioner) RATE_VALUES, RATE_ABS_TOLS, CONSTANT_VALUES, CONSTANT_ABS_TOLS, COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, - ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS, - true); + ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS); } TEST(CvodeSolverTest, solveWithBicgstabLinearSolverAndNoPreconditioner) @@ -525,8 +475,7 @@ TEST(CvodeSolverTest, solveWithBicgstabLinearSolverAndNoPreconditioner) RATE_VALUES, RATE_ABS_TOLS, CONSTANT_VALUES, CONSTANT_ABS_TOLS, COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, - ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS, - true); + ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS); } TEST(CvodeSolverTest, solveWithTfqmrLinearSolverAndNoPreconditioner) @@ -555,6 +504,5 @@ TEST(CvodeSolverTest, solveWithTfqmrLinearSolverAndNoPreconditioner) RATE_VALUES, RATE_ABS_TOLS, CONSTANT_VALUES, CONSTANT_ABS_TOLS, COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, - ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS, - true); + ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS); } diff --git a/tests/api/solver/forwardeulertests.cpp b/tests/api/solver/forwardeulertests.cpp index 0ebe496b9..77307b7f2 100644 --- a/tests/api/solver/forwardeulertests.cpp +++ b/tests/api/solver/forwardeulertests.cpp @@ -37,16 +37,19 @@ TEST(ForwardEulerSolverTest, stepValueWithInvalidNumber) EXPECT_EQ_ISSUES(instance, EXPECTED_ISSUES); } -namespace { - -void forwardEulerSolve(const libOpenCOR::Doubles &pStateValues, const libOpenCOR::Doubles &pStateAbsTols, - const libOpenCOR::Doubles &pRateValues, const libOpenCOR::Doubles &pRateAbsTols, - const libOpenCOR::Doubles &pConstantValues, const libOpenCOR::Doubles &pConstantAbsTols, - const libOpenCOR::Doubles &pComputedConstantValues, const libOpenCOR::Doubles &pComputedConstantAbsTols, - const libOpenCOR::Doubles &pAlgebraicValues, const libOpenCOR::Doubles &pAlgebraicAbsTols, - bool pCompiled) +TEST(ForwardEulerSolverTest, solve) { static const auto STEP = 0.0123; + static const auto STATE_VALUES = std::vector({-63.787727, 0.134748, 0.984255, 0.741178}); + static const auto STATE_ABS_TOLS = std::vector({0.000001, 0.000001, 0.000001, 0.000001}); + static const auto RATE_VALUES = std::vector({49.73577, -0.127963, -0.051257, 0.098331}); + static const auto RATE_ABS_TOLS = std::vector({0.000001, 0.000001, 0.000001, 0.000001}); + static const auto CONSTANT_VALUES = std::vector({1.0, 0.0, 0.3, 120.0, 36.0}); + static const auto CONSTANT_ABS_TOLS = std::vector({0.0, 0.0, 0.0, 0.0, 0.0}); + static const auto COMPUTED_CONSTANT_VALUES = std::vector({-10.613, -115.0, 12.0}); + static const auto COMPUTED_CONSTANT_ABS_TOLS = std::vector({0.0, 0.0, 0.0}); + static const auto ALGEBRAIC_VALUES = std::vector({0.0, -15.952418, -823.361177, 789.590304, 3.960664, 0.115617, 0.002884, 0.967035, 0.54037, 0.056315}); + static const auto ALGEBRAIC_ABS_TOLS = std::vector({0.0, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.00001, 0.000001}); auto file = libOpenCOR::File::create(libOpenCOR::resourcePath("api/solver/ode.cellml")); auto document = libOpenCOR::SedDocument::create(file); @@ -58,43 +61,9 @@ void forwardEulerSolve(const libOpenCOR::Doubles &pStateValues, const libOpenCOR simulation->setOdeSolver(solver); OdeModel::run(document, - pStateValues, pStateAbsTols, - pRateValues, pRateAbsTols, - pConstantValues, pConstantAbsTols, - pComputedConstantValues, pComputedConstantAbsTols, - pAlgebraicValues, pAlgebraicAbsTols, - pCompiled); -} - -} // namespace - -static const auto STATE_VALUES = std::vector({-63.787727, 0.134748, 0.984255, 0.741178}); -static const auto STATE_ABS_TOLS = std::vector({0.000001, 0.000001, 0.000001, 0.000001}); -static const auto RATE_VALUES = std::vector({49.73577, -0.127963, -0.051257, 0.098331}); -static const auto RATE_ABS_TOLS = std::vector({0.000001, 0.000001, 0.000001, 0.000001}); -static const auto CONSTANT_VALUES = std::vector({1.0, 0.0, 0.3, 120.0, 36.0}); -static const auto CONSTANT_ABS_TOLS = std::vector({0.0, 0.0, 0.0, 0.0, 0.0}); -static const auto COMPUTED_CONSTANT_VALUES = std::vector({-10.613, -115.0, 12.0}); -static const auto COMPUTED_CONSTANT_ABS_TOLS = std::vector({0.0, 0.0, 0.0}); -static const auto ALGEBRAIC_VALUES = std::vector({0.0, -15.952418, -823.361177, 789.590304, 3.960664, 0.115617, 0.002884, 0.967035, 0.54037, 0.056315}); -static const auto ALGEBRAIC_ABS_TOLS = std::vector({0.0, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.00001, 0.000001}); - -TEST(ForwardEulerSolverTest, compiledSolve) -{ - forwardEulerSolve(STATE_VALUES, STATE_ABS_TOLS, - RATE_VALUES, RATE_ABS_TOLS, - CONSTANT_VALUES, CONSTANT_ABS_TOLS, - COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, - ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS, - true); -} - -TEST(ForwardEulerSolverTest, interpretedSolve) -{ - forwardEulerSolve(STATE_VALUES, STATE_ABS_TOLS, - RATE_VALUES, RATE_ABS_TOLS, - CONSTANT_VALUES, CONSTANT_ABS_TOLS, - COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, - ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS, - false); + STATE_VALUES, STATE_ABS_TOLS, + RATE_VALUES, RATE_ABS_TOLS, + CONSTANT_VALUES, CONSTANT_ABS_TOLS, + COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, + ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS); } diff --git a/tests/api/solver/fourthorderrungekuttatests.cpp b/tests/api/solver/fourthorderrungekuttatests.cpp index 2452a7126..c0de99eaf 100644 --- a/tests/api/solver/fourthorderrungekuttatests.cpp +++ b/tests/api/solver/fourthorderrungekuttatests.cpp @@ -37,16 +37,19 @@ TEST(FourthOrderRungeKuttaSolverTest, stepValueWithInvalidNumber) EXPECT_EQ_ISSUES(instance, EXPECTED_ISSUES); } -namespace { - -void fourthOrderRungeKuttaSolve(const libOpenCOR::Doubles &pStateValues, const libOpenCOR::Doubles &pStateAbsTols, - const libOpenCOR::Doubles &pRateValues, const libOpenCOR::Doubles &pRateAbsTols, - const libOpenCOR::Doubles &pConstantValues, const libOpenCOR::Doubles &pConstantAbsTols, - const libOpenCOR::Doubles &pComputedConstantValues, const libOpenCOR::Doubles &pComputedConstantAbsTols, - const libOpenCOR::Doubles &pAlgebraicValues, const libOpenCOR::Doubles &pAlgebraicAbsTols, - bool pCompiled) +TEST(FourthOrderRungeKuttaSolverTest, solve) { static const auto STEP = 0.0123; + static const auto STATE_VALUES = std::vector({-63.821233, 0.134844, 0.984267, 0.741105}); + static const auto STATE_ABS_TOLS = std::vector({0.000001, 0.000001, 0.000001, 0.000001}); + static const auto RATE_VALUES = std::vector({49.702735, -0.127922, -0.051225, 0.098266}); + static const auto RATE_ABS_TOLS = std::vector({0.000001, 0.000001, 0.000001, 0.000001}); + static const auto CONSTANT_VALUES = std::vector({1.0, 0.0, 0.3, 120.0, 36.0}); + static const auto CONSTANT_ABS_TOLS = std::vector({0.0, 0.0, 0.0, 0.0, 0.0}); + static const auto COMPUTED_CONSTANT_VALUES = std::vector({-10.613, -115.0, 12.0}); + static const auto COMPUTED_CONSTANT_ABS_TOLS = std::vector({0.0, 0.0, 0.0}); + static const auto ALGEBRAIC_VALUES = std::vector({0.0, -15.96247, -823.402257, 789.661995, 3.963806, 0.115402, 0.002879, 0.967141, 0.540698, 0.056292}); + static const auto ALGEBRAIC_ABS_TOLS = std::vector({0.0, 0.00001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001}); auto file = libOpenCOR::File::create(libOpenCOR::resourcePath("api/solver/ode.cellml")); auto document = libOpenCOR::SedDocument::create(file); @@ -58,43 +61,9 @@ void fourthOrderRungeKuttaSolve(const libOpenCOR::Doubles &pStateValues, const l simulation->setOdeSolver(solver); OdeModel::run(document, - pStateValues, pStateAbsTols, - pRateValues, pRateAbsTols, - pConstantValues, pConstantAbsTols, - pComputedConstantValues, pComputedConstantAbsTols, - pAlgebraicValues, pAlgebraicAbsTols, - pCompiled); -} - -} // namespace - -static const auto STATE_VALUES = std::vector({-63.821233, 0.134844, 0.984267, 0.741105}); -static const auto STATE_ABS_TOLS = std::vector({0.000001, 0.000001, 0.000001, 0.000001}); -static const auto RATE_VALUES = std::vector({49.702735, -0.127922, -0.051225, 0.098266}); -static const auto RATE_ABS_TOLS = std::vector({0.000001, 0.000001, 0.000001, 0.000001}); -static const auto CONSTANT_VALUES = std::vector({1.0, 0.0, 0.3, 120.0, 36.0}); -static const auto CONSTANT_ABS_TOLS = std::vector({0.0, 0.0, 0.0, 0.0, 0.0}); -static const auto COMPUTED_CONSTANT_VALUES = std::vector({-10.613, -115.0, 12.0}); -static const auto COMPUTED_CONSTANT_ABS_TOLS = std::vector({0.0, 0.0, 0.0}); -static const auto ALGEBRAIC_VALUES = std::vector({0.0, -15.96247, -823.402257, 789.661995, 3.963806, 0.115402, 0.002879, 0.967141, 0.540698, 0.056292}); -static const auto ALGEBRAIC_ABS_TOLS = std::vector({0.0, 0.00001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001}); - -TEST(FourthOrderRungeKuttaSolverTest, compiledSolve) -{ - fourthOrderRungeKuttaSolve(STATE_VALUES, STATE_ABS_TOLS, - RATE_VALUES, RATE_ABS_TOLS, - CONSTANT_VALUES, CONSTANT_ABS_TOLS, - COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, - ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS, - true); -} - -TEST(FourthOrderRungeKuttaSolverTest, interpretedSolve) -{ - fourthOrderRungeKuttaSolve(STATE_VALUES, STATE_ABS_TOLS, - RATE_VALUES, RATE_ABS_TOLS, - CONSTANT_VALUES, CONSTANT_ABS_TOLS, - COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, - ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS, - false); + STATE_VALUES, STATE_ABS_TOLS, + RATE_VALUES, RATE_ABS_TOLS, + CONSTANT_VALUES, CONSTANT_ABS_TOLS, + COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, + ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS); } diff --git a/tests/api/solver/heuntests.cpp b/tests/api/solver/heuntests.cpp index 21d8a31b9..9bb9d50ae 100644 --- a/tests/api/solver/heuntests.cpp +++ b/tests/api/solver/heuntests.cpp @@ -37,16 +37,19 @@ TEST(HeunSolverTest, stepValueWithInvalidNumber) EXPECT_EQ_ISSUES(instance, EXPECTED_ISSUES); } -namespace { - -void heunSolve(const libOpenCOR::Doubles &pStateValues, const libOpenCOR::Doubles &pStateAbsTols, - const libOpenCOR::Doubles &pRateValues, const libOpenCOR::Doubles &pRateAbsTols, - const libOpenCOR::Doubles &pConstantValues, const libOpenCOR::Doubles &pConstantAbsTols, - const libOpenCOR::Doubles &pComputedConstantValues, const libOpenCOR::Doubles &pComputedConstantAbsTols, - const libOpenCOR::Doubles &pAlgebraicValues, const libOpenCOR::Doubles &pAlgebraicAbsTols, - bool pCompiled) +TEST(HeunSolverTest, solve) { static const auto STEP = 0.0123; + static const auto STATE_VALUES = std::vector({-63.691259, 0.134516, 0.984133, 0.74137}); + static const auto STATE_ABS_TOLS = std::vector({0.000001, 0.000001, 0.000001, 0.000001}); + static const auto RATE_VALUES = std::vector({49.66942, -0.127532, -0.051693, 0.097711}); + static const auto RATE_ABS_TOLS = std::vector({0.000001, 0.000001, 0.000001, 0.000001}); + static const auto CONSTANT_VALUES = std::vector({1.0, 0.0, 0.3, 120.0, 36.0}); + static const auto CONSTANT_ABS_TOLS = std::vector({0.0, 0.0, 0.0, 0.0, 0.0}); + static const auto COMPUTED_CONSTANT_VALUES = std::vector({-10.613, -115.0, 12.0}); + static const auto COMPUTED_CONSTANT_ABS_TOLS = std::vector({0.0, 0.0, 0.0}); + static const auto ALGEBRAIC_VALUES = std::vector({0.0, -15.923478, -823.166811, 789.421406, 3.951622, 0.116239, 0.002898, 0.966726, 0.539425, 0.056383}); + static const auto ALGEBRAIC_ABS_TOLS = std::vector({0.0, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001}); auto file = libOpenCOR::File::create(libOpenCOR::resourcePath("api/solver/ode.cellml")); auto document = libOpenCOR::SedDocument::create(file); @@ -58,43 +61,9 @@ void heunSolve(const libOpenCOR::Doubles &pStateValues, const libOpenCOR::Double simulation->setOdeSolver(solver); OdeModel::run(document, - pStateValues, pStateAbsTols, - pRateValues, pRateAbsTols, - pConstantValues, pConstantAbsTols, - pComputedConstantValues, pComputedConstantAbsTols, - pAlgebraicValues, pAlgebraicAbsTols, - pCompiled); -} - -} // namespace - -static const auto STATE_VALUES = std::vector({-63.691259, 0.134516, 0.984133, 0.74137}); -static const auto STATE_ABS_TOLS = std::vector({0.000001, 0.000001, 0.000001, 0.000001}); -static const auto RATE_VALUES = std::vector({49.66942, -0.127532, -0.051693, 0.097711}); -static const auto RATE_ABS_TOLS = std::vector({0.000001, 0.000001, 0.000001, 0.000001}); -static const auto CONSTANT_VALUES = std::vector({1.0, 0.0, 0.3, 120.0, 36.0}); -static const auto CONSTANT_ABS_TOLS = std::vector({0.0, 0.0, 0.0, 0.0, 0.0}); -static const auto COMPUTED_CONSTANT_VALUES = std::vector({-10.613, -115.0, 12.0}); -static const auto COMPUTED_CONSTANT_ABS_TOLS = std::vector({0.0, 0.0, 0.0}); -static const auto ALGEBRAIC_VALUES = std::vector({0.0, -15.923478, -823.166811, 789.421406, 3.951622, 0.116239, 0.002898, 0.966726, 0.539425, 0.056383}); -static const auto ALGEBRAIC_ABS_TOLS = std::vector({0.0, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001}); - -TEST(HeunSolverTest, compiledSolve) -{ - heunSolve(STATE_VALUES, STATE_ABS_TOLS, - RATE_VALUES, RATE_ABS_TOLS, - CONSTANT_VALUES, CONSTANT_ABS_TOLS, - COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, - ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS, - true); -} - -TEST(HeunSolverTest, interpretedSolve) -{ - heunSolve(STATE_VALUES, STATE_ABS_TOLS, - RATE_VALUES, RATE_ABS_TOLS, - CONSTANT_VALUES, CONSTANT_ABS_TOLS, - COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, - ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS, - false); + STATE_VALUES, STATE_ABS_TOLS, + RATE_VALUES, RATE_ABS_TOLS, + CONSTANT_VALUES, CONSTANT_ABS_TOLS, + COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, + ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS); } diff --git a/tests/api/solver/kinsoltests.cpp b/tests/api/solver/kinsoltests.cpp index 9871a7495..f43439338 100644 --- a/tests/api/solver/kinsoltests.cpp +++ b/tests/api/solver/kinsoltests.cpp @@ -112,9 +112,6 @@ TEST(KinsolSolverTest, bandedLinearSolverAndLowerHalfBandwidthValueWithNumberToo EXPECT_EQ_ISSUES(instance, EXPECTED_ISSUES); } -//---GRY--- RENAME THIS TEST compiledSolve AND CREATE ONE CALLED interpretedSolve ONCE WE CAN INTERPRET NLA-BASED -// MODELS. - static const auto ABS_TOL = 1e-05; namespace { diff --git a/tests/api/solver/odemodel.cpp b/tests/api/solver/odemodel.cpp index dc9087a07..aaccd56e6 100644 --- a/tests/api/solver/odemodel.cpp +++ b/tests/api/solver/odemodel.cpp @@ -23,8 +23,7 @@ void run(const libOpenCOR::SedDocumentPtr &pDocument, const libOpenCOR::Doubles &pRateValues, const libOpenCOR::Doubles &pRateAbsTols, const libOpenCOR::Doubles &pConstantValues, const libOpenCOR::Doubles &pConstantAbsTols, const libOpenCOR::Doubles &pComputedConstantValues, const libOpenCOR::Doubles &pComputedConstantAbsTols, - const libOpenCOR::Doubles &pAlgebraicValues, const libOpenCOR::Doubles &pAlgebraicAbsTols, - bool pCompiled) + const libOpenCOR::Doubles &pAlgebraicValues, const libOpenCOR::Doubles &pAlgebraicAbsTols) { static const auto OUTPUT_END_TIME = 50.0; static const auto NUMBER_OF_STEPS = 50000; @@ -34,7 +33,7 @@ void run(const libOpenCOR::SedDocumentPtr &pDocument, simulation->setOutputEndTime(OUTPUT_END_TIME); simulation->setNumberOfSteps(NUMBER_OF_STEPS); - auto instance = pDocument->instantiate(pCompiled); + auto instance = pDocument->instantiate(); instance->run(); diff --git a/tests/api/solver/odemodel.h b/tests/api/solver/odemodel.h index abdb13278..d00ea8cae 100644 --- a/tests/api/solver/odemodel.h +++ b/tests/api/solver/odemodel.h @@ -27,7 +27,6 @@ void run(const libOpenCOR::SedDocumentPtr &pDocument, const libOpenCOR::Doubles &pRateValues, const libOpenCOR::Doubles &pRateAbsTols, const libOpenCOR::Doubles &pConstantValues, const libOpenCOR::Doubles &pConstantAbsTols, const libOpenCOR::Doubles &pComputedConstantValues, const libOpenCOR::Doubles &pComputedConstantAbsTols, - const libOpenCOR::Doubles &pAlgebraicValues, const libOpenCOR::Doubles &pAlgebraicAbsTols, - bool pCompiled); + const libOpenCOR::Doubles &pAlgebraicValues, const libOpenCOR::Doubles &pAlgebraicAbsTols); } // namespace OdeModel diff --git a/tests/api/solver/secondorderrungekuttatests.cpp b/tests/api/solver/secondorderrungekuttatests.cpp index a52c723f0..d35df4ce5 100644 --- a/tests/api/solver/secondorderrungekuttatests.cpp +++ b/tests/api/solver/secondorderrungekuttatests.cpp @@ -37,16 +37,19 @@ TEST(SecondOrderRungeKuttaSolverTest, stepValueWithInvalidNumber) EXPECT_EQ_ISSUES(instance, EXPECTED_ISSUES); } -namespace { - -void secondOrderRungeKuttaSolve(const libOpenCOR::Doubles &pStateValues, const libOpenCOR::Doubles &pStateAbsTols, - const libOpenCOR::Doubles &pRateValues, const libOpenCOR::Doubles &pRateAbsTols, - const libOpenCOR::Doubles &pConstantValues, const libOpenCOR::Doubles &pConstantAbsTols, - const libOpenCOR::Doubles &pComputedConstantValues, const libOpenCOR::Doubles &pComputedConstantAbsTols, - const libOpenCOR::Doubles &pAlgebraicValues, const libOpenCOR::Doubles &pAlgebraicAbsTols, - bool pCompiled) +TEST(SecondOrderRungeKuttaSolverTest, solve) { static const auto STEP = 0.0123; + static const auto STATE_VALUES = std::vector({-63.886525, 0.135009, 0.984334, 0.740971}); + static const auto STATE_ABS_TOLS = std::vector({0.000001, 0.000001, 0.000001, 0.000001}); + static const auto RATE_VALUES = std::vector({49.725722, -0.128194, -0.050903, 0.098651}); + static const auto RATE_ABS_TOLS = std::vector({0.000001, 0.000001, 0.000001, 0.000001}); + static const auto CONSTANT_VALUES = std::vector({1.0, 0.0, 0.3, 120.0, 36.0}); + static const auto CONSTANT_ABS_TOLS = std::vector({0.0, 0.0, 0.0, 0.0, 0.0}); + static const auto COMPUTED_CONSTANT_VALUES = std::vector({-10.613, -115.0, 12.0}); + static const auto COMPUTED_CONSTANT_ABS_TOLS = std::vector({0.0, 0.0, 0.0}); + static const auto ALGEBRAIC_VALUES = std::vector({0.0, -15.982058, -823.516942, 789.779614, 3.969929, 0.114985, 0.00287, 0.967348, 0.541338, 0.056246}); + static const auto ALGEBRAIC_ABS_TOLS = std::vector({0.0, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.00001, 0.000001, 0.000001, 0.000001}); auto file = libOpenCOR::File::create(libOpenCOR::resourcePath("api/solver/ode.cellml")); auto document = libOpenCOR::SedDocument::create(file); @@ -58,43 +61,9 @@ void secondOrderRungeKuttaSolve(const libOpenCOR::Doubles &pStateValues, const l simulation->setOdeSolver(solver); OdeModel::run(document, - pStateValues, pStateAbsTols, - pRateValues, pRateAbsTols, - pConstantValues, pConstantAbsTols, - pComputedConstantValues, pComputedConstantAbsTols, - pAlgebraicValues, pAlgebraicAbsTols, - pCompiled); -} - -} // namespace - -static const auto STATE_VALUES = std::vector({-63.886525, 0.135009, 0.984334, 0.740971}); -static const auto STATE_ABS_TOLS = std::vector({0.000001, 0.000001, 0.000001, 0.000001}); -static const auto RATE_VALUES = std::vector({49.725722, -0.128194, -0.050903, 0.098651}); -static const auto RATE_ABS_TOLS = std::vector({0.000001, 0.000001, 0.000001, 0.000001}); -static const auto CONSTANT_VALUES = std::vector({1.0, 0.0, 0.3, 120.0, 36.0}); -static const auto CONSTANT_ABS_TOLS = std::vector({0.0, 0.0, 0.0, 0.0, 0.0}); -static const auto COMPUTED_CONSTANT_VALUES = std::vector({-10.613, -115.0, 12.0}); -static const auto COMPUTED_CONSTANT_ABS_TOLS = std::vector({0.0, 0.0, 0.0}); -static const auto ALGEBRAIC_VALUES = std::vector({0.0, -15.982058, -823.516942, 789.779614, 3.969929, 0.114985, 0.00287, 0.967348, 0.541338, 0.056246}); -static const auto ALGEBRAIC_ABS_TOLS = std::vector({0.0, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.00001, 0.000001, 0.000001, 0.000001}); - -TEST(SecondOrderRungeKuttaSolverTest, compiledSolve) -{ - secondOrderRungeKuttaSolve(STATE_VALUES, STATE_ABS_TOLS, - RATE_VALUES, RATE_ABS_TOLS, - CONSTANT_VALUES, CONSTANT_ABS_TOLS, - COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, - ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS, - true); -} - -TEST(SecondOrderRungeKuttaSolverTest, interpretedSolve) -{ - secondOrderRungeKuttaSolve(STATE_VALUES, STATE_ABS_TOLS, - RATE_VALUES, RATE_ABS_TOLS, - CONSTANT_VALUES, CONSTANT_ABS_TOLS, - COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, - ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS, - false); + STATE_VALUES, STATE_ABS_TOLS, + RATE_VALUES, RATE_ABS_TOLS, + CONSTANT_VALUES, CONSTANT_ABS_TOLS, + COMPUTED_CONSTANT_VALUES, COMPUTED_CONSTANT_ABS_TOLS, + ALGEBRAIC_VALUES, ALGEBRAIC_ABS_TOLS); } diff --git a/tests/bindings/javascript/ode.model.js b/tests/bindings/javascript/ode.model.js index c449a283b..e6e11738a 100644 --- a/tests/bindings/javascript/ode.model.js +++ b/tests/bindings/javascript/ode.model.js @@ -19,10 +19,15 @@ import { expectValues } from "./utils.js"; export function run( document, stateValues, + statePrecisions, rateValues, + ratePrecisions, constantValues, + constantPrecisions, computedConstantValues, + computedConstantPrecisions, algebraicValues, + algebraicPrecisions, ) { const simulation = document.simulations.get(0); @@ -39,9 +44,14 @@ export function run( instanceTask, 13000, stateValues, + statePrecisions, rateValues, + ratePrecisions, constantValues, + constantPrecisions, computedConstantValues, + computedConstantPrecisions, algebraicValues, + algebraicPrecisions, ); } diff --git a/tests/bindings/javascript/res/index.html b/tests/bindings/javascript/res/index.html index 28c6224a2..23ad6cbc9 100644 --- a/tests/bindings/javascript/res/index.html +++ b/tests/bindings/javascript/res/index.html @@ -73,11 +73,10 @@

-
-

- Issues: +

+

+

    -
@@ -110,12 +109,6 @@
- -
-

- Error: -

-
diff --git a/tests/bindings/javascript/res/res/libopencor.js b/tests/bindings/javascript/res/res/libopencor.js index 95727f40a..0d1f9b873 100644 --- a/tests/bindings/javascript/res/res/libopencor.js +++ b/tests/bindings/javascript/res/res/libopencor.js @@ -27,14 +27,34 @@ export function showPage(page) { }); } -function showError(error) { - if (!error.endsWith(".")) { - error += "."; +function showIssues(issues) { + const issuesElement = $("#issues"); + + issuesElement.empty(); + + if (issues instanceof Array) { + for (let i = 0; i < issues.length; ++i) { + issuesElement.append( + '
  • Error: ' + + formattedIssueDescription(issues[i]) + + "
  • ", + ); + } + } else { + for (let i = 0; i < issues.size(); ++i) { + const issue = issues.get(i); + + issuesElement.append( + '
  • ' + + issue.typeAsString + + ": " + + formattedIssueDescription(issue.description) + + "
  • ", + ); + } } - $("#errorMessage").html(error); - - updateFileUi(false, false, true, true, false); + updateFileUi(true, true, true, false); } function listFiles() { @@ -56,13 +76,11 @@ function listFiles() { function updateFileUi( fileInfoDisplay, fileIssuesDisplay, - fileErrorDisplay, resetButtonDisplay, simulationDisplay, ) { $("#fileInfo").css("display", fileInfoDisplay ? "block" : "none"); $("#fileIssues").css("display", fileIssuesDisplay ? "block" : "none"); - $("#fileError").css("display", fileErrorDisplay ? "block" : "none"); $("#reset").css("display", resetButtonDisplay ? "block" : "none"); $("#simulation").css("display", simulationDisplay ? "block" : "none"); @@ -94,7 +112,7 @@ export function reset() { resetObjects(); - updateFileUi(false, false, false, false, false); + updateFileUi(false, false, false, false); } function addAxisElement(axis, name) { @@ -257,36 +275,44 @@ $(() => { // Display any issues with the file or run it. - let hasIssues = false; - if (knownFile) { if (file.hasIssues) { - const issuesElement = $("#issues"); - const fileIssues = file.issues; + showIssues(file.issues); + } else { + // Retrieve some information about the simulation. - issuesElement.empty(); + document = new loc.SedDocument(file); - for (let i = 0; i < fileIssues.size(); ++i) { - const issue = fileIssues.get(i); + if (document.hasIssues) { + showIssues(document.issues); - issuesElement.append( - '
  • ' + - issue.typeAsString + - ": " + - formattedIssueDescription(issue.description) + - "
  • ", - ); + return; } - hasIssues = true; - } else { - // Retrieve some information about the simulation. - - document = new loc.SedDocument(file); simulation = document.simulations.get(0); + + if (simulation !== null && simulation.hasIssues) { + showIssues(simulation.issues); + + return; + } + instance = document.instantiate(); + + if (instance !== null && instance.hasIssues) { + showIssues(instance.issues); + + return; + } + instanceTask = instance.tasks.get(0); + if (instanceTask !== null && instanceTask.hasIssues) { + showIssues(instanceTask.issues); + + return; + } + $("#endingPoint").val(simulation.outputEndTime); $("#endingPointUnit").text(instanceTask.voiUnit); @@ -309,20 +335,20 @@ $(() => { // axes information. updatePlottingAreaAndAxesInfo(); + + updateFileUi(true, false, true, true); } } - - updateFileUi(true, hasIssues, false, true, knownFile && !hasIssues); } catch (exception) { - showError(exception.message); + showIssues([exception.message]); } }; fileReader.onerror = () => { - showError(fileReader.error.message); + showIssues([fileReader.error.message]); }; } else { - updateFileUi(false, false, false, false, false); + updateFileUi(false, false, false, false); } }; diff --git a/tests/bindings/javascript/sed.coverage.test.js b/tests/bindings/javascript/sed.coverage.test.js index bbcee8653..b45ec4547 100644 --- a/tests/bindings/javascript/sed.coverage.test.js +++ b/tests/bindings/javascript/sed.coverage.test.js @@ -16,7 +16,7 @@ limitations under the License. import libOpenCOR from "./libopencor.js"; import * as utils from "./utils.js"; -import { expectIssues } from "./utils.js"; +import { expectIssues, expectValues } from "./utils.js"; const loc = await libOpenCOR(); @@ -26,6 +26,7 @@ describe("Sed coverage tests", () => { let unsupportedSedChangesContentsPtr; let solverOdeContentsPtr; let solverNla1ContentsPtr; + let mathContentsPtr; beforeAll(() => { sedChangesContentsPtr = utils.allocateMemory( @@ -45,6 +46,7 @@ describe("Sed coverage tests", () => { loc, utils.SOLVER_NLA1_CONTENTS, ); + mathContentsPtr = utils.allocateMemory(loc, utils.MATH_CONTENTS); }); afterAll(() => { @@ -53,6 +55,7 @@ describe("Sed coverage tests", () => { utils.freeMemory(loc, unsupportedSedChangesContentsPtr); utils.freeMemory(loc, solverOdeContentsPtr); utils.freeMemory(loc, solverNla1ContentsPtr); + utils.freeMemory(loc, mathContentsPtr); }); function sedTaskExpectedSerialisation(withProperties) { @@ -514,14 +517,10 @@ describe("Sed coverage tests", () => { file.delete(); }); - /*---GRY--- TO BE UNCOMMENTED ONCE WE CAN INTERPRET A MODEL WITH ONE/SEVERAL NLA SYSTEM/S. test("SedInstanceAndSedInstanceTaskNonDifferentialModel", () => { const file = new loc.File(utils.CELLML_FILE); - file.setContents( - solverNla1ContentsPtr, - utils.SOLVER_NLA1_CONTENTS.length, - ); + file.setContents(solverNla1ContentsPtr, utils.SOLVER_NLA1_CONTENTS.length); const document = new loc.SedDocument(file); @@ -549,7 +548,6 @@ describe("Sed coverage tests", () => { document.delete(); file.delete(); }); - */ test("SedDocument", () => { let file = new loc.File(utils.HTTP_REMOTE_CELLML_FILE); @@ -634,4 +632,76 @@ describe("Sed coverage tests", () => { document.delete(); file.delete(); }); + + test("Math", () => { + const file = new loc.File(utils.CELLML_FILE); + + file.setContents(mathContentsPtr, utils.MATH_CONTENTS.length); + + const document = new loc.SedDocument(file); + const instance = document.instantiate(); + const instanceTask = instance.tasks.get(0); + + expect(instanceTask.constantCount).toBe(0); + expect(instanceTask.computedConstantCount).toBe(37); + expect(instanceTask.algebraicCount).toBe(0); + + instance.run(); + + expectValues( + instanceTask, + 0, + [], + [], + [], + [], + [], + [], + [ + 243.0, + 3.0, + 7.0, + 20.085536923187668, + 1.0986122886681098, + 0.47712125471966244, + 4.0, + 3.0, + 3.0, + 5.0, + 3.0, + 0.1411200080598672, + -0.9899924966004454, + -0.1425465430742778, + -1.0101086659079939, + 7.086167395737187, + -7.015252551434534, + 10.017874927409903, + 10.067661995777765, + 0.9950547536867305, + 0.0993279274194332, + 0.09982156966882273, + 1.0049698233136892, + 0.3046926540153975, + 1.2661036727794992, + 1.2490457723982544, + 1.2309594173407747, + 0.3398369094541219, + 0.3217505543966422, + 1.8184464592320668, + 1.7627471740390859, + 0.30951960420311175, + 1.8738202425274144, + 0.32745015023725843, + 0.34657359027997264, + Infinity, + NaN, + ], + [ + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + ], + [], + [], + ); + }); }); diff --git a/tests/bindings/javascript/sed.instance.test.js b/tests/bindings/javascript/sed.instance.test.js index 173a37596..ba5e838b1 100644 --- a/tests/bindings/javascript/sed.instance.test.js +++ b/tests/bindings/javascript/sed.instance.test.js @@ -270,7 +270,6 @@ describe("Sed instance tests", () => { file.delete(); }); - /*---GRY--- TO BE UNCOMMENTED ONCE WE CAN INTERPRET A MODEL WITH ONE/SEVERAL NLA SYSTEM/S. test("NLA model", () => { const file = new loc.File(utils.CELLML_FILE); @@ -327,9 +326,7 @@ describe("Sed instance tests", () => { document.delete(); file.delete(); }); - */ - /*---GRY--- TO BE UNCOMMENTED ONCE WE CAN INTERPRET A MODEL WITH ONE/SEVERAL NLA SYSTEM/S. test("DAE model", () => { const file = new loc.File(utils.CELLML_FILE); @@ -374,7 +371,6 @@ describe("Sed instance tests", () => { document.delete(); file.delete(); }); - */ test("DAE model with no ODE or NLA solver", () => { const file = new loc.File(utils.CELLML_FILE); @@ -389,7 +385,6 @@ describe("Sed instance tests", () => { const instance = document.instantiate(); - /*---GRY--- TO BE UNCOMMENTED ONCE WE CAN INTERPRET A MODEL WITH ONE/SEVERAL NLA SYSTEM/S. expectIssues(loc, instance, [ [ loc.Issue.Type.ERROR, @@ -400,7 +395,6 @@ describe("Sed instance tests", () => { "Simulation 'simulation1' is to be used with model 'model1' which requires an NLA solver but none is provided.", ], ]); - */ instance.delete(); document.delete(); diff --git a/tests/bindings/javascript/solver.coverage.test.js b/tests/bindings/javascript/solver.coverage.test.js index 8e26bd5a0..30e58f7c4 100644 --- a/tests/bindings/javascript/solver.coverage.test.js +++ b/tests/bindings/javascript/solver.coverage.test.js @@ -77,7 +77,6 @@ describe("Solver coverage tests", () => { file.delete(); }); - /*---GRY--- ENABLE THIS TEST ONCE WE CAN INTERPRET NLA-BASED MODELS. test("Algebraic changes", () => { // We want to solve a system of three unknowns: // Variables: @@ -116,8 +115,8 @@ describe("Solver coverage tests", () => { expect(instanceTask.stateCount).toBe(0); expect(instanceTask.rateCount).toBe(0); - expect(instanceTask.constantCount).toBe(0); - expect(instanceTask.computedConstantCount).toBe(0); + expect(instanceTask.constantCount).toBe(1); + expect(instanceTask.computedConstantCount).toBe(1); expect(instanceTask.algebraicCount).toBe(3); expect(instanceTask.algebraic(0).get(0)).toBeCloseTo(33.33333, 5); @@ -128,5 +127,4 @@ describe("Solver coverage tests", () => { document.delete(); file.delete(); }); - */ }); diff --git a/tests/bindings/javascript/solver.cvode.test.js b/tests/bindings/javascript/solver.cvode.test.js index 40e8af26a..3f1287f24 100644 --- a/tests/bindings/javascript/solver.cvode.test.js +++ b/tests/bindings/javascript/solver.cvode.test.js @@ -245,14 +245,26 @@ describe("Solver CVODE tests", () => { odeModel.run( document, - [-63.886006, 0.135007, 0.984334, 0.740972], - [49.72578, -0.128193, -0.050905, 0.098649], - [1.0, 0.0, 0.3, 120.0, 36.0], - [-10.613, -115.0, 12.0], [ - 0.0, -15.981902, -823.516722, 789.778714, 3.96988, 0.114988, 0.00287, - 0.967346, 0.541333, 0.056246, + -63.886250327293745, 0.13500806843489388, 0.9843336119444306, + 0.7409718295171003, ], + [3, 6, 6, 6], + [ + 49.725700307489824, -0.1281933409028202, -0.050902572494876855, + 0.09864999300596061, + ], + [3, 6, 5, 5], + [1, 0, 0.3, 120, 36], + [7, 7, 7, 7, 7], + [-10.613, -115, 12], + [7, 7, 7], + [ + 0, -15.981975098188123, -823.5166486227178, 789.7791369560812, + 3.9699027454989695, 0.11498635979013846, 0.002869628921262234, + 0.9673471419816907, 0.5413354590998287, 0.056246038290390175, + ], + [7, 4, 2, 3, 4, 5, 7, 6, 5, 6], ); document.delete(); @@ -272,14 +284,26 @@ describe("Solver CVODE tests", () => { odeModel.run( document, - [-63.886395, 0.135008, 0.984334, 0.740972], - [49.725709, -0.128194, -0.050903, 0.098651], - [1.0, 0.0, 0.3, 120.0, 36.0], - [-10.613, -115.0, 12.0], [ - 0.0, -15.982019, -823.51695, 789.77946, 3.969916, 0.114985, 0.00287, - 0.967348, 0.541337, 0.056246, + -63.88639507849153, 0.1350083126473699, 0.9843340818649865, + 0.7409715439143221, + ], + [6, 7, 7, 7], + [ + 49.72570880449381, -0.12819363057743782, -0.050903078877701416, + 0.09865054326291173, ], + [6, 7, 7, 7], + [1, 0, 0.3, 120, 36], + [7, 7, 7, 7, 7], + [-10.613, -115, 12], + [7, 7, 7], + [ + 0, -15.98201852354746, -823.5169497826655, 789.7794600735924, + 3.9699163220783995, 0.11498543510422823, 0.002869608152226214, + 0.9673475991991235, 0.5413368772943074, 0.05624593651946458, + ], + [7, 6, 5, 5, 7, 7, 7, 7, 7, 7], ); document.delete(); @@ -299,14 +323,26 @@ describe("Solver CVODE tests", () => { odeModel.run( document, - [-63.888155, 0.135013, 0.984336, 0.740968], - [49.725742, -0.128199, -0.050896, 0.098658], - [1.0, 0.0, 0.3, 120.0, 36.0], - [-10.613, -115.0, 12.0], [ - 0.0, -15.982546, -823.520056, 789.782845, 3.970081, 0.114974, 0.002869, - 0.967353, 0.541354, 0.056245, + -63.887813934797826, 0.13501191292213002, 0.9843355542098364, + 0.7409686747551986, + ], + [2, 4, 5, 4], + [ + 49.72620260336583, -0.12819788722747028, -0.05089750341316691, + 0.09865693092730915, + ], + [2, 4, 4, 4], + [1, 0, 0.3, 120, 36], + [7, 7, 7, 7, 7], + [-10.613, -115, 12], + [7, 7, 7], + [ + 0, -15.982444180439348, -823.5195918535778, 789.7821411983648, + 3.97004940067058, 0.11497637169423897, 0.0028694045813660754, + 0.9673520805329192, 0.5413507785037981, 0.056244938967039075, ], + [7, 2, 2, 2, 3, 4, 6, 4, 4, 5], ); document.delete(); @@ -326,14 +362,26 @@ describe("Solver CVODE tests", () => { odeModel.run( document, - [-63.88619, 0.135008, 0.984334, 0.740972], - [49.72565, -0.128193, -0.050904, 0.09865], - [1.0, 0.0, 0.3, 120.0, 36.0], - [-10.613, -115.0, 12.0], [ - 0.0, -15.981957, -823.516742, 789.779253, 3.969897, 0.114987, 0.00287, - 0.967347, 0.541335, 0.056246, + -63.886282061359374, 0.135008093417601, 0.9843339833821376, + 0.7409718122756039, ], + [3, 6, 6, 6], + [ + 49.72538296330156, -0.12819336464938047, -0.050903478581860076, + 0.09865009949522247, + ], + [3, 6, 5, 6], + [1, 0, 0.3, 120, 36], + [7, 7, 7, 7, 7], + [-10.613, -115, 12], + [7, 7, 7], + [ + 0, -15.981984618407811, -823.5169163515275, 789.7796868336728, + 3.9699057219161293, 0.11498615706894559, 0.0028696243680162202, + 0.967347242218821, 0.5413357700131285, 0.056246015978951215, + ], + [7, 4, 3, 3, 4, 5, 7, 6, 5, 6], ); document.delete(); @@ -353,14 +401,26 @@ describe("Solver CVODE tests", () => { odeModel.run( document, - [-54.957552, 0.114715, 0.971364, 0.756968], - [47.196456, -0.10208, -0.092979, 0.062976], - [1.0, 0.0, 0.3, 120.0, 36.0], - [-10.613, -115.0, 12.0], [ - 0.0, -13.303366, -791.427395, 757.54033, 3.153423, 0.188831, 0.004484, - 0.923844, 0.454648, 0.062887, + -54.95816860466446, 0.11471650712970222, 0.9713656209644033, + 0.756967274462986, + ], + [3, 5, 5, 6], + [ + 47.19692686937322, -0.10208187002903986, -0.0929754636050344, + 0.06297792796897843, ], + [3, 5, 5, 5], + [1, 0, 0.3, 120, 36], + [7, 7, 7, 7, 7], + [-10.613, -115, 12], + [7, 7, 7], + [ + 0, -13.303550581399337, -791.4336008281795, 757.5464682504468, + 3.1534773935628353, 0.1888245073098595, 0.004484319750222318, + 0.9238480455513784, 0.4546536035795939, 0.06288682169446458, + ], + [7, 3, 2, 2, 4, 5, 6, 5, 5, 6], ); document.delete(); @@ -380,14 +440,26 @@ describe("Solver CVODE tests", () => { odeModel.run( document, - [-63.886488, 0.135009, 0.984334, 0.740972], - [49.725561, -0.128191, -0.050903, 0.098651], - [1.0, 0.0, 0.3, 120.0, 36.0], - [-10.613, -115.0, 12.0], [ - 0.0, -15.982046, -823.518024, 789.780604, 3.969925, 0.114985, 0.00287, - 0.967348, 0.541338, 0.056246, + -63.88448960386081, 0.13500371980870626, 0.9843320337747878, + 0.740975648022661, + ], + [2, 4, 6, 5], + [ + 49.72470056973946, -0.1281877408620835, -0.05090942244126474, + 0.0986375194818581, + ], + [2, 4, 3, 4], + [1, 0, 0.3, 120, 36], + [7, 7, 7, 7, 7], + [-10.613, -115, 12], + [7, 7, 7], + [ + 0, -15.981446881158242, -823.5145165709908, 789.777104114329, + 3.9697376035476495, 0.114997608072391, 0.0028698815635271745, + 0.9673415799887405, 0.5413182085040471, 0.05624727622548335, ], + [7, 2, 1, 2, 3, 4, 6, 4, 4, 5], ); document.delete(); @@ -407,14 +479,26 @@ describe("Solver CVODE tests", () => { odeModel.run( document, - [9.518468, 0.367366, 0.01612, 0.491145], - [-0.864067, 0.064354, 0.001718, -0.052716], - [1.0, 0.0, 0.3, 120.0, 36.0], - [-10.613, -115.0, 12.0], [ - 0.0, 6.03944, -5.198281, 0.022996, 0.112958, 6.787605, 0.112665, - 0.018857, 0.032306, 0.140794, + 9.518467927238342, 0.36736556858518055, 0.01612044160716401, + 0.49114455278457375, ], + [7, 7, 7, 7], + [ + -0.8640672736946257, 0.06435448571982208, 0.0017177341020318897, + -0.05271611541688017, + ], + [7, 7, 7, 7], + [1, 0, 0.3, 120, 36], + [7, 7, 7, 7, 7], + [-10.613, -115, 12], + [7, 7, 7], + [ + 0, 6.039440378171502, -5.198280618006445, 0.02299557473368303, + 0.11295819992492505, 6.787604864938159, 0.11266498023914563, + 0.018856763703178375, 0.03230635782390115, 0.14079354343581396, + ], + [7, 7, 7, 7, 7, 7, 7, 7, 7, 7], ); document.delete(); @@ -434,14 +518,26 @@ describe("Solver CVODE tests", () => { odeModel.run( document, - [9.518406, 0.36737, 0.016121, 0.491141], - [-0.864079, 0.064354, 0.001718, -0.052715], - [1.0, 0.0, 0.3, 120.0, 36.0], - [-10.613, -115.0, 12.0], [ - 0.0, 6.039422, -5.198258, 0.022996, 0.112959, 6.787581, 0.112665, - 0.018857, 0.032306, 0.140793, + 9.518405563384688, 0.3673701689782409, 0.016120565942342176, + 0.49114094269101, + ], + [7, 7, 7, 7], + [ + -0.8640794309120806, 0.06435358502313818, 0.0017178663683860092, + -0.05271535789078276, ], + [7, 7, 7, 7], + [1, 0, 0.3, 120, 36], + [7, 7, 7, 7, 7], + [-10.613, -115, 12], + [7, 7, 7], + [ + 0, 6.039421669015407, -5.198258417710488, 0.02299638327958176, + 0.11295872334992325, 6.7875813482457605, 0.11266462892857637, + 0.018856879084051228, 0.03230648942381701, 0.14079343368050753, + ], + [7, 7, 7, 7, 7, 7, 7, 7, 7, 7], ); document.delete(); @@ -461,14 +557,26 @@ describe("Solver CVODE tests", () => { odeModel.run( document, - [9.518097, 0.367396, 0.016121, 0.491122], - [-0.864142, 0.064349, 0.001718, -0.052711], - [1.0, 0.0, 0.3, 120.0, 36.0], - [-10.613, -115.0, 12.0], [ - 0.0, 6.039329, -5.198098, 0.023, 0.112961, 6.787465, 0.112663, 0.018857, - 0.032307, 0.140793, + 9.5180969791175, 0.36739578686621527, 0.016121150676415584, + 0.4911219162005304, + ], + [5, 6, 6, 6], + [ + -0.8641450065405636, 0.06434891371052368, 0.0017179872368887157, + -0.05271147016114454, + ], + [5, 6, 2, 7], + [1, 0, 0.3, 120, 36], + [7, 7, 7, 7, 7], + [-10.613, -115, 12], + [7, 7, 7], + [ + 0, 6.03932909373525, -5.198099254585335, 0.02300043256789237, + 0.11296131335573398, 6.787464985864513, 0.11266289061538902, + 0.018857450013451658, 0.03230714060351441, 0.14079289059857297, ], + [7, 5, 5, 6, 7, 5, 7, 7, 7, 7], ); document.delete(); @@ -489,14 +597,26 @@ describe("Solver CVODE tests", () => { odeModel.run( document, - [-63.88632, 0.135008, 0.984334, 0.740972], - [49.725847, -0.128194, -0.050904, 0.09865], - [1.0, 0.0, 0.3, 120.0, 36.0], - [-10.613, -115.0, 12.0], [ - 0.0, -15.981996, -823.516944, 789.779343, 3.969909, 0.114986, 0.00287, - 0.967347, 0.541336, 0.056246, + -63.88610210255769, 0.13500770971929016, 0.9843329698410943, + 0.7409720924682706, ], + [3, 6, 5, 6], + [ + 49.726333013075525, -0.12819299724421862, -0.05091538950854948, + 0.09865029693084587, + ], + [2, 6, 4, 6], + [1, 0, 0.3, 120, 36], + [7, 7, 7, 7, 7], + [-10.613, -115, 12], + [7, 7, 7], + [ + 0, -15.981930630767307, -823.5162090645707, 789.7777832194464, + 3.9698888431371406, 0.1149873066730832, 0.002869650188840514, + 0.967346673786178, 0.5413340068738324, 0.056246142503663964, + ], + [7, 4, 2, 2, 4, 5, 7, 6, 5, 6], ); document.delete(); @@ -517,14 +637,26 @@ describe("Solver CVODE tests", () => { odeModel.run( document, - [-63.886025, 0.135008, 0.984333, 0.740972], - [49.725781, -0.128193, -0.050904, 0.098649], - [1.0, 0.0, 0.3, 120.0, 36.0], - [-10.613, -115.0, 12.0], [ - 0.0, -15.981908, -823.516709, 789.778962, 3.969882, 0.114988, 0.00287, - 0.967346, 0.541333, 0.056246, + -63.88580250259205, 0.13500689029927881, 0.9843332002100482, + 0.740972698029834, + ], + [4, 6, 6, 7], + [ + 49.725495533208736, -0.12819193324161038, -0.050904587288098005, + 0.09864807163623063, ], + [5, 6, 6, 6], + [1, 0, 0.3, 120, 36], + [7, 7, 7, 7, 7], + [-10.613, -115, 12], + [7, 7, 7], + [ + 0, -15.981840750777614, -823.515649881427, 789.7781734161576, + 3.969860742950322, 0.1149892205886294, 0.0028696931765173905, + 0.9673457274239244, 0.5413310715560667, 0.05624635314583791, + ], + [7, 4, 3, 3, 5, 6, 7, 6, 6, 7], ); document.delete(); @@ -545,14 +677,26 @@ describe("Solver CVODE tests", () => { odeModel.run( document, - [-63.886116, 0.135008, 0.984333, 0.740972], - [49.725704, -0.128193, -0.050903, 0.098649], - [1.0, 0.0, 0.3, 120.0, 36.0], - [-10.613, -115.0, 12.0], [ - 0.0, -15.981935, -823.516302, 789.778803, 3.96989, 0.114987, 0.00287, - 0.967347, 0.541334, 0.056246, + -63.88590096494604, 0.13500714432443245, 0.9843333109015502, + 0.7409725093281302, + ], + [3, 5, 5, 5], + [ + 49.72555013681926, -0.12819226308184883, -0.050905813346241904, + 0.09864850534796382, + ], + [3, 5, 5, 5], + [1, 0, 0.3, 120, 36], + [7, 7, 7, 7, 7], + [-10.613, -115, 12], + [7, 7, 7], + [ + 0, -15.981870289483812, -823.5158795070533, 789.7784045107686, + 3.969869977962206, 0.11498859158427523, 0.0028696790487148967, + 0.967346038445092, 0.5413320362365842, 0.056246283919026335, ], + [7, 3, 2, 2, 4, 5, 6, 5, 5, 6], ); document.delete(); diff --git a/tests/bindings/javascript/solver.forwardeuler.test.js b/tests/bindings/javascript/solver.forwardeuler.test.js index ea81ef54d..391330ccb 100644 --- a/tests/bindings/javascript/solver.forwardeuler.test.js +++ b/tests/bindings/javascript/solver.forwardeuler.test.js @@ -75,14 +75,26 @@ describe("Solver Forward Euler tests", () => { odeModel.run( document, - [-63.787727, 0.134748, 0.984255, 0.741178], - [49.73577, -0.127963, -0.051257, 0.098331], - [1.0, 0.0, 0.3, 120.0, 36.0], - [-10.613, -115.0, 12.0], [ - 0.0, -15.952418, -823.361177, 789.590304, 3.960664, 0.115617, 0.002884, - 0.967035, 0.54037, 0.056315, + -63.787727309843625, 0.13474847497254067, 0.9842548213064144, + 0.7411775397899871, ], + [7, 7, 7, 7], + [ + 49.7357696419333, -0.12796285748502675, -0.05125742132748939, + 0.09833105129202654, + ], + [7, 7, 7, 7], + [1, 0, 0.3, 120, 36], + [7, 7, 7, 7, 7], + [-10.613, -115, 12], + [7, 7, 7], + [ + 0, -15.952418192953086, -823.361177370372, 789.5903038580856, + 3.9606641552180855, 0.11561746333925438, 0.002883800022109427, + 0.9670345037893372, 0.5403702607259089, 0.05631535007932082, + ], + [7, 7, 7, 7, 7, 7, 7, 7, 7, 7], ); solver.delete(); diff --git a/tests/bindings/javascript/solver.fourthorderrungekutta.test.js b/tests/bindings/javascript/solver.fourthorderrungekutta.test.js index 818774925..82a7b004d 100644 --- a/tests/bindings/javascript/solver.fourthorderrungekutta.test.js +++ b/tests/bindings/javascript/solver.fourthorderrungekutta.test.js @@ -75,14 +75,26 @@ describe("Solver Fourth-Order Runge-Kutta tests", () => { odeModel.run( document, - [-63.821233, 0.134844, 0.984267, 0.741105], - [49.702735, -0.127922, -0.051225, 0.098266], - [1.0, 0.0, 0.3, 120.0, 36.0], - [-10.613, -115.0, 12.0], [ - 0.0, -15.96247, -823.402257, 789.661995, 3.963806, 0.115402, 0.002879, - 0.967141, 0.540698, 0.056292, + -63.821233121797334, 0.13484386454832029, 0.9842671578347936, + 0.7411048872918694, ], + [7, 7, 7, 7], + [ + 49.70273520660513, -0.1279222891468715, -0.051224913137817824, + 0.0982660916488527, + ], + [7, 7, 7, 7], + [1, 0, 0.3, 120, 36], + [7, 7, 7, 7, 7], + [-10.613, -115, 12], + [7, 7, 7], + [ + 0, -15.9624699365392, -823.4022571720553, 789.6619953203995, + 3.9638055464297928, 0.11540244924165802, 0.002878972863622386, + 0.9671411492217459, 0.540698489075857, 0.05629176887371371, + ], + [7, 7, 7, 7, 7, 7, 7, 7, 7, 7], ); solver.delete(); diff --git a/tests/bindings/javascript/solver.heun.test.js b/tests/bindings/javascript/solver.heun.test.js index 34226e7f2..5eb435873 100644 --- a/tests/bindings/javascript/solver.heun.test.js +++ b/tests/bindings/javascript/solver.heun.test.js @@ -75,14 +75,26 @@ describe("Solver Heun tests", () => { odeModel.run( document, - [-63.691259, 0.134516, 0.984133, 0.74137], - [49.66942, -0.127532, -0.051693, 0.097711], - [1.0, 0.0, 0.3, 120.0, 36.0], - [-10.613, -115.0, 12.0], [ - 0.0, -15.923478, -823.166811, 789.421406, 3.951622, 0.116239, 0.002898, - 0.966726, 0.539425, 0.056383, + -63.69125875774881, 0.13451645104446466, 0.9841326274924806, + 0.7413698256563633, ], + [7, 7, 7, 7], + [ + 49.66942041994269, -0.12753246872257323, -0.051692906785376816, + 0.09771071198596298, + ], + [7, 7, 7, 7], + [1, 0, 0.3, 120, 36], + [7, 7, 7, 7, 7], + [-10.613, -115, 12], + [7, 7, 7], + [ + 0, -15.923477627324644, -823.1668113969306, 789.4214057021819, + 3.95162235535478, 0.11623876280854108, 0.002897743423202202, + 0.9667255844752269, 0.539425339436885, 0.05638329929313714, + ], + [7, 7, 7, 7, 7, 7, 7, 7, 7, 7], ); solver.delete(); diff --git a/tests/bindings/javascript/solver.kinsol.test.js b/tests/bindings/javascript/solver.kinsol.test.js index 0b4bbbb7f..f4322e4cb 100644 --- a/tests/bindings/javascript/solver.kinsol.test.js +++ b/tests/bindings/javascript/solver.kinsol.test.js @@ -16,8 +16,7 @@ limitations under the License. import libOpenCOR from "./libopencor.js"; import * as utils from "./utils.js"; -//---GRY--- TO BE ENABLED ONCE WE CAN INTERPRET NLA-BASED MODELS. -// import { expectIssues } from "./utils.js"; +import { expectIssues } from "./utils.js"; const loc = await libOpenCOR(); @@ -41,16 +40,10 @@ describe("Solver KINSOL tests", () => { utils.freeMemory(loc, solverNla2ContentsPtr); }); - test("Dummy", () => {}); - - /*---GRY--- ENABLE THOSE TESTS ONCE WE CAN INTERPRET NLA-BASED MODELS. test("Maximum number of iterations value with invalid number", () => { const file = new loc.File(utils.CELLML_FILE); - file.setContents( - solverNla1ContentsPtr, - utils.SOLVER_NLA1_CONTENTS.length, - ); + file.setContents(solverNla1ContentsPtr, utils.SOLVER_NLA1_CONTENTS.length); const document = new loc.SedDocument(file); const simulation = document.simulations.get(0); @@ -63,7 +56,7 @@ describe("Solver KINSOL tests", () => { expectIssues(loc, instance, [ [ loc.Issue.Type.ERROR, - "The maximum number of iterations cannot be equal to 0. It must be greater than 0.", + "The maximum number of iterations cannot be equal to -1. It must be greater than 0.", ], ]); @@ -75,10 +68,7 @@ describe("Solver KINSOL tests", () => { test("Banded linear solver and upper half-bandwidth value with number too small", () => { const file = new loc.File(utils.CELLML_FILE); - file.setContents( - solverNla2ContentsPtr, - utils.SOLVER_NLA2_CONTENTS.length, - ); + file.setContents(solverNla2ContentsPtr, utils.SOLVER_NLA2_CONTENTS.length); const document = new loc.SedDocument(file); const simulation = document.simulations.get(0); @@ -104,10 +94,7 @@ describe("Solver KINSOL tests", () => { test("Banded linear solver and upper half-bandwidth value with number too big", () => { const file = new loc.File(utils.CELLML_FILE); - file.setContents( - solverNla1ContentsPtr, - utils.SOLVER_NLA1_CONTENTS.length, - ); + file.setContents(solverNla1ContentsPtr, utils.SOLVER_NLA1_CONTENTS.length); const document = new loc.SedDocument(file); const simulation = document.simulations.get(0); @@ -133,10 +120,7 @@ describe("Solver KINSOL tests", () => { test("Banded linear solver and lower half-bandwidth value with number too small", () => { const file = new loc.File(utils.CELLML_FILE); - file.setContents( - solverNla2ContentsPtr, - utils.SOLVER_NLA2_CONTENTS.length, - ); + file.setContents(solverNla2ContentsPtr, utils.SOLVER_NLA2_CONTENTS.length); const document = new loc.SedDocument(file); const simulation = document.simulations.get(0); @@ -162,10 +146,7 @@ describe("Solver KINSOL tests", () => { test("Banded linear solver and lower half-bandwidth value with number too big", () => { const file = new loc.File(utils.CELLML_FILE); - file.setContents( - solverNla1ContentsPtr, - utils.SOLVER_NLA1_CONTENTS.length, - ); + file.setContents(solverNla1ContentsPtr, utils.SOLVER_NLA1_CONTENTS.length); const document = new loc.SedDocument(file); const simulation = document.simulations.get(0); @@ -215,10 +196,7 @@ describe("Solver KINSOL tests", () => { test("Solve", () => { const file = new loc.File(utils.CELLML_FILE); - file.setContents( - solverNla1ContentsPtr, - utils.SOLVER_NLA1_CONTENTS.length, - ); + file.setContents(solverNla1ContentsPtr, utils.SOLVER_NLA1_CONTENTS.length); const document = new loc.SedDocument(file); const instance = document.instantiate(); @@ -235,10 +213,7 @@ describe("Solver KINSOL tests", () => { test("Solve with banded linear solver", () => { const file = new loc.File(utils.CELLML_FILE); - file.setContents( - solverNla2ContentsPtr, - utils.SOLVER_NLA2_CONTENTS.length, - ); + file.setContents(solverNla2ContentsPtr, utils.SOLVER_NLA2_CONTENTS.length); const document = new loc.SedDocument(file); const simulation = document.simulations.get(0); @@ -262,10 +237,7 @@ describe("Solver KINSOL tests", () => { test("Solve with GMRES linear solver", () => { const file = new loc.File(utils.CELLML_FILE); - file.setContents( - solverNla1ContentsPtr, - utils.SOLVER_NLA1_CONTENTS.length, - ); + file.setContents(solverNla1ContentsPtr, utils.SOLVER_NLA1_CONTENTS.length); const document = new loc.SedDocument(file); const simulation = document.simulations.get(0); @@ -287,10 +259,7 @@ describe("Solver KINSOL tests", () => { test("Solve with BiCGStab linear solver", () => { const file = new loc.File(utils.CELLML_FILE); - file.setContents( - solverNla2ContentsPtr, - utils.SOLVER_NLA2_CONTENTS.length, - ); + file.setContents(solverNla2ContentsPtr, utils.SOLVER_NLA2_CONTENTS.length); const document = new loc.SedDocument(file); const simulation = document.simulations.get(0); @@ -312,10 +281,7 @@ describe("Solver KINSOL tests", () => { test("Solve with TFQMR linear solver", () => { const file = new loc.File(utils.CELLML_FILE); - file.setContents( - solverNla1ContentsPtr, - utils.SOLVER_NLA1_CONTENTS.length, - ); + file.setContents(solverNla1ContentsPtr, utils.SOLVER_NLA1_CONTENTS.length); const document = new loc.SedDocument(file); const simulation = document.simulations.get(0); @@ -333,5 +299,4 @@ describe("Solver KINSOL tests", () => { document.delete(); file.delete(); }); - */ }); diff --git a/tests/bindings/javascript/solver.secondorderrungekutta.test.js b/tests/bindings/javascript/solver.secondorderrungekutta.test.js index 1f08ee9ae..db8404028 100644 --- a/tests/bindings/javascript/solver.secondorderrungekutta.test.js +++ b/tests/bindings/javascript/solver.secondorderrungekutta.test.js @@ -75,14 +75,26 @@ describe("Solver Second-Order Runge-Kutta tests", () => { odeModel.run( document, - [-63.886525, 0.135009, 0.984334, 0.740971], - [49.725722, -0.128194, -0.050903, 0.098651], - [1.0, 0.0, 0.3, 120.0, 36.0], - [-10.613, -115.0, 12.0], [ - 0.0, -15.982058, -823.516942, 789.779614, 3.969929, 0.114985, 0.00287, - 0.967348, 0.541338, 0.056246, + -63.88652516147998, 0.13500864016199698, 0.9843341849003562, + 0.7409712245332702, ], + [7, 7, 7, 7], + [ + 49.725721814665114, -0.12819399753080235, -0.0509025737713709, + 0.09865110169688351, + ], + [7, 7, 7, 7], + [1, 0, 0.3, 120, 36], + [7, 7, 7, 7, 7], + [-10.613, -115, 12], + [7, 7, 7], + [ + 0, -15.982057548443994, -823.5169415955212, 789.7796140225834, + 3.969928522897354, 0.11498460412672941, 0.0028695894879267065, + 0.9673480100796346, 0.5413381517778336, 0.056245845061545056, + ], + [7, 7, 7, 7, 7, 7, 7, 7, 7, 7], ); solver.delete(); diff --git a/tests/bindings/javascript/utils.js.in b/tests/bindings/javascript/utils.js.in index 66adef441..84291582b 100644 --- a/tests/bindings/javascript/utils.js.in +++ b/tests/bindings/javascript/utils.js.in @@ -105,6 +105,7 @@ export const SOLVER_NLA2_CONTENTS = stringToArrayBuffer( export const ALGEBRAIC_CONTENTS = stringToArrayBuffer(`@ALGEBRAIC_CONTENTS@`); export const DAE_CONTENTS = stringToArrayBuffer(`@DAE_CONTENTS@`); +export const MATH_CONTENTS = stringToArrayBuffer(`@MATH_CONTENTS@`); export const NLA_CONTENTS = stringToArrayBuffer(`@NLA_CONTENTS@`); export const OVERCONSTRAINED_CONTENTS = stringToArrayBuffer( @@ -151,41 +152,67 @@ export function expectIssues(loc, logger, expectedIssues) { } } +function expectValue(value, expectedValue, precision) { + if (expectedValue === Infinity) { + expect(value).toBe(Infinity); + } else if (Number.isNaN(expectedValue)) { + expect(Number.isNaN(value)).toBe(true); + } else { + expect(value).toBeCloseTo(expectedValue, precision); + } +} + export function expectValues( instanceTask, index, stateValues, + statePrecisions, rateValues, + ratePrecisions, constantValues, + constantPrecisions, computedConstantValues, + computedConstantPrecisions, algebraicValues, + algebraicPrecisions, ) { for (let i = 0; i < instanceTask.stateCount; ++i) { - expect(instanceTask.stateAsArray(i)[index]).toBeCloseTo(stateValues[i], 6); + expectValue( + instanceTask.stateAsArray(i)[index], + stateValues[i], + statePrecisions[i], + ); } for (let i = 0; i < instanceTask.rateCount; ++i) { - expect(instanceTask.rateAsArray(i)[index]).toBeCloseTo(rateValues[i], 6); + expectValue( + instanceTask.rateAsArray(i)[index], + rateValues[i], + ratePrecisions[i], + ); } for (let i = 0; i < instanceTask.constantCount; ++i) { - expect(instanceTask.constantAsArray(i)[index]).toBeCloseTo( + expectValue( + instanceTask.constantAsArray(i)[index], constantValues[i], - 6, + constantPrecisions[i], ); } for (let i = 0; i < instanceTask.computedConstantCount; ++i) { - expect(instanceTask.computedConstantAsArray(i)[index]).toBeCloseTo( + expectValue( + instanceTask.computedConstantAsArray(i)[index], computedConstantValues[i], - 6, + computedConstantPrecisions[i], ); } for (let i = 0; i < instanceTask.algebraicCount; ++i) { - expect(instanceTask.algebraicAsArray(i)[index]).toBeCloseTo( + expectValue( + instanceTask.algebraicAsArray(i)[index], algebraicValues[i], - 6, + algebraicPrecisions[i], ); } } diff --git a/tests/bindings/python/ode_model.py b/tests/bindings/python/ode_model.py index 1093b34cb..9f9b3c925 100644 --- a/tests/bindings/python/ode_model.py +++ b/tests/bindings/python/ode_model.py @@ -28,14 +28,13 @@ def run( computed_constant_abs_tols, algebraic_values, algebraic_abs_tols, - compiled=True, ): simulation = document.simulations[0] simulation.output_end_time = 50.0 simulation.number_of_steps = 50000 - instance = document.instantiate(compiled) + instance = document.instantiate() instance.run() diff --git a/tests/bindings/python/test_sed_coverage.py b/tests/bindings/python/test_sed_coverage.py index 1df010de3..0ff724e36 100644 --- a/tests/bindings/python/test_sed_coverage.py +++ b/tests/bindings/python/test_sed_coverage.py @@ -14,8 +14,9 @@ import libopencor as loc +import math import utils -from utils import assert_issues +from utils import assert_issues, assert_values def test_initialise(): @@ -503,3 +504,110 @@ def test_solver(): instance.run() assert instance.has_issues == False + + +def test_math(): + computed_constant_values = [ + 243.0, + 3.0, + 7.0, + 20.085536923187668, + 1.0986122886681098, + 0.47712125471966244, + 4.0, + 3.0, + 3.0, + 5.0, + 3.0, + 0.14112000805986721, + -0.98999249660044542, + -0.1425465430742778, + -1.0101086659079939, + 7.0861673957371867, + -7.0152525514345339, + 10.017874927409903, + 10.067661995777765, + 0.99505475368673046, + 0.099327927419433207, + 0.099821569668822732, + 1.0049698233136892, + 0.3046926540153975, + 1.266103672779499, + 1.2490457723982544, + 1.2309594173407747, + 0.33983690945412193, + 0.32175055439664219, + 1.8184464592320668, + 1.7627471740390861, + 0.30951960420311175, + 1.8738202425274144, + 0.32745015023725843, + 0.34657359027997264, + math.inf, + math.nan, + ] + computed_constant_abs_tols = [ + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + 0.0000001, + ] + + file = loc.File(utils.resource_path("api/sed/math.cellml")) + document = loc.SedDocument(file) + instance = document.instantiate() + instance_task = instance.tasks[0] + + assert instance_task.constant_count == 0 + assert instance_task.computed_constant_count == 37 + assert instance_task.algebraic_count == 0 + + instance.run() + + assert_values( + instance_task, + 0, + [], + [], + [], + [], + [], + [], + computed_constant_values, + computed_constant_abs_tols, + [], + [], + ) diff --git a/tests/bindings/python/test_sed_instance.py b/tests/bindings/python/test_sed_instance.py index 66c5c25fc..2b70228eb 100644 --- a/tests/bindings/python/test_sed_instance.py +++ b/tests/bindings/python/test_sed_instance.py @@ -113,34 +113,26 @@ def test_unsuitable_constrained_cellml_file(): assert_issues(instance, expected_issues) -def run_algebraic_model(compiled): +def run_algebraic_model(): file = loc.File(utils.resource_path("api/sed/algebraic.cellml")) document = loc.SedDocument(file) - instance = document.instantiate(compiled) + instance = document.instantiate() instance.run() assert instance.has_issues == False -def test_compiled_algebraic_model(): - run_algebraic_model(True) - - -def test_interpreted_algebraic_model(): - run_algebraic_model(False) +def test_algebraic_model(): + run_algebraic_model() -def run_ode_model(compiled): +def run_ode_model(): expected_issues = [ [ loc.Issue.Type.Error, ( - ( - "At t = 0.00140013827899707, mxstep steps taken before reaching tout." - if compiled - else "At t = 0.00140013827900052, mxstep steps taken before reaching tout." - ) + "At t = 0.00140013827899707, mxstep steps taken before reaching tout." if platform.system() == "Darwin" else "At t = 0.00140013827899996, mxstep steps taken before reaching tout." ), @@ -154,7 +146,7 @@ def run_ode_model(compiled): cvode.maximum_number_of_steps = 10 - instance = document.instantiate(compiled) + instance = document.instantiate() assert instance.has_issues == False @@ -164,19 +156,15 @@ def run_ode_model(compiled): cvode.maximum_number_of_steps = 500 - instance = document.instantiate(compiled) + instance = document.instantiate() instance.run() assert instance.has_issues == False -def test_compiled_ode_model(): - run_ode_model(True) - - -def test_interpreted_ode_model(): - run_ode_model(False) +def test_ode_model(): + run_ode_model() def test_ode_model_with_no_ode_solver(): @@ -198,8 +186,6 @@ def test_ode_model_with_no_ode_solver(): def test_nla_model(): - # ---GRY--- AS FOR THE ALGEBRAIC AND ODE MODELS, WE WILL NEED TO ADD AN INTERPRETED VERSION OF THIS TEST. - expected_issues = [ [ loc.Issue.Type.Error, @@ -245,8 +231,6 @@ def test_nla_model_with_no_nla_solver(): def test_dae_model(): - # ---GRY--- AS FOR THE ALGEBRAIC AND ODE MODELS, WE WILL NEED TO ADD AN INTERPRETED VERSION OF THIS TEST. - expected_issues = [ [ loc.Issue.Type.Error, diff --git a/tests/bindings/python/test_solver_cvode.py b/tests/bindings/python/test_solver_cvode.py index 7cb2dc43e..81c882392 100644 --- a/tests/bindings/python/test_solver_cvode.py +++ b/tests/bindings/python/test_solver_cvode.py @@ -195,7 +195,6 @@ def cvode_solve( computed_constant_abs_tols, algebraic_values, algebraic_abs_tols, - compiled, ): file = loc.File(utils.resource_path("api/solver/ode.cellml")) document = loc.SedDocument(file) @@ -212,11 +211,10 @@ def cvode_solve( computed_constant_abs_tols, algebraic_values, algebraic_abs_tols, - compiled, ) -def test_compiled_solve(): +def test_solve(): state_values = [-63.886, 0.135007, 0.984333, 0.740973] state_abs_tols = [0.001, 0.000001, 0.000001, 0.000001] rate_values = [49.726, -0.128192, -0.050904, 0.098649] @@ -261,56 +259,6 @@ def test_compiled_solve(): computed_constant_abs_tols, algebraic_values, algebraic_abs_tols, - True, - ) - - -def test_interpreted_solve(): - state_values = [-63.886, 0.135008, 0.984333, 0.740972] - state_abs_tols = [0.001, 0.000001, 0.000001, 0.000001] - rate_values = [49.726, -0.128193, -0.05090, 0.09865] - rate_abs_tols = [0.001, 0.000001, 0.00001, 0.00001] - constant_values = [1.0, 0.0, 0.3, 120.0, 36.0] - constant_abs_tols = [0.0, 0.0, 0.0, 0.0, 0.0] - computed_constant_values = [-10.613, -115.0, 12.0] - computed_constant_abs_tols = [0.0, 0.0, 0.0] - algebraic_values = [ - 0.0, - -15.9819, - -823.517, - 789.779, - 3.9699, - 0.11499, - 0.002869, - 0.967347, - 0.54133, - 0.056246, - ] - algebraic_abs_tols = [ - 0.0, - 0.0001, - 0.001, - 0.001, - 0.0001, - 0.00001, - 0.000001, - 0.000001, - 0.00001, - 0.000001, - ] - - cvode_solve( - state_values, - state_abs_tols, - rate_values, - rate_abs_tols, - constant_values, - constant_abs_tols, - computed_constant_values, - computed_constant_abs_tols, - algebraic_values, - algebraic_abs_tols, - False, ) diff --git a/tests/bindings/python/test_solver_forwardeuler.py b/tests/bindings/python/test_solver_forwardeuler.py index fda196bfc..a1d57a3e1 100644 --- a/tests/bindings/python/test_solver_forwardeuler.py +++ b/tests/bindings/python/test_solver_forwardeuler.py @@ -54,7 +54,6 @@ def forward_euler_solve( computed_constant_abs_tols, algebraic_values, algebraic_abs_tols, - compiled, ): file = loc.File(utils.resource_path("api/solver/ode.cellml")) document = loc.SedDocument(file) @@ -77,7 +76,6 @@ def forward_euler_solve( computed_constant_abs_tols, algebraic_values, algebraic_abs_tols, - compiled, ) @@ -115,7 +113,7 @@ def forward_euler_solve( ] -def test_compiled_solve(): +def test_solve(): forward_euler_solve( state_values, state_abs_tols, @@ -127,21 +125,4 @@ def test_compiled_solve(): computed_constant_abs_tols, algebraic_values, algebraic_abs_tols, - True, - ) - - -def test_interpreted_solve(): - forward_euler_solve( - state_values, - state_abs_tols, - rate_values, - rate_abs_tols, - constant_values, - constant_abs_tols, - computed_constant_values, - computed_constant_abs_tols, - algebraic_values, - algebraic_abs_tols, - False, ) diff --git a/tests/bindings/python/test_solver_fourthorderrungekutta.py b/tests/bindings/python/test_solver_fourthorderrungekutta.py index b145fc427..e987d9463 100644 --- a/tests/bindings/python/test_solver_fourthorderrungekutta.py +++ b/tests/bindings/python/test_solver_fourthorderrungekutta.py @@ -54,7 +54,6 @@ def fourth_order_runge_kutta_solve( computed_constant_abs_tols, algebraic_values, algebraic_abs_tols, - compiled, ): file = loc.File(utils.resource_path("api/solver/ode.cellml")) document = loc.SedDocument(file) @@ -77,7 +76,6 @@ def fourth_order_runge_kutta_solve( computed_constant_abs_tols, algebraic_values, algebraic_abs_tols, - compiled, ) @@ -115,7 +113,7 @@ def fourth_order_runge_kutta_solve( ] -def test_compiled_solve(): +def test_solve(): fourth_order_runge_kutta_solve( state_values, state_abs_tols, @@ -127,21 +125,4 @@ def test_compiled_solve(): computed_constant_abs_tols, algebraic_values, algebraic_abs_tols, - True, - ) - - -def test_interpreted_solve(): - fourth_order_runge_kutta_solve( - state_values, - state_abs_tols, - rate_values, - rate_abs_tols, - constant_values, - constant_abs_tols, - computed_constant_values, - computed_constant_abs_tols, - algebraic_values, - algebraic_abs_tols, - False, ) diff --git a/tests/bindings/python/test_solver_heun.py b/tests/bindings/python/test_solver_heun.py index c4107b936..c3d4cffb2 100644 --- a/tests/bindings/python/test_solver_heun.py +++ b/tests/bindings/python/test_solver_heun.py @@ -54,7 +54,6 @@ def heun_solve( computed_constant_abs_tols, algebraic_values, algebraic_abs_tols, - compiled, ): file = loc.File(utils.resource_path("api/solver/ode.cellml")) document = loc.SedDocument(file) @@ -77,7 +76,6 @@ def heun_solve( computed_constant_abs_tols, algebraic_values, algebraic_abs_tols, - compiled, ) @@ -115,7 +113,7 @@ def heun_solve( ] -def test_compiled_solve(): +def test_solve(): heun_solve( state_values, state_abs_tols, @@ -127,21 +125,4 @@ def test_compiled_solve(): computed_constant_abs_tols, algebraic_values, algebraic_abs_tols, - True, - ) - - -def test_interpreted_solve(): - heun_solve( - state_values, - state_abs_tols, - rate_values, - rate_abs_tols, - constant_values, - constant_abs_tols, - computed_constant_values, - computed_constant_abs_tols, - algebraic_values, - algebraic_abs_tols, - False, ) diff --git a/tests/bindings/python/test_solver_kinsol.py b/tests/bindings/python/test_solver_kinsol.py index 7a6892ba3..5aeb25fac 100644 --- a/tests/bindings/python/test_solver_kinsol.py +++ b/tests/bindings/python/test_solver_kinsol.py @@ -124,10 +124,6 @@ def test_banded_linear_solver_and_lower_half_bandwidth_value_with_number_too_big assert_issues(instance, expected_issues) -# ---GRY--- RENAME THIS TEST test_compiled_solve AND CREATE ONE CALLED test_interpreted_solve ONCE WE CAN INTERPRET -# NLA-BASED MODELS. - - ABS_TOL = 1e-05 diff --git a/tests/bindings/python/test_solver_secondorderrungekutta.py b/tests/bindings/python/test_solver_secondorderrungekutta.py index 1f17a5070..fb8c4c31a 100644 --- a/tests/bindings/python/test_solver_secondorderrungekutta.py +++ b/tests/bindings/python/test_solver_secondorderrungekutta.py @@ -54,7 +54,6 @@ def second_order_runge_kutta_solve( computed_constant_abs_tols, algebraic_values, algebraic_abs_tols, - compiled, ): file = loc.File(utils.resource_path("api/solver/ode.cellml")) document = loc.SedDocument(file) @@ -77,7 +76,6 @@ def second_order_runge_kutta_solve( computed_constant_abs_tols, algebraic_values, algebraic_abs_tols, - compiled, ) @@ -115,7 +113,7 @@ def second_order_runge_kutta_solve( ] -def test_compiled_solve(): +def test_solve(): second_order_runge_kutta_solve( state_values, state_abs_tols, @@ -127,21 +125,4 @@ def test_compiled_solve(): computed_constant_abs_tols, algebraic_values, algebraic_abs_tols, - True, - ) - - -def test_interpreted_solve(): - second_order_runge_kutta_solve( - state_values, - state_abs_tols, - rate_values, - rate_abs_tols, - constant_values, - constant_abs_tols, - computed_constant_values, - computed_constant_abs_tols, - algebraic_values, - algebraic_abs_tols, - False, ) diff --git a/tests/bindings/python/utils.py.in b/tests/bindings/python/utils.py.in index c217a6643..314122f36 100644 --- a/tests/bindings/python/utils.py.in +++ b/tests/bindings/python/utils.py.in @@ -15,6 +15,7 @@ import base64 import libopencor as loc +import math import os import pathlib import platform @@ -79,6 +80,15 @@ def assert_issues(logger, expected_issues): assert issues[i].type_as_string == "Warning" +def assert_value(value, expected_value, abs_tol): + if math.isinf(expected_value): + assert math.isinf(value) is True + elif math.isnan(expected_value): + assert math.isnan(value) is True + else: + assert value == pytest.approx(expected_value, abs=abs_tol) + + def assert_values( instance_task, index, @@ -94,28 +104,30 @@ def assert_values( algebraic_abs_tols, ): for i in range(instance_task.state_count): - assert instance_task.state(i)[index] == pytest.approx( - state_values[i], abs=state_abs_tols[i] - ) + assert_value(instance_task.state(i)[index], state_values[i], state_abs_tols[i]) for i in range(instance_task.rate_count): - assert instance_task.rate(i)[index] == pytest.approx( - rate_values[i], abs=rate_abs_tols[i] - ) + assert_value(instance_task.rate(i)[index], rate_values[i], rate_abs_tols[i]) for i in range(instance_task.constant_count): - assert instance_task.constant(i)[index] == pytest.approx( - constant_values[i], abs=constant_abs_tols[i] + assert_value( + instance_task.constant(i)[index], + constant_values[i], + constant_abs_tols[i], ) for i in range(instance_task.computed_constant_count): - assert instance_task.computed_constant(i)[index] == pytest.approx( - computed_constant_values[i], abs=computed_constant_abs_tols[i] + assert_value( + instance_task.computed_constant(i)[index], + computed_constant_values[i], + computed_constant_abs_tols[i], ) for i in range(instance_task.algebraic_count): - assert instance_task.algebraic(i)[index] == pytest.approx( - algebraic_values[i], abs=algebraic_abs_tols[i] + assert_value( + instance_task.algebraic(i)[index], + algebraic_values[i], + algebraic_abs_tols[i], ) diff --git a/tests/install/src/main.cpp b/tests/install/src/main.cpp index 38e478b30..0735a673c 100644 --- a/tests/install/src/main.cpp +++ b/tests/install/src/main.cpp @@ -5,10 +5,10 @@ int main() { // Output the version of libOpenCOR. - std::cout << "---[ libOpenCOR version information ]---" << std::endl; - std::cout << std::endl; - std::cout << "Version: " << libOpenCOR::version() << std::endl; - std::cout << "Version string: " << libOpenCOR::versionString() << std::endl; + std::cout << "---[ libOpenCOR version information ]---\n"; + std::cout << "\n"; + std::cout << "Version: " << libOpenCOR::version() << "\n"; + std::cout << "Version string: " << libOpenCOR::versionString() << "\n"; // Load a COMBINE archive from GitHub and output some information about it. @@ -25,21 +25,21 @@ int main() auto instanceTask = instance->tasks()[0]; - std::cout << std::endl; - std::cout << "---[ File information ]---" << std::endl; - std::cout << std::endl; - std::cout << "URL: " << file->url() << std::endl; - std::cout << "Number of:" << std::endl; - std::cout << " - States and rates: " << instanceTask->stateCount() << std::endl; - std::cout << " - Constants: " << instanceTask->constantCount() << std::endl; - std::cout << " - Computed constants: " << instanceTask->computedConstantCount() << std::endl; - std::cout << " - Algebraic variables: " << instanceTask->algebraicCount() << std::endl; + std::cout << "\n"; + std::cout << "---[ File information ]---\n"; + std::cout << "\n"; + std::cout << "URL: " << file->url() << "\n"; + std::cout << "Number of:\n"; + std::cout << " - States and rates: " << instanceTask->stateCount() << "\n"; + std::cout << " - Constants: " << instanceTask->constantCount() << "\n"; + std::cout << " - Computed constants: " << instanceTask->computedConstantCount() << "\n"; + std::cout << " - Algebraic variables: " << instanceTask->algebraicCount() << "\n"; // Output the results of the simulation. - std::cout << std::endl; - std::cout << "---[ Simulation results ]---" << std::endl; - std::cout << std::endl; + std::cout << "\n"; + std::cout << "---[ Simulation results ]---\n"; + std::cout << "\n"; std::cout << instanceTask->voiName() << " (" << instanceTask->voiUnit() << ")"; for (size_t i = 0; i < instanceTask->stateCount(); ++i) { @@ -62,7 +62,7 @@ int main() std::cout << "," << instanceTask->algebraicName(i) << " (" << instanceTask->algebraicUnit(i) << ")"; } - std::cout << std::endl; + std::cout << "\n"; for (size_t j = 0; j < instanceTask->voi().size(); ++j) { std::cout << instanceTask->voi()[j]; @@ -87,7 +87,7 @@ int main() std::cout << "," << instanceTask->algebraic(i)[j]; } - std::cout << std::endl; + std::cout << "\n"; } return 0; diff --git a/tests/res/api/sed/math.cellml b/tests/res/api/sed/math.cellml new file mode 100644 index 000000000..f799076b4 --- /dev/null +++ b/tests/res/api/sed/math.cellml @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + power + + + 3.0 + 5.0 + + + + + sqrt + + + 9.0 + 0.5 + + + + + abs + + + -7.0 + + + + + exp + + + 3.0 + + + + + ln + + + 3.0 + + + + + log + + + 3.0 + + + + + ceiling + + + 3.5 + + + + + floor + + + 3.5 + + + + + min + + + 3.0 + 5.0 + + + + + max + + + 3.0 + 5.0 + + + + + rem + + + 13.0 + 5.0 + + + + + + + sin + + + 3.0 + + + + + cos + + + 3.0 + + + + + tan + + + 3.0 + + + + + sec + + + 3.0 + + + + + csc + + + 3.0 + + + + + cot + + + 3.0 + + + + + sinh + + + 3.0 + + + + + cosh + + + 3.0 + + + + + tanh + + + 3.0 + + + + + sech + + + 3.0 + + + + + csch + + + 3.0 + + + + + coth + + + 3.0 + + + + + arcsin + + + 0.3 + + + + + arccos + + + 0.3 + + + + + arctan + + + 3.0 + + + + + arcsec + + + 3.0 + + + + + arccsc + + + 3.0 + + + + + arccot + + + 3.0 + + + + + arcsinh + + + 3.0 + + + + + arccosh + + + 3.0 + + + + + arctanh + + + 0.3 + + + + + arcsech + + + 0.3 + + + + + arccsch + + + 3.0 + + + + + arccoth + + + 3.0 + + + + + + + infinity + + + + + notanumber + + + + + diff --git a/tests/utils.cpp b/tests/utils.cpp index 38ac39af6..da21c8726 100644 --- a/tests/utils.cpp +++ b/tests/utils.cpp @@ -20,26 +20,12 @@ limitations under the License. #include "../extern/modp_b64/modp_b64.h" +#include #include #include namespace libOpenCOR { -void printIssues(const LoggerPtr &pLogger) -{ - printf("---[ISSUES]---[BEGIN]\n"); // NOLINT - - for (auto &issue : pLogger->issues()) { - const auto *type = (issue->type() == Issue::Type::ERROR) ? - "ERROR" : - "WARNING"; - - printf("%s: %s\n", type, issue->description().c_str()); // NOLINT - } - - printf("---[ISSUES]---[END]\n"); // NOLINT -} - void expectEqualIssues(const LoggerPtr &pLogger, const ExpectedIssues &pExpectedIssues) { auto issues = pLogger->issues(); @@ -58,6 +44,21 @@ void expectEqualIssues(const LoggerPtr &pLogger, const ExpectedIssues &pExpected } } +namespace { + +void expectEqualValue(double pValue, double pExpectedValue, double pAbsTol) +{ + if (std::isinf(pExpectedValue)) { + EXPECT_TRUE(std::isinf(pValue)); + } else if (std::isnan(pExpectedValue)) { + EXPECT_TRUE(std::isnan(pValue)); + } else { + EXPECT_NEAR(pValue, pExpectedValue, pAbsTol); + } +} + +} // namespace + void expectEqualValues(const SedInstanceTaskPtr &pInstanceTask, size_t pIndex, const Doubles &pStateValues, const Doubles &pStateAbsTols, const Doubles &pRateValues, const Doubles &pRateAbsTols, @@ -66,23 +67,23 @@ void expectEqualValues(const SedInstanceTaskPtr &pInstanceTask, size_t pIndex, const Doubles &pAlgebraicValues, const Doubles &pAlgebraicAbsTols) { for (size_t i = 0; i < pInstanceTask->stateCount(); ++i) { - EXPECT_NEAR(pInstanceTask->state(i)[pIndex], pStateValues[i], pStateAbsTols[i]); + expectEqualValue(pInstanceTask->state(i)[pIndex], pStateValues[i], pStateAbsTols[i]); } for (size_t i = 0; i < pInstanceTask->rateCount(); ++i) { - EXPECT_NEAR(pInstanceTask->rate(i)[pIndex], pRateValues[i], pRateAbsTols[i]); + expectEqualValue(pInstanceTask->rate(i)[pIndex], pRateValues[i], pRateAbsTols[i]); } for (size_t i = 0; i < pInstanceTask->constantCount(); ++i) { - EXPECT_NEAR(pInstanceTask->constant(i)[pIndex], pConstantValues[i], pConstantAbsTols[i]); + expectEqualValue(pInstanceTask->constant(i)[pIndex], pConstantValues[i], pConstantAbsTols[i]); } for (size_t i = 0; i < pInstanceTask->computedConstantCount(); ++i) { - EXPECT_NEAR(pInstanceTask->computedConstant(i)[pIndex], pComputedConstantValues[i], pComputedConstantAbsTols[i]); + expectEqualValue(pInstanceTask->computedConstant(i)[pIndex], pComputedConstantValues[i], pComputedConstantAbsTols[i]); } for (size_t i = 0; i < pInstanceTask->algebraicCount(); ++i) { - EXPECT_NEAR(pInstanceTask->algebraic(i)[pIndex], pAlgebraicValues[i], pAlgebraicAbsTols[i]); + expectEqualValue(pInstanceTask->algebraic(i)[pIndex], pAlgebraicValues[i], pAlgebraicAbsTols[i]); } } diff --git a/tests/utils.h.in b/tests/utils.h.in index ef1e6edd7..2b117f78f 100644 --- a/tests/utils.h.in +++ b/tests/utils.h.in @@ -66,8 +66,6 @@ static constexpr auto WARNING_CELLML_CONTENTS = "@WARNING_CELLML_CONTENTS_C@"; static constexpr auto SEDML_CONTENTS = "@SEDML_CONTENTS_C@"; static constexpr auto BASE64_COMBINE_ARCHIVE_CONTENTS = "@BASE64_COMBINE_ARCHIVE_CONTENTS@"; -void printIssues(const LoggerPtr &pLogger); - typedef struct { Issue::Type type;