From 7c29cd8b20d3ef1f60f56ed8ceaac9c22c647247 Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Wed, 25 Jun 2025 14:43:51 -0400 Subject: [PATCH 01/26] Updates to build: allow argparse (and integration with CLion) --- .gitignore | 1 + CMakeLists.txt | 15 +++++++++++++-- IDAWin/CMakeLists.txt | 31 +++++++------------------------ conanfile.py | 1 + 4 files changed, 22 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index 0f8a2f4ad..f57332c81 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ IDAWin/tests/smoke/SimID_1489333437_0_.ida CMakeUserPresets.json conan-build conan-build/* +conan_provider.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index f0400e856..751592c4c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ # Top Level CMake File # ############################################## -cmake_minimum_required(VERSION 3.13) +cmake_minimum_required(VERSION 3.14) project(vcell-ode-numerics) set(CMAKE_CXX_STANDARD 20) if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") @@ -195,10 +195,21 @@ set(DOWNLOAD_EXTRACT_TIMESTAMP true) cmake_policy(SET CMP0135 NEW) ############################################# # -# Declare and install dependency: Google-test +# Declare and dynamically net-install dependencies # ############################################## include(FetchContent) +############################################# +# Argparse +############################################## +FetchContent_Declare( + argparse + GIT_REPOSITORY https://github.com/p-ranav/argparse.git +) +FetchContent_MakeAvailable(argparse) +############################################# +# GoogleTest +############################################## FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git diff --git a/IDAWin/CMakeLists.txt b/IDAWin/CMakeLists.txt index 0dcf9f0a3..76cf35abc 100644 --- a/IDAWin/CMakeLists.txt +++ b/IDAWin/CMakeLists.txt @@ -20,7 +20,7 @@ set (EXE_SRC_FILES ) add_library(IDAWin STATIC ${SRC_FILES} ${HEADER_FILES}) -target_link_libraries(IDAWin sundials ExpressionParser vcellmessaging) +target_link_libraries(IDAWin sundials ExpressionParser vcellmessaging argparse) set(EXE_FILE SundialsSolverStandalone) if (ARCH_64bit) set(EXE_FILE ${EXE_FILE}_x64) @@ -32,30 +32,13 @@ target_link_libraries(${EXE_FILE} IDAWin) install(TARGETS ${EXE_FILE} RUNTIME DESTINATION ${OPTION_EXE_DIRECTORY}) install(TARGETS IDAWin ARCHIVE DESTINATION bin) - +# # # # # # # # # # # # # # # # # +# smoke test as a python script, for bash test example, see NFsim/tests/smoke +# # # # # # # # # # # # # # # # # enable_testing() - -if (MINGW) - set(test_sundials_exe ${CMAKE_BINARY_DIR}/bin/${EXE_FILE}.exe) - set(python_cmd py) -else (MINGW) - set(test_sundials_exe ${CMAKE_BINARY_DIR}/bin/${EXE_FILE}) - set(python_cmd python3) -endif (MINGW) set(test_dir ${CMAKE_CURRENT_SOURCE_DIR}/tests/smoke) - -# smoke test as a python script, for bash test example, see NFsim/tests/smoke -add_test(NAME ${EXE_FILE}_smoke COMMAND ${python_cmd} ${test_dir}/smoke.py ${test_sundials_exe} WORKING_DIRECTORY ${test_dir}) - - -add_executable( - hello_test - hello_test.cpp -) -target_link_libraries( - hello_test - GTest::gtest_main -) - include(GoogleTest) +add_test(NAME ${EXE_FILE}_smoke COMMAND ${python_cmd} ${test_dir}/smoke.py ${test_sundials_exe} WORKING_DIRECTORY ${test_dir}) +add_executable(hello_test hello_test.cpp) +target_link_libraries(hello_test GTest::gtest_main) gtest_discover_tests(hello_test) \ No newline at end of file diff --git a/conanfile.py b/conanfile.py index 1d76fd804..2e7c89975 100644 --- a/conanfile.py +++ b/conanfile.py @@ -30,6 +30,7 @@ def build(self): cmake.definitions["OPTION_TARGET_DOCS"] = "ON" if self.options.generate_docs else "OFF" def requirements(self): + self.requires("argparse/[>=3.2 <4.0]") if self.options.include_messaging: self.requires("libcurl/[<9.0]") From f3c31071f1acd8787919b9c569ef819a36d3734e Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Wed, 25 Jun 2025 14:46:43 -0400 Subject: [PATCH 02/26] [WIP] Cleaned up most warnings --- ExpressionParser/ASTAddNode.cpp | 6 +- ExpressionParser/ASTAddNode.h | 4 +- ExpressionParser/ASTAndNode.cpp | 6 +- ExpressionParser/ASTAndNode.h | 4 +- ExpressionParser/ASTExpression.cpp | 6 +- ExpressionParser/ASTExpression.h | 4 +- ExpressionParser/ASTFloatNode.cpp | 16 +- ExpressionParser/ASTFloatNode.h | 4 +- ExpressionParser/ASTFuncNode.cpp | 147 +++-- ExpressionParser/ASTFuncNode.h | 8 +- ExpressionParser/ASTIdNode.cpp | 18 +- ExpressionParser/ASTIdNode.h | 10 +- ExpressionParser/ASTInvertTermNode.cpp | 6 +- ExpressionParser/ASTInvertTermNode.h | 4 +- ExpressionParser/ASTMinusTermNode.cpp | 6 +- ExpressionParser/ASTMinusTermNode.h | 4 +- ExpressionParser/ASTMultNode.cpp | 15 +- ExpressionParser/ASTMultNode.h | 4 +- ExpressionParser/ASTNotNode.cpp | 6 +- ExpressionParser/ASTNotNode.h | 4 +- ExpressionParser/ASTOrNode.cpp | 6 +- ExpressionParser/ASTOrNode.h | 4 +- ExpressionParser/ASTPowerNode.cpp | 16 +- ExpressionParser/ASTPowerNode.h | 4 +- ExpressionParser/ASTRelationalNode.cpp | 8 +- ExpressionParser/ASTRelationalNode.h | 8 +- ExpressionParser/Exception.cpp | 9 +- ExpressionParser/Expression.cpp | 34 +- ExpressionParser/Expression.h | 20 +- ExpressionParser/ExpressionParser.cpp | 16 +- ExpressionParser/ExpressionParser.h | 7 +- ExpressionParser/ExpressionParserConstants.h | 4 +- .../ExpressionParserTokenManager.cpp | 22 +- .../ExpressionParserTokenManager.h | 12 +- .../ExpressionParserTreeConstants.h | 2 +- ExpressionParser/ExpressionTest.cpp | 90 ++- ExpressionParser/JJTExpressionParserState.h | 5 +- ExpressionParser/Node.cpp | 22 +- ExpressionParser/Node.h | 18 +- ExpressionParser/ParseException.cpp | 4 +- ExpressionParser/SimpleCharStream.cpp | 14 +- ExpressionParser/SimpleCharStream.h | 14 +- ExpressionParser/SimpleSymbolTable.cpp | 4 +- ExpressionParser/SimpleSymbolTable.h | 5 +- ExpressionParser/SimpleSymbolTableEntry.cpp | 4 +- ExpressionParser/SimpleSymbolTableEntry.h | 6 +- ExpressionParser/StackMachine.cpp | 54 +- ExpressionParser/SymbolTable.h | 2 +- ExpressionParser/SymbolTableEntry.h | 3 +- ExpressionParser/Token.h | 3 +- ExpressionParserTest/ExpressionParserTest.cpp | 53 +- IDAWin/OdeResultSet.cpp | 212 ++++--- IDAWin/OdeResultSet.h | 113 ++-- IDAWin/StoppedByUserException.cpp | 9 +- IDAWin/StoppedByUserException.h | 7 +- IDAWin/VCellCVodeSolver.cpp | 49 +- IDAWin/VCellCVodeSolver.h | 15 +- IDAWin/VCellIDASolver.cpp | 19 +- IDAWin/VCellIDASolver.h | 3 +- IDAWin/VCellSundialsSolver.cpp | 517 ++++++++---------- IDAWin/VCellSundialsSolver.h | 97 ++-- VCellMessaging/src/SimulationMessaging.cpp | 26 +- 62 files changed, 836 insertions(+), 986 deletions(-) diff --git a/ExpressionParser/ASTAddNode.cpp b/ExpressionParser/ASTAddNode.cpp index da0d3994e..5a3a19a31 100644 --- a/ExpressionParser/ASTAddNode.cpp +++ b/ExpressionParser/ASTAddNode.cpp @@ -13,9 +13,9 @@ ASTAddNode::ASTAddNode(int i) : Node(i) { ASTAddNode::~ASTAddNode() { } -string ASTAddNode::infixString(int lang, NameScope* nameScope) +std::string ASTAddNode::infixString(int lang, NameScope* nameScope) { - string buffer("("); + std::string buffer("("); for (int i = 0;i < jjtGetNumChildren(); i ++){ ASTMinusTermNode* pointer = dynamic_cast(jjtGetChild(i)); if (pointer){ @@ -31,7 +31,7 @@ string ASTAddNode::infixString(int lang, NameScope* nameScope) return buffer; } -void ASTAddNode::getStackElements(vector& elements) { +void ASTAddNode::getStackElements(std::vector& elements) { for (int i = 0;i < jjtGetNumChildren(); i ++){ jjtGetChild(i)->getStackElements(elements); if (i>0) diff --git a/ExpressionParser/ASTAddNode.h b/ExpressionParser/ASTAddNode.h index b6728eaba..85bc7ee7f 100644 --- a/ExpressionParser/ASTAddNode.h +++ b/ExpressionParser/ASTAddNode.h @@ -8,8 +8,8 @@ class ASTAddNode : public Node public: ASTAddNode(int i); ~ASTAddNode(); - string infixString(int lang, NameScope* nameScope); - void getStackElements(vector& elements); + std::string infixString(int lang, NameScope* nameScope); + void getStackElements(std::vector& elements); double evaluate(int evalType, double* values=0); Node* copyTree(); diff --git a/ExpressionParser/ASTAndNode.cpp b/ExpressionParser/ASTAndNode.cpp index f9a9adc84..a4d06ab73 100644 --- a/ExpressionParser/ASTAndNode.cpp +++ b/ExpressionParser/ASTAndNode.cpp @@ -16,10 +16,10 @@ bool ASTAndNode::isBoolean() { return true; } -string ASTAndNode::infixString(int lang, NameScope* nameScope) +std::string ASTAndNode::infixString(int lang, NameScope* nameScope) { - string buffer; + std::string buffer; if(lang == LANGUAGE_VISIT){ for (int i=0;i& elements) { +void ASTAndNode::getStackElements(std::vector& elements) { for (int i=0;igetStackElements(elements);; if (i>0) diff --git a/ExpressionParser/ASTAndNode.h b/ExpressionParser/ASTAndNode.h index 9eb15acbe..5df29e60e 100644 --- a/ExpressionParser/ASTAndNode.h +++ b/ExpressionParser/ASTAndNode.h @@ -8,8 +8,8 @@ class ASTAndNode : public Node public: ASTAndNode(int i); ~ASTAndNode(); - string infixString(int lang, NameScope* nameScope); - void getStackElements(vector& elements); + std::string infixString(int lang, NameScope* nameScope); + void getStackElements(std::vector& elements); double evaluate(int evalType, double* values=0); bool isBoolean(); diff --git a/ExpressionParser/ASTExpression.cpp b/ExpressionParser/ASTExpression.cpp index 87c425dcd..50a276019 100644 --- a/ExpressionParser/ASTExpression.cpp +++ b/ExpressionParser/ASTExpression.cpp @@ -11,9 +11,9 @@ ASTExpression::ASTExpression(int i) : Node(i) { ASTExpression::~ASTExpression() { } -string ASTExpression::infixString(int lang, NameScope* nameScope) +std::string ASTExpression::infixString(int lang, NameScope* nameScope) { - string buffer; + std::string buffer; for (int i = 0; i < jjtGetNumChildren(); i++) { buffer += jjtGetChild(i)->infixString(lang, nameScope); @@ -21,7 +21,7 @@ string ASTExpression::infixString(int lang, NameScope* nameScope) return buffer; } -void ASTExpression::getStackElements(vector& elements) { +void ASTExpression::getStackElements(std::vector& elements) { for (int i = 0; i < jjtGetNumChildren(); i++) { jjtGetChild(i)->getStackElements(elements); } diff --git a/ExpressionParser/ASTExpression.h b/ExpressionParser/ASTExpression.h index 547d6a178..e74aaf2da 100644 --- a/ExpressionParser/ASTExpression.h +++ b/ExpressionParser/ASTExpression.h @@ -8,8 +8,8 @@ class ASTExpression : public Node public: ASTExpression(int i); ~ASTExpression(); - string infixString(int lang, NameScope* nameScope); - void getStackElements(vector& elements); + std::string infixString(int lang, NameScope* nameScope); + void getStackElements(std::vector& elements); double evaluate(int evalType, double* values=0); Node* copyTree(); diff --git a/ExpressionParser/ASTFloatNode.cpp b/ExpressionParser/ASTFloatNode.cpp index b52cbcfa5..df9668fe6 100644 --- a/ExpressionParser/ASTFloatNode.cpp +++ b/ExpressionParser/ASTFloatNode.cpp @@ -1,5 +1,4 @@ -#include - +#include #include "ASTFloatNode.h" #include "RuntimeException.h" #include "ExpressionParserTreeConstants.h" @@ -20,21 +19,16 @@ ASTFloatNode::ASTFloatNode(int i) : Node(i) , value(0) ASTFloatNode::~ASTFloatNode() { } -string ASTFloatNode::infixString(int lang, NameScope* nameScope) +std::string ASTFloatNode::infixString(int lang, NameScope* nameScope) { //if (value == NULL) { // return string("NULL"); //} else - if (value == 0.0) { - return string("0.0"); - } else { - char s[256]; - sprintf(s, "%.20lg\0", value); - return string(s); - } + if (value == 0.0) return std::string{"0.0"}; + return std::format(":.20g", value); } -void ASTFloatNode::getStackElements(vector& elements) { +void ASTFloatNode::getStackElements(std::vector& elements) { elements.push_back(StackElement(value)); } diff --git a/ExpressionParser/ASTFloatNode.h b/ExpressionParser/ASTFloatNode.h index 03082f6eb..e11567a3c 100644 --- a/ExpressionParser/ASTFloatNode.h +++ b/ExpressionParser/ASTFloatNode.h @@ -10,8 +10,8 @@ class ASTFloatNode : public Node ASTFloatNode(int i); ~ASTFloatNode(); double value; - string infixString(int lang, NameScope* nameScope); - void getStackElements(vector& elements); + std::string infixString(int lang, NameScope* nameScope); + void getStackElements(std::vector& elements); double evaluate(int evalType, double* values=0); Node* copyTree(); diff --git a/ExpressionParser/ASTFuncNode.cpp b/ExpressionParser/ASTFuncNode.cpp index d531ba1dd..59bf3a0e6 100644 --- a/ExpressionParser/ASTFuncNode.cpp +++ b/ExpressionParser/ASTFuncNode.cpp @@ -2,10 +2,10 @@ #include #include -using std::min; -using std::max; + #include "ASTFuncNode.h" +#include #include "RuntimeException.h" #include "ExpressionException.h" #include "MathUtil.h" @@ -59,7 +59,7 @@ int StackMachine_LookupTable[] = {TYPE_EXP, TYPE_SQRT, TYPE_ABS, TYPE_POW, TYPE_ACOTH, TYPE_ASECH, TYPE_FACTORIAL, TYPE_J1 }; -const string functionNamesVCML[] = { +const std::string functionNamesVCML[] = { "exp", // 0 "sqrt", // 1 "abs", // 2 @@ -112,10 +112,10 @@ ASTFuncNode::ASTFuncNode(int i) : Node(i) { ASTFuncNode::~ASTFuncNode() { } -void ASTFuncNode::setFunctionFromParserToken(string parserToken) +void ASTFuncNode::setFunctionFromParserToken(std::string parserToken) { for (int i = 0; i < parserNumFunctions; i++){ - string definedToken = functionNamesVCML[i]; + std::string definedToken = functionNamesVCML[i]; if (definedToken == parserToken){ funcType = i; funcName = parserToken; @@ -125,9 +125,8 @@ void ASTFuncNode::setFunctionFromParserToken(string parserToken) throw RuntimeException("unsupported function '" + parserToken + "'"); } -string ASTFuncNode::infixString(int lang, NameScope* nameScope) -{ - string buffer; +std::string ASTFuncNode::infixString(int lang, NameScope* nameScope){ + std::string buffer; switch (funcType) { case POW : @@ -167,7 +166,7 @@ string ASTFuncNode::infixString(int lang, NameScope* nameScope) return buffer; } -void ASTFuncNode::getStackElements(vector& elements) { +void ASTFuncNode::getStackElements(std::vector& elements) { for (int i = 0; i < jjtGetNumChildren(); i++) { jjtGetChild(i)->getStackElements(elements); } @@ -196,53 +195,47 @@ double ASTFuncNode::evaluate(int evalType, double* values) Node* mantissaChild = child0; double exponent = 0.0; double mantissa = 0.0; - ExpressionException* exponentException = 0; - ExpressionException* mantissaException = 0; + ExpressionException* exponentException = nullptr; + ExpressionException* mantissaException = nullptr; try { exponent = exponentChild->evaluate(evalType, values); } catch (ExpressionException& e) { if (evalType == EVALUATE_VECTOR) - throw e; + throw; exponentException = new ExpressionException(e.getMessage()); } try { mantissa = mantissaChild->evaluate(evalType, values); } catch (ExpressionException& e) { if (evalType == EVALUATE_VECTOR) - throw e; + throw; mantissaException = new ExpressionException(e.getMessage()); } - if (exponentException == NULL && mantissaException == NULL) { + if (exponentException == nullptr && mantissaException == nullptr) { if (mantissa < 0.0 && (MathUtil::round(exponent) != exponent)) { - char problem[100]; - sprintf(problem, "pow(u,v) and u=%lf<0 and v=%lf not an integer", mantissa, exponent); - string errorMsg = getFunctionDomainError(problem, values, "u", mantissaChild, "v", exponentChild); + std::string problem{std::format("pow(u,v) and u={}<0 and v={} not an integer", mantissa, exponent)}; + std::string errorMsg = getFunctionDomainError(problem, values, "u", mantissaChild, "v", exponentChild); throw FunctionDomainException(errorMsg); } if (mantissa == 0.0 && exponent < 0) { - char problem[100]; - sprintf(problem, "pow(u,v) and u=0 and v=%lf<0 divide by zero", exponent); - string errorMsg = getFunctionDomainError(problem, values, "u", mantissaChild, "v", exponentChild); + std::string problem{std::format("pow(u,v) and u=0 and v={}<0 divide by zero", mantissa, exponent)}; + std::string errorMsg = getFunctionDomainError(problem, values, "u", mantissaChild, "v", exponentChild); throw FunctionDomainException(errorMsg); } result = pow(mantissa, exponent); if (MathUtil::double_infinity == -result || MathUtil::double_infinity == result || result != result) { - char problem[1000]; - sprintf(problem, "u^v evaluated to %lf, u=%lf, v=%lf", result, mantissa, exponent); - string errorMsg = getFunctionDomainError(problem, values, "u", mantissaChild, "v", exponentChild); + std::string problem = std::format("u^v evaluated to {}, u={}, v={}", result, mantissa, exponent); + std::string errorMsg = getFunctionDomainError(problem, values, "u", mantissaChild, "v", exponentChild); throw FunctionDomainException(errorMsg); } - } else if (exponentException == 0 && exponent == 0.0) { + } else if (exponentException == nullptr && exponent == 0.0) { result = 1.0; - } else if (mantissaException == 0 && mantissa == 1.0) { + } else if (mantissaException == nullptr && mantissa == 1.0) { result = 1.0; } else { - if (mantissaException != NULL) { - throw (*mantissaException); - } else if (exponentException != NULL) { - throw (*exponentException); - } + if (mantissaException != nullptr) throw *mantissaException; + if (exponentException != nullptr) throw *exponentException; } break; } @@ -252,9 +245,8 @@ double ASTFuncNode::evaluate(int evalType, double* values) throw RuntimeException("log() expects 1 argument"); double argument = child0->evaluate(evalType, values); if (argument <= 0.0) { - char problem[1000]; - sprintf(problem, "log(u) and u=%lf <= 0.0 is undefined", argument); - string errorMsg = getFunctionDomainError(problem, values, "u", child0); + std::string problem{std::format("log(u) and u={} <= 0.0 is undefined", argument)}; + std::string errorMsg = getFunctionDomainError(problem, values, "u", child0); throw FunctionDomainException(errorMsg); } result = log(argument); @@ -274,9 +266,8 @@ double ASTFuncNode::evaluate(int evalType, double* values) throw RuntimeException("sqrt() expects 1 argument"); double argument = child0->evaluate(evalType, values); if (argument < 0) { - char problem[1000]; - sprintf(problem, "sqrt(u) where u=%lf<0 is undefined", argument); - string errorMsg = getFunctionDomainError(problem, values, "u", child0); + std::string problem{std::format("sqrt(u) where u={}<0 is undefined", argument)}; + std::string errorMsg = getFunctionDomainError(problem, values, "u", child0); throw FunctionDomainException(errorMsg); } result = sqrt(argument); @@ -312,9 +303,8 @@ double ASTFuncNode::evaluate(int evalType, double* values) throw RuntimeException("asin() expects 1 argument"); double argument = child0->evaluate(evalType, values); if (fabs(argument) > 1.0) { - char problem[1000]; - sprintf(problem, "asin(u) and u=%lf and |u|>1.0 undefined", argument); - string errorMsg = getFunctionDomainError(problem, values, "u", child0); + std::string problem{std::format("asin(u) and u={} and |u|>1.0 undefined", argument)}; + std::string errorMsg = getFunctionDomainError(problem, values, "u", child0); throw FunctionDomainException(errorMsg); } result = asin(argument); @@ -326,9 +316,8 @@ double ASTFuncNode::evaluate(int evalType, double* values) throw RuntimeException("acos() expects 1 argument"); double argument = child0->evaluate(evalType, values); if (fabs(argument) > 1.0) { - char problem[1000]; - sprintf(problem, "acos(u) and u=%lf and |u|>1.0 undefined", argument); - string errorMsg = getFunctionDomainError(problem, values, "u", child0); + std::string problem{std::format("acos(u) and u={} and |u|>1.0 undefined", argument)}; + std::string errorMsg = getFunctionDomainError(problem, values, "u", child0); throw FunctionDomainException(errorMsg); } result = acos(argument); @@ -357,7 +346,7 @@ double ASTFuncNode::evaluate(int evalType, double* values) throw RuntimeException("max() expects 2 arguments"); double argument0 = child0->evaluate(evalType, values); double argument1 = jjtGetChild(1)->evaluate(evalType, values); - result = max(argument0, argument1); + result = std::max(argument0, argument1); break; } case MIN : @@ -366,7 +355,7 @@ double ASTFuncNode::evaluate(int evalType, double* values) throw RuntimeException("min() expects 2 arguments"); double argument0 = child0->evaluate(evalType, values); double argument1 = jjtGetChild(1)->evaluate(evalType, values); - result = min(argument0, argument1); + result = std::min(argument0, argument1); break; } case CEIL : @@ -392,9 +381,8 @@ double ASTFuncNode::evaluate(int evalType, double* values) double argument = child0->evaluate(evalType, values); result = sin(argument); if (result == 0) { - char problem[1000]; - sprintf(problem, "csc(u)=1/sin(u) and sin(u)=0 and u=%lf", argument); - string errorMsg = getFunctionDomainError(problem, values, "u", child0); + std::string problem{std::format("csc(u)=1/sin(u) and sin(u)=0 and u={}", argument)}; + std::string errorMsg = getFunctionDomainError(problem, values, "u", child0); throw FunctionDomainException(errorMsg); } result = 1/result; @@ -407,9 +395,8 @@ double ASTFuncNode::evaluate(int evalType, double* values) double argument = child0->evaluate(evalType, values); result = tan(argument); if (result == 0) { - char problem[1000]; - sprintf(problem, "cot(u)=1/tan(u) and tan(u)=0 and u=%lf", argument); - string errorMsg = getFunctionDomainError(problem, values, "u", child0); + std::string problem{std::format("cot(u)=1/tan(u) and tan(u)=0 and u={}", argument)}; + std::string errorMsg = getFunctionDomainError(problem, values, "u", child0); throw FunctionDomainException(errorMsg); } result = 1/result; @@ -422,9 +409,8 @@ double ASTFuncNode::evaluate(int evalType, double* values) double argument = child0->evaluate(evalType, values); result = cos(argument); if (result == 0) { - char problem[1000]; - sprintf(problem, "sec(u)=1/cos(u) and cos(u)=0 and u=%lf", argument); - string errorMsg = getFunctionDomainError(problem, values, "u", child0); + std::string problem{std::format("sec(u)=1/cos(u) and cos(u)=0 and u={}", argument)}; + std::string errorMsg = getFunctionDomainError(problem, values, "u", child0); throw FunctionDomainException(errorMsg); } result = 1/result; @@ -436,9 +422,8 @@ double ASTFuncNode::evaluate(int evalType, double* values) throw RuntimeException("acsc() expects 1 argument"); double argument = child0->evaluate(evalType, values); if (fabs(argument) < 1.0){ - char problem[1000]; - sprintf(problem, "acsc(u) and -1evaluate(evalType, values); if (argument == 0) { - string errorMsg = getFunctionDomainError("acot(u)=atan(1/u) and u=0", values, "u", child0); + std::string errorMsg = getFunctionDomainError("acot(u)=atan(1/u) and u=0", values, "u", child0); throw FunctionDomainException(errorMsg); } result = MathUtil::acot(argument); @@ -462,9 +447,8 @@ double ASTFuncNode::evaluate(int evalType, double* values) throw RuntimeException("asec() expects 1 argument"); double argument = child0->evaluate(evalType, values); if (fabs(argument) < 1.0){ - char problem[1000]; - sprintf(problem, "asec(u) and -1evaluate(evalType, values); if (argument == 0.0){ - char problem[1000]; - sprintf(problem, "csch(u) and |u| = 0, u=%lf", argument); - string errorMsg = getFunctionDomainError(problem, values, "u", child0); + std::string problem{std::format("csch(u) and |u| = 0, u={}", argument)}; + std::string errorMsg = getFunctionDomainError(problem, values, "u", child0); throw FunctionDomainException(errorMsg); } result = MathUtil::csch(argument); @@ -514,9 +497,8 @@ double ASTFuncNode::evaluate(int evalType, double* values) throw RuntimeException("coth() expects 1 argument"); double argument = child0->evaluate(evalType, values); if (argument == 0.0){ - char problem[1000]; - sprintf(problem, "coth(u) and |u| = 0, u=%lf", argument); - string errorMsg = getFunctionDomainError(problem, values, "u", child0); + std::string problem{std::format("coth(u) and |u| = 0, u={}", argument)}; + std::string errorMsg = getFunctionDomainError(problem, values, "u", child0); throw FunctionDomainException(errorMsg); } result = MathUtil::coth(argument); @@ -545,9 +527,8 @@ double ASTFuncNode::evaluate(int evalType, double* values) throw RuntimeException("acosh() expects 1 argument"); double argument = child0->evaluate(evalType, values); if (argument < 1.0){ - char problem[1000]; - sprintf(problem, "acosh(u) and u=%lf<1.0", argument); - string errorMsg = getFunctionDomainError(problem, values, "u", child0); + std::string problem{std::format("acosh(u) and u={}<1.0", argument)}; + std::string errorMsg = getFunctionDomainError(problem, values, "u", child0); throw FunctionDomainException(errorMsg); } result = MathUtil::acosh(argument); @@ -559,9 +540,8 @@ double ASTFuncNode::evaluate(int evalType, double* values) throw RuntimeException("atanh() expects 1 argument"); double argument = child0->evaluate(evalType, values); if (fabs(argument) >= 1.0){ - char problem[1000]; - sprintf(problem, "atanh(u) and |u| >= 1.0, u=%lf", argument); - string errorMsg = getFunctionDomainError(problem, values, "u", child0); + std::string problem{std::format("atanh(u) and |u| >= 1.0, u={}", argument)}; + std::string errorMsg = getFunctionDomainError(problem, values, "u", child0); throw FunctionDomainException(errorMsg); } result = MathUtil::atanh(argument); @@ -573,7 +553,7 @@ double ASTFuncNode::evaluate(int evalType, double* values) throw RuntimeException("acsch() expects 1 argument"); double argument = child0->evaluate(evalType, values); if (argument == 0.0){ - string errorMsg = getFunctionDomainError("acsch(u) and u=0", values, "u", child0); + std::string errorMsg = getFunctionDomainError("acsch(u) and u=0", values, "u", child0); throw FunctionDomainException(errorMsg); } result = MathUtil::acsch(argument); @@ -585,9 +565,8 @@ double ASTFuncNode::evaluate(int evalType, double* values) throw RuntimeException("acoth() expects 1 argument"); double argument = child0->evaluate(evalType, values); if (fabs(argument) <= 1.0){ - char problem[1000]; - sprintf(problem, "acoth(u) and |u| <= 1.0, u=%lf", argument); - string errorMsg = getFunctionDomainError(problem, values, "u", child0); + std::string problem{std::format("acoth(u) and |u| <= 1.0, u={}", argument)}; + std::string errorMsg = getFunctionDomainError(problem, values, "u", child0); throw FunctionDomainException(errorMsg); } result = MathUtil::acoth(argument); @@ -599,9 +578,8 @@ double ASTFuncNode::evaluate(int evalType, double* values) throw RuntimeException("asech() expects 1 argument"); double argument = child0->evaluate(evalType, values); if (argument <= 0.0 || argument > 1.0){ - char problem[1000]; - sprintf(problem, "asech(u) and u <= 0.0 or u > 1.0, u=%lf", argument); - string errorMsg = getFunctionDomainError(problem, values, "u", child0); + std::string problem{std::format("asech(u) and u <= 0.0 or u > 1.0, u={}", argument)}; + std::string errorMsg = getFunctionDomainError(problem, values, "u", child0); throw FunctionDomainException(errorMsg); } result = MathUtil::asech(argument); @@ -612,10 +590,9 @@ double ASTFuncNode::evaluate(int evalType, double* values) if (jjtGetNumChildren()!= 1) throw RuntimeException("factorial() expects 1 argument"); double argument = child0->evaluate(evalType, values); - if (argument < 0.0 || (argument-(int)argument) != 0){ - char problem[1000]; - sprintf(problem, "factorial(u) and u=%lf < 0.0, or u is not an integer", argument); - string errorMsg = getFunctionDomainError(problem, values, "u", child0); + if (argument < 0.0 || (argument-static_cast(argument)) != 0){ + std::string problem{std::format("factorial(u) and u={} < 0.0, or u is not an integer", argument)}; + std::string errorMsg = getFunctionDomainError(problem, values, "u", child0); throw FunctionDomainException(errorMsg); } result = MathUtil::factorial(argument); @@ -637,8 +614,8 @@ double ASTFuncNode::evaluate(int evalType, double* values) } //result is NAN if (MathUtil::double_infinity == -result || MathUtil::double_infinity == result || result != result) { - char problem[1000]; - sprintf(problem, "%s evaluated to infinity or NaN", infixString(LANGUAGE_DEFAULT,0).c_str(), functionNamesVCML[funcType].c_str()); + //std::string problem{std::format("{} evaluated to infinity or NaN", infixString(LANGUAGE_DEFAULT,0).c_str(), functionNamesVCML[funcType].c_str())}; // unmatched specifier? + std::string problem{std::format("{} evaluated to infinity or NaN", infixString(LANGUAGE_DEFAULT,0).c_str())}; throw FunctionRangeException(problem); } return result; diff --git a/ExpressionParser/ASTFuncNode.h b/ExpressionParser/ASTFuncNode.h index 64914c8ed..6db3c653f 100644 --- a/ExpressionParser/ASTFuncNode.h +++ b/ExpressionParser/ASTFuncNode.h @@ -8,9 +8,9 @@ class ASTFuncNode : public Node public: ASTFuncNode(int i); ~ASTFuncNode(); - void setFunctionFromParserToken(string parserToken); - string infixString(int lang, NameScope* nameScope); - void getStackElements(vector& elements); + void setFunctionFromParserToken(std::string parserToken); + std::string infixString(int lang, NameScope* nameScope); + void getStackElements(std::vector& elements); double evaluate(int evalType, double* values=0); Node* copyTree(); @@ -18,7 +18,7 @@ class ASTFuncNode : public Node private: int funcType; - string funcName; + std::string funcName; ASTFuncNode(); }; diff --git a/ExpressionParser/ASTIdNode.cpp b/ExpressionParser/ASTIdNode.cpp index 57d05cf6e..5dd47fd44 100644 --- a/ExpressionParser/ASTIdNode.cpp +++ b/ExpressionParser/ASTIdNode.cpp @@ -1,4 +1,4 @@ -#include +#include #include "ASTIdNode.h" #include "ExpressionException.h" @@ -23,9 +23,9 @@ ASTIdNode::ASTIdNode(int i) : Node(i), symbolTableEntry(NULL) ASTIdNode::~ASTIdNode() { } -string ASTIdNode::infixString(int lang, NameScope* nameScope) +std::string ASTIdNode::infixString(int lang, NameScope* nameScope) { - string idName(name); + std::string idName(name); return idName; if (nameScope == NULL) { @@ -48,12 +48,12 @@ string ASTIdNode::infixString(int lang, NameScope* nameScope) */ char chrs[20]; sprintf(chrs, "%d\0", lang); - throw RuntimeException(string("Lanaguage '") + chrs + " not supported"); + throw RuntimeException(std::string("Lanaguage '") + chrs + " not supported"); } } -void ASTIdNode::getStackElements(vector& elements) { +void ASTIdNode::getStackElements(std::vector& elements) { if (symbolTableEntry == NULL){ throw ExpressionException("tryin to evaluate unbound identifier '" + infixString(LANGUAGE_DEFAULT, 0)+"'"); } @@ -90,7 +90,7 @@ double ASTIdNode::evaluate(int evalType, double* values) { } } -SymbolTableEntry* ASTIdNode::getBinding(string symbol) +SymbolTableEntry* ASTIdNode::getBinding(std::string symbol) { if (name == symbol){ return symbolTableEntry; @@ -109,13 +109,13 @@ void ASTIdNode::bind(SymbolTable* symbolTable) symbolTableEntry = symbolTable->getEntry(name); if (symbolTableEntry == NULL){ - string id = name; + std::string id = name; throw ExpressionBindingException("error binding identifier '" + id + "'"); } } -void ASTIdNode::getSymbols(vector& symbols, int language, NameScope* nameScope) { - string infix = infixString(language, nameScope); +void ASTIdNode::getSymbols(std::vector& symbols, int language, NameScope* nameScope) { + std::string infix = infixString(language, nameScope); for (int i = 0; i < (int)symbols.size(); i ++) { if (symbols[i] == infix) { return; diff --git a/ExpressionParser/ASTIdNode.h b/ExpressionParser/ASTIdNode.h index 549fbc75f..f6976f7b9 100644 --- a/ExpressionParser/ASTIdNode.h +++ b/ExpressionParser/ASTIdNode.h @@ -10,14 +10,14 @@ class ASTIdNode : public Node public: ASTIdNode(int i); ~ASTIdNode(); - string name; - string infixString(int lang, NameScope* nameScope); + std::string name; + std::string infixString(int lang, NameScope* nameScope); SymbolTableEntry* symbolTableEntry; - SymbolTableEntry* getBinding(string symbol); + SymbolTableEntry* getBinding(std::string symbol); void bind(SymbolTable* symbolTable); - void getStackElements(vector& elements); + void getStackElements(std::vector& elements); double evaluate(int evalType, double* values=0); - void getSymbols(vector& symbols, int language, NameScope* nameScope); + void getSymbols(std::vector& symbols, int language, NameScope* nameScope); Node* copyTree(); bool equals(Node* node); diff --git a/ExpressionParser/ASTInvertTermNode.cpp b/ExpressionParser/ASTInvertTermNode.cpp index 278966254..897dbdde3 100644 --- a/ExpressionParser/ASTInvertTermNode.cpp +++ b/ExpressionParser/ASTInvertTermNode.cpp @@ -14,12 +14,12 @@ ASTInvertTermNode::ASTInvertTermNode(int i) : Node(i) { ASTInvertTermNode::~ASTInvertTermNode() { } -string ASTInvertTermNode::infixString(int lang, NameScope* nameScope) +std::string ASTInvertTermNode::infixString(int lang, NameScope* nameScope) { return jjtGetChild(0)->infixString(lang,nameScope); } -void ASTInvertTermNode::getStackElements(vector& elements) { +void ASTInvertTermNode::getStackElements(std::vector& elements) { jjtGetChild(0)->getStackElements(elements); elements.push_back(StackElement(TYPE_DIV)); } @@ -35,7 +35,7 @@ double ASTInvertTermNode::evaluate(int evalType, double* values) { // // form error message for user's consumption. // - string errorMsg = getFunctionDomainError("divide by zero", 0, "divisor", jjtGetChild(0)); + std::string errorMsg = getFunctionDomainError("divide by zero", 0, "divisor", jjtGetChild(0)); throw DivideByZeroException(errorMsg); } else { return (1.0 / childValue); diff --git a/ExpressionParser/ASTInvertTermNode.h b/ExpressionParser/ASTInvertTermNode.h index f94ee718d..87fb9dcd1 100644 --- a/ExpressionParser/ASTInvertTermNode.h +++ b/ExpressionParser/ASTInvertTermNode.h @@ -8,8 +8,8 @@ class ASTInvertTermNode : public Node public: ASTInvertTermNode(int i); ~ASTInvertTermNode(); - string infixString(int lang, NameScope* nameScope); - void getStackElements(vector& elements); + std::string infixString(int lang, NameScope* nameScope); + void getStackElements(std::vector& elements); double evaluate(int evalType, double* values=0); Node* copyTree(); diff --git a/ExpressionParser/ASTMinusTermNode.cpp b/ExpressionParser/ASTMinusTermNode.cpp index 3fa6acce5..448630d13 100644 --- a/ExpressionParser/ASTMinusTermNode.cpp +++ b/ExpressionParser/ASTMinusTermNode.cpp @@ -12,15 +12,15 @@ ASTMinusTermNode::ASTMinusTermNode(int i) : Node(i) { ASTMinusTermNode::~ASTMinusTermNode() { } -string ASTMinusTermNode::infixString(int lang, NameScope* nameScope) +std::string ASTMinusTermNode::infixString(int lang, NameScope* nameScope) { - string buffer(" - "); + std::string buffer(" - "); buffer += jjtGetChild(0)->infixString(lang,nameScope); return buffer; } -void ASTMinusTermNode::getStackElements(vector& elements) { +void ASTMinusTermNode::getStackElements(std::vector& elements) { jjtGetChild(0)->getStackElements(elements); elements.push_back(StackElement(TYPE_SUB)); } diff --git a/ExpressionParser/ASTMinusTermNode.h b/ExpressionParser/ASTMinusTermNode.h index b88593419..649b4c866 100644 --- a/ExpressionParser/ASTMinusTermNode.h +++ b/ExpressionParser/ASTMinusTermNode.h @@ -8,8 +8,8 @@ class ASTMinusTermNode : public Node public: ASTMinusTermNode(int i); ~ASTMinusTermNode(); - string infixString(int lang, NameScope* nameScope); - void getStackElements(vector& elements); + std::string infixString(int lang, NameScope* nameScope); + void getStackElements(std::vector& elements); double evaluate(int evalType, double* values=0); Node* copyTree(); diff --git a/ExpressionParser/ASTMultNode.cpp b/ExpressionParser/ASTMultNode.cpp index daf267bc5..f68e6f762 100644 --- a/ExpressionParser/ASTMultNode.cpp +++ b/ExpressionParser/ASTMultNode.cpp @@ -7,7 +7,6 @@ //#include "ParseException.h" #include -using std::stringstream; ASTMultNode::ASTMultNode() : Node(JJTMULTNODE) { } @@ -27,7 +26,7 @@ bool ASTMultNode::isBoolean() { return true; } -string ASTMultNode::infixString(int lang, NameScope* nameScope) +std::string ASTMultNode::infixString(int lang, NameScope* nameScope) { bool* boolChildFlags = new bool[jjtGetNumChildren()]; bool bAllBoolean = true; @@ -43,7 +42,7 @@ string ASTMultNode::infixString(int lang, NameScope* nameScope) } - stringstream buffer; + std::stringstream buffer; buffer << "("; if (bAllBoolean || bNoBoolean || (lang != LANGUAGE_C && lang != LANGUAGE_VISIT)) { // old way @@ -59,8 +58,8 @@ string ASTMultNode::infixString(int lang, NameScope* nameScope) } } } else { - stringstream conditionBuffer; - stringstream valueBuffer; + std::stringstream conditionBuffer; + std::stringstream valueBuffer; for (int i=0;i 0) { @@ -97,12 +96,12 @@ string ASTMultNode::infixString(int lang, NameScope* nameScope) } } buffer << ")"; - string s = buffer.str(); + std::string s = buffer.str(); delete [] boolChildFlags; return s; } -void ASTMultNode::getStackElements(vector& elements) { +void ASTMultNode::getStackElements(std::vector& elements) { int startSize = (int)elements.size(); @@ -148,7 +147,7 @@ void ASTMultNode::getStackElements(vector& elements) { if (indexBooleanChildren>0){ int finalSize = (int)elements.size(); int size = finalSize-startSize; - vector::reverse_iterator iter = elements.rbegin(); + std::vector::reverse_iterator iter = elements.rbegin(); for (int offset = 0; offset < size; ++offset) { if ((*iter).type==TYPE_BZ && (*iter).branchOffset==0){ (*iter).branchOffset = offset+1; diff --git a/ExpressionParser/ASTMultNode.h b/ExpressionParser/ASTMultNode.h index 02daa2833..3a2d40046 100644 --- a/ExpressionParser/ASTMultNode.h +++ b/ExpressionParser/ASTMultNode.h @@ -8,8 +8,8 @@ class ASTMultNode : public Node public: ASTMultNode(int i); ~ASTMultNode(); - string infixString(int lang, NameScope* nameScope); - void getStackElements(vector& elements); + std::string infixString(int lang, NameScope* nameScope); + void getStackElements(std::vector& elements); double evaluate(int evalType, double* values=0); bool isBoolean(); diff --git a/ExpressionParser/ASTNotNode.cpp b/ExpressionParser/ASTNotNode.cpp index ba0235b01..7c3daf231 100644 --- a/ExpressionParser/ASTNotNode.cpp +++ b/ExpressionParser/ASTNotNode.cpp @@ -17,9 +17,9 @@ bool ASTNotNode::isBoolean() { return true; } -string ASTNotNode::infixString(int lang, NameScope* nameScope) +std::string ASTNotNode::infixString(int lang, NameScope* nameScope) { - string buffer; + std::string buffer; if (lang == LANGUAGE_VISIT){ buffer.append("not("); }else{ @@ -31,7 +31,7 @@ string ASTNotNode::infixString(int lang, NameScope* nameScope) return buffer; } -void ASTNotNode::getStackElements(vector& elements) { +void ASTNotNode::getStackElements(std::vector& elements) { jjtGetChild(0)->getStackElements(elements); elements.push_back(StackElement(TYPE_NOT)); } diff --git a/ExpressionParser/ASTNotNode.h b/ExpressionParser/ASTNotNode.h index f3e7206d6..006eccf96 100644 --- a/ExpressionParser/ASTNotNode.h +++ b/ExpressionParser/ASTNotNode.h @@ -8,8 +8,8 @@ class ASTNotNode : public Node public: ASTNotNode(int i); ~ASTNotNode(); - string infixString(int lang, NameScope* nameScope); - void getStackElements(vector& elements); + std::string infixString(int lang, NameScope* nameScope); + void getStackElements(std::vector& elements); double evaluate(int evalType, double* values=0); bool isBoolean(); diff --git a/ExpressionParser/ASTOrNode.cpp b/ExpressionParser/ASTOrNode.cpp index 3d1b274a5..211ec6db8 100644 --- a/ExpressionParser/ASTOrNode.cpp +++ b/ExpressionParser/ASTOrNode.cpp @@ -16,9 +16,9 @@ bool ASTOrNode::isBoolean() { return true; } -string ASTOrNode::infixString(int lang, NameScope* nameScope) +std::string ASTOrNode::infixString(int lang, NameScope* nameScope) { - string buffer; + std::string buffer; if(lang == LANGUAGE_VISIT){ for (int i=0;i& elements) { +void ASTOrNode::getStackElements(std::vector& elements) { for (int i=0;igetStackElements(elements);; if (i>0) diff --git a/ExpressionParser/ASTOrNode.h b/ExpressionParser/ASTOrNode.h index 8cc4646d0..1105cf17d 100644 --- a/ExpressionParser/ASTOrNode.h +++ b/ExpressionParser/ASTOrNode.h @@ -8,8 +8,8 @@ class ASTOrNode : public Node public: ASTOrNode(int i); ~ASTOrNode(); - string infixString(int lang, NameScope* nameScope); - void getStackElements(vector& elements); + std::string infixString(int lang, NameScope* nameScope); + void getStackElements(std::vector& elements); double evaluate(int evalType, double* values=0); bool isBoolean(); diff --git a/ExpressionParser/ASTPowerNode.cpp b/ExpressionParser/ASTPowerNode.cpp index f65939045..45811536d 100644 --- a/ExpressionParser/ASTPowerNode.cpp +++ b/ExpressionParser/ASTPowerNode.cpp @@ -18,15 +18,15 @@ ASTPowerNode::ASTPowerNode(int i) : Node(i) { ASTPowerNode::~ASTPowerNode() { } -string ASTPowerNode::infixString(int lang, NameScope* nameScope) +std::string ASTPowerNode::infixString(int lang, NameScope* nameScope) { if (jjtGetNumChildren() != 2) { char ch[20]; sprintf(ch, "%d\0", jjtGetNumChildren()); - throw RuntimeException("There are" + string(ch) + " arguments for the power operator, expecting 2"); + throw RuntimeException("There are" + std::string(ch) + " arguments for the power operator, expecting 2"); } - string buffer; + std::string buffer; if (lang == LANGUAGE_DEFAULT || lang == LANGUAGE_MATLAB) { buffer += "("; buffer += jjtGetChild(0)->infixString(lang, nameScope); @@ -44,7 +44,7 @@ string ASTPowerNode::infixString(int lang, NameScope* nameScope) return buffer; } -void ASTPowerNode::getStackElements(vector& elements) { +void ASTPowerNode::getStackElements(std::vector& elements) { jjtGetChild(0)->getStackElements(elements);; jjtGetChild(1)->getStackElements(elements);; elements.push_back(StackElement(TYPE_POW)); @@ -83,22 +83,22 @@ double ASTPowerNode::evaluate(int evalType, double* values) { if (exponentException == NULL && baseException == NULL) { if (baseValue == 0.0 && exponentValue < 0.0) { - string childString = infixString(LANGUAGE_DEFAULT,0); + std::string childString = infixString(LANGUAGE_DEFAULT,0); char problem[1000]; sprintf(problem, "u^v and u=0 and v=%lf<0", exponentValue); - string errorMsg = getFunctionDomainError(problem, values, "u", baseChild, "v", exponentChild); + std::string errorMsg = getFunctionDomainError(problem, values, "u", baseChild, "v", exponentChild); throw DivideByZeroException(errorMsg); } else if (baseValue < 0.0 && exponentValue != MathUtil::round(exponentValue)) { char problem[1000]; sprintf(problem, "u^v and u=%lf<0 and v=%lf not an integer: undefined", baseValue, exponentValue); - string errorMsg = getFunctionDomainError(problem, values, "u", baseChild, "v", exponentChild); + std::string errorMsg = getFunctionDomainError(problem, values, "u", baseChild, "v", exponentChild); throw FunctionDomainException(errorMsg); } else { double result = pow(baseValue, exponentValue); if (MathUtil::double_infinity == -result || MathUtil::double_infinity == result || result != result) { char problem[1000]; sprintf(problem, "u^v evaluated to %lf, u=%lf, v=%lf", result, baseValue); - string errorMsg = getFunctionDomainError(problem, values, "u", baseChild, "v", exponentChild); + std::string errorMsg = getFunctionDomainError(problem, values, "u", baseChild, "v", exponentChild); throw FunctionDomainException(errorMsg); } return result; diff --git a/ExpressionParser/ASTPowerNode.h b/ExpressionParser/ASTPowerNode.h index 3ee096c40..80f1423ec 100644 --- a/ExpressionParser/ASTPowerNode.h +++ b/ExpressionParser/ASTPowerNode.h @@ -8,8 +8,8 @@ class ASTPowerNode : public Node public: ASTPowerNode(int i); ~ASTPowerNode(); - string infixString(int lang, NameScope* nameScope); - void getStackElements(vector& elements); + std::string infixString(int lang, NameScope* nameScope); + void getStackElements(std::vector& elements); double evaluate(int evalType, double* values=0); Node* copyTree(); diff --git a/ExpressionParser/ASTRelationalNode.cpp b/ExpressionParser/ASTRelationalNode.cpp index 28a7af491..c60d71fec 100644 --- a/ExpressionParser/ASTRelationalNode.cpp +++ b/ExpressionParser/ASTRelationalNode.cpp @@ -32,7 +32,7 @@ bool ASTRelationalNode::isBoolean() { return true; } -void ASTRelationalNode::setOperationFromToken(string op) +void ASTRelationalNode::setOperationFromToken(std::string op) { if (op == ">"){ operation = GT; @@ -53,9 +53,9 @@ void ASTRelationalNode::setOperationFromToken(string op) } -string ASTRelationalNode::infixString(int lang, NameScope* nameScope) +std::string ASTRelationalNode::infixString(int lang, NameScope* nameScope) { - string buffer; + std::string buffer; if(lang == LANGUAGE_VISIT){ if(jjtGetNumChildren() != 2){ throw ParseException("ASTRelationalNode for VISIT expecting 2 children"); @@ -103,7 +103,7 @@ string ASTRelationalNode::infixString(int lang, NameScope* nameScope) return buffer; } -void ASTRelationalNode::getStackElements(vector& elements) { +void ASTRelationalNode::getStackElements(std::vector& elements) { for (int i=0;igetStackElements(elements);; if (i>0) diff --git a/ExpressionParser/ASTRelationalNode.h b/ExpressionParser/ASTRelationalNode.h index 06bdfe397..ead56138b 100644 --- a/ExpressionParser/ASTRelationalNode.h +++ b/ExpressionParser/ASTRelationalNode.h @@ -8,9 +8,9 @@ class ASTRelationalNode : public Node public: ASTRelationalNode(int i); ~ASTRelationalNode(); - void setOperationFromToken(string op); - string infixString(int lang, NameScope* nameScope); - void getStackElements(vector& elements); + void setOperationFromToken(std::string op); + std::string infixString(int lang, NameScope* nameScope); + void getStackElements(std::vector& elements); double evaluate(int evalType, double* values=0); bool isBoolean(); @@ -19,7 +19,7 @@ class ASTRelationalNode : public Node private: int operation; - string opString; + std::string opString; ASTRelationalNode(); }; diff --git a/ExpressionParser/Exception.cpp b/ExpressionParser/Exception.cpp index c380ecf56..0deebdd33 100644 --- a/ExpressionParser/Exception.cpp +++ b/ExpressionParser/Exception.cpp @@ -1,6 +1,5 @@ -#include #include -#include +#include #include "Exception.h" #include "ParserException.h" @@ -29,7 +28,7 @@ VCell::Exception::~Exception(void) throw( ) { } -string VCell::Exception::getExactMessage() { +std::string VCell::Exception::getExactMessage() { return message; } @@ -37,7 +36,7 @@ const char * VCell::Exception::what() const throw( ){ return message.c_str( ); } -string VCell::Exception::getMessage(void) +std::string VCell::Exception::getMessage(void) { return title + " : " + getExactMessage(); } @@ -125,7 +124,7 @@ static void itoa1(int n, char* s, int base) reverse(s); } -string VCell::Exception::add_escapes(string str) +std::string VCell::Exception::add_escapes(string str) { string retval; char ch; diff --git a/ExpressionParser/Expression.cpp b/ExpressionParser/Expression.cpp index 13ca1ad03..a8da926f3 100644 --- a/ExpressionParser/Expression.cpp +++ b/ExpressionParser/Expression.cpp @@ -3,9 +3,7 @@ #include #include #include -using std::cout; -using std::endl; -using std::istringstream; + #include "Expression.h" #include "ExpressionParser.h" @@ -38,7 +36,7 @@ Expression::Expression(const Expression &rhs) * create and bind in single ctor * equivalent to default constructor if #expString is empty */ -Expression::Expression(string expString, SymbolTable & symbolTable) +Expression::Expression(std::string expString, SymbolTable & symbolTable) :rootNode(NULL), stackMachine(NULL) { @@ -48,14 +46,14 @@ Expression::Expression(string expString, SymbolTable & symbolTable) } } -Expression::Expression(string expString) +Expression::Expression(std::string expString) :rootNode(NULL), stackMachine(NULL) { init(expString); } -void Expression::init(const string & expString) { +void Expression::init(const std::string & expString) { if (expString.length() == 0) { throw ParserException("Empty expression"); @@ -77,7 +75,7 @@ void Expression::init(const string & expString) { } } - string trimstr = trim(expString); + std::string trimstr = trim(expString); if (trimstr[trimstr.length() - 1] != ';'){ trimstr += ";"; } @@ -100,7 +98,7 @@ Expression & Expression::operator=(const Expression &rhs) { void Expression::showStackInstructions(void) { getStackMachine()->showInstructions(); - cout.flush(); + std::cout.flush(); } double Expression::evaluateConstant(void) @@ -122,7 +120,7 @@ double Expression::evaluateVectorTree(double* values) } } -string Expression::getEvaluationSummary(double* values) +std::string Expression::getEvaluationSummary(double* values) { return rootNode->getNodeSummary(values, rootNode); } @@ -136,11 +134,11 @@ double Expression::evaluateVector(double* values) } } -void Expression::parseExpression(string exp) +void Expression::parseExpression(std::string exp) { //parseCount++; try { - istringstream iss(exp); + std::istringstream iss(exp); ExpressionParser parser(&iss); delete rootNode; @@ -158,12 +156,12 @@ void Expression::parseExpression(string exp) } } -string Expression::infix(void) +std::string Expression::infix(void) { return rootNode->infixString(LANGUAGE_DEFAULT, 0); } -string Expression::infix_Visit(void) +std::string Expression::infix_Visit(void) { return rootNode->infixString(LANGUAGE_VISIT, 0); } @@ -174,7 +172,7 @@ void Expression::bindExpression(SymbolTable* symbolTable) rootNode->bind(symbolTable); } -string Expression::trim(string str) +std::string Expression::trim(std::string str) { int len = (int)str.length(); int st = 0; @@ -191,11 +189,11 @@ string Expression::trim(string str) inline StackMachine* Expression::getStackMachine() { if (stackMachine == NULL) { - vector elements_vector; + std::vector elements_vector; rootNode->getStackElements(elements_vector); StackElement* elements = new StackElement[elements_vector.size()]; int i = 0; - for (vector::iterator iter = elements_vector.begin(); iter != elements_vector.end(); iter ++) { + for (std::vector::iterator iter = elements_vector.begin(); iter != elements_vector.end(); iter ++) { elements[i ++] = *iter; } stackMachine = new StackMachine(elements, (int)elements_vector.size()); @@ -204,11 +202,11 @@ inline StackMachine* Expression::getStackMachine() { return stackMachine; } -void Expression::getSymbols(vector& symbols) { +void Expression::getSymbols(std::vector& symbols) { rootNode->getSymbols(symbols, LANGUAGE_DEFAULT, 0); } -SymbolTableEntry* Expression::getSymbolBinding(string symbol){ +SymbolTableEntry* Expression::getSymbolBinding(std::string symbol){ return rootNode->getBinding(symbol); } diff --git a/ExpressionParser/Expression.h b/ExpressionParser/Expression.h index 383758141..1938d0984 100644 --- a/ExpressionParser/Expression.h +++ b/ExpressionParser/Expression.h @@ -14,11 +14,11 @@ class Expression { public: Expression(void); - Expression(string expString); + Expression(std::string expString); /** * symbolTable must remain valid memory */ - Expression(string expString, SymbolTable & symbolTable); + Expression(std::string expString, SymbolTable & symbolTable); Expression(Expression* expression); Expression(const Expression &); ~Expression(void); @@ -30,22 +30,22 @@ class Expression // exercise the new way of evaluating vector by using stack machine double evaluateVector(double* values); - string infix(void); + std::string infix(void); /** * symbolTable must remain valid memory */ void bindExpression(SymbolTable* symbolTable); - static string trim(string str); - void getSymbols(vector& symbols); + static std::string trim(std::string str); + void getSymbols(std::vector& symbols); - string getEvaluationSummary(double* values); + std::string getEvaluationSummary(double* values); - SymbolTableEntry* getSymbolBinding(string symbol); + SymbolTableEntry* getSymbolBinding(std::string symbol); double evaluateProxy(); void showStackInstructions(); void substituteInPlace(Expression* origExp, Expression* newExp); - string infix_Visit(void); + std::string infix_Visit(void); bool isConstant( ) const; private: @@ -57,13 +57,13 @@ class Expression //static long derivativeCount; //static long substituteCount; //static long bindCount; - void parseExpression(string exp); + void parseExpression(std::string exp); StackMachine* stackMachine; inline StackMachine* getStackMachine(); /** * common ctor code */ - void init(const string & expString); + void init(const std::string & expString); }; } #endif diff --git a/ExpressionParser/ExpressionParser.cpp b/ExpressionParser/ExpressionParser.cpp index 126840811..b82b26d1a 100644 --- a/ExpressionParser/ExpressionParser.cpp +++ b/ExpressionParser/ExpressionParser.cpp @@ -1,5 +1,7 @@ #include -#include +#include +#include +#include #include "ASTOrNode.h" #include "ASTAndNode.h" @@ -24,7 +26,7 @@ #endif int64 jj_la1_0[] = {0x2000,0x1000,0x30000,0x30000,0xc0000,0xc0000,0x10934000,0x10100000,0x10934000,0x40000000,}; -ExpressionParser::ExpressionParser(istream* stream) +ExpressionParser::ExpressionParser(std::istream* stream) { init(); jj_input_stream = new SimpleCharStream(stream, 1, 1); @@ -539,13 +541,13 @@ void ExpressionParser::jj_add_error_token(int kind, int pos) if (pos == jj_endpos + 1) { jj_lasttokens[jj_endpos++] = kind; } else if (jj_endpos != 0) { - jj_expentry = new vector; + jj_expentry = new std::vector; for (int i = 0; i < jj_endpos; i++) { jj_expentry->push_back(jj_lasttokens[i]); } bool exists = false; - for (vector< vector* >::iterator iter = jj_expentries.begin(); iter != jj_expentries.end(); iter ++) { - vector* oldentry = (vector*)(*iter); + for (std::vector< std::vector* >::iterator iter = jj_expentries.begin(); iter != jj_expentries.end(); iter ++) { + std::vector* oldentry = (std::vector*)(*iter); if (oldentry->size() == jj_expentry->size()) { exists = true; for (unsigned int i = 0; i < jj_expentry->size(); i++) { @@ -976,7 +978,7 @@ ParseException& ExpressionParser::generateParseException(void) } for (int i = 0; i < 31; i++) { if (la1tokens[i]) { - jj_expentry = new vector; + jj_expentry = new std::vector; jj_expentry->push_back(i); jj_expentries.push_back(jj_expentry); } @@ -991,7 +993,7 @@ ParseException& ExpressionParser::generateParseException(void) int* etsLengthArray = new int[numETS]; int** exptokseq = new int*[numETS]; for (int i = 0; i < numETS; i++) { - vector* entry = (vector*)(jj_expentries.at(i)); + std::vector* entry = (std::vector*)(jj_expentries.at(i)); etsLengthArray[i] = (int)entry->size(); exptokseq[i] = new int[etsLengthArray[i]]; for (int j = 0; j < etsLengthArray[i]; j ++) { diff --git a/ExpressionParser/ExpressionParser.h b/ExpressionParser/ExpressionParser.h index 41aaffc02..e4dc45f69 100644 --- a/ExpressionParser/ExpressionParser.h +++ b/ExpressionParser/ExpressionParser.h @@ -2,7 +2,6 @@ #define EXPRESSIONPARSER_H #include -using std::vector; #include "Token.h" #include "ASTExpression.h" @@ -31,7 +30,7 @@ class ExpressionParser { public: ExpressionParser(ExpressionParserTokenManager* tm); - ExpressionParser(istream* stream); + ExpressionParser(std::istream* stream); ~ExpressionParser(); ASTExpression* Expression(void); @@ -70,8 +69,8 @@ class ExpressionParser { LookaheadSuccess jj_ls; - vector< vector* > jj_expentries; - vector *jj_expentry; + std::vector*> jj_expentries; + std::vector *jj_expentry; int jj_kind; int* jj_lasttokens; int jj_endpos; diff --git a/ExpressionParser/ExpressionParserConstants.h b/ExpressionParser/ExpressionParserConstants.h index e629a6d55..004b713d1 100644 --- a/ExpressionParser/ExpressionParserConstants.h +++ b/ExpressionParser/ExpressionParserConstants.h @@ -1,6 +1,8 @@ #ifndef EXPRESSIONPARSERCONSTANTS_H #define EXPRESSIONPARSERCONSTANTS_H +#include + const int EEOF = 0; const int RELATIONAL_OPERATOR = 5; const int LT = 6; @@ -27,7 +29,7 @@ const int DIGIT = 26; const int DEFAULT = 0; -const string tokenImage[] = { +const std::string tokenImage[] = { "", "\" \"", "\"\\t\"", diff --git a/ExpressionParser/ExpressionParserTokenManager.cpp b/ExpressionParser/ExpressionParserTokenManager.cpp index 299b2169f..1484ac744 100644 --- a/ExpressionParser/ExpressionParserTokenManager.cpp +++ b/ExpressionParser/ExpressionParserTokenManager.cpp @@ -8,13 +8,13 @@ int ExpressionParserTokenManager::jjnextStates[] = {35, 36, 41, 42, 31, 32, 31, 32, 33, 22, 23, 39, 40, 43, 44, }; -string* ExpressionParserTokenManager::jjstrLiteralImages[] = { - new string(""), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, new string("\136"), new string("\53"), new string("\55"), new string("\52"), new string("\57"), - NULL, NULL, NULL, NULL, NULL, NULL, NULL, new string("\73"), new string("\50"), new string("\51"), new string("\54"), +std::string* ExpressionParserTokenManager::jjstrLiteralImages[] = { + new std::string(""), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, new std::string("\136"), new std::string("\53"), new std::string("\55"), new std::string("\52"), new std::string("\57"), + NULL, NULL, NULL, NULL, NULL, NULL, NULL, new std::string("\73"), new std::string("\50"), new std::string("\51"), new std::string("\54"), }; -string ExpressionParserTokenManager::lexStateNames[] = { +std::string ExpressionParserTokenManager::lexStateNames[] = { "DEFAULT", }; @@ -22,7 +22,7 @@ int64 ExpressionParserTokenManager::jjtoToken[] = { 0x789ff021L, }; int64 ExpressionParserTokenManager::jjtoSkip[] = { 0x1eL, }; -void ExpressionParserTokenManager::setDebugStream(ostream* os) +void ExpressionParserTokenManager::setDebugStream(std::ostream* os) { debugStream = os; } @@ -527,7 +527,7 @@ void ExpressionParserTokenManager::SwitchTo(int lexState) if (lexState >= 1 || lexState < 0) { char ex[20]; sprintf(ex, "%d\0", lexState); - throw RuntimeException("Error: Ignoring invalid lexical state: " + string(ex) + ".State unchanged."); + throw RuntimeException("Error: Ignoring invalid lexical state: " + std::string(ex) + ".State unchanged."); } else curLexState = lexState; @@ -537,7 +537,7 @@ Token* ExpressionParserTokenManager::jjFillToken(void) { Token* t = Token::newToken(jjmatchedKind); t->kind = jjmatchedKind; - string* im = jjstrLiteralImages[jjmatchedKind]; + std::string* im = jjstrLiteralImages[jjmatchedKind]; t->image = (im == NULL) ? input_stream->GetImage() : *im; t->beginLine = input_stream->getBeginLine(); t->beginColumn = input_stream->getBeginColumn(); @@ -590,7 +590,7 @@ Token* ExpressionParserTokenManager::getNextToken(void) } int error_line = input_stream->getEndLine(); int error_column = input_stream->getEndColumn(); - string error_after = ""; + std::string error_after = ""; bool EOFSeen = false; try { input_stream->readChar(); @@ -613,8 +613,8 @@ Token* ExpressionParserTokenManager::getNextToken(void) if (EOFSeen) sprintf(chrs, "Lexical error at line %d, column %d. Encountered: \0", error_line, error_column); else { - string a = Exception::add_escapes(string(&curChar, 1)); - string b = Exception::add_escapes(error_after); + std::string a = Exception::add_escapes(std::string(&curChar, 1)); + std::string b = Exception::add_escapes(error_after); sprintf(chrs, "Lexical error at line %d, column %d. Encountered: \"%s\" (%d) after : \"%s\"\0", error_line, error_column, a.c_str(), curChar, b.c_str()); } diff --git a/ExpressionParser/ExpressionParserTokenManager.h b/ExpressionParser/ExpressionParserTokenManager.h index aeb7d4872..1c4bec14d 100644 --- a/ExpressionParser/ExpressionParserTokenManager.h +++ b/ExpressionParser/ExpressionParserTokenManager.h @@ -3,8 +3,8 @@ #include #include -using std::string; -using std::ostream; + + #include "Token.h" #include "SimpleCharStream.h" @@ -33,10 +33,10 @@ class ExpressionParserTokenManager static int64 jjtoSkip[]; char curChar; - static string* jjstrLiteralImages[]; - static string lexStateNames[]; - ostream* debugStream; - void setDebugStream(ostream* os); + static std::string* jjstrLiteralImages[]; + static std::string lexStateNames[]; + std::ostream* debugStream; + void setDebugStream(std::ostream* os); void ReInit(SimpleCharStream* stream); void ReInit(SimpleCharStream* stream, int lexState); void SwitchTo(int lexState); diff --git a/ExpressionParser/ExpressionParserTreeConstants.h b/ExpressionParser/ExpressionParserTreeConstants.h index 591d78555..b9622f8b3 100644 --- a/ExpressionParser/ExpressionParserTreeConstants.h +++ b/ExpressionParser/ExpressionParserTreeConstants.h @@ -16,7 +16,7 @@ const int JJTFUNCNODE = 11; const int JJTFLOATNODE = 12; const int JJTIDNODE = 13; -const string jjtNodeName[] = { +const std::string jjtNodeName[] = { "Expression", "void", "OrNode", diff --git a/ExpressionParser/ExpressionTest.cpp b/ExpressionParser/ExpressionTest.cpp index b7f8905ca..8f4899132 100644 --- a/ExpressionParser/ExpressionTest.cpp +++ b/ExpressionParser/ExpressionTest.cpp @@ -1,6 +1,4 @@ #include -#include -#include #include "MathUtil.h" #include "Exception.h" @@ -11,11 +9,9 @@ #include "IOException.h" #include #include +#include #include -using std::cout; -using std::endl; -using std::max; -using std::ifstream; + ExpressionTest::ExpressionTest(void) { @@ -29,9 +25,9 @@ void ExpressionTest::testEvaluateVector(void) { try { double d = 0.0; - cout << "Parser: evaluating vector" << endl; + std::cout << "Parser: evaluating vector" << std::endl; Expression* exp = new Expression("a+b/c"); - string ss[] = { "a", "b", "c" }; + std::string ss[] = { "a", "b", "c" }; SimpleSymbolTable* simpleSymbolTable = new SimpleSymbolTable(ss, 3); const int n = 3; @@ -41,7 +37,7 @@ void ExpressionTest::testEvaluateVector(void) for (int i = 0; i < n; i ++) { d = exp->evaluateVector(v[i]); - cout << i << "." << exp->infix() << " = " << d << endl; + std::cout << i << "." << exp->infix() << " = " << d << std::endl; } delete exp; delete simpleSymbolTable; @@ -56,10 +52,10 @@ void ExpressionTest::testEvaluateConstant(void) { try { double d = 0.0; - cout << "Parser: evaluating constant" << endl; + std::cout << "Parser: evaluating constant" << std::endl; Expression* exp = new Expression("(3/5"); d = exp->evaluateConstant(); - cout << exp->infix() << " = " << d << endl; + std::cout << exp->infix() << " = " << d << std::endl; delete exp; } catch (Exception& ex) { Exception::rethrowException(ex); @@ -70,7 +66,7 @@ void ExpressionTest::testEvaluateConstant(void) void ExpressionTest::testParser(int count, char* javaresult, double cvalue, char* expStr, SymbolTable* symbolTable, double* values) { Expression* exp = 0; - string badmsg; + std::string badmsg; try { double javavalue = -0.0; double d = 0.0; @@ -85,14 +81,14 @@ void ExpressionTest::testParser(int count, char* javaresult, double cvalue, char } else { n = sscanf(javaresult, "%lg", &javavalue); if (n != 1) { - cout << " Not a Number:: " << javaresult ; + std::cout << " Not a Number:: " << javaresult ; return; } } bool bException = false; - string before = Expression::trim(string(expStr)); + std::string before = Expression::trim(std::string(expStr)); exp = new Expression(expStr); - string exceptionMsg = ""; + std::string exceptionMsg = ""; try { exp->bindExpression(symbolTable); d = exp->evaluateVector(values); @@ -105,7 +101,7 @@ void ExpressionTest::testParser(int count, char* javaresult, double cvalue, char //cout << count << " EVAL_YES :: all NaN" << endl; } else if (javavalue == d && d == cvalue && d == dtree || fabs(javavalue - d) < 1E-14 && fabs(cvalue - d) < 1E-14 - || fabs(javavalue - d)/max(fabs(d),fabs(javavalue)) < 1E-14 && fabs(cvalue - d)/max(fabs(d),fabs(cvalue)) < 1E-14 ) { + || fabs(javavalue - d)/std::max(fabs(d),fabs(javavalue)) < 1E-14 && fabs(cvalue - d)/std::max(fabs(d),fabs(cvalue)) < 1E-14 ) { //cout << count << " EVAL_YES " << endl; } else { if (bException) { @@ -116,16 +112,16 @@ void ExpressionTest::testParser(int count, char* javaresult, double cvalue, char //printf("Java/Infix_C/C++: %.20g/%.20g/Exception\n", count, cvalue, javavalue); //cout << expStr << endl; } else { - cout << endl << "-------------------------------------------" << endl; + std::cout << std::endl << "-------------------------------------------" << std::endl; printf("%d. EVAL_NO Java/Infix_C/C++ Tree/C++: %.20lg/%.20lg/%.20lg/%.20lg\n", count, javavalue, cvalue, dtree, d); printf("Java~Infix_C/Java~C++/Infix_C~C++: %.20lg/%.20lg/%.20lg/%.20lg\n", fabs((javavalue-cvalue)/javavalue), fabs((javavalue-d)/javavalue), fabs((dtree-cvalue)/cvalue), fabs((d-cvalue)/cvalue)); - cout << expStr << endl; + std::cout << expStr << std::endl; } } delete exp; } catch (Exception& ex) { - cout << ex.getMessage() << endl; + std::cout << ex.getMessage() << std::endl; } } @@ -138,7 +134,7 @@ void ExpressionTest::testParser(char* filename) printf("%s=%.20lg\n", exp->infix().c_str(), d); */ - string ids[] = {"id_0", "id_1", "id_2", "id_3", + std::string ids[] = {"id_0", "id_1", "id_2", "id_3", "id_4", "id_5", "id_6", "id_7", "id_8", "id_9"}; SimpleSymbolTable* symbolTable = new SimpleSymbolTable(ids, 10); @@ -146,12 +142,12 @@ void ExpressionTest::testParser(char* filename) const int n = 10; double v[m][n] = {{0,1,2,3,4,5,6,7,8,9 },{ 1,2,3,4,5,6,7,8,9,10}}; - ifstream ifs(filename); + std::ifstream ifs(filename); if (!ifs.is_open()) { - throw IOException(string("") + "Can't open file '" + filename); + throw IOException(std::string("") + "Can't open file '" + filename); } int count = 0; - vector badmsg; + std::vector badmsg; bool bInfinity = false; bool bNAN = false; bool bException = false; @@ -164,7 +160,7 @@ void ExpressionTest::testParser(char* filename) count ++; bInfinity = false; bNAN = false; - cout << count << "...."; + std::cout << count << "...."; memset(line, 0, 1000*sizeof(char)); ifs >> line; int n = -1; @@ -175,19 +171,19 @@ void ExpressionTest::testParser(char* filename) } else { n = sscanf(line, "%lg", &value); if (n != 1) { - cout << " Not a Number:: " << line ; + std::cout << " Not a Number:: " << line ; } } memset(line, 0, 1000*sizeof(char)); ifs.getline(line, 1000); if (n != 1 && !bInfinity && !bNAN) { - cout << line; + std::cout << line; goto label_1; } bException = false; - string before = Expression::trim(string(line)); + std::string before = Expression::trim(std::string(line)); exp = new Expression(line); - string exceptionMsg = ""; + std::string exceptionMsg = ""; try { exp->bindExpression(symbolTable); d = exp->evaluateVector(v[0]); @@ -196,51 +192,51 @@ void ExpressionTest::testParser(char* filename) exceptionMsg = ex.getMessage(); } if ((MathUtil::double_infinity == d || MathUtil::double_infinity == -d) && bInfinity) { - cout << " INFINITY:::::::::::::Before/After: Infinity/" << d; + std::cout << " INFINITY:::::::::::::Before/After: Infinity/" << d; } else if (d != d && bNAN) { - cout << " NaN:::::::::::::Before/After: NaN/" << d; - } else if (value == d || fabs(value - d) < 1E-14 || fabs(value - d)/max(fabs(d),fabs(value)) < 1E-14) { - cout << " EVAL_YES "; + std::cout << " NaN:::::::::::::Before/After: NaN/" << d; + } else if (value == d || fabs(value - d) < 1E-14 || fabs(value - d)/std::max(fabs(d),fabs(value)) < 1E-14) { + std::cout << " EVAL_YES "; } else { - string afterparsing = exp->infix(); + std::string afterparsing = exp->infix(); if (before == afterparsing) { - cout << "INFIX_YES " << endl; + std::cout << "INFIX_YES " << std::endl; } else { - cout << "INFIX_NO " << endl; + std::cout << "INFIX_NO " << std::endl; } - cout << "Before: " << before << endl; - cout << "After : " << exp->infix() << endl; + std::cout << "Before: " << before << std::endl; + std::cout << "After : " << exp->infix() << std::endl; if (bException) { char chrs[2560]; sprintf(chrs, "%d. C++ throws exception: %s", count, exceptionMsg.c_str()); badmsg.push_back(chrs); - cout << chrs << " EVAL_NO "; + std::cout << chrs << " EVAL_NO "; printf("Before/After: %.20g/Exception ", value); } else if (bInfinity) { char chrs[256]; sprintf(chrs, "%d. Java evaluation is Infinity", count); badmsg.push_back(chrs); - cout << chrs << " EVAL_NO "; + std::cout << chrs << " EVAL_NO "; printf("Before/After: Infinity/%.20lg ", d); } else if (bNAN) { char chrs[256]; sprintf(chrs, "%d. Java evaluation is NAN", count); badmsg.push_back(chrs); - cout << chrs << " EVAL_NO "; + std::cout << chrs << " EVAL_NO "; printf("Before/After: NAN/%.20lg ", d); } else { - double m = max(fabs(d),fabs(value)); + double m = std::max(fabs(d),fabs(value)); if (m < 1e-100) { char chrs[256]; sprintf(chrs, "%d. abs(error)=%lg", count, fabs(d-value)); badmsg.push_back(chrs); - cout << chrs << " EVAL_NO "; + std::cout << chrs << " EVAL_NO "; } else { char chrs[256]; sprintf(chrs, "%d. relative(error)=%lg", count, fabs((d-value)/m)); badmsg.push_back(chrs); - cout << chrs << " EVAL_NO "; + std::cout << chrs << " EVAL_NO "; } printf("Before/After: %.20lg/%.20lg ", value, d); } @@ -248,13 +244,13 @@ void ExpressionTest::testParser(char* filename) delete exp; } catch (Exception& ex) { - cout << ex.getMessage() << endl; + std::cout << ex.getMessage() << std::endl; } label_1: - cout << endl << "-------------------------------------------" << endl; + std::cout << std::endl << "-------------------------------------------" << std::endl; } - cout << "BAD " << badmsg.size() << endl; + std::cout << "BAD " << badmsg.size() << std::endl; for (int i = 0; i < badmsg.size(); i ++) { - cout << badmsg[i] << endl; + std::cout << badmsg[i] << std::endl; } } diff --git a/ExpressionParser/JJTExpressionParserState.h b/ExpressionParser/JJTExpressionParserState.h index 4b4b76d86..6ae92e293 100644 --- a/ExpressionParser/JJTExpressionParserState.h +++ b/ExpressionParser/JJTExpressionParserState.h @@ -2,7 +2,6 @@ #define JJEXPRESSIONPARSERSTATE_H #include -using std::vector; #include "Node.h" @@ -25,8 +24,8 @@ class JJTExpressionParserState private: void popMark(void); - vector nodes; - vector marks; + std::vector nodes; + std::vector marks; int sp; int mk; bool node_created; diff --git a/ExpressionParser/Node.cpp b/ExpressionParser/Node.cpp index 2bfc7c730..9589a3922 100644 --- a/ExpressionParser/Node.cpp +++ b/ExpressionParser/Node.cpp @@ -3,8 +3,6 @@ #include #include #include -using std::cout; -using std::endl; #include "Node.h" #include "Expression.h" @@ -77,8 +75,8 @@ int Node::jjtGetNumChildren() { return numChildren; } -void Node::dump(string prefix) { - cout << toString(prefix) << endl; +void Node::dump(std::string prefix) { + std::cout << toString(prefix) << std::endl; if (children != 0) { for (int i = 0; i < numChildren; ++i) { Node* n = children[i]; @@ -89,19 +87,19 @@ void Node::dump(string prefix) { } } -string Node::toString(string prefix) +std::string Node::toString(std::string prefix) { return prefix + infixString(LANGUAGE_DEFAULT, 0); } -void Node::getSymbols(vector& symbols, int language, NameScope* nameScope) +void Node::getSymbols(std::vector& symbols, int language, NameScope* nameScope) { for (int i=0;igetSymbols(symbols, language, nameScope); } } -SymbolTableEntry* Node::getBinding(string symbol) +SymbolTableEntry* Node::getBinding(std::string symbol) { for (int i=0;igetBinding(symbol); @@ -123,8 +121,8 @@ bool Node::isBoolean() { return false; } -string Node::getFunctionDomainError(string problem, double* values, string argumentName1, Node* node1, string argumentName2, Node* node2){ - string errorMsg = problem + ": " + argumentName1 + "=" + getNodeSummary(values, node1); +std::string Node::getFunctionDomainError(std::string problem, double* values, std::string argumentName1, Node* node1, std::string argumentName2, Node* node2){ + std::string errorMsg = problem + ": " + argumentName1 + "=" + getNodeSummary(values, node1); if (node2 == 0) { return errorMsg; } @@ -132,9 +130,9 @@ string Node::getFunctionDomainError(string problem, double* values, string argum return errorMsg; } -string Node::getNodeSummary(double* values, Node* node){ - string errorMsg; - vector symbols; +std::string Node::getNodeSummary(double* values, Node* node){ + std::string errorMsg; + std::vector symbols; node->getSymbols(symbols, LANGUAGE_DEFAULT, 0); if (symbols.size() > 0) { errorMsg += "\"" + node->infixString(LANGUAGE_DEFAULT, 0) + "\"\n where:\n"; diff --git a/ExpressionParser/Node.h b/ExpressionParser/Node.h index 869c8c4bf..5fb203cd8 100644 --- a/ExpressionParser/Node.h +++ b/ExpressionParser/Node.h @@ -2,8 +2,6 @@ #define SIMPLENODE_H #include #include -using std::string; -using std::vector; #define LANGUAGE_DEFAULT 0 #define LANGUAGE_C 1 @@ -30,7 +28,7 @@ class Node Node(int unused); virtual ~Node(void); virtual Node* copyTree()=0; - virtual void getStackElements(vector& elements)=0; + virtual void getStackElements(std::vector& elements)=0; virtual double evaluate(int type, double* values=0)=0; void jjtOpen(); @@ -46,14 +44,14 @@ class Node Node* abandonChild(int i); Node* jjtGetChild(int i); int jjtGetNumChildren(); - void dump(string prefix); - virtual string infixString(int lang, NameScope* nameScope)=0; - string toString(string prefix); - virtual void getSymbols(vector& symbols, int language, NameScope* nameScope); - virtual SymbolTableEntry* getBinding(string symbol); + void dump(std::string prefix); + virtual std::string infixString(int lang, NameScope* nameScope)=0; + std::string toString(std::string prefix); + virtual void getSymbols(std::vector& symbols, int language, NameScope* nameScope); + virtual SymbolTableEntry* getBinding(std::string symbol); virtual void bind(SymbolTable* symbolTable); - static string getFunctionDomainError(string problem, double* values, string argumentName1, Node* node1, string argumentName2="", Node* node2=0); - static string getNodeSummary(double* values, Node* node); + static std::string getFunctionDomainError(std::string problem, double* values, std::string argumentName1, Node* node1, std::string argumentName2="", Node* node2=0); + static std::string getNodeSummary(double* values, Node* node); virtual bool isBoolean(); void jjtAddChild(Node* n); diff --git a/ExpressionParser/ParseException.cpp b/ExpressionParser/ParseException.cpp index 913518e33..1c1a77def 100644 --- a/ExpressionParser/ParseException.cpp +++ b/ExpressionParser/ParseException.cpp @@ -2,7 +2,7 @@ #include "ParseException.h" -string ParseException::eol = string("\n"); +std::string ParseException::eol = string("\n"); ParseException::ParseException() : Exception("ParseException", "") { @@ -38,7 +38,7 @@ ParseException::ParseException(Token* currentTokenVal, int** expectedTokenSequen tokenImage = tokenImageVal; } -string ParseException::getExactMessage(void) +std::string ParseException::getExactMessage(void) { if (!specialConstructor) { return Exception::getExactMessage(); diff --git a/ExpressionParser/SimpleCharStream.cpp b/ExpressionParser/SimpleCharStream.cpp index bc1e03f3c..9d17c551a 100644 --- a/ExpressionParser/SimpleCharStream.cpp +++ b/ExpressionParser/SimpleCharStream.cpp @@ -7,11 +7,11 @@ bool SimpleCharStream::staticFlag = false; -SimpleCharStream::SimpleCharStream(istream* dstream, int startline, int startcolumn, int buffersize) +SimpleCharStream::SimpleCharStream(std::istream* dstream, int startline, int startcolumn, int buffersize) { init(dstream, startline, startcolumn, buffersize); } -void SimpleCharStream::init(istream* dstream, int startline, int startcolumn, int buffersize) { +void SimpleCharStream::init(std::istream* dstream, int startline, int startcolumn, int buffersize) { tokenBegin = 0; bufpos = -1; prevCharIsCR = false; @@ -34,12 +34,12 @@ void SimpleCharStream::init(istream* dstream, int startline, int startcolumn, i memset(bufcolumn, 0, buffersize * sizeof(int)); } -SimpleCharStream::SimpleCharStream(istream* dstream, int startline, int startcolumn) +SimpleCharStream::SimpleCharStream(std::istream* dstream, int startline, int startcolumn) { init(dstream, startline, startcolumn, 4096); } -SimpleCharStream::SimpleCharStream(istream* dstream) +SimpleCharStream::SimpleCharStream(std::istream* dstream) { init(dstream, 1, 1, 4096); } @@ -254,12 +254,12 @@ void SimpleCharStream::backup(int amount) bufpos += bufsize; } -string SimpleCharStream::GetImage(void) +std::string SimpleCharStream::GetImage(void) { if (bufpos >= tokenBegin) - return string(buffer, tokenBegin, bufpos - tokenBegin + 1); + return std::string(buffer, tokenBegin, bufpos - tokenBegin + 1); else - return string(buffer, tokenBegin, bufsize - tokenBegin) + string(buffer, 0, bufpos + 1); + return std::string(buffer, tokenBegin, bufsize - tokenBegin) + std::string(buffer, 0, bufpos + 1); } char* SimpleCharStream::GetSuffix(int len) diff --git a/ExpressionParser/SimpleCharStream.h b/ExpressionParser/SimpleCharStream.h index dc17afc73..1c036a1ae 100644 --- a/ExpressionParser/SimpleCharStream.h +++ b/ExpressionParser/SimpleCharStream.h @@ -3,8 +3,6 @@ #include #include -using std::istream; -using std::string; class SimpleCharStream { @@ -20,7 +18,7 @@ class SimpleCharStream int line; bool prevCharIsCR; bool prevCharIsLF; - istream* inputStream; + std::istream* inputStream; char* buffer; int maxNextCharInd; int inBuf; @@ -28,12 +26,12 @@ class SimpleCharStream void ExpandBuff(bool wrapAround); void FillBuff(void); void UpdateLineColumn(char c); - void init(istream* dstream, int startline, int startcolumn, int buffersize); + void init(std::istream* dstream, int startline, int startcolumn, int buffersize); public: - SimpleCharStream(istream* dstream, int startline, int startcolumn, int buffersize); - SimpleCharStream(istream* dstream, int startline, int startcolumn); - SimpleCharStream(istream* dstream); + SimpleCharStream(std::istream* dstream, int startline, int startcolumn, int buffersize); + SimpleCharStream(std::istream* dstream, int startline, int startcolumn); + SimpleCharStream(std::istream* dstream); ~SimpleCharStream(void); static bool staticFlag; @@ -48,7 +46,7 @@ class SimpleCharStream int getBeginColumn(void); int getBeginLine(void); void backup(int amount); - string GetImage(void); + std::string GetImage(void); char* GetSuffix(int len); void Done(void); void adjustBeginLineColumn(int newLine, int newCol); diff --git a/ExpressionParser/SimpleSymbolTable.cpp b/ExpressionParser/SimpleSymbolTable.cpp index 4b9360aff..f0cce92ff 100644 --- a/ExpressionParser/SimpleSymbolTable.cpp +++ b/ExpressionParser/SimpleSymbolTable.cpp @@ -1,6 +1,6 @@ #include "SimpleSymbolTable.h" -SimpleSymbolTable::SimpleSymbolTable(string* symbols, int symbolCount, ValueProxy** valueProxies) +SimpleSymbolTable::SimpleSymbolTable(std::string* symbols, int symbolCount, ValueProxy** valueProxies) { for (int i = 0; i < symbolCount; i ++){ steArray.push_back(new SimpleSymbolTableEntry(symbols[i],i,0, valueProxies == 0 ? 0 : valueProxies[i])); @@ -16,7 +16,7 @@ SimpleSymbolTable::~SimpleSymbolTable(void) } -SymbolTableEntry* SimpleSymbolTable::getLocalEntry(const string & identifier) const +SymbolTableEntry* SimpleSymbolTable::getLocalEntry(const std::string & identifier) const { for (unsigned int i = 0; i < steArray.size(); i++){ if (steArray[i]->getName() == identifier){ diff --git a/ExpressionParser/SimpleSymbolTable.h b/ExpressionParser/SimpleSymbolTable.h index 61399e895..1d27a22f7 100644 --- a/ExpressionParser/SimpleSymbolTable.h +++ b/ExpressionParser/SimpleSymbolTable.h @@ -2,7 +2,6 @@ #define SIMPLESYMBOLTABLE_H #include -using std::vector; #include "SymbolTable.h" #include "SimpleSymbolTableEntry.h" @@ -10,7 +9,7 @@ using std::vector; class SimpleSymbolTable : public SymbolTable { public: - SimpleSymbolTable(string* symbols, int symbolCount, ValueProxy** valueProxies=0); + SimpleSymbolTable(std::string* symbols, int symbolCount, ValueProxy** valueProxies=0); /** * non-standard copy constructor -- transfers ownership of * implementation to new object; rhs will be unusable @@ -40,7 +39,7 @@ class SimpleSymbolTable : public SymbolTable private: SimpleSymbolTable & operator=(const SimpleSymbolTable &); - vector steArray; + std::vector steArray; }; #endif diff --git a/ExpressionParser/SimpleSymbolTableEntry.cpp b/ExpressionParser/SimpleSymbolTableEntry.cpp index 30e50cac3..96e3d5634 100644 --- a/ExpressionParser/SimpleSymbolTableEntry.cpp +++ b/ExpressionParser/SimpleSymbolTableEntry.cpp @@ -1,7 +1,7 @@ #include "SimpleSymbolTableEntry.h" #include "ExpressionException.h" -SimpleSymbolTableEntry::SimpleSymbolTableEntry(const string& nameValue, int indexVal, NameScope* namescopeVal, ValueProxy* proxyVal) +SimpleSymbolTableEntry::SimpleSymbolTableEntry(const std::string& nameValue, int indexVal, NameScope* namescopeVal, ValueProxy* proxyVal) : name(nameValue), index(indexVal), namescope(namescopeVal), valueProxy(proxyVal) { bConstant = false; @@ -28,7 +28,7 @@ int SimpleSymbolTableEntry::getIndex() { return index; } -string& SimpleSymbolTableEntry::getName() { +std::string& SimpleSymbolTableEntry::getName() { return name; } diff --git a/ExpressionParser/SimpleSymbolTableEntry.h b/ExpressionParser/SimpleSymbolTableEntry.h index ffebcd439..a66e23448 100644 --- a/ExpressionParser/SimpleSymbolTableEntry.h +++ b/ExpressionParser/SimpleSymbolTableEntry.h @@ -6,12 +6,12 @@ class SimpleSymbolTableEntry : public SymbolTableEntry { public: - SimpleSymbolTableEntry(const string& nameValue, int indexVal, NameScope* namescopeVal, ValueProxy* proxyVal); + SimpleSymbolTableEntry(const std::string& nameValue, int indexVal, NameScope* namescopeVal, ValueProxy* proxyVal); ~SimpleSymbolTableEntry(void); double getConstantValue(); VCell::Expression* getExpression(); int getIndex(); - string& getName(); + std::string& getName(); NameScope* getNameScope(); //VCUnitDefinition getUnitDefinition()=0; bool isConstant(); @@ -20,7 +20,7 @@ class SimpleSymbolTableEntry : public SymbolTableEntry ValueProxy* getValueProxy() { return valueProxy; }; private: - string name; + std::string name; int index; NameScope* namescope; bool bConstant; diff --git a/ExpressionParser/StackMachine.cpp b/ExpressionParser/StackMachine.cpp index a5f92d863..9b4839dd1 100644 --- a/ExpressionParser/StackMachine.cpp +++ b/ExpressionParser/StackMachine.cpp @@ -5,8 +5,8 @@ #include "Node.h" #include "StackMachine.h" -#include -#include +#include +#include #include "MathUtil.h" #include "DivideByZeroException.h" #include "FunctionDomainException.h" @@ -15,10 +15,6 @@ #include "ValueProxy.h" #include #include -using std::cout; -using std::endl; -using std::max; -using std::min; StackMachine::StackMachine(StackElement* arg_elements, int size) { elements = arg_elements; @@ -46,13 +42,13 @@ void StackMachine::showInstructions(){ StackElement *token = elements; for (int i=0; itype==TYPE_BZ){ - cout << "BZ " << token->branchOffset << endl; + std::cout << "BZ " << token->branchOffset << std::endl; }else if (token->type==TYPE_IDENTIFIER){ - cout << "PUSH " << "var[" << token->vectorIndex << "]" << endl; + std::cout << "PUSH " << "var[" << token->vectorIndex << "]" << std::endl; }else if (token->type==TYPE_FLOAT){ - cout << "PUSH " << token->value << endl; + std::cout << "PUSH " << token->value << std::endl; }else{ - cout << opCodes[token->type] << "()" << endl; + std::cout << opCodes[token->type] << "()" << std::endl; } } } @@ -155,7 +151,7 @@ double StackMachine::evaluate(double* values){ if (*tos < 0) { char problem[1000]; sprintf(problem, "sqrt(u) where u=%lf<0 is undefined", *tos); - throw FunctionDomainException(string(problem)); + throw FunctionDomainException(std::string(problem)); } *tos = sqrt(*tos); break; @@ -167,11 +163,11 @@ double StackMachine::evaluate(double* values){ if (*tos < 0.0 && (MathUtil::round(arg2) != arg2)) { char problem[1000]; sprintf(problem, "pow(u,v) and u=%lf<0 and v=%lf not an integer", *tos, arg2); - throw FunctionDomainException(string(problem)); + throw FunctionDomainException(std::string(problem)); } else if (*tos == 0.0 && arg2 < 0) { char problem[100]; sprintf(problem, "pow(u,v) and u=0 and v=%lf<0 divide by zero", arg2); - throw FunctionDomainException(string(problem)); + throw FunctionDomainException(std::string(problem)); } if (arg2 == 0.0) { *tos = 1.0; @@ -182,7 +178,7 @@ double StackMachine::evaluate(double* values){ if (MathUtil::double_infinity == -result || MathUtil::double_infinity == result || result != result) { char problem[1000]; sprintf(problem, "u^v evaluated to %lf, u=%lf, v=%lf", result, *tos, arg2); - throw FunctionDomainException(string(problem)); + throw FunctionDomainException(std::string(problem)); } *tos = result; } @@ -193,7 +189,7 @@ double StackMachine::evaluate(double* values){ } else if (*tos < 0.0) { char problem[1000]; sprintf(problem, "log(u) and u=%lf < 0.0 is undefined", *tos); - throw FunctionDomainException(string(problem)); + throw FunctionDomainException(std::string(problem)); } *tos = log(*tos); break; @@ -210,7 +206,7 @@ double StackMachine::evaluate(double* values){ if (fabs(*tos) > 1.0) { char problem[1000]; sprintf(problem, "asin(u) and u=%lf and |u|>1.0 undefined", *tos); - throw FunctionDomainException(string(problem)); + throw FunctionDomainException(std::string(problem)); } *tos = asin(*tos); break; @@ -218,7 +214,7 @@ double StackMachine::evaluate(double* values){ if (fabs(*tos) > 1.0) { char problem[1000]; sprintf(problem, "acos(u) and u=%lf and |u|>1.0 undefined", *tos); - throw FunctionDomainException(string(problem)); + throw FunctionDomainException(std::string(problem)); } *tos = acos(*tos); break; @@ -231,11 +227,11 @@ double StackMachine::evaluate(double* values){ break; case TYPE_MAX: // 29, pop 2 push 1 arg2 = *(tos--); - *tos = max(*tos, arg2); + *tos = std::max(*tos, arg2); break; case TYPE_MIN: // 30, pop 2 push 1 arg2 = *(tos--); - *tos = min(*tos, arg2); + *tos = std::min(*tos, arg2); break; case TYPE_CEIL: // 31, pop 1 push 1 *tos = ceil(*tos); @@ -249,7 +245,7 @@ double StackMachine::evaluate(double* values){ if (result == 0) { char problem[1000]; sprintf(problem, "csc(u)=1/sin(u) and sin(u)=0 and u=%lf", *tos); - throw FunctionDomainException(string(problem)); + throw FunctionDomainException(std::string(problem)); } *tos = 1/result; } @@ -260,7 +256,7 @@ double StackMachine::evaluate(double* values){ if (result == 0) { char problem[1000]; sprintf(problem, "cot(u)=1/tan(u) and tan(u)=0 and u=%lf", *tos); - throw FunctionDomainException(string(problem)); + throw FunctionDomainException(std::string(problem)); } *tos = 1/result; } @@ -271,7 +267,7 @@ double StackMachine::evaluate(double* values){ if (result == 0) { char problem[1000]; sprintf(problem, "sec(u)=1/cos(u) and cos(u)=0 and u=%lf", *tos); - throw FunctionDomainException(string(problem)); + throw FunctionDomainException(std::string(problem)); } *tos = 1/result; } @@ -280,7 +276,7 @@ double StackMachine::evaluate(double* values){ if (fabs(*tos) < 1.0){ char problem[1000]; sprintf(problem, "acsc(u) and -1= 1.0){ char problem[1000]; sprintf(problem, "atanh(u) and |u| >= 1.0, u=%lf", *tos); - throw FunctionDomainException(string(problem)); + throw FunctionDomainException(std::string(problem)); } *tos = MathUtil::atanh(*tos); break; @@ -353,7 +349,7 @@ double StackMachine::evaluate(double* values){ if (fabs(*tos) <= 1.0){ char problem[1000]; sprintf(problem, "acoth(u) and |u| <= 1.0, u=%lf", *tos); - throw FunctionDomainException(string(problem)); + throw FunctionDomainException(std::string(problem)); } *tos = MathUtil::acoth(*tos); break; @@ -361,7 +357,7 @@ double StackMachine::evaluate(double* values){ if (*tos <= 0.0 || *tos > 1.0){ char problem[1000]; sprintf(problem, "asech(u) and u <= 0.0 or u > 1.0, u=%lf", *tos); - throw FunctionDomainException(string(problem)); + throw FunctionDomainException(std::string(problem)); } *tos = MathUtil::asech(*tos); break; @@ -369,7 +365,7 @@ double StackMachine::evaluate(double* values){ if (*tos < 0.0 || (*tos-(int)*tos) != 0){ char problem[1000]; sprintf(problem, "factorial(u) and u=%lf < 0.0 or is not an integer", *tos); - throw FunctionDomainException(string(problem)); + throw FunctionDomainException(std::string(problem)); } *tos = MathUtil::factorial(*tos); break; diff --git a/ExpressionParser/SymbolTable.h b/ExpressionParser/SymbolTable.h index d85fe4aed..37ea81c44 100644 --- a/ExpressionParser/SymbolTable.h +++ b/ExpressionParser/SymbolTable.h @@ -2,7 +2,7 @@ #define SYMBOLTABLE_H #include -using std::string; + class SymbolTableEntry; diff --git a/ExpressionParser/SymbolTableEntry.h b/ExpressionParser/SymbolTableEntry.h index c39abc04c..e3b445843 100644 --- a/ExpressionParser/SymbolTableEntry.h +++ b/ExpressionParser/SymbolTableEntry.h @@ -2,7 +2,6 @@ #define SYMBOLTABLEENTRY_H #include -using std::string; class ValueProxy; namespace VCell { @@ -15,7 +14,7 @@ class SymbolTableEntry { virtual double getConstantValue()=0; virtual VCell::Expression* getExpression()=0; virtual int getIndex()=0; - virtual string& getName()=0; + virtual std::string& getName()=0; virtual NameScope* getNameScope()=0; //VCUnitDefinition getUnitDefinition()=0; virtual bool isConstant()=0; diff --git a/ExpressionParser/Token.h b/ExpressionParser/Token.h index 03a53fc37..e17c0def4 100644 --- a/ExpressionParser/Token.h +++ b/ExpressionParser/Token.h @@ -2,7 +2,6 @@ #define TOKEN_H #include -using std::string; class Token { @@ -12,7 +11,7 @@ class Token int kind; int beginLine, beginColumn, endLine, endColumn; - string image; + std::string image; Token* next; Token* specialToken; diff --git a/ExpressionParserTest/ExpressionParserTest.cpp b/ExpressionParserTest/ExpressionParserTest.cpp index 4cc934c97..4ac91cd4a 100644 --- a/ExpressionParserTest/ExpressionParserTest.cpp +++ b/ExpressionParserTest/ExpressionParserTest.cpp @@ -1,41 +1,38 @@ #include "Expression.h" #include "Exception.h" -using namespace VCell; -#include + #include -using std::cout; -using std::endl; -using std::cerr; -int main(int argc, char *argv[]) { - - vector expStrs; - expStrs.push_back(string("log(0)")); - expStrs.push_back(string("(1<(2*(1<2)))*5+(1<(2*(2<1)))*5")); - expStrs.push_back(string("(1<2)*3+5*log(0)*(0>0)+2")); - expStrs.push_back(string("(1<2)*1*2+3*5+4+(1<1)")); - expStrs.push_back(string("(1<3)*(1 + +int main() { + std::vector expStrs; + expStrs.push_back(std::string("log(0)")); + expStrs.push_back(std::string("(1<(2*(1<2)))*5+(1<(2*(2<1)))*5")); + expStrs.push_back(std::string("(1<2)*3+5*log(0)*(0>0)+2")); + expStrs.push_back(std::string("(1<2)*1*2+3*5+4+(1<1)")); + expStrs.push_back(std::string("(1<3)*(1 " << expression.evaluateConstant() << endl; - cout << "tree eval (constant) ==> " << expression.evaluateConstantTree() << endl; - cout << "stack machine (vector) ==> " << expression.evaluateVector(values) << endl; - cout << "tree eval (vector) ==> " << expression.evaluateVectorTree(values) << endl; + std::cout << "stack machine (constant) ==> " << expression.evaluateConstant() << std::endl; + std::cout << "tree eval (constant) ==> " << expression.evaluateConstantTree() << std::endl; + std::cout << "stack machine (vector) ==> " << expression.evaluateVector(values) << std::endl; + std::cout << "tree eval (vector) ==> " << expression.evaluateVectorTree(values) << std::endl; } catch (const char* ex) { - cerr << "ExpressionParserTest failed : " << ex << endl; - } catch (string& ex) { - cerr << "ExpressionParserTest failed : " << ex << endl; - } catch (Exception& ex) { - cerr << "ExpressionParserTest failed : " << ex.getMessage() << endl; + std::cerr << "ExpressionParserTest failed : " << ex << std::endl; + } catch (std::string& ex) { + std::cerr << "ExpressionParserTest failed : " << ex << std::endl; + } catch (VCell::Exception& ex) { + std::cerr << "ExpressionParserTest failed : " << ex.getMessage() << std::endl; } catch (...) { - cerr << "ExpressionParserTest failed : unknown error." << endl; + std::cerr << "ExpressionParserTest failed : unknown error." << std::endl; } } } diff --git a/IDAWin/OdeResultSet.cpp b/IDAWin/OdeResultSet.cpp index 9ca5758d0..376e80d0a 100644 --- a/IDAWin/OdeResultSet.cpp +++ b/IDAWin/OdeResultSet.cpp @@ -1,190 +1,170 @@ +#include #include "OdeResultSet.h" -#include "Expression.h" -#include "SymbolTable.h" #include "Exception.h" +#include "Expression.h" -#include +OdeResultSet::OdeResultSet(): columnWeights(nullptr), rowData(nullptr), numRowsAllocated(0), numRowsUsed(0), numFunctionColumns(0), numDataColumns(0) +{} -OdeResultSet::OdeResultSet() -{ - columnWeights = NULL; - numFunctionColumns = 0; - numRowsAllocated = 0; - numRowsUsed = 0; - numDataColumns = 0; - rowData = 0; -} - -OdeResultSet::~OdeResultSet() -{ - for (int i = 0; i < (int)columns.size(); i ++) { - delete columns[i].expression; - } - columns.clear(); +OdeResultSet::~OdeResultSet(){ + for (const Column& column : this->columns) delete column.expression; + this->columns.clear(); delete[] rowData; delete[] columnWeights; } -void OdeResultSet::addColumn(const string& aColumn) { - if (numRowsAllocated != 0) { - throw VCell::Exception("Can't add column when rowData is not empty"); - } - columns.push_back(Column(aColumn, 0)); - numDataColumns ++; +void OdeResultSet::addColumn(const std::string& aColumn) { + if (0 != this->numRowsAllocated) throw VCell::Exception("Can't add column when rowData is not empty"); + this->columns.emplace_back(aColumn, nullptr); + this->numDataColumns++; } -void OdeResultSet::bindFunctionExpression(SymbolTable* symbolTable) { - for (int i = 0; i < (int)columns.size(); i ++) { - if (columns[i].expression != 0) { - columns[i].expression->bindExpression(symbolTable); - } +void OdeResultSet::bindFunctionExpression(SymbolTable* symbolTable) const { + for (const Column& column : this->columns) { + if (column.expression == nullptr) continue; + column.expression->bindExpression(symbolTable); } } -void OdeResultSet::addFunctionColumn(const string& aColumn, const string& exp) { - columns.push_back(Column(aColumn, new VCell::Expression(exp))); - numFunctionColumns ++; +void OdeResultSet::addFunctionColumn(const std::string& aColumn, const std::string& columnExpression) { + this->columns.push_back(Column(aColumn, new VCell::Expression(columnExpression))); + this->numFunctionColumns ++; } -void OdeResultSet::addRow(double* aRow) { - if (numRowsAllocated == 0) { - numRowsAllocated = 500; - rowData = new double[numRowsAllocated * numDataColumns]; - memset(rowData, 0, numRowsAllocated * numDataColumns * sizeof(double)); - } else if (numRowsAllocated == numRowsUsed) { - int oldNumRowsAllocated = numRowsAllocated; - double* oldRowData = rowData; - numRowsAllocated += 500; - rowData = new double[numRowsAllocated * numDataColumns]; - memset(rowData, 0, numRowsAllocated * numDataColumns * sizeof(double)); - memcpy(rowData, oldRowData, oldNumRowsAllocated * numDataColumns * sizeof(double)); +void OdeResultSet::addRow(const double* aRow) { + if (this->numRowsAllocated == 0) { + this->numRowsAllocated = 500; + this->rowData = new double[this->numRowsAllocated * this->numDataColumns]; + memset(this->rowData, 0, this->numRowsAllocated * this->numDataColumns * sizeof(double)); + } else if (this->numRowsAllocated == this->numRowsUsed) { + const int oldNumRowsAllocated = this->numRowsAllocated; + const double* oldRowData = this->rowData; + this->numRowsAllocated += 500; + this->rowData = new double[this->numRowsAllocated * this->numDataColumns]; + memset(this->rowData, 0, this->numRowsAllocated * this->numDataColumns * sizeof(double)); + memcpy(this->rowData, oldRowData, oldNumRowsAllocated * this->numDataColumns * sizeof(double)); delete[] oldRowData; } - int index = numRowsUsed * numDataColumns; - for (int i = 0; i < numDataColumns; i ++, index ++) { - rowData[index] = aRow[i]; + int index = this->numRowsUsed * this->numDataColumns; + for (int i = 0; i < this->numDataColumns; i ++, index ++) { + this->rowData[index] = aRow[i]; } - numRowsUsed ++; + this->numRowsUsed++; } -void OdeResultSet::setColumnWeights(double* weights){ - delete[] columnWeights; - columnWeights = new double[columns.size()]; - memcpy(columnWeights, weights, columns.size() * sizeof(double)); +void OdeResultSet::setColumnWeights(const double* weights){ + delete[] this->columnWeights; + this->columnWeights = new double[this->columns.size()]; + memcpy(this->columnWeights, weights, this->columns.size() * sizeof(double)); } -double* OdeResultSet::getRowData(int index) { - if (index >= numRowsUsed) { +double* OdeResultSet::getRowData(const int index) { + if (index >= this->numRowsUsed) { throw VCell::Exception("OdeResultSet::getRowData(int index), row index is out of bounds"); } - return rowData + index * numDataColumns; + return this->rowData + index * this->numDataColumns; } void OdeResultSet::clearData() { - numRowsUsed = 0; - memset(rowData, 0, numRowsAllocated * numDataColumns * sizeof(double)); + this->numRowsUsed = 0; + memset(this->rowData, 0, this->numRowsAllocated * this->numDataColumns * sizeof(double)); } -int OdeResultSet::findColumn(const string& aColumn) { +int OdeResultSet::findColumn(const std::string& aColumn) const { int columnIndex = 0; - for (vector::iterator iter = columns.begin(); iter < columns.end(); iter++) { - if ((*iter).name == aColumn) { - break; - } - columnIndex ++; - } - if (columnIndex == columns.size()) { - columnIndex = -1; + for (const Column& column : this->columns) { + if (column.name == aColumn) break; + columnIndex++; } + if (columnIndex == this->columns.size()) columnIndex = -1; return columnIndex; } double OdeResultSet::getColumnWeight(int index) { - if (index >= (int)columns.size()) { - throw "OdeResultSet::getColumnWeight(int index), column index is out of bounds"; + if (index >= (int)this->columns.size()) { + throw std::out_of_range("OdeResultSet::getColumnWeight(int index), column index is out of bounds"); } return columnWeights[index]; } -int OdeResultSet::getNumColumns() { - return (int)columns.size(); +int OdeResultSet::getNumColumns() const { + return static_cast(this->columns.size()); } -string& OdeResultSet::getColumnName(int index) { - if (index >= (int)columns.size()) { - throw "OdeResultSet::getColumnName(int index), column index is out of bounds"; +std::string& OdeResultSet::getColumnName(const int index) { + if (index >= static_cast(this->columns.size())) { + throw std::out_of_range("OdeResultSet::getColumnName(int index), column index is out of bounds"); } - return columns[index].name; + return this->columns[index].name; } -int OdeResultSet::getNumRows() { - return numRowsUsed; +int OdeResultSet::getNumRows() const { + return this->numRowsUsed; } -VCell::Expression* OdeResultSet::getColumnFunctionExpression(int columnIndex) { - if (columnIndex >= (int)columns.size()) { - throw "OdeResultSet::getColumnFunctionExpression(), column index is out of bounds"; +VCell::Expression* OdeResultSet::getColumnFunctionExpression(const int columnIndex) const { + if (columnIndex >= static_cast(this->columns.size())) { + throw std::out_of_range("OdeResultSet::getColumnFunctionExpression(), column index is out of bounds"); } - return columns[columnIndex].expression; + return this->columns[columnIndex].expression; } -void OdeResultSet::getColumnData(int columnIndex, int numParams, double* paramValues, double* colData) { - int numCols = getNumColumns(); - if (columnIndex < 0 || columnIndex >= numCols){ - throw "OdeResultSet::getColumnData(int columnIndex), columnIndex out of bounds"; +void OdeResultSet::getColumnData(const int index, const int numParams, const double* paramValues, double* colData) const { + if (index < 0 || index >= this->getNumColumns()){ + throw std::out_of_range("OdeResultSet::getColumnData(int columnIndex), columnIndex out of bounds"); } - if (columns[columnIndex].expression == 0) { - for (int i = 0; i < numRowsUsed; i++){ - colData[i] = rowData[i * numDataColumns + columnIndex]; + if (this->columns[index].expression == nullptr) { + for (int i = 0; i < this->numRowsUsed; i++){ + colData[i] = this->rowData[i * this->numDataColumns + index]; } } else { // Function - double* values = new double[numDataColumns + numParams]; - memcpy(values + numDataColumns, paramValues, numParams * sizeof(double)); - for (int i = 0; i < numRowsUsed; i++){ - memcpy(values, rowData + i * numDataColumns, numDataColumns * sizeof(double)); - colData[i] = columns[columnIndex].expression->evaluateVector(values); + auto* values = new double[this->numDataColumns + numParams]; + memcpy(values + this->numDataColumns, paramValues, numParams * sizeof(double)); + for (int i = 0; i < this->numRowsUsed; i++){ + memcpy(values, this->rowData + i * this->numDataColumns, this->numDataColumns * sizeof(double)); + colData[i] = this->columns[index].expression->evaluateVector(values); } delete[] values; } } -void OdeResultSet::copyInto(OdeResultSet* otherOdeResultSet) { +void OdeResultSet::copyInto(OdeResultSet* otherOdeResultSet) const { // columns - if (otherOdeResultSet->columns.size() != columns.size()) { - for (int i = 0; i < (int)otherOdeResultSet->columns.size(); i ++) { - delete otherOdeResultSet->columns[i].expression; + if (otherOdeResultSet->columns.size() != this->columns.size()) { + for (const Column& column : otherOdeResultSet->columns) { + delete column.expression; } otherOdeResultSet->columns.clear(); - for (int i = 0; i < (int)columns.size(); i ++) { - if (columns[i].expression == 0) { - otherOdeResultSet->addColumn(columns[i].name); + for (const Column& column : this->columns) { + if (nullptr == column.expression) { + otherOdeResultSet->addColumn(column.name); } else { - otherOdeResultSet->addFunctionColumn(columns[i].name, columns[i].expression->infix()); + otherOdeResultSet->addFunctionColumn(column.name, column.expression->infix()); } } - if (columnWeights != 0) { - otherOdeResultSet->setColumnWeights(columnWeights); + if (nullptr != this->columnWeights) { + otherOdeResultSet->setColumnWeights(this->columnWeights); } } // rows - if (otherOdeResultSet->numRowsAllocated != numRowsAllocated) { + if (otherOdeResultSet->numRowsAllocated != this->numRowsAllocated) { delete[] otherOdeResultSet->rowData; - otherOdeResultSet->rowData = new double[numRowsAllocated * numDataColumns]; - otherOdeResultSet->numRowsAllocated = numRowsAllocated; + otherOdeResultSet->rowData = new double[this->numRowsAllocated * this->numDataColumns]; + otherOdeResultSet->numRowsAllocated = this->numRowsAllocated; } - otherOdeResultSet->numRowsUsed = numRowsUsed; - memcpy(otherOdeResultSet->rowData, rowData, numRowsAllocated * numDataColumns * sizeof(double)); + otherOdeResultSet->numRowsUsed = this->numRowsUsed; + memcpy(otherOdeResultSet->rowData, this->rowData, this->numRowsAllocated * this->numDataColumns * sizeof(double)); } void OdeResultSet::addEmptyRows(int numRowsToAdd) { - if (numRowsAllocated < numRowsUsed + numRowsToAdd) { - int oldNumRowsAllocated = numRowsAllocated; - double* oldRowData = rowData; - numRowsAllocated = numRowsUsed + numRowsToAdd; - rowData = new double[numRowsAllocated * numDataColumns]; - memset(rowData, 0, numRowsAllocated * numDataColumns * sizeof(double)); - memcpy(rowData, oldRowData, oldNumRowsAllocated * numDataColumns * sizeof(double)); + if (this->numRowsAllocated < this->numRowsUsed + numRowsToAdd) { + int oldNumRowsAllocated = this->numRowsAllocated; + double* oldRowData = this->rowData; + this->numRowsAllocated = this->numRowsUsed + numRowsToAdd; + this->rowData = new double[this->numRowsAllocated * this->numDataColumns]; + memset(this->rowData, 0, this->numRowsAllocated * this->numDataColumns * sizeof(double)); + memcpy(this->rowData, oldRowData, oldNumRowsAllocated * this->numDataColumns * sizeof(double)); delete[] oldRowData; } - numRowsUsed += numRowsToAdd; + this->numRowsUsed += numRowsToAdd; } diff --git a/IDAWin/OdeResultSet.h b/IDAWin/OdeResultSet.h index 08eb0fcfe..42a8505e7 100644 --- a/IDAWin/OdeResultSet.h +++ b/IDAWin/OdeResultSet.h @@ -2,67 +2,84 @@ #define ODERESULTSET_H #include +#include #include namespace VCell { class Expression; } + class SymbolTable; struct Column { std::string name; - VCell::Expression* expression; - - Column(std::string arg_name, VCell::Expression* exp) { - name = arg_name; - expression = exp; + VCell::Expression *expression; + + Column(std::string arg_name, VCell::Expression *exp) { + this->name = std::move(arg_name); + this->expression = exp; } }; -class OdeResultSet -{ -public: - OdeResultSet(); - ~OdeResultSet(); - void addColumn(const std::string& aColumn); - void addFunctionColumn(const std::string& aColumn, const std::string& columnExpression); - void addRow(double* aRow); - void setColumnWeights(double* weights); - - void bindFunctionExpression(SymbolTable* symbolTable); - - double* getRowData(int index); - const double* getRowData() { - return rowData; - } +class OdeResultSet { + public: + OdeResultSet(); + + ~OdeResultSet(); + + // Column Methods + [[nodiscard]] int getNumColumns() const; + + std::vector getColumns() { return this->columns; } + + void addColumn(const std::string &aColumn); + + [[nodiscard]] int findColumn(const std::string &aColumn) const; + + std::string &getColumnName(int index); + + double getColumnWeight(int index); + + void setColumnWeights(const double *weights); + + void getColumnData(int index, int numParams, const double *paramValues, double *colData) const; + + // Function Methods + void addFunctionColumn(const std::string &aColumn, const std::string &columnExpression); + + [[nodiscard]] int getNumFunctionColumns() const { return this->numFunctionColumns; } + [[nodiscard]] int getNumDataColumns() const { return this->numDataColumns; } + + [[nodiscard]] VCell::Expression *getColumnFunctionExpression(int columnIndex) const; + + void bindFunctionExpression(SymbolTable *symbolTable) const; + + // Row Methods + [[nodiscard]] int getNumRows() const; + + void addRow(const double *aRow); + + double *getRowData(int index); + + [[nodiscard]] double *getAllRowData() const { return this->rowData; } + + void addEmptyRows(int numRowsToAdd); + + // Misc. + void copyInto(OdeResultSet *otherOdeResultSet) const; + + void clearData(); - int findColumn(const std::string& aColumn); - double getColumnWeight(int index); - std::string& getColumnName(int index); - void getColumnData(int index, int numParams, double* paramValues, double* colData); - - int getNumColumns(); - int getNumRows(); - int getNumFunctionColumns() { return numFunctionColumns; } - int getNumDataColumns() { return numDataColumns; } - std::vector getColumns(){ return columns;} - - VCell::Expression* getColumnFunctionExpression(int columnIndex); - void clearData(); - - void copyInto(OdeResultSet* otherOdeResultSet); - void addEmptyRows(int numRowsToAdd); - -private: - // 0 : t - // 1 ~ N : variable names; - std::vector columns; - double* columnWeights; - double* rowData; - int numRowsAllocated; - int numRowsUsed; - int numFunctionColumns; - int numDataColumns; + private: + // 0 : t + // 1 ~ N : variable names; + std::vector columns; + double *columnWeights; + double *rowData; + int numRowsAllocated; + int numRowsUsed; + int numFunctionColumns; + int numDataColumns; }; #endif diff --git a/IDAWin/StoppedByUserException.cpp b/IDAWin/StoppedByUserException.cpp index 6bc89b05a..e1ef6787c 100644 --- a/IDAWin/StoppedByUserException.cpp +++ b/IDAWin/StoppedByUserException.cpp @@ -1,9 +1,6 @@ #include "StoppedByUserException.h" -StoppedByUserException::StoppedByUserException(string msg) : Exception("StoppedByUserException: " + msg) -{ -} +StoppedByUserException::StoppedByUserException(const string& msg) + : Exception("StoppedByUserException: " + msg){} -StoppedByUserException::~StoppedByUserException(void) throw( ) -{ -} +StoppedByUserException::~StoppedByUserException() noexcept = default; diff --git a/IDAWin/StoppedByUserException.h b/IDAWin/StoppedByUserException.h index 813db45b3..de2f56c6e 100644 --- a/IDAWin/StoppedByUserException.h +++ b/IDAWin/StoppedByUserException.h @@ -3,11 +3,10 @@ #include -class StoppedByUserException : public VCell::Exception -{ +class StoppedByUserException final : public VCell::Exception { public: - StoppedByUserException(string msg); - ~StoppedByUserException(void) throw( ); + explicit StoppedByUserException(const string& msg); + ~StoppedByUserException() noexcept override; }; #endif diff --git a/IDAWin/VCellCVodeSolver.cpp b/IDAWin/VCellCVodeSolver.cpp index b36e7e5ad..f81ed0a59 100644 --- a/IDAWin/VCellCVodeSolver.cpp +++ b/IDAWin/VCellCVodeSolver.cpp @@ -1,17 +1,14 @@ #include "VCellCVodeSolver.h" #include "OdeResultSet.h" #include -#include #include -#include +#include #include #include #include -#include "StoppedByUserException.h" -#include #include #include -using std::stringstream; +#include #ifdef USE_MESSAGING #include @@ -122,17 +119,17 @@ void VCellCVodeSolver::throwCVodeErrorMessage(int returnCode) { throw "CV_LSOLVE_FAIL: the linear solver's solve routine failed in an unrecoverable manner"; } case CV_REPTD_RHSFUNC_ERR: { - stringstream ss; + std::stringstream ss; ss << "CV_REPTD_RHSFUNC_ERR: repeated recoverable right-hand side function errors : " << recoverableErrMsg; throw ss.str(); } case CV_UNREC_RHSFUNC_ERR:{ - stringstream ss; + std::stringstream ss; ss << "CV_UNREC_RHSFUNC_ERR: the right-hand side failed in a recoverable manner, but no recovery is possible : " << recoverableErrMsg; throw ss.str(); } case CV_FIRST_RHSFUNC_ERR: { - stringstream ss; + std::stringstream ss; ss << "CV_FIRST_RHSFUNC_ERR: The right-hand side routine failed at the first call : " << recoverableErrMsg; throw ss.str(); } @@ -178,8 +175,8 @@ Input format: */ void VCellCVodeSolver::readEquations(std::istream& inputstream) { try { - string name; - string exp; + std::string name; + std::string exp; rateExpressions = new Expression*[NEQ]; @@ -192,7 +189,7 @@ void VCellCVodeSolver::readEquations(std::istream& inputstream) { try { initialConditionExpressions[i] = readExpression(inputstream); } catch (VCell::Exception& ex) { - throw VCell::Exception(string("Initial condition expression for [") + variableNames[i] + "] " + ex.getMessage()); + throw VCell::Exception(std::string("Initial condition expression for [") + variableNames[i] + "] " + ex.getMessage()); } // RATE @@ -200,13 +197,13 @@ void VCellCVodeSolver::readEquations(std::istream& inputstream) { try { rateExpressions[i] = readExpression(inputstream); } catch (VCell::Exception& ex) { - throw VCell::Exception(string("Rate expression for [") + variableNames[i] + "] " + ex.getMessage()); + throw VCell::Exception(std::string("Rate expression for [") + variableNames[i] + "] " + ex.getMessage()); } } } catch (char* ex) { - throw VCell::Exception(string("VCellCVodeSolver::readInput() : ") + ex); + throw VCell::Exception(std::string("VCellCVodeSolver::readInput() : ") + ex); } catch (VCell::Exception& ex) { - throw VCell::Exception(string("VCellCVodeSolver::readInput() : ") + ex.getMessage()); + throw VCell::Exception(std::string("VCellCVodeSolver::readInput() : ") + ex.getMessage()); } catch (...) { throw "VCellCVodeSolver::readInput() : caught unknown exception"; } @@ -230,15 +227,15 @@ int VCellCVodeSolver::RHS (realtype t, N_Vector y, N_Vector r) { } recoverableErrMsg = ""; return 0; - }catch (DivideByZeroException e){ + }catch (DivideByZeroException& e){ std::cout << "failed to evaluate residual: " << e.getMessage() << std::endl; recoverableErrMsg = e.getMessage(); return 1; - }catch (FunctionDomainException e){ + }catch (FunctionDomainException& e){ std::cout << "failed to evaluate residual: " << e.getMessage() << std::endl; recoverableErrMsg = e.getMessage(); return 1; - }catch (FunctionRangeException e){ + }catch (FunctionRangeException& e){ std::cout << "failed to evaluate residual: " << e.getMessage() << std::endl; recoverableErrMsg = e.getMessage(); return 1; @@ -260,14 +257,11 @@ int VCellCVodeSolver::RootFn_callback(realtype t, N_Vector y, realtype *gout, vo } void VCellCVodeSolver::solve(double* paramValues, bool bPrintProgress, FILE* outputFile, void (*checkStopRequested)(double, long)) { - if (checkStopRequested != 0) { - checkStopRequested(STARTING_TIME, 0); - } - + if (checkStopRequested != nullptr) checkStopRequested(STARTING_TIME, 0); writeFileHeader(outputFile); // clear data in result set before solving - odeResultSet->clearData(); + this->odeResultSet->clearData(); // copy parameter values to the end of values, these will stay the same during solving memset(values, 0, (NEQ + 1) * sizeof(double)); @@ -301,9 +295,9 @@ void VCellCVodeSolver::initCVode(double* paramValues) { void VCellCVodeSolver::reInit(double t) { int flag = 0; - if (solver == 0) { + if (solver == nullptr) { solver = CVodeCreate(CV_BDF, CV_NEWTON); - if (solver == 0) { + if (solver == nullptr) { throw "VCellCVodeSolver:: Out of memory"; } flag = CVodeMalloc(solver, RHS_callback, t, y, ToleranceType, RelativeTolerance, &AbsoluteTolerance); @@ -434,10 +428,9 @@ void VCellCVodeSolver::cvodeSolve(bool bPrintProgress, FILE* outputFile, void (* if (returnCode == CV_ROOT_RETURN || iterationCount % keepEvery == 0 || Time >= ENDING_TIME){ outputCount++; if (outputCount * (NEQ + 1) * bytesPerSample > MaxFileSizeBytes){ - /* if more than one gigabyte, then fail */ - char msg[100]; - sprintf(msg, "output exceeded maximum %d bytes", MaxFileSizeBytes); - throw VCell::Exception(msg); + /* if more than one gigabyte, then fail */ + const std::string msg{"output exceeded maximum " + std::to_string(MaxFileSizeBytes) + " bytes"}; + throw VCell::Exception(msg.c_str()); } writeData(Time, outputFile); if (bPrintProgress) { diff --git a/IDAWin/VCellCVodeSolver.h b/IDAWin/VCellCVodeSolver.h index ebb4d3f76..f9cceb84a 100644 --- a/IDAWin/VCellCVodeSolver.h +++ b/IDAWin/VCellCVodeSolver.h @@ -2,19 +2,20 @@ #define VCELLCVODESOLVER_H #include "VCellSundialsSolver.h" +#include class VCellCVodeSolver : public VCellSundialsSolver { public: VCellCVodeSolver(); - ~VCellCVodeSolver(); + ~VCellCVodeSolver() override; - void solve(double* paramValues=0, bool bPrintProgress=false, FILE* outputFile=0, void (*checkStopRequested)(double, long)=0); + void solve(double* paramValues=nullptr, bool bPrintProgress=false, FILE* outputFile=nullptr, void (*checkStopRequested)(double, long)=nullptr) override; double RHS(double* allValues, int equationIndex); protected: - void readEquations(std::istream& inputstream); - void initialize(); - string getSolverName() { return "CVODE"; } + void readEquations(std::istream& inputstream) override; + void initialize() override; + std::string getSolverName() override { return "CVODE"; } private: VCell::Expression** rateExpressions; @@ -52,8 +53,8 @@ class VCellCVodeSolver : public VCellSundialsSolver { void checkCVodeFlag(int flag); void reInit(realtype t); - bool fixInitialDiscontinuities(double t); - void updateTandVariableValues(realtype t, N_Vector y); + bool fixInitialDiscontinuities(double t) override; + void updateTandVariableValues(realtype t, N_Vector y) override; void onCVodeReturn(realtype Time, int returnCode); diff --git a/IDAWin/VCellIDASolver.cpp b/IDAWin/VCellIDASolver.cpp index bd0e5de63..4a4fce35c 100644 --- a/IDAWin/VCellIDASolver.cpp +++ b/IDAWin/VCellIDASolver.cpp @@ -9,13 +9,14 @@ #include #include #include -using std::stringstream; + #ifdef USE_MESSAGING #include #endif #include +#include #include #include //#include @@ -158,7 +159,7 @@ void VCellIDASolver::throwIDAErrorMessage(int returnCode) { throw "IDA_CONSTR_FAIL: The inequality constraints were violated, and the solver was unable to recover."; } case IDA_REP_RES_ERR:{ - stringstream ss; + std::stringstream ss; ss << "IDA_REP_RES_ERR: The user's residual function repeatedly returned a recoverable error flag, but the solver was unable to recover. " << recoverableErrMsg; throw ss.str(); } @@ -172,7 +173,7 @@ void VCellIDASolver::throwIDAErrorMessage(int returnCode) { throw "IDA_BAD_EWT: Some component of the error weight vector is zero (illegal)."; } case IDA_FIRST_RES_FAIL:{ - stringstream ss; + std::stringstream ss; ss << "IDA_FIRST_RES_FAIL: The user's residual function returned a recoverable error flag on the first call, but IDA was unable to recover. " << recoverableErrMsg; throw ss.str(); } @@ -294,8 +295,8 @@ Input format: void VCellIDASolver::readEquations(std::istream& inputstream) { try { - string token; - string exp; + std::string token; + std::string exp; // // test whether it is a "VAR" block (i.e. whether VAR) @@ -317,7 +318,7 @@ void VCellIDASolver::readEquations(std::istream& inputstream) { try { initialConditionExpressions[i] = readExpression(inputstream); } catch (VCell::Exception& ex) { - throw VCell::Exception(string("Initial condition expression for [") + variableNames[i] + "] " + ex.getMessage()); + throw VCell::Exception(std::string("Initial condition expression for [") + variableNames[i] + "] " + ex.getMessage()); } } @@ -373,15 +374,15 @@ void VCellIDASolver::readEquations(std::istream& inputstream) { try { rhsExpressions[i] = readExpression(inputstream); } catch (VCell::Exception& ex) { - stringstream ss; + std::stringstream ss; ss << "RHS[" << i << "] " << ex.getMessage(); throw VCell::Exception(ss.str()); } } } catch (const char* ex) { - throw VCell::Exception(string("VCellIDASolver::readInput() : ") + ex); + throw VCell::Exception(std::string("VCellIDASolver::readInput() : ") + ex); } catch (VCell::Exception& ex) { - throw VCell::Exception(string("VCellIDASolver::readInput() : ") + ex.getMessage()); + throw VCell::Exception(std::string("VCellIDASolver::readInput() : ") + ex.getMessage()); } catch (...) { throw VCell::Exception("VCellIDASolver::readInput() caught unknown exception"); } diff --git a/IDAWin/VCellIDASolver.h b/IDAWin/VCellIDASolver.h index 2d4bae607..459701003 100644 --- a/IDAWin/VCellIDASolver.h +++ b/IDAWin/VCellIDASolver.h @@ -2,6 +2,7 @@ #define VCELLIDASOLVER_H #include "VCellSundialsSolver.h" +#include class VCellIDASolver : public VCellSundialsSolver { public: @@ -14,7 +15,7 @@ class VCellIDASolver : public VCellSundialsSolver { void updateTempRowData(double currTime); void readEquations(std::istream& inputstream); void initialize(); - string getSolverName() { return "IDA"; } + std::string getSolverName() { return "IDA"; } private: VCell::Expression** rhsExpressions; // can be rate expression in ODE case or RHS expression in DAE case diff --git a/IDAWin/VCellSundialsSolver.cpp b/IDAWin/VCellSundialsSolver.cpp index bc40c3692..06257a695 100644 --- a/IDAWin/VCellSundialsSolver.cpp +++ b/IDAWin/VCellSundialsSolver.cpp @@ -2,10 +2,12 @@ #include "StoppedByUserException.h" #include "VCellSundialsSolver.h" #include "OdeResultSet.h" -#include -#include +#include +#include +#include #include -using std::stringstream; +#include + #include @@ -13,28 +15,20 @@ using std::stringstream; #include #endif -static void trimString(string& str) -{ - string::size_type pos = str.find_last_not_of(" \r\n"); - if(pos != string::npos) { +static void trimString(std::string &str) { + std::string::size_type pos = str.find_last_not_of(" \r\n"); + if (pos != std::string::npos) { str.erase(pos + 1); pos = str.find_first_not_of(" \r\n"); - if(pos != string::npos) { - str.erase(0, pos); - } - } - else { - str.erase(str.begin(), str.end()); - } + if (pos != std::string::npos) { str.erase(0, pos); } + } else { str.erase(str.begin(), str.end()); } } -VCell::Expression* VCellSundialsSolver::readExpression(std::istream& inputstream) { - string exp; +VCell::Expression *VCellSundialsSolver::readExpression(std::istream &inputstream) { + std::string exp; getline(inputstream, exp); trimString(exp); - if (*(exp.end() - 1) != ';') { - throw VCell::Exception(BAD_EXPRESSION_MSG); - } + if (*(exp.end() - 1) != ';') { throw VCell::Exception(BAD_EXPRESSION_MSG); } return new VCell::Expression(exp); } @@ -42,57 +36,53 @@ VCellSundialsSolver::VCellSundialsSolver() { NEQ = 0; NPARAM = 0; STARTING_TIME = 0.0; - ENDING_TIME = 0.0; + ENDING_TIME = 0.0; RelativeTolerance = 0.0; AbsoluteTolerance = 0.0; keepEvery = 0; - maxTimeStep = 0.0; + maxTimeStep = 0.0; - solver = 0; - initialConditionSymbolTable = 0; - initialConditionExpressions = 0; - values = 0; - tempRowData = 0; + solver = nullptr; + initialConditionSymbolTable = nullptr; + initialConditionExpressions = nullptr; + values = nullptr; + tempRowData = nullptr; - variableNames = 0; - paramNames = 0; - allSymbols = 0; + variableNames = nullptr; + paramNames = nullptr; + allSymbols = nullptr; numAllSymbols = 0; - defaultSymbolTable = 0; + defaultSymbolTable = nullptr; numDiscontinuities = 0; - odeDiscontinuities = 0; - discontinuityValues = 0; - discontinuitySymbolTable = 0; - rootsFound = 0; + odeDiscontinuities = nullptr; + discontinuityValues = nullptr; + discontinuitySymbolTable = nullptr; + rootsFound = nullptr; - odeResultSet = new OdeResultSet(); - y = 0; + odeResultSet = new OdeResultSet(); + y = nullptr; - events = 0; + events = nullptr; numEvents = 0; } VCellSundialsSolver::~VCellSundialsSolver() { N_VDestroy_Serial(y); - for (int i = 0; i < NEQ; i ++) { - delete initialConditionExpressions[i]; - } + for (int i = 0; i < NEQ; i++) { delete initialConditionExpressions[i]; } delete[] initialConditionExpressions; delete[] variableNames; delete[] paramNames; delete[] allSymbols; delete defaultSymbolTable; - for (int i = 0; i < numDiscontinuities; i ++) { - delete odeDiscontinuities[i]; - } + for (int i = 0; i < numDiscontinuities; i++) { delete odeDiscontinuities[i]; } delete[] odeDiscontinuities; delete[] rootsFound; delete[] discontinuityValues; delete discontinuitySymbolTable; - + delete[] values; delete[] tempRowData; delete initialConditionSymbolTable; @@ -100,37 +90,36 @@ VCellSundialsSolver::~VCellSundialsSolver() { outputTimes.clear(); - for (int i = 0; i < numEvents; i ++) { - delete events[i]; - } + for (int i = 0; i < numEvents; i++) { delete events[i]; } delete[] events; + + for (EventExecution* elem : eventExeList) { + delete elem; + } + eventExeList.clear(); } void VCellSundialsSolver::updateTempRowData(double currTime) { - tempRowData[0] = currTime; - for (int i = 0; i < NEQ; i++) { - tempRowData[i+1] = NV_Ith_S(y,i); - } + tempRowData[0] = currTime; + for (int i = 0; i < NEQ; i++) { tempRowData[i + 1] = NV_Ith_S(y, i); } } -void VCellSundialsSolver::writeData(double currTime, FILE* outputFile) { +void VCellSundialsSolver::writeData(double currTime, FILE *outputFile) { updateTempRowData(currTime); odeResultSet->addRow(tempRowData); writeFileData(outputFile); } -void VCellSundialsSolver::writeFileData(FILE* outputFile) { - if (outputFile != 0) { - fprintf(outputFile, "%0.17E", tempRowData[0]); - for (int i = 1; i < NEQ+1; i++) { - fprintf(outputFile, "\t%0.17E", tempRowData[i]); - } +void VCellSundialsSolver::writeFileData(FILE *outputFile) { + if (outputFile != nullptr) { + fprintf(outputFile, "%0.17E", tempRowData[0]); + for (int i = 1; i < NEQ + 1; i++) { fprintf(outputFile, "\t%0.17E", tempRowData[i]); } fprintf(outputFile, "\n"); } } -void VCellSundialsSolver::writeFileHeader(FILE* outputFile) { - if (outputFile != 0) { +void VCellSundialsSolver::writeFileHeader(FILE *outputFile) { + if (outputFile != nullptr) { // Print header... for (int i = 0; i < odeResultSet->getNumColumns(); i++) { fprintf(outputFile, "%s:", odeResultSet->getColumnName(i).data()); @@ -139,38 +128,41 @@ void VCellSundialsSolver::writeFileHeader(FILE* outputFile) { } } -void VCellSundialsSolver::printProgress(double currTime, double& lastPercentile, clock_t& lastTime, double increment, FILE* outputFile) { +void VCellSundialsSolver::printProgress(double currTime, double &lastPercentile, clock_t &lastTime, double increment, + FILE *outputFile) const { fflush(outputFile); if (currTime == STARTING_TIME) { // print 0% -#ifdef USE_MESSAGING + #ifdef USE_MESSAGING SimulationMessaging::getInstVar()->setWorkerEvent(new WorkerEvent(JOB_PROGRESS, lastPercentile, currTime)); -#else + #else printf("[[[progress:%lg%%]]]", lastPercentile*100.0); fflush(stdout); -#endif + #endif } else { clock_t currentTime = clock(); - double duration = (double)(currentTime - lastTime) / CLOCKS_PER_SEC; - if (duration >= 2){ //send out message every 2 seconds - double newPercentile = (long)((currTime - STARTING_TIME)/(ENDING_TIME - STARTING_TIME)/increment) * increment; + double duration = (double) (currentTime - lastTime) / CLOCKS_PER_SEC; + if (duration >= 2) { //send out message every 2 seconds + double newPercentile = (long) ((currTime - STARTING_TIME) / (ENDING_TIME - STARTING_TIME) / increment) * + increment; /*while (true) { double midTime = (percentile + increment) * (ENDING_TIME - STARTING_TIME); - if (STARTING_TIME + midTime > currTime) { + if (STARTING_TIME + midTime > currTime) { break; } percentile += increment; }*/ - if ( lastPercentile != newPercentile) { -#ifdef USE_MESSAGING - SimulationMessaging::getInstVar()->setWorkerEvent(new WorkerEvent(JOB_PROGRESS, newPercentile, currTime)); + if (lastPercentile != newPercentile) { + #ifdef USE_MESSAGING + SimulationMessaging::getInstVar()-> + setWorkerEvent(new WorkerEvent(JOB_PROGRESS, newPercentile, currTime)); SimulationMessaging::getInstVar()->setWorkerEvent(new WorkerEvent(JOB_DATA, newPercentile, currTime)); -#else + #else printf("[[[progress:%lg%%]]]", newPercentile*100.0); - printf("[[[data:%lg]]]", currTime); + printf("[[[data:%lg]]]", currTime); fflush(stdout); -#endif + #endif lastPercentile = newPercentile; lastTime = currentTime; } @@ -178,34 +170,22 @@ void VCellSundialsSolver::printProgress(double currTime, double& lastPercentile, } } -void VCellSundialsSolver::readInput(std::istream& inputstream) { +void VCellSundialsSolver::readInput(std::istream &inputstream) { try { - if (solver != 0) { - throw "readInput should only be called once"; - } - string name; + if (solver != 0) { throw "readInput should only be called once"; } + std::string name; while (!inputstream.eof()) { name = ""; inputstream >> name; - if (name.empty()) { - continue; - } + if (name.empty()) { continue; } if (name == "SOLVER") { inputstream >> name; - if (name != getSolverName()) { - throw "Wrong solver"; - } - } else if (name == "STARTING_TIME") { - inputstream >> STARTING_TIME; - } else if (name == "ENDING_TIME") { - inputstream >> ENDING_TIME; - } else if (name == "RELATIVE_TOLERANCE") { - inputstream >> RelativeTolerance; - } else if (name == "ABSOLUTE_TOLERANCE") { - inputstream >> AbsoluteTolerance; - } else if (name == "MAX_TIME_STEP") { - inputstream >> maxTimeStep; - } else if (name == "KEEP_EVERY") { + if (name != getSolverName()) { throw "Wrong solver"; } + } else if (name == "STARTING_TIME") { inputstream >> STARTING_TIME; } else if ( + name == "ENDING_TIME") { inputstream >> ENDING_TIME; } else if ( + name == "RELATIVE_TOLERANCE") { inputstream >> RelativeTolerance; } else if ( + name == "ABSOLUTE_TOLERANCE") { inputstream >> AbsoluteTolerance; } else if ( + name == "MAX_TIME_STEP") { inputstream >> maxTimeStep; } else if (name == "KEEP_EVERY") { inputstream >> keepEvery; } else if (name == "OUTPUT_TIME_STEP") { double outputTimeStep = 0.0; @@ -215,113 +195,90 @@ void VCellSundialsSolver::readInput(std::istream& inputstream) { while (STARTING_TIME + count * outputTimeStep < ENDING_TIME + 1E-12 * ENDING_TIME) { timePoint = STARTING_TIME + count * outputTimeStep; outputTimes.push_back(timePoint); - count ++; + count++; } ENDING_TIME = outputTimes[outputTimes.size() - 1]; } else if (name == "OUTPUT_TIMES") { - int totalNumTimePoints; + int totalNumTimePoints; double timePoint; inputstream >> totalNumTimePoints; - for (int i = 0; i < totalNumTimePoints; i ++) { + for (int i = 0; i < totalNumTimePoints; i++) { inputstream >> timePoint; - if (timePoint > STARTING_TIME && timePoint <= ENDING_TIME) { - outputTimes.push_back(timePoint); - } + if (timePoint > STARTING_TIME && timePoint <= ENDING_TIME) { outputTimes.push_back(timePoint); } } - if (outputTimes[outputTimes.size() - 1] < ENDING_TIME) { - outputTimes.push_back(ENDING_TIME); - } - } else if (name == "DISCONTINUITIES") { - readDiscontinuities(inputstream); - } else if (name == "NUM_PARAMETERS") { + if (outputTimes[outputTimes.size() - 1] < ENDING_TIME) { outputTimes.push_back(ENDING_TIME); } + } else if (name == "DISCONTINUITIES") { readDiscontinuities(inputstream); } else if ( + name == "NUM_PARAMETERS") { inputstream >> NPARAM; - paramNames = new string[NPARAM]; - for (int i = 0; i < NPARAM; i ++) { - inputstream >> paramNames[i]; - } + paramNames = new std::string[NPARAM]; + for (int i = 0; i < NPARAM; i++) { inputstream >> paramNames[i]; } } else if (name == "NUM_EQUATIONS") { inputstream >> NEQ; - variableNames = new string[NEQ]; - initialConditionExpressions = new VCell::Expression*[NEQ]; - readEquations(inputstream); - } else if (name == "EVENTS") { - readEvents(inputstream); - } else { - string msg = "Unexpected token \"" + name + "\" in the input file!"; + variableNames = new std::string[NEQ]; + initialConditionExpressions = new VCell::Expression *[NEQ]; + readEquations(inputstream); + } else if (name == "EVENTS") { readEvents(inputstream); } else { + std::string msg = "Unexpected token \"" + name + "\" in the input file!"; throw VCell::Exception(msg); } } initialize(); - } catch (char* ex) { - throw VCell::Exception(string("VCellSundialsSolver::readInput() : ") + ex); - } catch (VCell::Exception& ex) { - throw VCell::Exception(string("VCellSundialsSolver::readInput() : ") + ex.getMessage()); - } catch (...) { - throw "VCellSundialsSolver::readInput() : caught unknown exception"; + } catch (char *ex) { throw VCell::Exception(std::string("VCellSundialsSolver::readInput() : ") + ex); } catch ( + VCell::Exception &ex) { + throw VCell::Exception(std::string("VCellSundialsSolver::readInput() : ") + ex.getMessage()); } } -void VCellSundialsSolver::readEvents(std::istream& inputstream) { - string token; +void VCellSundialsSolver::readEvents(std::istream &inputstream) { + std::string token; inputstream >> numEvents; - events = new Event*[numEvents]; - for (int i = 0; i < numEvents; i ++) { + events = new Event *[numEvents]; + for (int i = 0; i < numEvents; i++) { events[i] = new Event(); - while (true) { + while (true) { // Break on "EVENTASSIGNMENTS" inputstream >> token; - if (token == "EVENT") { - inputstream >> events[i]->name; - } else if (token == "TRIGGER") { - try { - events[i]->triggerExpression = readExpression(inputstream); - } catch (VCell::Exception& ex) { - throw VCell::Exception(string("trigger expression") + " " + ex.getMessage()); + if (token == "EVENT") { inputstream >> events[i]->name; } else if (token == "TRIGGER") { + try { events[i]->triggerExpression = readExpression(inputstream); } catch (VCell::Exception &ex) { + throw VCell::Exception(std::string("trigger expression") + " " + ex.getMessage()); } } else if (token == "DELAY") { inputstream >> token; events[i]->bUseValuesAtTriggerTime = token == "true"; - try { - events[i]->delayDurationExpression = readExpression(inputstream); - } catch (VCell::Exception& ex) { - throw VCell::Exception(string("delay duration expression") + " " + ex.getMessage()); + try { events[i]->delayDurationExpression = readExpression(inputstream); } catch (VCell::Exception &ex) { + throw VCell::Exception(std::string("delay duration expression") + " " + ex.getMessage()); } } else if (token == "EVENTASSIGNMENTS") { - inputstream >> events[i]->numEventAssignments; - events[i]->eventAssignments = new EventAssignment*[events[i]->numEventAssignments]; - for (int j = 0; j < events[i]->numEventAssignments; j ++) { - EventAssignment* ea = new EventAssignment(); + int numEventAssignments; + inputstream >> numEventAssignments; + for (int j = 0; j < numEventAssignments; j++) { + EventAssignment *ea = new EventAssignment(); inputstream >> ea->varIndex; - try { - ea->assignmentExpression = readExpression(inputstream); - } catch (VCell::Exception& ex) { - throw VCell::Exception(string("event assignment expression") + " " + ex.getMessage()); + try { ea->assignmentExpression = readExpression(inputstream); } catch (VCell::Exception &ex) { + throw VCell::Exception(std::string("event assignment expression") + " " + ex.getMessage()); } - events[i]->eventAssignments[j] = ea; + events[i]->eventAssignmentsVec->push_back(ea); } break; - } else { - string msg = "Unexpected token \"" + token + "\" in the input file!"; - throw VCell::Exception(msg); - } + } else { throw VCell::Exception("Unexpected token \"" + token + "\" in the input file!"); } } } } -void VCellSundialsSolver::readDiscontinuities(std::istream& inputstream) { +void VCellSundialsSolver::readDiscontinuities(std::istream &inputstream) { inputstream >> numDiscontinuities; - odeDiscontinuities = new OdeDiscontinuity*[numDiscontinuities]; - string line; - string exp; - for (int i = 0; i < numDiscontinuities; i ++) { - OdeDiscontinuity* od = new OdeDiscontinuity; + odeDiscontinuities = new OdeDiscontinuity *[numDiscontinuities]; + std::string line; + std::string exp; + for (int i = 0; i < numDiscontinuities; i++) { + OdeDiscontinuity *od = new OdeDiscontinuity; inputstream >> od->discontinuitySymbol; line = ""; getline(inputstream, line); - string::size_type pos = line.find(";"); - if (pos == string::npos) { - string msg = string("discontinuity expression ") + BAD_EXPRESSION_MSG; + std::string::size_type pos = line.find(";"); + if (pos == std::string::npos) { + std::string msg = std::string("discontinuity expression ") + BAD_EXPRESSION_MSG; throw VCell::Exception(msg); } exp = line.substr(0, pos + 1); @@ -331,7 +288,7 @@ void VCellSundialsSolver::readDiscontinuities(std::istream& inputstream) { exp = line.substr(pos + 1); trimString(exp); if (*(exp.end() - 1) != ';') { - string msg = string("discontinuity root expression ") + BAD_EXPRESSION_MSG; + std::string msg = std::string("discontinuity root expression ") + BAD_EXPRESSION_MSG; throw VCell::Exception(msg); } od->rootFindingExpression = new VCell::Expression(exp); @@ -343,71 +300,57 @@ void VCellSundialsSolver::readDiscontinuities(std::istream& inputstream) { void VCellSundialsSolver::initialize() { // add parameters to symbol table numAllSymbols = 1 + NEQ + NPARAM + numDiscontinuities; - allSymbols = new string[numAllSymbols]; // t, variables, parameters, discontinuities + allSymbols = new std::string[numAllSymbols]; // t, variables, parameters, discontinuities //t allSymbols[0] = "t"; odeResultSet->addColumn("t"); // variables - for (int i = 0 ; i < NEQ; i ++) { + for (int i = 0; i < NEQ; i++) { allSymbols[1 + i] = variableNames[i]; odeResultSet->addColumn(variableNames[i]); } // parameters - for (int i = 0 ; i < NPARAM; i ++) { - allSymbols[1 + NEQ + i] = paramNames[i]; - } + for (int i = 0; i < NPARAM; i++) { allSymbols[1 + NEQ + i] = paramNames[i]; } //discontinuities - for (int i = 0 ; i < numDiscontinuities; i ++) { + for (int i = 0; i < numDiscontinuities; i++) { allSymbols[1 + NEQ + NPARAM + i] = odeDiscontinuities[i]->discontinuitySymbol; } // default symbol table has variables, parameters and discontinuities. - defaultSymbolTable = new SimpleSymbolTable(allSymbols, numAllSymbols); + defaultSymbolTable = new SimpleSymbolTable(allSymbols, numAllSymbols); // initial condition can only be function of parameters. initialConditionSymbolTable = new SimpleSymbolTable(allSymbols + 1 + NEQ, NPARAM); - for (int i = 0; i < NEQ; i ++) { - initialConditionExpressions[i]->bindExpression(initialConditionSymbolTable); - } + for (int i = 0; i < NEQ; i++) { initialConditionExpressions[i]->bindExpression(initialConditionSymbolTable); } if (numDiscontinuities > 0) { - rootsFound = new int[2*numDiscontinuities]; + rootsFound = new int[2 * numDiscontinuities]; discontinuityValues = new double[numDiscontinuities]; // discontinuities can't be function of discontinuity symbols discontinuitySymbolTable = new SimpleSymbolTable(allSymbols, 1 + NEQ + NPARAM); - for (int i = 0; i < numDiscontinuities; i ++) { + for (int i = 0; i < numDiscontinuities; i++) { odeDiscontinuities[i]->discontinuityExpression->bindExpression(discontinuitySymbolTable); odeDiscontinuities[i]->rootFindingExpression->bindExpression(discontinuitySymbolTable); } } - if (numEvents > 0) { - for (int i = 0; i < numEvents; i ++) { - events[i]->bind(defaultSymbolTable); - } - } - try { - values = new realtype[1 + NEQ + NPARAM + numDiscontinuities]; - tempRowData = new realtype[1 + NEQ]; - } catch (...) { - throw "Out of Memory"; - } + if (numEvents > 0) { for (int i = 0; i < numEvents; i++) { events[i]->bind(defaultSymbolTable); } } + values = new realtype[1 + NEQ + NPARAM + numDiscontinuities]; + tempRowData = new realtype[1 + NEQ]; y = N_VNew_Serial(NEQ); - if (y == 0) { - throw "Out of Memory"; - } + if (y == nullptr) { throw "Out of Memory"; } } bool VCellSundialsSolver::updateDiscontinuities(realtype t, bool bOnRootReturn) { - if (numDiscontinuities == 0) { - return false; - } + if (numDiscontinuities == 0) { return false; } bool bUpdated = false; updateTandVariableValues(t, y); - for (int i = 0; i < numDiscontinuities; i ++) { - std::cout << odeDiscontinuities[i]->discontinuitySymbol << " " << odeDiscontinuities[i]->discontinuityExpression->infix() << " " << discontinuityValues[i]; + for (int i = 0; i < numDiscontinuities; i++) { + std::cout << odeDiscontinuities[i]->discontinuitySymbol << " " << odeDiscontinuities[i]->discontinuityExpression + ->infix() << " " << + discontinuityValues[i]; if (bOnRootReturn) { if (rootsFound[2 * i] && rootsFound[2 * i + 1]) { std::cout << " inverted "; @@ -417,66 +360,58 @@ bool VCellSundialsSolver::updateDiscontinuities(realtype t, bool bOnRootReturn) std::cout << " evaluated "; double oldValue = discontinuityValues[i]; discontinuityValues[i] = odeDiscontinuities[i]->discontinuityExpression->evaluateVector(values); - if (oldValue != discontinuityValues[i]) { - bUpdated = true; - } - } else { - std::cout << " nonroot "; - } + if (oldValue != discontinuityValues[i]) { bUpdated = true; } + } else { std::cout << " nonroot "; } } else { std::cout << " evaluated after event execution "; double oldValue = discontinuityValues[i]; discontinuityValues[i] = odeDiscontinuities[i]->discontinuityExpression->evaluateVector(values); - if (oldValue != discontinuityValues[i]) { - bUpdated = true; - } + if (oldValue != discontinuityValues[i]) { bUpdated = true; } } std::cout << discontinuityValues[i] << std::endl; } std::cout << std::endl; // copy discontinuity values to values to evaluate RHS - if (bUpdated) { - memcpy(values + 1 + NEQ + NPARAM, discontinuityValues, numDiscontinuities * sizeof(double)); - } + if (bUpdated) { memcpy(values + 1 + NEQ + NPARAM, discontinuityValues, numDiscontinuities * sizeof(double)); } return bUpdated; } void VCellSundialsSolver::initDiscontinuities() { - if (numDiscontinuities == 0) { - return; - } + if (numDiscontinuities == 0) { return; } updateTandVariableValues(STARTING_TIME, y); - // init discontinuities - for (int i = 0; i < numDiscontinuities; i ++) { + // init discontinuities + for (int i = 0; i < numDiscontinuities; i++) { discontinuityValues[i] = odeDiscontinuities[i]->discontinuityExpression->evaluateVector(values); } // copy discontinuity values to values to evaluate RHS memcpy(values + 1 + NEQ + NPARAM, discontinuityValues, numDiscontinuities * sizeof(double)); } -void VCellSundialsSolver::checkDiscontinuityConsistency() { - if (numDiscontinuities == 0) { - return; - } +void VCellSundialsSolver::checkDiscontinuityConsistency() const { + if (numDiscontinuities == 0) { return; } - for (int i = 0; i < numDiscontinuities; i ++) { + for (int i = 0; i < numDiscontinuities; i++) { double realValue = odeDiscontinuities[i]->discontinuityExpression->evaluateVector(values); if (discontinuityValues[i] != realValue) { - stringstream ss; - ss << "at time " << values[0] << ", discontinuity " << odeDiscontinuities[i]->discontinuityExpression->infix() << " evaluated to " << (realValue ? "TRUE" : "FALSE") << ", solver assumed " << (discontinuityValues[i] ? "TRUE" : "FALSE") << std::endl; + std::stringstream ss; + ss << "at time " << values[0] << ", discontinuity " << odeDiscontinuities[i]->discontinuityExpression-> + infix() << " evaluated to " << + (realValue ? "TRUE" : "FALSE") << ", solver assumed " << (discontinuityValues[i] ? "TRUE" : "FALSE") + << std::endl; throw ss.str(); } } } void VCellSundialsSolver::solveInitialDiscontinuities(double t) { - std::cout << "------------------solveInitialDiscontinuities--at-time-" << t << "--------------------------" << std::endl; + std::cout << "------------------solveInitialDiscontinuities--at-time-" << t << "--------------------------" << + std::endl; bool bFoundRoot = false; - string roots_at_initial_str = ""; + std::string roots_at_initial_str; updateTandVariableValues(t, y); - for (int i = 0; i < numDiscontinuities; i ++) { + for (int i = 0; i < numDiscontinuities; i++) { double v = odeDiscontinuities[i]->rootFindingExpression->evaluateVector(values); if (fabs(v) < AbsoluteTolerance) { roots_at_initial_str += odeDiscontinuities[i]->discontinuityExpression->infix() + "; "; @@ -485,23 +420,23 @@ void VCellSundialsSolver::solveInitialDiscontinuities(double t) { } if (bFoundRoot) { - std::cout << "solveInitialDiscontinuities() : roots found at time " << t << "; " << roots_at_initial_str << std::endl; + std::cout << "solveInitialDiscontinuities() : roots found at time " << t << "; " << roots_at_initial_str << + std::endl; int count = 0; - int maxCount = (int)pow(2.0, numDiscontinuities); + int maxCount = (int) pow(2.0, numDiscontinuities); while (count < maxCount) { - try { - if (!fixInitialDiscontinuities(t)) { - break; - } - } catch (const char* err) { - stringstream str; - str << "found discontinuities at time " << t << " but unable to initialize : " << err << "\nDiscontinuities at time 0 are : " << roots_at_initial_str; + try { if (!fixInitialDiscontinuities(t)) { break; } } catch (const char *err) { + std::stringstream str; + str << "found discontinuities at time " << t << " but unable to initialize : " << err << + "\nDiscontinuities at time 0 are : " << roots_at_initial_str; throw str.str(); } - count ++; + count++; } if (count >= maxCount) { - string str = "found discontinuities at time 0 but unable to initialize due to max iterations.\nDiscontinuities at time 0 are : " + roots_at_initial_str; + std::string str = + "found discontinuities at time 0 but unable to initialize due to max iterations.\nDiscontinuities at time 0 are : " + + roots_at_initial_str; throw str; } } @@ -512,16 +447,16 @@ void VCellSundialsSolver::printVariableValues(realtype t) { updateTandVariableValues(t, y); std::cout << "variable values are" << std::endl; std::cout << "t " << values[0] << std::endl; - for (int i = 0; i < NEQ; i ++) { - std::cout << variableNames[i] << " " << values[i + 1] << std::endl; - } + for (int i = 0; i < NEQ; i++) { std::cout << variableNames[i] << " " << values[i + 1] << std::endl; } std::cout << std::endl; } void VCellSundialsSolver::printDiscontinuityValues() { std::cout << std::endl << "discontinuities values are" << std::endl; - for (int i = 0; i < numDiscontinuities; i ++) { - std::cout << odeDiscontinuities[i]->discontinuitySymbol << " " << odeDiscontinuities[i]->discontinuityExpression->infix() << " " << discontinuityValues[i] << std::endl; + for (int i = 0; i < numDiscontinuities; i++) { + std::cout << odeDiscontinuities[i]->discontinuitySymbol << " " << odeDiscontinuities[i]->discontinuityExpression + ->infix() << " " << + discontinuityValues[i] << std::endl; } std::cout << std::endl; } @@ -531,7 +466,7 @@ int VCellSundialsSolver::RootFn(realtype t, N_Vector y, realtype *gout) { //cout << "RootFn " << endl; //printVariableValues(); - for (int i = 0; i < numDiscontinuities; i ++) { + for (int i = 0; i < numDiscontinuities; i++) { double r = odeDiscontinuities[i]->rootFindingExpression->evaluateVector(values); if (r == 0) { gout[2 * i] = 1e-200; @@ -539,95 +474,85 @@ int VCellSundialsSolver::RootFn(realtype t, N_Vector y, realtype *gout) { } else { gout[2 * i] = r; gout[2 * i + 1] = r; - } + } //cout << "gout[" << i << "]=" << gout[i] << endl; - } + } return 0; } void VCellSundialsSolver::testEventTriggers(realtype Time) { - if (numEvents == 0) { - return; - } + if (numEvents == 0) { return; } updateTandVariableValues(Time, y); - for (int i = 0; i < numEvents; i ++) { + + std::vector eventExecutions; + for (int i = 0; i < numEvents; i++) { bool oldTriggerValue = events[i]->triggerValue; bool newTriggerValue = events[i]->triggerExpression->evaluateVector(values) != 0.0; events[i]->triggerValue = newTriggerValue; if (!oldTriggerValue && newTriggerValue) { // triggered - EventExecution* ee = new EventExecution(events[i]); - ee->exeTime = Time; + eventExecutions.emplace_back(new EventExecution(events[i])); + eventExecutions.back()->exeTime = Time; if (events[i]->hasDelay()) { - ee->exeTime = Time + events[i]->delayDurationExpression->evaluateVector(values); + eventExecutions.back()->exeTime = Time + events[i]->delayDurationExpression->evaluateVector(values); } if (events[i]->bUseValuesAtTriggerTime) { - ee->targetValues = new double[events[i]->numEventAssignments]; - for (int j = 0; j < events[i]->numEventAssignments; j ++) { - ee->targetValues[j] = events[i]->eventAssignments[j]->assignmentExpression->evaluateVector(values); - } - } - bool bInserted = false; - for (std::list::iterator iter = eventExeList.begin(); iter != eventExeList.end(); iter ++) { - if ((*iter)->exeTime > ee->exeTime) { - eventExeList.insert(iter, ee); // sort them by execution time - bInserted = true; - break; + const unsigned long numEventAssignments = events[i]->eventAssignmentsVec->size(); + eventExecutions.back()->targetValues = new double[numEventAssignments]; + for (unsigned long j = 0; j < numEventAssignments; j++) { + eventExecutions.back()->targetValues[j] = events[i]->eventAssignmentsVec->at(j)->assignmentExpression->evaluateVector(values); } } - if (!bInserted) { - eventExeList.push_back(ee); - } } } + auto compLambda = [](const EventExecution *e1, const EventExecution *e2) { + return e1->exeTime < e2->exeTime; + }; + std::ranges::sort(eventExecutions, compLambda); + std::list eventExecutionsSorted(std::make_move_iterator(eventExecutions.begin()), std::make_move_iterator(eventExecutions.end())); + eventExeList.splice(eventExeList.end(), eventExecutionsSorted); } -bool VCellSundialsSolver::executeEvents(realtype Time) { - if (numEvents == 0) { - return false; - } - testEventTriggers(Time); +bool VCellSundialsSolver::executeEvents(const realtype realTimeVar) { + if (numEvents == 0) { return false; } + testEventTriggers(realTimeVar); static double epsilon = 1e-15; bool bExecuted = false; - while (eventExeList.size() > 0) { - std::list::iterator iter = eventExeList.begin(); - EventExecution* ee = *iter; - - if (ee->exeTime > Time + epsilon) { // not time yet - return bExecuted; + while (!eventExeList.empty()) { + auto iter = eventExeList.begin(); + EventExecution *ee = *iter; + + if (ee->exeTime > realTimeVar + epsilon) return bExecuted; // not time yet + if (ee->exeTime < realTimeVar) { + std::stringstream ss; + ss << "missed Event '" << ee->event0->name << "' with trigger " << ee->event0->triggerExpression->infix() + << ", scheduled time = " << ee->exeTime << ", current time = " << realTimeVar << std::endl; + throw ss.str(); } - if (fabs(ee->exeTime - Time) < epsilon) { // execute - updateTandVariableValues(Time, y); - double* y_data = NV_DATA_S(y); // assign the values - for (int i = 0; i < ee->event0->numEventAssignments; i ++) { - EventAssignment* ea = ee->event0->eventAssignments[i]; - if (ee->event0->bUseValuesAtTriggerTime) { - y_data[ea->varIndex] = ee->targetValues[i]; - } else { + if (fabs(ee->exeTime - realTimeVar) < epsilon) { // execute + updateTandVariableValues(realTimeVar, y); + double *y_data = NV_DATA_S(y); // assign the values + for (int i = 0; i < ee->event0->eventAssignmentsVec->size(); i++) { + EventAssignment *ea = ee->event0->eventAssignmentsVec->at(i); + if (ee->event0->bUseValuesAtTriggerTime) { y_data[ea->varIndex] = ee->targetValues[i]; } else { y_data[ea->varIndex] = ea->assignmentExpression->evaluateVector(values); } } - std::cout << std::endl << "Executed event " << ee->event0->name << " at time " << Time << std::endl; + std::cout << std::endl << "Executed event " << ee->event0->name << " at time " << realTimeVar << std::endl; eventExeList.pop_front(); // delete from the list delete ee; bExecuted = true; - testEventTriggers(Time); // retest all triggers again. - } else if (ee->exeTime < Time) { - stringstream ss; - ss << "missed Event '" << ee->event0->name << "' with trigger " << ee->event0->triggerExpression->infix() - << ", scheduled time = " << ee->exeTime << ", current time = " << Time << std::endl; - throw ss.str(); + testEventTriggers(realTimeVar); // retest all triggers again. } - } return bExecuted; } double VCellSundialsSolver::getNextEventTime() { - if (eventExeList.size() > 0) { - std::list::iterator iter = eventExeList.begin(); - EventExecution* ee = *iter; + if (!eventExeList.empty()) { + auto iter = eventExeList.begin(); + EventExecution *ee = *iter; return ee->exeTime; } @@ -635,9 +560,7 @@ double VCellSundialsSolver::getNextEventTime() { } void VCellSundialsSolver::checkStopRequested(double time, long numIterations) { -#ifdef USE_MESSAGING - if (SimulationMessaging::getInstVar()->isStopRequested()) { - throw StoppedByUserException("stopped by user"); - } -#endif + #ifdef USE_MESSAGING + if (SimulationMessaging::getInstVar()->isStopRequested()) { throw StoppedByUserException("stopped by user"); } + #endif } diff --git a/IDAWin/VCellSundialsSolver.h b/IDAWin/VCellSundialsSolver.h index a0dab9b1c..b3c26f638 100644 --- a/IDAWin/VCellSundialsSolver.h +++ b/IDAWin/VCellSundialsSolver.h @@ -5,8 +5,6 @@ #include #include #include -#include -#include #include @@ -15,8 +13,6 @@ #include #include -#include - class SymbolTable; class OdeResultSet; @@ -32,47 +28,45 @@ struct EventAssignment { ~EventAssignment() { delete assignmentExpression; } - void bind(SymbolTable* symbolTable) { + void bind(SymbolTable* symbolTable) const { assignmentExpression->bindExpression(symbolTable); } }; struct Event { - string name; + std::string name; VCell::Expression* triggerExpression; bool bUseValuesAtTriggerTime; VCell::Expression* delayDurationExpression; - int numEventAssignments; - EventAssignment** eventAssignments; + std::vector* eventAssignmentsVec; bool triggerValue; Event() { - bUseValuesAtTriggerTime = false; - triggerExpression = 0; - triggerValue = false; - delayDurationExpression = 0; - eventAssignments = 0; + this->bUseValuesAtTriggerTime = false; + this->triggerExpression = nullptr; + this->triggerValue = false; + this->delayDurationExpression = nullptr; + this->eventAssignmentsVec = new std::vector; } ~Event() { - delete triggerExpression; - delete delayDurationExpression; - for (int i = 0; i < numEventAssignments; i ++) { - delete eventAssignments[i]; - } - delete[] eventAssignments; + delete this->triggerExpression; + delete this->delayDurationExpression; + for (const EventAssignment* elem : *this->eventAssignmentsVec) delete elem; + this->eventAssignmentsVec->clear(); + delete this->eventAssignmentsVec; } - bool hasDelay() { - return delayDurationExpression != 0; + [[nodiscard]] bool hasDelay() const { + return this->delayDurationExpression != nullptr; } - void bind(SymbolTable* symbolTable) { - triggerExpression->bindExpression(symbolTable); - if (delayDurationExpression != 0) { - delayDurationExpression->bindExpression(symbolTable); + void bind(SymbolTable* symbolTable) const { + this->triggerExpression->bindExpression(symbolTable); + if (this->delayDurationExpression != nullptr) { + this->delayDurationExpression->bindExpression(symbolTable); } - for (int i = 0; i < numEventAssignments; i ++) { - eventAssignments[i]->bind(symbolTable); + for (const EventAssignment* elem : *this->eventAssignmentsVec) { + elem->bind(symbolTable); } } }; @@ -83,22 +77,23 @@ struct EventExecution { double* targetValues; EventExecution(Event* e) { - event0 = e; - targetValues = 0; + this->exeTime = RCONST(0.0); + this->event0 = e; + this->targetValues = nullptr; } ~EventExecution() { - delete targetValues; + delete this->targetValues; } }; struct OdeDiscontinuity { - string discontinuitySymbol; + std::string discontinuitySymbol; VCell::Expression* discontinuityExpression; VCell::Expression* rootFindingExpression; ~OdeDiscontinuity() { - delete discontinuityExpression; - delete rootFindingExpression; + delete this->discontinuityExpression; + delete this->rootFindingExpression; } }; @@ -108,14 +103,14 @@ class VCellSundialsSolver { virtual ~VCellSundialsSolver(); void readInput(std::istream& inputstream); - virtual void solve(double* paramValues=0, bool bPrintProgress=false, FILE* outputFile=0, void (*checkStopRequested)(double, long)=0) = 0; - OdeResultSet* getResultSet() { return odeResultSet; } - int getNumEquations() { return NEQ; } - VCell::Expression** getInitialConditionExpressions() { return initialConditionExpressions; } - void setStartingTime(realtype newStartingTime) { STARTING_TIME = newStartingTime; } - void setEndingTime(realtype newEndingTime) { ENDING_TIME = newEndingTime; } - void setOutputTimes(int count, double* newOutputTimes); - SymbolTable* getSymbolTable() { return defaultSymbolTable;} + virtual void solve(double* paramValues=nullptr, bool bPrintProgress=false, FILE* outputFile=nullptr, void (*checkStopRequested)(double, long)=nullptr) = 0; + OdeResultSet* getResultSet() { return this->odeResultSet; } + [[nodiscard]] int getNumEquations() const { return this->NEQ; } + VCell::Expression** getInitialConditionExpressions() { return this->initialConditionExpressions; } + void setStartingTime(realtype newStartingTime) { this->STARTING_TIME = newStartingTime; } + void setEndingTime(realtype newEndingTime) { this->ENDING_TIME = newEndingTime; } + //void setOutputTimes(int count, double* newOutputTimes); + SymbolTable* getSymbolTable() { return this->defaultSymbolTable;} static void checkStopRequested(double, long); @@ -131,7 +126,7 @@ class VCellSundialsSolver { OdeResultSet* odeResultSet; // mainly for parameter optimization use but it also stores column names void* solver; // the memory for solver - string recoverableErrMsg; + std::string recoverableErrMsg; int NEQ; int NPARAM; @@ -141,7 +136,7 @@ class VCellSundialsSolver { realtype AbsoluteTolerance; long keepEvery; double maxTimeStep; - vector outputTimes; + std::vector outputTimes; double* tempRowData; // data for current time to be written to output file and to be added to odeResultSet int numDiscontinuities; @@ -150,9 +145,9 @@ class VCellSundialsSolver { double* discontinuityValues; int* rootsFound; - string* paramNames; - string* variableNames; // variables - string* allSymbols; + std::string* paramNames; + std::string* variableNames; // variables + std::string* allSymbols; int numAllSymbols; SymbolTable* defaultSymbolTable; @@ -161,7 +156,7 @@ class VCellSundialsSolver { virtual void updateTempRowData(double currTime); void writeFileData(FILE* outputFile); void writeFileHeader(FILE* outputFile); - void printProgress(double currTime, double& lastPercentile, clock_t& lastTime, double increment, FILE* outputFile); + void printProgress(double currTime, double& lastPercentile, clock_t& lastTime, double increment, FILE* outputFile) const; void readDiscontinuities(std::istream& inputstream); virtual void readEquations(std::istream& inputstream) = 0; @@ -169,7 +164,7 @@ class VCellSundialsSolver { void initDiscontinuities(); bool updateDiscontinuities(realtype t, bool bOnRootReturn); - void checkDiscontinuityConsistency(); + void checkDiscontinuityConsistency() const; void solveInitialDiscontinuities(double t); virtual bool fixInitialDiscontinuities(double t)=0; @@ -179,10 +174,10 @@ class VCellSundialsSolver { virtual void updateTandVariableValues(realtype t, N_Vector y)=0; int RootFn(realtype t, N_Vector y, realtype *gout); - virtual string getSolverName()=0; + virtual std::string getSolverName()=0; - VCell::Expression* readExpression(std::istream& inputstream); - bool executeEvents(realtype Time); + static VCell::Expression* readExpression(std::istream& inputstream); + bool executeEvents(realtype realTimeVar); double getNextEventTime(); private: diff --git a/VCellMessaging/src/SimulationMessaging.cpp b/VCellMessaging/src/SimulationMessaging.cpp index cb2ee8c0c..e127d2b85 100644 --- a/VCellMessaging/src/SimulationMessaging.cpp +++ b/VCellMessaging/src/SimulationMessaging.cpp @@ -3,18 +3,12 @@ #include #include #include -using std::cerr; -using std::cout; -using std::endl; #include #ifdef USE_MESSAGING #if ( !defined(_WIN32) && !defined(_WIN64) ) // UNIX #include -#include -#include -#include -#include +#include #endif static const char* TIMETOLIVE_PROPERTY = "JMSTimeToLive"; @@ -283,19 +277,19 @@ void SimulationMessaging::sendStatus() { const char* messaging_http_url = s_url.c_str(); curl_easy_setopt(curl, CURLOPT_URL, messaging_http_url); - cout << "curl -XPOST " << messaging_http_url << endl; + std::cout << "curl -XPOST " << messaging_http_url << std::endl; // // print message to stdout // - cout << "!!!SimulationMessaging::sendStatus [" << (long)m_simKey << ":" << getStatusString(workerEvent->status); + std::cout << "!!!SimulationMessaging::sendStatus [" << (long)m_simKey << ":" << getStatusString(workerEvent->status); if (revisedMsg != NULL) { - cout << ":" << revisedMsg; + std::cout << ":" << revisedMsg; } else { - cout << ":" << workerEvent->progress << ":" << workerEvent->timepoint; + std::cout << ":" << workerEvent->progress << ":" << workerEvent->timepoint; } - cout << "]" << endl; + std::cout << "]" << std::endl; @@ -324,7 +318,7 @@ void SimulationMessaging::sendStatus() { #if ( defined(_WIN32) || defined(_WIN64) ) SetEvent(hMessagingThreadEndEvent); #else // UNIX - cout << "!!!thread exiting" << endl; + std::cout << "!!!thread exiting" << std::endl; pthread_exit(NULL); #endif } @@ -422,7 +416,7 @@ bool SimulationMessaging::lockMessaging() return true; #else // UNIX if (pthread_mutex_lock(&mutex_messaging)) { - cout << "Cannot acquire mutex, fatal error." << endl; + std::cout << "Cannot acquire mutex, fatal error." << std::endl; exit(1); } #endif @@ -591,7 +585,7 @@ void SimulationMessaging::waitUntilFinished() { break; } #else - cout << "!!!waiting for thread to exit" << endl; + std::cout << "!!!waiting for thread to exit" << std::endl; pthread_join(newWorkerEventThread, NULL); #endif } @@ -746,7 +740,7 @@ void* startMessagingThread(void* lpParam){ break; default: - std::cout<< "Wait error: " << waitReturn << endl; + std::cout<< "Wait error: " << waitReturn << std::endl; break; } } From b1be7be7f83ed8c5d97da368533ced509ba5c236 Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Wed, 25 Jun 2025 14:46:52 -0400 Subject: [PATCH 03/26] Converted to Argparse entry --- IDAWin/SundialsSolverStandalone.cpp | 376 +++++++++++----------------- 1 file changed, 147 insertions(+), 229 deletions(-) diff --git a/IDAWin/SundialsSolverStandalone.cpp b/IDAWin/SundialsSolverStandalone.cpp index e3e2ec661..0c7a95747 100644 --- a/IDAWin/SundialsSolverStandalone.cpp +++ b/IDAWin/SundialsSolverStandalone.cpp @@ -1,273 +1,191 @@ -// Debug -#ifdef _DEBUG - //#define _CRTDBG_MAP_ALLOC - #ifdef _CRTDBG_MAP_ALLOC - #include - #include - #else - //#include - #endif -#endif // Messaging #ifdef USE_MESSAGING #include +#include #endif // Standard Includes #include #include #include -#include -#include // Local Includes #include "VCellCVodeSolver.h" #include "VCellIDASolver.h" -#include "OdeResultSet.h" #include "StoppedByUserException.h" #include - - - +#include #define CVODE_SOLVER "CVODE" #define IDA_SOLVER "IDA" -void printUsage() { - std::string usageMessage{"Usage: SundialsSolverStandalone input output"}; - #ifdef USE_MESSAGING - usageMessage += " [-tid 0]"; - #endif - std::cout << usageMessage << std::endl; -} - -void loadJMSInfo(std::istream& ifsInput, int taskID) { - char *broker = new char[256]; - char *smqusername = new char[256]; - char *password = new char[256]; - char *qname = new char[256]; - char *tname = new char[256]; - char *vcusername = new char[256]; - string nextToken; - int simKey, jobIndex; - - while (!ifsInput.eof()) { - nextToken = ""; - ifsInput >> nextToken; - if (nextToken.size() == 0) { - continue; - } else if (nextToken[0] == '#') { - getline(ifsInput, nextToken); - continue; - } else if (nextToken == "JMS_PARAM_END") { - break; - } else if (nextToken == "JMS_BROKER") { - std::string brokerStr; - ifsInput >> brokerStr; - memset(broker, 0, 256 * sizeof(char)); - strncpy(broker, brokerStr.c_str(), 256); - } else if (nextToken == "JMS_USER") { - std::string usernameStr, passwordStr; - ifsInput >> usernameStr >> passwordStr; - memset(smqusername, 0, 256 * sizeof(char)); - memset(password, 0, 256 * sizeof(char)); - strncpy(smqusername, usernameStr.c_str(), 256); - strncpy(password, passwordStr.c_str(), 256); - } else if (nextToken == "JMS_QUEUE") { - std::string qnameStr; - ifsInput >> qnameStr; - memset(qname, 0, 256 * sizeof(char)); - strncpy(qname, qnameStr.c_str(), 256); - } else if (nextToken == "JMS_TOPIC") { - std::string topicStr; - ifsInput >> topicStr; - memset(tname, 0, 256 * sizeof(char)); - strncpy(tname, topicStr.c_str(), 256); - } else if (nextToken == "VCELL_USER") { - std::string vcusernameStr; - ifsInput >> vcusernameStr; - memset(vcusername, 0, 256 * sizeof(char)); - strncpy(vcusername, vcusernameStr.c_str(), 256); - } else if (nextToken == "SIMULATION_KEY") { - ifsInput >> simKey; - continue; - } else if (nextToken == "JOB_INDEX") { - ifsInput >> jobIndex; - continue; - } - } - -#ifdef USE_MESSAGING - if (taskID >= 0) { - SimulationMessaging::create(broker, smqusername, password, qname, tname, vcusername, simKey, jobIndex, taskID); - } else { - SimulationMessaging::create(); - } -#endif -} - -void errExit(int returnCode, std::string& errorMsg) { -#ifdef USE_MESSAGING - if (returnCode != 0) { - if (SimulationMessaging::getInstVar() != 0 && !SimulationMessaging::getInstVar()->isStopRequested()) { - SimulationMessaging::getInstVar()->setWorkerEvent(new WorkerEvent(JOB_FAILURE, errorMsg.c_str())); - } - } - if (SimulationMessaging::getInstVar() != 0) { - SimulationMessaging::getInstVar()->waitUntilFinished(); - delete SimulationMessaging::getInstVar(); - } else { - if (returnCode != 0) { - std::cerr << errorMsg << std::endl; - } - } -#else - if (returnCode != 0) { - std::cerr << errorMsg << std::endl; - } -#endif -} +int parseAndRunWithArgParse(int argc, char *argv[]); +void activateSolver(std::ifstream& inputFileStream, FILE* outputFile, int taskID); +void loadJMSInfo(std::istream &ifsInput, int taskID); +void errExit(int returnCode, std::string &errorMsg); int main(int argc, char *argv[]) { - std::cout - << "Sundials Standalone version " << g_GIT_DESCRIBE - << std::endl; std::cout << std::setprecision(20); + return parseAndRunWithArgParse(argc, argv); +} +int parseAndRunWithArgParse(int argc, char *argv[]) { int taskID = -1; - string inputfname; - string outputfname; - string solver; - string errMsg; + std::string inputFilePath; + std::string outputFilePath; + std::string errMsg; int returnCode = 0; - if (argc < 3) { - std::cout << "Missing arguments!" << std::endl; - printUsage(); - exit(1); + argparse::ArgumentParser argumentParser("program_name", g_GIT_DESCRIBE); + argumentParser.add_argument("input").help("path to directory with input files.").store_into(inputFilePath); + argumentParser.add_argument("output").help("path to directory for output files.").store_into(outputFilePath); + #ifdef USE_MESSAGING + argumentParser.add_argument("-tid").help("path to solver to run.").store_into(taskID); + #endif + + try { + argumentParser.parse_args(argc, argv); } - for (int i = 1; i < argc; i ++) { - if (!strcmp(argv[i], "-tid")) { -#ifdef USE_MESSAGING - i ++; - if (i >= argc) { - std::cout << "Missing taskID!" << std::endl; - printUsage(); - exit(1); - } - for (int j = 0; j < (int)strlen(argv[i]); j ++) { - if (argv[i][j] < '0' || argv[i][j] > '9') { - std::cout << "Wrong argument : " << argv[i] << ", taskID must be an integer!" << std::endl; - printUsage(); - exit(1); - } - } - taskID = atoi(argv[i]); -#else - std::cout << "Wrong argument : " << argv[i] << std::endl; - printUsage(); - exit(1); -#endif - } else { - inputfname = argv[i]; - i ++; - outputfname = argv[i]; - } + catch (const std::exception& err) { + std::cerr << err.what() << std::endl; + std::cerr << argumentParser; + return -1; } - FILE* outputFile = NULL; - std::ifstream inputstream(inputfname.c_str()); - try { - if (!inputstream.is_open()) { - throw std::string("input file [") + inputfname + "] doesn't exit!"; - } + FILE *outputFile = NULL; + std::ifstream inputFileStream{inputFilePath}; + try { + if (!inputFileStream.is_open()) { throw std::runtime_error("input file [" + inputFilePath + "] doesn't exit!"); } - // Open the output file... + // Open the output file... if ((outputFile = fopen(argv[2], "w")) == NULL) { - throw std::string("Could not open output file[") + outputfname + "] for writing."; - } - - string nextToken; - - while (!inputstream.eof()) { - nextToken = ""; - inputstream >> nextToken; - if (nextToken.empty()) { - continue; - } else if (nextToken[0] == '#') { - getline(inputstream, nextToken); - continue; - } else if (nextToken == "JMS_PARAM_BEGIN") { - loadJMSInfo(inputstream, taskID); -#ifdef USE_MESSAGING - SimulationMessaging::getInstVar()->start(); // start the thread -#endif - } else if (nextToken == "SOLVER") { - inputstream >> solver; - break; - } - } -#ifdef USE_MESSAGING - // should only happen during testing for solver compiled with messaging but run locally. - if (SimulationMessaging::getInstVar() == nullptr) { - SimulationMessaging::create(); + throw std::runtime_error("Could not open output file[" + outputFilePath + "] for writing."); } -#endif - - if (solver.empty()) { - throw "Solver not defined "; - } - -#ifdef _CRTDBG_MAP_ALLOC - _CrtMemState s1, s2, s3; -#endif - errMsg += solver + " solver failed : "; -#ifdef _CRTDBG_MAP_ALLOC - _CrtMemCheckpoint( &s1 ); -#endif - VCellSundialsSolver* vss = 0; - if (solver == IDA_SOLVER) { - vss = new VCellIDASolver(); - } else if (solver == CVODE_SOLVER) { - vss = new VCellCVodeSolver(); - } else { - std::stringstream ss; - ss << "Solver " << solver << " not defined!"; - throw ss.str(); - } - vss->readInput(inputstream); - vss->solve(0, true, outputFile, VCellSundialsSolver::checkStopRequested); + activateSolver(inputFileStream, outputFile, taskID); - delete vss; -#ifdef _CRTDBG_MAP_ALLOC - _CrtMemCheckpoint( &s2 ); - if ( _CrtMemDifference( &s3, &s1, &s2) ) - _CrtMemDumpStatistics( &s3 ); - _CrtDumpMemoryLeaks(); -#endif - } catch (const char* ex) { + } catch (const char *ex) { errMsg += ex; returnCode = -1; - } catch (string& ex) { + } catch (std::string &ex) { errMsg += ex; returnCode = -1; - } catch (StoppedByUserException) { - returnCode = 0; // stopped by user; - } catch (VCell::Exception& ex) { + } catch (StoppedByUserException&) { + returnCode = 0; // stopped by user; + } catch (VCell::Exception &ex) { errMsg += ex.getMessage(); returnCode = -1; + } catch (const std::exception& err) { + errMsg += err.what(); + returnCode = -1; } catch (...) { errMsg += "unknown error"; returnCode = -1; } - - if (outputFile != NULL) { - fclose(outputFile); - } - if (inputstream.is_open()) { - inputstream.close(); - } + if (outputFile != NULL) { fclose(outputFile); } + if (inputFileStream.is_open()) { inputFileStream.close(); } errExit(returnCode, errMsg); -#ifdef _CRTDBG_MAP_ALLOC - _CrtDumpMemoryLeaks(); -#endif return returnCode; } + +void activateSolver(std::ifstream& inputFileStream, FILE* outputFile, int taskID) { + std::string solver; + + while (!inputFileStream.eof()) { // Note break statement if "SOLVER" encountered + std::string nextToken; + inputFileStream >> nextToken; + if (nextToken.empty()) continue; + if (nextToken[0] == '#') getline(inputFileStream, nextToken); + else if (nextToken == "JMS_PARAM_BEGIN") { + loadJMSInfo(inputFileStream, taskID); + #ifdef USE_MESSAGING + SimulationMessaging::getInstVar()->start(); // start the thread + #endif + } else if (nextToken == "SOLVER") { + inputFileStream >> solver; + break; + } + } + #ifdef USE_MESSAGING + // should only happen during testing for solver compiled with messaging but run locally. + if (SimulationMessaging::getInstVar() == nullptr) { SimulationMessaging::create(); } + #endif + + if (solver.empty()) { throw "Solver not defined "; } + VCellSundialsSolver *vss = nullptr; + if (solver == IDA_SOLVER) { + vss = new VCellIDASolver(); + } else if (solver == CVODE_SOLVER) { + vss = new VCellCVodeSolver(); + } else { + std::stringstream ss; + ss << "Solver " << solver << " not defined!"; + throw ss.str(); + } + vss->readInput(inputFileStream); + vss->solve(nullptr, true, outputFile, VCellSundialsSolver::checkStopRequested); + + delete vss; +} + +void loadJMSInfo(std::istream &ifsInput, int taskID) { + #ifndef USE_MESSAGING + return; // Only useful for messaging; let's not waste time! + #else + + if (taskID < 0) { + SimulationMessaging::create(); + return; // No need to do any parsing + } + std::string broker; + std::string smqUserName; + std::string password; + std::string qName; + std::string topicName; + std::string vCellUsername; + int simKey, jobIndex; + + while (!ifsInput.eof()) { + std::string nextToken; + ifsInput >> nextToken; + if (nextToken.empty()) continue; + if (nextToken[0] == '#') { + // getline(ifsInput, nextToken); // Is this ignoring because of a comment? + ifsInput.ignore('\n'); + continue; + } + if (nextToken == "JMS_PARAM_END") { ifsInput.ignore(EOF); } else if ( + nextToken == "JMS_BROKER") { ifsInput >> broker; } else if ( + nextToken == "JMS_USER") { ifsInput >> smqUserName >> password; } else if ( + nextToken == "JMS_QUEUE") { ifsInput >> qName; } else if ( + nextToken == "JMS_TOPIC") { ifsInput >> topicName; } else if (nextToken == "VCELL_USER") { + ifsInput >> vCellUsername; + } else if (nextToken == "SIMULATION_KEY") { + ifsInput >> simKey; + continue; + } else if (nextToken == "JOB_INDEX") { + ifsInput >> jobIndex; + continue; + } + } + + SimulationMessaging::create(broker.c_str(), smqUserName.c_str(), + password.c_str(), qName.c_str(), topicName.c_str(), + vCellUsername.c_str(), simKey, jobIndex, taskID); + #endif +} + +void errExit(int returnCode, std::string &errorMsg) { + #ifdef USE_MESSAGING + if (returnCode != 0) { + if (SimulationMessaging::getInstVar() != nullptr && !SimulationMessaging::getInstVar()->isStopRequested()) { + SimulationMessaging::getInstVar()->setWorkerEvent(new WorkerEvent(JOB_FAILURE, errorMsg.c_str())); + } + } + #endif + + if (returnCode != 0) std::cerr << errorMsg << std::endl; + #ifdef USE_MESSAGING + else if (SimulationMessaging::getInstVar() != nullptr) { + SimulationMessaging::getInstVar()->waitUntilFinished(); + delete SimulationMessaging::getInstVar(); + } + #endif +} From 9418160b4b6f879e132e5569a1b534a9c8117e44 Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Mon, 30 Jun 2025 09:12:38 -0400 Subject: [PATCH 04/26] Moved input-reading outside of solver --- IDAWin/CMakeLists.txt | 27 +- IDAWin/SteadyStateDriver.cpp | 4 + IDAWin/SteadyStateDriver.h | 8 + IDAWin/SundialsSolverStandalone.cpp | 127 +++---- IDAWin/VCellCVodeSolver.cpp | 70 ++-- IDAWin/VCellCVodeSolver.h | 2 + IDAWin/VCellIDASolver.cpp | 131 ++++--- IDAWin/VCellIDASolver.h | 18 +- IDAWin/VCellSolver.h | 25 ++ IDAWin/VCellSolverFactory.cpp | 514 ++++++++++++++++++++++++++++ IDAWin/VCellSolverFactory.h | 26 ++ IDAWin/VCellSolverInput.cpp | 7 + IDAWin/VCellSolverInput.h | 81 +++++ IDAWin/VCellSundialsSolver.cpp | 336 +++++++++++------- IDAWin/VCellSundialsSolver.h | 266 +++++++------- 15 files changed, 1203 insertions(+), 439 deletions(-) create mode 100644 IDAWin/SteadyStateDriver.cpp create mode 100644 IDAWin/SteadyStateDriver.h create mode 100644 IDAWin/VCellSolver.h create mode 100644 IDAWin/VCellSolverFactory.cpp create mode 100644 IDAWin/VCellSolverFactory.h create mode 100644 IDAWin/VCellSolverInput.cpp create mode 100644 IDAWin/VCellSolverInput.h diff --git a/IDAWin/CMakeLists.txt b/IDAWin/CMakeLists.txt index 76cf35abc..d9480bc26 100644 --- a/IDAWin/CMakeLists.txt +++ b/IDAWin/CMakeLists.txt @@ -1,18 +1,25 @@ project(IDAWin) set (SRC_FILES - OdeResultSet.cpp - StoppedByUserException.cpp - VCellCVodeSolver.cpp - VCellIDASolver.cpp - VCellSundialsSolver.cpp + OdeResultSet.cpp + StoppedByUserException.cpp + VCellCVodeSolver.cpp + VCellIDASolver.cpp + VCellSundialsSolver.cpp + SteadyStateDriver.cpp + VCellSolverFactory.cpp + VCellSolverInput.cpp ) set (HEADER_FILES - OdeResultSet.h - StoppedByUserException.h - VCellCVodeSolver.h - VCellIDASolver.h - VCellSundialsSolver.h + OdeResultSet.h + StoppedByUserException.h + VCellSolverFactory.h + VCellSolver.h + VCellSolverInput.h + VCellSundialsSolver.h + VCellCVodeSolver.h + VCellIDASolver.h + SteadyStateDriver.h ) set (EXE_SRC_FILES diff --git a/IDAWin/SteadyStateDriver.cpp b/IDAWin/SteadyStateDriver.cpp new file mode 100644 index 000000000..297021cce --- /dev/null +++ b/IDAWin/SteadyStateDriver.cpp @@ -0,0 +1,4 @@ +// +// Created by Logan Drescher on 6/25/25. +// +#include "SteadyStateDriver.h" diff --git a/IDAWin/SteadyStateDriver.h b/IDAWin/SteadyStateDriver.h new file mode 100644 index 000000000..d70cdd6f8 --- /dev/null +++ b/IDAWin/SteadyStateDriver.h @@ -0,0 +1,8 @@ +// +// Created by Logan Drescher on 6/25/25. +// + +#ifndef STEADYSTATEDRIVER_H +#define STEADYSTATEDRIVER_H + +#endif //STEADYSTATEDRIVER_H diff --git a/IDAWin/SundialsSolverStandalone.cpp b/IDAWin/SundialsSolverStandalone.cpp index 0c7a95747..07f4ecf8f 100644 --- a/IDAWin/SundialsSolverStandalone.cpp +++ b/IDAWin/SundialsSolverStandalone.cpp @@ -13,6 +13,8 @@ #include "StoppedByUserException.h" #include #include + +#include "VCellSolverFactory.h" #define CVODE_SOLVER "CVODE" #define IDA_SOLVER "IDA" @@ -86,90 +88,47 @@ int parseAndRunWithArgParse(int argc, char *argv[]) { } void activateSolver(std::ifstream& inputFileStream, FILE* outputFile, int taskID) { - std::string solver; - - while (!inputFileStream.eof()) { // Note break statement if "SOLVER" encountered - std::string nextToken; - inputFileStream >> nextToken; - if (nextToken.empty()) continue; - if (nextToken[0] == '#') getline(inputFileStream, nextToken); - else if (nextToken == "JMS_PARAM_BEGIN") { - loadJMSInfo(inputFileStream, taskID); - #ifdef USE_MESSAGING - SimulationMessaging::getInstVar()->start(); // start the thread - #endif - } else if (nextToken == "SOLVER") { - inputFileStream >> solver; - break; - } - } - #ifdef USE_MESSAGING - // should only happen during testing for solver compiled with messaging but run locally. - if (SimulationMessaging::getInstVar() == nullptr) { SimulationMessaging::create(); } - #endif - - if (solver.empty()) { throw "Solver not defined "; } - VCellSundialsSolver *vss = nullptr; - if (solver == IDA_SOLVER) { - vss = new VCellIDASolver(); - } else if (solver == CVODE_SOLVER) { - vss = new VCellCVodeSolver(); - } else { - std::stringstream ss; - ss << "Solver " << solver << " not defined!"; - throw ss.str(); - } - vss->readInput(inputFileStream); - vss->solve(nullptr, true, outputFile, VCellSundialsSolver::checkStopRequested); - - delete vss; -} - -void loadJMSInfo(std::istream &ifsInput, int taskID) { - #ifndef USE_MESSAGING - return; // Only useful for messaging; let's not waste time! - #else - - if (taskID < 0) { - SimulationMessaging::create(); - return; // No need to do any parsing - } - std::string broker; - std::string smqUserName; - std::string password; - std::string qName; - std::string topicName; - std::string vCellUsername; - int simKey, jobIndex; - - while (!ifsInput.eof()) { - std::string nextToken; - ifsInput >> nextToken; - if (nextToken.empty()) continue; - if (nextToken[0] == '#') { - // getline(ifsInput, nextToken); // Is this ignoring because of a comment? - ifsInput.ignore('\n'); - continue; - } - if (nextToken == "JMS_PARAM_END") { ifsInput.ignore(EOF); } else if ( - nextToken == "JMS_BROKER") { ifsInput >> broker; } else if ( - nextToken == "JMS_USER") { ifsInput >> smqUserName >> password; } else if ( - nextToken == "JMS_QUEUE") { ifsInput >> qName; } else if ( - nextToken == "JMS_TOPIC") { ifsInput >> topicName; } else if (nextToken == "VCELL_USER") { - ifsInput >> vCellUsername; - } else if (nextToken == "SIMULATION_KEY") { - ifsInput >> simKey; - continue; - } else if (nextToken == "JOB_INDEX") { - ifsInput >> jobIndex; - continue; - } - } - - SimulationMessaging::create(broker.c_str(), smqUserName.c_str(), - password.c_str(), qName.c_str(), topicName.c_str(), - vCellUsername.c_str(), simKey, jobIndex, taskID); - #endif + // std::string solver; + // + // while (!inputFileStream.eof()) { // Note break statement if "SOLVER" encountered + // std::string nextToken; + // inputFileStream >> nextToken; + // if (nextToken.empty()) continue; + // if (nextToken[0] == '#') getline(inputFileStream, nextToken); + // else if (nextToken == "JMS_PARAM_BEGIN") { + // loadJMSInfo(inputFileStream, taskID); + // #ifdef USE_MESSAGING + // SimulationMessaging::getInstVar()->start(); // start the thread + // #endif + // } else if (nextToken == "SOLVER") { + // inputFileStream >> solver; + // break; + // } + // } + // #ifdef USE_MESSAGING + // // should only happen during testing for solver compiled with messaging but run locally. + // if (SimulationMessaging::getInstVar() == nullptr) { SimulationMessaging::create(); } + // #endif + // + // if (solver.empty()) { throw "Solver not defined "; } + // VCellSundialsSolver *vss = nullptr; + // + // if (solver == IDA_SOLVER) { + // vss = new VCellIDASolver(); + // } else if (solver == CVODE_SOLVER) { + // vss = new VCellCVodeSolver(); + // } else { + // std::stringstream ss; + // ss << "Solver " << solver << " not defined!"; + // throw ss.str(); + // } + // + // vss->solve(nullptr, true, outputFile, VCellSundialsSolver::checkStopRequested); + // delete vss; + + + VCellSolver* targetSolver = VCellSolverFactory::produceVCellSolver(inputFileStream, taskID); + targetSolver->solve(nullptr, true, outputFile, VCellSundialsSolver::checkStopRequested); } void errExit(int returnCode, std::string &errorMsg) { diff --git a/IDAWin/VCellCVodeSolver.cpp b/IDAWin/VCellCVodeSolver.cpp index f81ed0a59..e37b7d418 100644 --- a/IDAWin/VCellCVodeSolver.cpp +++ b/IDAWin/VCellCVodeSolver.cpp @@ -9,20 +9,16 @@ #include #include #include - -#ifdef USE_MESSAGING -#include -#endif - #include - #include /* prototypes for CVODE fcts. and consts. */ #include /* serial N_Vector types, fcts., and macros */ #include /* prototype for CVDense */ //#include /* prototype for CVSPGMR */ #include /* definitions DenseMat DENSE_ELEM */ #include /* definition of type realtype */ - +#ifdef USE_MESSAGING +#include +#endif #define ToleranceType CV_SS /** @@ -151,13 +147,13 @@ VCellCVodeSolver::VCellCVodeSolver() : VCellSundialsSolver() { VCellCVodeSolver::~VCellCVodeSolver() { CVodeFree(&solver); - for (int i = 0; i < NEQ; i ++) { + for (int i = 0; i < NUM_EQUATIONS; i ++) { delete rateExpressions[i]; } delete[] rateExpressions; } -/* +/**---------------------------------------------------- Input format: STARTING_TIME 0.0 ENDING_TIME 0.1 @@ -172,15 +168,15 @@ Input format: RATE ((20.0 * x_o * D_B0) - (50.0 * x_i)); ODE x_o INIT 0.0; RATE ( - ((20.0 * x_o * D_B0) - (50.0 * x_i)) + (1505000.0 * (3.322259136212625E-4 - (3.322259136212625E-4 * x_o) - (3.322259136212625E-4 * x_i))) - (100.0 * x_o)); -*/ +--------------------------------------------------------------*/ void VCellCVodeSolver::readEquations(std::istream& inputstream) { try { std::string name; std::string exp; - rateExpressions = new Expression*[NEQ]; + rateExpressions = new Expression*[NUM_EQUATIONS]; - for (int i = 0; i < NEQ; i ++) { + for (int i = 0; i < NUM_EQUATIONS; i ++) { // ODE inputstream >> name >> variableNames[i]; @@ -209,11 +205,35 @@ void VCellCVodeSolver::readEquations(std::istream& inputstream) { } } +void VCellCVodeSolver::readEquations(VCellSolverInputBreakdown& inputBreakdown) { + try { + for (int i = 0; i < NUM_EQUATIONS; i ++) { + variableNames[i] = inputBreakdown.modelSettings.VARIABLE_NAMES[i]; + try { + initialConditionExpressions[i] = new Expression(inputBreakdown.modelSettings.INITIAL_CONDITION_EXPRESSIONS[i]); + } catch (VCell::Exception& ex) { + throw VCell::Exception(std::string("Initial condition expression for [") + variableNames[i] + "] " + ex.getMessage()); + } + } + + rateExpressions = new Expression*[inputBreakdown.modelSettings.RATE_EXPRESSIONS.size()]; + for (int i = 0; i < inputBreakdown.modelSettings.RATE_EXPRESSIONS.size(); i++) { + rateExpressions[i] = new Expression(inputBreakdown.modelSettings.RATE_EXPRESSIONS[i]); + } + } catch (char* ex) { + throw VCell::Exception(std::string("VCellCVodeSolver::readInput() : ") + ex); + } catch (VCell::Exception& ex) { + throw VCell::Exception(std::string("VCellCVodeSolver::readInput() : ") + ex.getMessage()); + } catch (...) { + throw "VCellCVodeSolver::readInput() : caught unknown exception"; + } +} + void VCellCVodeSolver::initialize() { VCellSundialsSolver::initialize(); // rate can be function of variables, parameters and discontinuities. - for (int i = 0; i < NEQ; i ++) { + for (int i = 0; i < NUM_EQUATIONS; i ++) { rateExpressions[i]->bindExpression(defaultSymbolTable); } } @@ -222,7 +242,7 @@ int VCellCVodeSolver::RHS (realtype t, N_Vector y, N_Vector r) { try { updateTandVariableValues(t, y); double* r_data = NV_DATA_S(r); - for (int i = 0; i < NEQ; i ++) { + for (int i = 0; i < NUM_EQUATIONS; i ++) { r_data[i] = rateExpressions[i]->evaluateVector(values); } recoverableErrMsg = ""; @@ -264,9 +284,9 @@ void VCellCVodeSolver::solve(double* paramValues, bool bPrintProgress, FILE* out this->odeResultSet->clearData(); // copy parameter values to the end of values, these will stay the same during solving - memset(values, 0, (NEQ + 1) * sizeof(double)); - memcpy(values + 1 + NEQ, paramValues, NPARAM * sizeof(double)); - memset(values + 1 + NEQ + NPARAM, 0, numDiscontinuities * sizeof(double)); + memset(values, 0, (NUM_EQUATIONS + 1) * sizeof(double)); + memcpy(values + 1 + NUM_EQUATIONS, paramValues, NUM_PARAMETERS * sizeof(double)); + memset(values + 1 + NUM_EQUATIONS + NUM_PARAMETERS, 0, numDiscontinuities * sizeof(double)); initCVode(paramValues); cvodeSolve(bPrintProgress, outputFile, checkStopRequested); @@ -274,7 +294,7 @@ void VCellCVodeSolver::solve(double* paramValues, bool bPrintProgress, FILE* out void VCellCVodeSolver::initCVode(double* paramValues) { //Initialize y, variable portion of values - for (int i = 0; i < NEQ; i ++) { + for (int i = 0; i < NUM_EQUATIONS; i ++) { NV_Ith_S(y, i) = initialConditionExpressions[i]->evaluateVector(paramValues); } @@ -305,7 +325,7 @@ void VCellCVodeSolver::reInit(double t) { flag = CVodeSetFdata(solver, this); checkCVodeFlag(flag); - flag = CVDense(solver, NEQ); + flag = CVDense(solver, NUM_EQUATIONS); //flag = CVSpgmr(solver, PREC_NONE, 0); checkCVodeFlag(flag); @@ -317,8 +337,8 @@ void VCellCVodeSolver::reInit(double t) { } bool VCellCVodeSolver::fixInitialDiscontinuities(double t) { - double* oldy = new double[NEQ]; - memcpy(oldy, NV_DATA_S(y), NEQ * sizeof(realtype)); + double* oldy = new double[NUM_EQUATIONS]; + memcpy(oldy, NV_DATA_S(y), NUM_EQUATIONS * sizeof(realtype)); double epsilon = std::max(1e-15, ENDING_TIME * 1e-10); double currentTime = t; @@ -341,11 +361,11 @@ bool VCellCVodeSolver::fixInitialDiscontinuities(double t) { } } if (bInitChanged) { - memcpy(values + 1 + NEQ + NPARAM, discontinuityValues, numDiscontinuities * sizeof(double)); + memcpy(values + 1 + NUM_EQUATIONS + NUM_PARAMETERS, discontinuityValues, numDiscontinuities * sizeof(double)); } //revert y - memcpy(NV_DATA_S(y), oldy, NEQ * sizeof(realtype)); + memcpy(NV_DATA_S(y), oldy, NUM_EQUATIONS * sizeof(realtype)); reInit(t); delete[] oldy; @@ -427,7 +447,7 @@ void VCellCVodeSolver::cvodeSolve(bool bPrintProgress, FILE* outputFile, void (* if (returnCode == CV_ROOT_RETURN || iterationCount % keepEvery == 0 || Time >= ENDING_TIME){ outputCount++; - if (outputCount * (NEQ + 1) * bytesPerSample > MaxFileSizeBytes){ + if (outputCount * (NUM_EQUATIONS + 1) * bytesPerSample > MaxFileSizeBytes){ /* if more than one gigabyte, then fail */ const std::string msg{"output exceeded maximum " + std::to_string(MaxFileSizeBytes) + " bytes"}; throw VCell::Exception(msg.c_str()); @@ -487,5 +507,5 @@ void VCellCVodeSolver::cvodeSolve(bool bPrintProgress, FILE* outputFile, void (* void VCellCVodeSolver::updateTandVariableValues(realtype t, N_Vector y) { values[0] = t; - memcpy(values + 1, NV_DATA_S(y), NEQ * sizeof(realtype)); + memcpy(values + 1, NV_DATA_S(y), NUM_EQUATIONS * sizeof(realtype)); } diff --git a/IDAWin/VCellCVodeSolver.h b/IDAWin/VCellCVodeSolver.h index f9cceb84a..85d9decc4 100644 --- a/IDAWin/VCellCVodeSolver.h +++ b/IDAWin/VCellCVodeSolver.h @@ -14,8 +14,10 @@ class VCellCVodeSolver : public VCellSundialsSolver { protected: void readEquations(std::istream& inputstream) override; + void readEquations(VCellSolverInputBreakdown& inputBreakdown) override; void initialize() override; std::string getSolverName() override { return "CVODE"; } + VCellSolverTypes getSolverType() override { return VCellSolverTypes::CVODE; } private: VCell::Expression** rateExpressions; diff --git a/IDAWin/VCellIDASolver.cpp b/IDAWin/VCellIDASolver.cpp index 4a4fce35c..4d45a1a7f 100644 --- a/IDAWin/VCellIDASolver.cpp +++ b/IDAWin/VCellIDASolver.cpp @@ -9,20 +9,16 @@ #include #include #include - - -#ifdef USE_MESSAGING -#include -#endif - #include #include #include #include //#include #include - #include +#ifdef USE_MESSAGING +#include +#endif /** * calling sequence @@ -214,7 +210,7 @@ VCellIDASolver::~VCellIDASolver() { N_VDestroy_Serial(yp); N_VDestroy_Serial(id); - for (int i = 0; i < NEQ; i ++) { + for (int i = 0; i < NUM_EQUATIONS; i ++) { delete rhsExpressions[i]; delete[] transformMatrix[i]; delete[] inverseTransformMatrix[i]; @@ -305,7 +301,7 @@ void VCellIDASolver::readEquations(std::istream& inputstream) { if (token != "VAR"){ throw "expecting VAR"; } - for (int i = 0; i < NEQ; i ++) { + for (int i = 0; i < NUM_EQUATIONS; i ++) { // consume "VAR" token, but first line has it's "VAR" already consumed if (i > 0){ inputstream >> token; @@ -323,28 +319,28 @@ void VCellIDASolver::readEquations(std::istream& inputstream) { } //TRANSFORM - transformMatrix = new double*[NEQ]; + transformMatrix = new double*[NUM_EQUATIONS]; inputstream >> token; if (token != "TRANSFORM") { throw "expecting TRANSFORM"; } getline(inputstream, exp); // go to next line - for (int i = 0; i < NEQ; i ++) { - transformMatrix[i] = new double[NEQ]; - for (int j = 0; j < NEQ; j ++) { + for (int i = 0; i < NUM_EQUATIONS; i ++) { + transformMatrix[i] = new double[NUM_EQUATIONS]; + for (int j = 0; j < NUM_EQUATIONS; j ++) { inputstream >> transformMatrix[i][j]; } } //INVERSETRANSFORM - inverseTransformMatrix = new double*[NEQ]; + inverseTransformMatrix = new double*[NUM_EQUATIONS]; inputstream >> token; if (token != "INVERSETRANSFORM") { throw "expecting INVERSETRANSFORM"; } getline(inputstream, exp); // go to next line - for (int i = 0; i < NEQ; i ++) { - inverseTransformMatrix[i] = new double[NEQ]; - for (int j = 0; j < NEQ; j ++) { + for (int i = 0; i < NUM_EQUATIONS; i ++) { + inverseTransformMatrix[i] = new double[NUM_EQUATIONS]; + for (int j = 0; j < NUM_EQUATIONS; j ++) { inputstream >> inverseTransformMatrix[i][j]; } } @@ -364,13 +360,13 @@ void VCellIDASolver::readEquations(std::istream& inputstream) { throw "expecting ALGEBRAIC"; } inputstream >> numAlgebraic; - if (numDifferential + numAlgebraic != NEQ) { + if (numDifferential + numAlgebraic != NUM_EQUATIONS) { throw "numDifferential + numAlgebraic != NEQ"; } getline(inputstream, exp); // go to next line - rhsExpressions = new Expression*[NEQ]; - for (int i = 0; i < NEQ; i ++) { + rhsExpressions = new Expression*[NUM_EQUATIONS]; + for (int i = 0; i < NUM_EQUATIONS; i ++) { try { rhsExpressions[i] = readExpression(inputstream); } catch (VCell::Exception& ex) { @@ -388,15 +384,61 @@ void VCellIDASolver::readEquations(std::istream& inputstream) { } } +void VCellIDASolver::readEquations(VCellSolverInputBreakdown& inputBreakdown) { + try { + for (int i = 0; i < NUM_EQUATIONS; i ++) { + variableNames[i] = inputBreakdown.modelSettings.VARIABLE_NAMES[i]; + try { + initialConditionExpressions[i] = new Expression(inputBreakdown.modelSettings.INITIAL_CONDITION_EXPRESSIONS[i]); + } catch (VCell::Exception& ex) { + throw VCell::Exception(std::string("Initial condition expression for [") + variableNames[i] + "] " + ex.getMessage()); + } + } + transformMatrix = new double*[NUM_EQUATIONS]; + for (int i = 0; i < NUM_EQUATIONS; i ++) { + transformMatrix[i] = new double[NUM_EQUATIONS]; + for (int j = 0; j < NUM_EQUATIONS; j ++) { + transformMatrix[i][j] = std::stod(inputBreakdown.modelSettings.FLAT_TRANSFORM_MATRIX[i * NUM_EQUATIONS + j]); + } + } + + inverseTransformMatrix = new double*[NUM_EQUATIONS]; + for (int i = 0; i < NUM_EQUATIONS; i ++) { + inverseTransformMatrix[i] = new double[NUM_EQUATIONS]; + for (int j = 0; j < NUM_EQUATIONS; j ++) { + inverseTransformMatrix[i][j] = std::stod(inputBreakdown.modelSettings.FLAT_INVERSE_TRANSFORM_MATRIX[i * NUM_EQUATIONS + j]); + } + } + numDifferential = inputBreakdown.modelSettings.NUM_DIFFERENTIAL; + numAlgebraic = inputBreakdown.modelSettings.NUM_ALGEBRAIC; + rhsExpressions = new Expression*[NUM_EQUATIONS]; + for (int i = 0; i < NUM_EQUATIONS; i ++) { + try { + rhsExpressions[i] = new Expression(inputBreakdown.modelSettings.RATE_EXPRESSIONS[i]); + } catch (VCell::Exception& ex) { + std::stringstream ss; + ss << "RHS[" << i << "] " << ex.getMessage(); + throw VCell::Exception(ss.str()); + } + } + } catch (const char* ex) { + throw VCell::Exception(std::string("VCellIDASolver::readInput() : ") + ex); + } catch (VCell::Exception& ex) { + throw VCell::Exception(std::string("VCellIDASolver::readInput() : ") + ex.getMessage()); + } catch (...) { + throw VCell::Exception("VCellIDASolver::readInput() caught unknown exception"); + } +} + void VCellIDASolver::initialize() { VCellSundialsSolver::initialize(); - for (int i = 0; i < NEQ; i ++) { + for (int i = 0; i < NUM_EQUATIONS; i ++) { rhsExpressions[i]->bindExpression(defaultSymbolTable); } - yp = N_VNew_Serial(NEQ); - id = N_VNew_Serial(NEQ); + yp = N_VNew_Serial(NUM_EQUATIONS); + id = N_VNew_Serial(NUM_EQUATIONS); if (yp == 0 || id == 0) { throw "Out of Memory"; @@ -416,9 +458,9 @@ void VCellIDASolver::solve(double* paramValues, bool bPrintProgress, FILE* outpu // copy parameter values to the end of values, these will stay the same during solving // values[0] is time, y values will be copied to 1~NEQ of values in residual function - memset(values, 0, (NEQ + 1 + NPARAM) * sizeof(double)); - memcpy(values + NEQ + 1, paramValues, NPARAM * sizeof(double)); - memset(values + 1 + NEQ + NPARAM, 0, numDiscontinuities * sizeof(double)); + memset(values, 0, (NUM_EQUATIONS + 1 + NUM_PARAMETERS) * sizeof(double)); + memcpy(values + NUM_EQUATIONS + 1, paramValues, NUM_PARAMETERS * sizeof(double)); + memset(values + 1 + NUM_EQUATIONS + NUM_PARAMETERS, 0, numDiscontinuities * sizeof(double)); initIDA(paramValues); idaSolve(bPrintProgress, outputFile, checkStopRequested); @@ -431,18 +473,18 @@ void VCellIDASolver::solve(double* paramValues, bool bPrintProgress, FILE* outpu void VCellIDASolver::initIDA(double* paramValues) { // compute initial condition - double* initCond = new double[NEQ]; - for (int i = 0; i < NEQ; i ++) { + double* initCond = new double[NUM_EQUATIONS]; + for (int i = 0; i < NUM_EQUATIONS; i ++) { initCond[i] = initialConditionExpressions[i]->evaluateVector(paramValues); } // must initialize y and yp before call IDAMalloc // Initialize y, yp and id. transform initial condition to y - for (int i = 0; i < NEQ; i ++) { + for (int i = 0; i < NUM_EQUATIONS; i ++) { NV_Ith_S(id, i) = i < numDifferential ? RCONST(1) : RCONST(0); NV_Ith_S(yp, i) = 0; // Initialize yp to be 0, they will be reinitialized later. NV_Ith_S(y, i) = 0; - for (int j = 0; j < NEQ; j ++) { + for (int j = 0; j < NUM_EQUATIONS; j ++) { NV_Ith_S(y, i) += transformMatrix[i][j] * initCond[j]; } } @@ -481,7 +523,7 @@ void VCellIDASolver::reInit(double t) { checkIDAFlag(flag); // choose the linear solver (Dense "direct" matrix LU decomposition solver). - flag = IDADense(solver, NEQ); + flag = IDADense(solver, NUM_EQUATIONS); //flag = IDASpgmr(solver, 0); checkIDAFlag(flag); @@ -507,8 +549,8 @@ bool VCellIDASolver::fixInitialDiscontinuities(double t) { int flag = IDARootInit(solver, 0, RootFn_callback, this); checkIDAFlag(flag); - double* oldy = new double[NEQ]; - memcpy(oldy, NV_DATA_S(y), NEQ * sizeof(realtype)); + double* oldy = new double[NUM_EQUATIONS]; + memcpy(oldy, NV_DATA_S(y), NUM_EQUATIONS * sizeof(realtype)); double epsilon = std::max(1e-15, ENDING_TIME * 1e-8); double currentTime = t; @@ -530,12 +572,12 @@ bool VCellIDASolver::fixInitialDiscontinuities(double t) { } } if (bInitChanged) { - memcpy(values + 1 + NEQ + NPARAM, discontinuityValues, numDiscontinuities * sizeof(double)); + memcpy(values + 1 + NUM_EQUATIONS + NUM_PARAMETERS, discontinuityValues, numDiscontinuities * sizeof(double)); } //revert y and yp - memcpy(NV_DATA_S(y), oldy, NEQ * sizeof(realtype)); - memset(NV_DATA_S(yp), 0, NEQ * sizeof(realtype)); + memcpy(NV_DATA_S(y), oldy, NUM_EQUATIONS * sizeof(realtype)); + memset(NV_DATA_S(yp), 0, NUM_EQUATIONS * sizeof(realtype)); reInit(t); flag = IDARootInit(solver, 2*numDiscontinuities, RootFn_callback, this); @@ -622,11 +664,10 @@ void VCellIDASolver::idaSolve(bool bPrintProgress, FILE* outputFile, void (*chec if (returnCode == IDA_ROOT_RETURN || iterationCount % keepEvery == 0 || Time >= ENDING_TIME){ outputCount ++; - if (outputCount *(NEQ + 1) * bytesPerSample > MaxFileSizeBytes){ - /* if more than one gigabyte, then fail */ - char msg[100]; - sprintf(msg, "output exceeded %ld bytes\n", MaxFileSizeBytes); - throw VCell::Exception(msg); + if (outputCount *(NUM_EQUATIONS + 1) * bytesPerSample > MaxFileSizeBytes){ + /* if more than one gigabyte, then fail */ + std::string problem = std::format("output exceeded {} bytes\n", MaxFileSizeBytes); + throw VCell::Exception(problem); } writeData(Time, outputFile); if (bPrintProgress) { @@ -685,9 +726,9 @@ void VCellIDASolver::idaSolve(bool bPrintProgress, FILE* outputFile, void (*chec // override updateTempRowData, since y values need to be transformed to the original variables. void VCellIDASolver::updateTempRowData(double currTime) { tempRowData[0] = currTime; - for (int i = 0; i < NEQ; i ++) { + for (int i = 0; i < NUM_EQUATIONS; i ++) { tempRowData[i + 1] = 0; - for (int j = 0; j < NEQ; j ++) { + for (int j = 0; j < NUM_EQUATIONS; j ++) { tempRowData[i + 1] += inverseTransformMatrix[i][j] * NV_Ith_S(y, j); } } @@ -695,9 +736,9 @@ void VCellIDASolver::updateTempRowData(double currTime) { void VCellIDASolver::updateTandVariableValues(realtype t, N_Vector y) { values[0] = t; - for (int i = 0; i < NEQ; i ++) { + for (int i = 0; i < NUM_EQUATIONS; i ++) { values[i + 1] = 0; - for (int j = 0; j < NEQ; j ++) { + for (int j = 0; j < NUM_EQUATIONS; j ++) { values[i + 1] += inverseTransformMatrix[i][j] * NV_Ith_S(y, j); } } diff --git a/IDAWin/VCellIDASolver.h b/IDAWin/VCellIDASolver.h index 459701003..0ba22673f 100644 --- a/IDAWin/VCellIDASolver.h +++ b/IDAWin/VCellIDASolver.h @@ -7,15 +7,17 @@ class VCellIDASolver : public VCellSundialsSolver { public: VCellIDASolver(); - ~VCellIDASolver(); + ~VCellIDASolver() override; - void solve(double* paramValues=0, bool bPrintProgress=false, FILE* outputFile=0, void (*checkStopRequested)(double, long)=0); + void solve(double* paramValues=0, bool bPrintProgress=false, FILE* outputFile=0, void (*checkStopRequested)(double, long)=0) override; protected: - void updateTempRowData(double currTime); - void readEquations(std::istream& inputstream); - void initialize(); - std::string getSolverName() { return "IDA"; } + void updateTempRowData(double currTime) override; + void readEquations(std::istream& inputstream) override; + void readEquations(VCellSolverInputBreakdown& inputBreakdown) override; + void initialize() override; + std::string getSolverName() override { return "IDA"; } + VCellSolverTypes getSolverType() override { return VCellSolverTypes::IDA; } private: VCell::Expression** rhsExpressions; // can be rate expression in ODE case or RHS expression in DAE case @@ -60,8 +62,8 @@ class VCellIDASolver : public VCellSundialsSolver { void checkIDAFlag(int flag); void reInit(realtype t); - bool fixInitialDiscontinuities(double t); - void updateTandVariableValues(realtype t, N_Vector y); + bool fixInitialDiscontinuities(double t) override; + void updateTandVariableValues(realtype t, N_Vector y) override; void onIDAReturn(realtype Time, int returnCode); }; diff --git a/IDAWin/VCellSolver.h b/IDAWin/VCellSolver.h new file mode 100644 index 000000000..1d95dc705 --- /dev/null +++ b/IDAWin/VCellSolver.h @@ -0,0 +1,25 @@ +// +// Created by Logan Drescher on 6/26/25. +// + +#ifndef VCELLSOLVER_H +#define VCELLSOLVER_H +#include "VCellSolverInput.h" + +/** + * Interfacing class for all solvers; we generally only care about configuring and running... + */ +class VCellSolver { + public: + virtual ~VCellSolver() = default; + + virtual void configureFromInput(VCellSolverInputBreakdown& inputBreakdown) = 0; + + virtual void solve(double *paramValues = nullptr, bool bPrintProgress = false, FILE *outputFile = nullptr, + void (*checkStopRequested)(double, long) = nullptr) = 0; + + protected: + VCellSolver() = default; +}; + +#endif //VCELLSOLVER_H diff --git a/IDAWin/VCellSolverFactory.cpp b/IDAWin/VCellSolverFactory.cpp new file mode 100644 index 000000000..5a905d73e --- /dev/null +++ b/IDAWin/VCellSolverFactory.cpp @@ -0,0 +1,514 @@ +// +// Created by Logan Drescher on 6/26/25. +// +#include "VCellSolverFactory.h" +#include "VCellSundialsSolver.h" +#include +#include +#include +#include +#include +#include + +#include "Exception.h" +#include "VCellCVodeSolver.h" +#include "VCellIDASolver.h" +#ifdef USE_MESSAGING +static void loadJMSInfo(std::istream &ifsInput, int taskID); +#endif +// Forward Declarations +static void readDiscontinuities (std::istream &inputStream, VCellSolverInputBreakdown& inputBreakdown); +static void readEvents(std::istream &inputStream, VCellSolverInputBreakdown& inputBreakdown); +static std::string getExpressionString(std::istream& inputStream); +static std::string getExpressionString(std::string& unparsedString); +static void trimString(std::string &str); +// static VCell::Expression* readExpression(std::istream &inputstream); +static void collectCVodeEquations(std::ifstream& inputFileStream, VCellSolverInputBreakdown& inputBreakdown); +static void collectIDAEquations(std::ifstream& inputFileStream, VCellSolverInputBreakdown& inputBreakdown); +static void collectSteadyStateTerms(std::ifstream& inputFileStream, VCellSolverInputBreakdown& inputBreakdown); +static void loadJMSInfo(std::istream &ifsInput, int taskID); + +VCellSolver* VCellSolverFactory::produceVCellSolver(std::ifstream& inputFileStream, int taskID){ + VCellSolverInputBreakdown inputBreakdown = parseInputFile(inputFileStream, taskID); + VCellSolver* desiredSolver; + switch (inputBreakdown.solverType) { + case VCellSolverTypes::CVODE: + desiredSolver = new VCellCVodeSolver(); + break; + case VCellSolverTypes::IDA: + desiredSolver = new VCellIDASolver(); + break; + // case VCellSolverTypes::STEADY_STATE: + // break; + default: + throw new VCell::Exception("Unknown VCellSolver type"); + } + desiredSolver->configureFromInput(inputBreakdown); + return desiredSolver; +} + +VCellSolverInputBreakdown VCellSolverFactory::parseInputFile(std::ifstream& inputFileStream, int taskID) { + VCellSolverInputBreakdown inputBreakdown; + while (!inputFileStream.eof()) { + std::string nextToken; + inputFileStream >> nextToken; + if (nextToken.empty()) continue; + if (nextToken[0] == '#') std::getline(inputFileStream, nextToken); + else if (nextToken == "JMS_PARAM_BEGIN") { + #ifdef USE_MESSAGING + loadJMSInfo(inputFileStream, taskID); + SimulationMessaging::getInstVar()->start(); // start the thread + #endif + } else if (nextToken == "SOLVER") { + std::string solverName; + inputFileStream >> solverName; + inputBreakdown.solverType = determineSolverType(solverName); + } else if (nextToken == "JMS_PARAM_BEGIN") { + loadJMSInfo(inputFileStream, taskID); + #ifdef USE_MESSAGING + SimulationMessaging::getInstVar()->start(); // start the thread + #endif + } + else if (nextToken == "STARTING_TIME") { inputFileStream >> inputBreakdown.timeCourseSettings.STARTING_TIME; } + else if (nextToken == "ENDING_TIME") { inputFileStream >> inputBreakdown.timeCourseSettings.ENDING_TIME; } + else if (nextToken == "RELATIVE_TOLERANCE") { inputFileStream >> inputBreakdown.timeCourseSettings.RELATIVE_TOLERANCE; } + else if (nextToken == "ABSOLUTE_TOLERANCE") { inputFileStream >> inputBreakdown.timeCourseSettings.ABSOLUTE_TOLERANCE; } + else if (nextToken == "MAX_TIME_STEP") { inputFileStream >> inputBreakdown.timeCourseSettings.MAX_TIME_STEP; } + else if (nextToken == "KEEP_EVERY") { inputFileStream >> inputBreakdown.timeCourseSettings.KEEP_EVERY; } + else if (nextToken == "OUTPUT_TIME_STEP"){ inputFileStream >> inputBreakdown.timeCourseSettings.OUTPUT_TIME_STEP; } + else if (nextToken == "OUTPUT_TIMES") { + std::size_t numOfTimePoints; + inputFileStream >> numOfTimePoints; + double timePoint; + for (std::size_t i = 0; i < numOfTimePoints; i++) { + inputFileStream >> timePoint; + const bool isValidTimePoint = timePoint > inputBreakdown.timeCourseSettings.STARTING_TIME + && timePoint <= inputBreakdown.timeCourseSettings.ENDING_TIME; + if (isValidTimePoint) inputBreakdown.timeCourseSettings.TIME_POINTS.push_back(timePoint); + } + if (inputBreakdown.timeCourseSettings.TIME_POINTS.back() < inputBreakdown.timeCourseSettings.ENDING_TIME) + inputBreakdown.timeCourseSettings.TIME_POINTS.push_back(inputBreakdown.timeCourseSettings.ENDING_TIME); + } else if (nextToken == "DISCONTINUITIES") { + readDiscontinuities(inputFileStream, inputBreakdown); + } else if (nextToken == "NUM_PARAMETERS") { + std::size_t numParameters; + inputFileStream >> numParameters; + for (std::size_t i = 0; i < numParameters; i++) { + std::string name; + inputFileStream >> name; + inputBreakdown.modelSettings.PARAMETER_NAMES.push_back(name); + } + } else if (nextToken == "NUM_EQUATIONS") { VCellSolverFactory::processEquations(inputFileStream, inputBreakdown); } + else if (nextToken == "EVENTS") { readEvents(inputFileStream, inputBreakdown); } + else if (nextToken == "STEADY_STATE") { + std::string confirmation; + inputFileStream >> confirmation; + if (confirmation == "TRUE") { collectSteadyStateTerms(inputFileStream, inputBreakdown); } + } + else throw VCell::Exception("Unexpected token \"" + nextToken + "\" in the input file!"); + + } + return inputBreakdown; +} + +VCellSolverTypes VCellSolverFactory::determineSolverType(const std::string& solverName){ + if (solverName == "CVODE") return VCellSolverTypes::CVODE; + if (solverName == "IDA") return VCellSolverTypes::IDA; + if (solverName == "CSSS") return VCellSolverTypes::STEADY_STATE; // copasi-style steady-state + throw new std::runtime_error("Unknown solver type: `" + solverName + "`"); +} + +void VCellSolverFactory::processEquations(std::ifstream& inputFileStream, VCellSolverInputBreakdown& inputBreakdown) { + if (inputBreakdown.solverType == VCellSolverTypes::IDA) collectIDAEquations(inputFileStream, inputBreakdown); + else collectCVodeEquations(inputFileStream, inputBreakdown); +} + +/** * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Local Helper Functions + * * * * * * * * * * * * * * * * * * * * * * * * * * * **/ + +#ifdef USE_MESSAGING +static void loadJMSInfo(std::istream &ifsInput, int taskID) { + if (taskID < 0) { + SimulationMessaging::create(); + return; // No need to do any parsing + } + std::string broker; + std::string smqUserName; + std::string password; + std::string qName; + std::string topicName; + std::string vCellUsername; + int simKey, jobIndex; + + while (!ifsInput.eof()) { + std::string nextToken; + ifsInput >> nextToken; + if (nextToken.empty()) continue; + if (nextToken[0] == '#') { + // std::getline(ifsInput, nextToken); // Is this ignoring because of a comment? + ifsInput.ignore('\n'); + continue; + } + if (nextToken == "JMS_PARAM_END") { ifsInput.ignore(EOF); } else if ( + nextToken == "JMS_BROKER") { ifsInput >> broker; } else if ( + nextToken == "JMS_USER") { ifsInput >> smqUserName >> password; } else if ( + nextToken == "JMS_QUEUE") { ifsInput >> qName; } else if ( + nextToken == "JMS_TOPIC") { ifsInput >> topicName; } else if (nextToken == "VCELL_USER") { + ifsInput >> vCellUsername; + } else if (nextToken == "SIMULATION_KEY") { + ifsInput >> simKey; + continue; + } else if (nextToken == "JOB_INDEX") { + ifsInput >> jobIndex; + continue; + } + } + + SimulationMessaging::create(broker.c_str(), smqUserName.c_str(), + password.c_str(), qName.c_str(), topicName.c_str(), + vCellUsername.c_str(), simKey, jobIndex, taskID); + +} +#endif + +static void readDiscontinuities(std::istream &inputStream, VCellSolverInputBreakdown& inputBreakdown) { + VCellSolverInputBreakdown::DiscontinuityComponents discontinuityComponents; + std::size_t numOfDiscontinuities; + inputStream >> numOfDiscontinuities; + + for (std::size_t i = 0; i < numOfDiscontinuities; i++) { + inputStream >> discontinuityComponents.symbol; + + std::string line = ""; + std::getline(inputStream, line); + std::string::size_type pos = line.find(";"); + if (pos == std::string::npos) { + std::string msg = std::string("discontinuity expression ") + BAD_EXPRESSION_MSG; + throw VCell::Exception(msg); + } + std::string exp = line.substr(0, pos + 1); + trimString(exp); + discontinuityComponents.discontinuityExpression = new VCell::Expression(exp); + + exp = line.substr(pos + 1); + discontinuityComponents.rootFindingExpression = new VCell::Expression(getExpressionString(exp)); + + inputBreakdown.discontinuitiesSettings.DISCONTINUITIES.push_back(std::move(discontinuityComponents)); + } +} + +static void readEvents(std::istream &inputStream, VCellSolverInputBreakdown& inputBreakdown) { + std::size_t numOfEvents; + std::string token; + + inputStream >> numOfEvents; + //events = new Event *[numEvents]; + for (std::size_t i = 0; i < numOfEvents; i++) { + VCellSolverInputBreakdown::EventComponents eventComponents; + while (true) { // Break on "EVENTASSIGNMENTS" + inputStream >> token; + if (token == "EVENT") { inputStream >> eventComponents.eventName; } + else if (token == "TRIGGER") { + try { + eventComponents.triggerExpression = getExpressionString(inputStream); + } catch (VCell::Exception &ex) { + throw VCell::Exception(std::string("trigger expression") + " " + ex.getMessage()); + } + } else if (token == "DELAY") { + inputStream >> token; + eventComponents.shouldUseValuesAtTriggerTime = (token == "true"); + try { + eventComponents.delayDurationExpression = getExpressionString(inputStream); + } catch (VCell::Exception &ex) { + throw VCell::Exception(std::string("delay duration expression") + " " + ex.getMessage()); + } + } else if (token == "EVENTASSIGNMENTS") { + int numEventAssignments; + inputStream >> numEventAssignments; + for (int j = 0; j < numEventAssignments; j++) { + std::pair eventAssignment; + inputStream >> eventAssignment.first; //varIndex + try { + eventAssignment.second = getExpressionString(inputStream); // assignment expression + } catch (VCell::Exception &ex) { + throw VCell::Exception(std::string("event assignment expression") + " " + ex.getMessage()); + } + eventComponents.eventAssignments.push_back(std::move(eventAssignment)); + //eventComponents.eventAssignments.emplace_back(varIndex, assignmentExpression); // should try this in the future, more descriptive + } + break; + } else { throw VCell::Exception("Unexpected token \"" + token + "\" in the input file!"); } + } + inputBreakdown.eventSettings.EVENTS.push_back(std::move(eventComponents)); + } +} + +static void trimString(std::string &str) { + if (std::string::size_type pos = str.find_last_not_of(" \r\n"); pos != std::string::npos) { + str.erase(pos + 1); + pos = str.find_first_not_of(" \r\n"); + if (pos != std::string::npos) { str.erase(0, pos); } + } else { str.erase(str.begin(), str.end()); } +} + +static std::string getExpressionString(std::istream& inputStream) { + std::string exp; + std::getline(inputStream, exp); + return getExpressionString(exp); +} + +static std::string getExpressionString(std::string& unparsedString) { + trimString(unparsedString); + if (*(unparsedString.end() - 1) != ';') { throw VCell::Exception(std::format("Expression `{}` is not terminated with a ';' character!", unparsedString)); } + return unparsedString; +} + +/**---------------------------------------------------- +Input format: + STARTING_TIME 0.0 + ENDING_TIME 0.1 + RELATIVE_TOLERANCE 1.0E-9 + ABSOLUTE_TOLERANCE 1.0E-9 + MAX_TIME_STEP 1.0 + KEEP_EVERY 1 + DISCONTINUITIES 1 + D_B0 (t > 0.0432); (-0.0432 + t); + NUM_EQUATIONS 2 + ODE x_i INIT 0.0; + RATE ((20.0 * x_o * D_B0) - (50.0 * x_i)); + ODE x_o INIT 0.0; + RATE ( - ((20.0 * x_o * D_B0) - (50.0 * x_i)) + (1505000.0 * (3.322259136212625E-4 - (3.322259136212625E-4 * x_o) - (3.322259136212625E-4 * x_i))) - (100.0 * x_o)); +--------------------------------------------------------------*/ + +static void collectCVodeEquations(std::ifstream& inputFileStream, VCellSolverInputBreakdown& inputBreakdown) { + std::size_t numOfEquations; + inputFileStream >> numOfEquations; + std::string exp; + try { + std::string keyword; + for (std::size_t i = 0; i < numOfEquations; i++) { + // ODE + std::string varName; + inputFileStream >> keyword >> varName; + inputBreakdown.modelSettings.VARIABLE_NAMES.push_back(varName); + + // INIT + inputFileStream >> keyword; + try { + std::string expression{getExpressionString(inputFileStream)}; + inputBreakdown.modelSettings.INITIAL_CONDITION_EXPRESSIONS.push_back(expression); + } catch (VCell::Exception& ex) { + const std::string errMsg{"Initial condition expression for [" + inputBreakdown.modelSettings.VARIABLE_NAMES.back() + "] " + ex.getMessage()}; + throw VCell::Exception(errMsg); + } + + // RATE + inputFileStream >> keyword; + try { + std::string expression{getExpressionString(inputFileStream)}; + inputBreakdown.modelSettings.RATE_EXPRESSIONS.push_back(expression); + } catch (VCell::Exception& ex) { + const std::string errMsg{"Rate expression for [" + inputBreakdown.modelSettings.VARIABLE_NAMES.back() + "] " + ex.getMessage()}; + throw VCell::Exception(errMsg); + } + } + inputBreakdown.modelSettings.NUM_DIFFERENTIAL = inputBreakdown.modelSettings.RATE_EXPRESSIONS.size(); + inputBreakdown.modelSettings.NUM_ALGEBRAIC = 0; + + } catch (char* ex) { + throw VCell::Exception(std::string("VCellSolverFactory::collectCVodeEquations() : ") + ex); + } catch (VCell::Exception& ex) { + throw VCell::Exception(std::string("VCellSolverFactory::collectCVodeEquations() : ") + ex.getMessage()); + } +} + +/**---------------------------------------------------- +Input format: + STARTING_TIME 0.0 + ENDING_TIME 0.1 + RELATIVE_TOLERANCE 1.0E-9 + ABSOLUTE_TOLERANCE 1.0E-9 + MAX_TIME_STEP 1.0 + KEEP_EVERY 1 + DISCONTINUITIES 1 + D_B0 (t > 0.0432); (-0.0432 + t); + NUM_EQUATIONS 2 + VAR x_i INIT (0.0); + VAR x_o INIT (0.8); + TRANSFORM + 3.322259136212625E-4 0.0 + 0.0 1.0 + INVERSETRANSFORM + 3010.0 0.0 + 0.0 1.0 + RHS DIFFERENTIAL 1 ALGEBRAIC 1 + (3.322259136212625E-4 * ((20.0 * x_o * D_B0) - (50.0 * x_i))); + ((1505000.0 * (3.3222591362126253E-4 - (3.322259136212625E-4 * x_i) - (3.322259136212625E-4 * x_o))) - (100.0 * x_o)); +--------------------------------------------------------------*/ +static void collectIDAEquations(std::ifstream& inputFileStream, VCellSolverInputBreakdown& inputBreakdown) { + std::size_t numOfEquations; + inputFileStream >> numOfEquations; + try { + std::string keyword; + std::string _ignored; + + // There should be number of: + // * VAR statements + // * TRANSFORM entries + // * INVERSETRANSFORM entries + // * RHS entries + // We will remove sections when we've gotten the necessary amount of each + std::unordered_map sections = {{"VAR", 0}, {"TRANSFORM", 0} , {"INVERSETRANSFORM", 0}, {"RHS", 0} }; + + while (!sections.empty()) { + inputFileStream >> keyword; + if (!sections.contains(keyword)) throw VCell::Exception(std::string("Encountered unexpected keyword `") + keyword + "`"); + + if (keyword == "VAR") { + std::string varName, initialConditionExpression; + inputFileStream >> varName; + inputBreakdown.modelSettings.VARIABLE_NAMES.push_back(std::move(varName)); + inputFileStream >> keyword; // INIT keyword + try { + inputBreakdown.modelSettings.INITIAL_CONDITION_EXPRESSIONS.push_back(getExpressionString(inputFileStream)); + } catch (VCell::Exception& ex) { + throw VCell::Exception(std::string("Initial condition expression for [") + inputBreakdown.modelSettings.VARIABLE_NAMES.back() + "] " + ex.getMessage()); + } + // check for completion + if (sections[keyword] == numOfEquations - 1) sections.erase(keyword); + else sections[keyword]++; + } else if (keyword == "TRANSFORM") { + std::getline(inputFileStream, _ignored); // go to next line + for (std::size_t i = 0; i < numOfEquations * numOfEquations; i++) { + std::string nextTerm; + inputFileStream >> nextTerm; + inputBreakdown.modelSettings.FLAT_TRANSFORM_MATRIX.push_back(nextTerm); + } + // Affirm completion + sections.erase(keyword); + } else if (keyword == "INVERSETRANSFORM") { + std::getline(inputFileStream, _ignored); // go to next line + for (std::size_t i = 0; i < numOfEquations * numOfEquations; i++) { + std::string nextTerm; + inputFileStream >> nextTerm; + inputBreakdown.modelSettings.FLAT_INVERSE_TRANSFORM_MATRIX.push_back(nextTerm); + } + // Affirm completion + sections.erase(keyword); + } else if (keyword == "RHS") { + std::size_t numDifferentials, numAlgebraics; + inputFileStream >> keyword >> numDifferentials; + if (keyword != "DIFFERENTIAL") throw VCell::Exception("Error: Expected keyword `DIFFERENTIAL`, found `" + keyword +"`"); + inputBreakdown.modelSettings.NUM_DIFFERENTIAL = numDifferentials; + inputFileStream >> keyword >> numAlgebraics; + if (keyword != "ALGEBRAIC") throw VCell::Exception("Error: Expected keyword `ALGEBRAIC`, found `" + keyword +"`"); + inputBreakdown.modelSettings.NUM_ALGEBRAIC = numAlgebraics; + if (numDifferentials + numAlgebraics != numOfEquations) { + throw VCell::Exception(std::format("The sum of differential ({}) and algebraic ({}) equations does not equal the total number of equations ({})", numDifferentials, numAlgebraics, numOfEquations)); + } + std::getline(inputFileStream, _ignored); // go to next line + for (std::size_t i = 0; i < numOfEquations; i ++) { + try { + inputBreakdown.modelSettings.RATE_EXPRESSIONS.push_back(getExpressionString(inputFileStream)); + } catch (VCell::Exception& ex) { + throw VCell::Exception(std::format("RHS[{}] {}", i, ex.getMessage())); + } + } + // Affirm completion + sections.erase(keyword); + } else { + std::string errMsg{"Fatal error: inconsistent state - `" + keyword + "` is a valid section, but no logic exists to deal with it!"}; + throw VCell::Exception(errMsg); + } + } + } catch (const char* ex) { + throw VCell::Exception(std::string("VCellSolverFactory::collectIDAEquations() : ") + ex); + } catch (VCell::Exception& ex) { + throw VCell::Exception(std::string("VCellSolverFactory::collectIDAEquations() : ") + ex.getMessage()); + } catch (...) { + throw VCell::Exception("VCellSolverFactory::collectIDAEquations() caught unknown exception"); + } +} + +/**---------------------------------------------------- +Input format: + ... (STEADY_STATE TRUE) + RESOLUTION 0.0000000001 + MAX_NEWTON_ITERATIONS + MAXIMUM_FORWARD_DURATION + DERIVATION_FACTOR 0.001 + STOP_CRITERIA DISTANCE_AND_RATE + ... +--------------------------------------------------------------*/ +static void collectSteadyStateTerms(std::ifstream& inputFileStream, VCellSolverInputBreakdown& inputBreakdown) { + std::unordered_set sections = {"RESOLUTION", "MAX_NEWTON_ITERATIONS", + "MAXIMUM_FORWARD_DURATION", "DERIVATION_FACTOR", "STOP_CRITERIA"}; + + do { + std::string keyword; + inputFileStream >> keyword; + if (keyword == "RESOLUTION") { inputFileStream >> inputBreakdown.steadyStateSettings.RESOLUTION; } + else if (keyword == "MAXIMUM_NEWTON_ITERATIONS") { inputFileStream >> inputBreakdown.steadyStateSettings.MAX_ITERATIONS; } + else if (keyword == "MAXIMUM_FORWARD_DURATION") { inputFileStream >> inputBreakdown.steadyStateSettings.MAX_FORWARD_DURATION; } + else if (keyword == "DERIVATION_FACTOR") { inputFileStream >> inputBreakdown.steadyStateSettings.DERIVATION_FACTOR; } + else if (keyword == "STOP_CRITERIA") { + std::string stopCriteria; + inputFileStream >> stopCriteria; + if (stopCriteria == "DISTANCE_ONLY") { inputBreakdown.steadyStateSettings.STOP_CRITERIA = VCellSteadyStateStopCriteria::DISTANCE_ONLY; } + else if (stopCriteria == "RATE_ONLY") { inputBreakdown.steadyStateSettings.STOP_CRITERIA = VCellSteadyStateStopCriteria::RATE_ONLY; } + else if (stopCriteria == "DISTANCE_AND_RATE") { inputBreakdown.steadyStateSettings.STOP_CRITERIA = VCellSteadyStateStopCriteria::DISTANCE_AND_RATE; } + else if (stopCriteria == "DISTANCE_OR_RATE") { inputBreakdown.steadyStateSettings.STOP_CRITERIA = VCellSteadyStateStopCriteria::DISTANCE_OR_RATE; } + else throw VCell::Exception("Unknown stop criteria `" + keyword + "`"); + } else throw VCell::Exception("Unknown steady state keyword `" + keyword + "`"); + if (!sections.contains(keyword)) throw VCell::Exception("Duplicate section `" + keyword + "` detected."); + sections.erase(keyword); + } while (!sections.empty()); +} + +static void loadJMSInfo(std::istream &ifsInput, int taskID) { + #ifndef USE_MESSAGING + return; // Only useful for messaging; let's not waste time! + #else + + if (taskID < 0) { + SimulationMessaging::create(); + return; // No need to do any parsing + } + std::string broker; + std::string smqUserName; + std::string password; + std::string qName; + std::string topicName; + std::string vCellUsername; + int simKey, jobIndex; + + while (!ifsInput.eof()) { + std::string nextToken; + ifsInput >> nextToken; + if (nextToken.empty()) continue; + if (nextToken[0] == '#') { + // getline(ifsInput, nextToken); // Is this ignoring because of a comment? + ifsInput.ignore('\n'); + continue; + } + if (nextToken == "JMS_PARAM_END") { ifsInput.ignore(EOF); } else if ( + nextToken == "JMS_BROKER") { ifsInput >> broker; } else if ( + nextToken == "JMS_USER") { ifsInput >> smqUserName >> password; } else if ( + nextToken == "JMS_QUEUE") { ifsInput >> qName; } else if ( + nextToken == "JMS_TOPIC") { ifsInput >> topicName; } else if (nextToken == "VCELL_USER") { + ifsInput >> vCellUsername; + } else if (nextToken == "SIMULATION_KEY") { + ifsInput >> simKey; + continue; + } else if (nextToken == "JOB_INDEX") { + ifsInput >> jobIndex; + continue; + } + } + + SimulationMessaging::create(broker.c_str(), smqUserName.c_str(), + password.c_str(), qName.c_str(), topicName.c_str(), + vCellUsername.c_str(), simKey, jobIndex, taskID); + #endif +} diff --git a/IDAWin/VCellSolverFactory.h b/IDAWin/VCellSolverFactory.h new file mode 100644 index 000000000..9e81b35df --- /dev/null +++ b/IDAWin/VCellSolverFactory.h @@ -0,0 +1,26 @@ +// +// Created by Logan Drescher on 6/26/25. +// + +#ifndef VCELLSOLVERFACTORY_H +#define VCELLSOLVERFACTORY_H +#include +#include +#include "VCellSolver.h" +#include "VCellSolverInput.h" + + + + +class VCellSolverFactory { + public: + static VCellSolver* produceVCellSolver(std::ifstream& inputFileStream, int taskID); + static VCellSolverInputBreakdown parseInputFile(std::ifstream& inputFileStream, int taskID); + private: + static VCellSolverTypes determineSolverType(const std::string& solverName); + static void processEquations(std::ifstream& inputFileStream, VCellSolverInputBreakdown& inputBreakdown); +}; + + + +#endif //VCELLSOLVERFACTORY_H diff --git a/IDAWin/VCellSolverInput.cpp b/IDAWin/VCellSolverInput.cpp new file mode 100644 index 000000000..8f0f7bf26 --- /dev/null +++ b/IDAWin/VCellSolverInput.cpp @@ -0,0 +1,7 @@ +// +// Created by Logan Drescher on 6/26/25. +// + +#include "VCellSolverInput.h" + +// See "TODO" in header file diff --git a/IDAWin/VCellSolverInput.h b/IDAWin/VCellSolverInput.h new file mode 100644 index 000000000..08b9ca4d1 --- /dev/null +++ b/IDAWin/VCellSolverInput.h @@ -0,0 +1,81 @@ +// +// Created by Logan Drescher on 6/26/25. +// + +#ifndef VCELLSOLVERINPUT_H +#define VCELLSOLVERINPUT_H +#include +#include +#include +#include "Expression.h" + +enum VCellSolverTypes{ CVODE, IDA, STEADY_STATE}; +enum VCellSteadyStateStopCriteria{DISTANCE_ONLY, RATE_ONLY, DISTANCE_OR_RATE, DISTANCE_AND_RATE}; + +/// TODO: For better memory management, vectors should have their size initialized by the +/// values in the input file, and allocated on the heap! +struct VCellSolverInputBreakdown { + // Typedefs + + struct ModelSettings { + std::vector PARAMETER_NAMES; + std::vector VARIABLE_NAMES; + std::vector INITIAL_CONDITION_EXPRESSIONS; + std::vector RATE_EXPRESSIONS; + std::vector FLAT_TRANSFORM_MATRIX; + std::vector FLAT_INVERSE_TRANSFORM_MATRIX; + int NUM_DIFFERENTIAL; + int NUM_ALGEBRAIC; + }; + + struct TimeCourseSettings { + realtype STARTING_TIME; + realtype ENDING_TIME; + realtype RELATIVE_TOLERANCE; + realtype ABSOLUTE_TOLERANCE; + double OUTPUT_TIME_STEP = 0.0; + double MAX_TIME_STEP = 0.0; + std::vector TIME_POINTS; + long KEEP_EVERY = 0.0l; + }; + + struct SteadyStateSettings { + realtype RESOLUTION; + realtype DERIVATION_FACTOR; // how far to newton step to satisfy df/dt + realtype MAX_ITERATIONS; // how many times to newton-step before giving up + realtype MAX_FORWARD_DURATION; + VCellSteadyStateStopCriteria STOP_CRITERIA; + }; + + struct DiscontinuityComponents { + std::string symbol; + VCell::Expression* discontinuityExpression; + VCell::Expression* rootFindingExpression; + }; + + + struct DiscontinuitiesSettings { + std::vector DISCONTINUITIES; + }; + + struct EventComponents { + std::string eventName; + std::string triggerExpression; + std::string delayDurationExpression; + std::vector> eventAssignments; + bool shouldUseValuesAtTriggerTime = true; + }; + + struct EventSettings { + std::vector EVENTS; + }; + + VCellSolverTypes solverType; + ModelSettings modelSettings; + TimeCourseSettings timeCourseSettings; + SteadyStateSettings steadyStateSettings; + DiscontinuitiesSettings discontinuitiesSettings; + EventSettings eventSettings; +}; + +#endif //VCELLSOLVERINPUT_H diff --git a/IDAWin/VCellSundialsSolver.cpp b/IDAWin/VCellSundialsSolver.cpp index 06257a695..99286c686 100644 --- a/IDAWin/VCellSundialsSolver.cpp +++ b/IDAWin/VCellSundialsSolver.cpp @@ -1,4 +1,4 @@ -#include +#include "SimpleSymbolTable.h" #include "StoppedByUserException.h" #include "VCellSundialsSolver.h" #include "OdeResultSet.h" @@ -8,33 +8,21 @@ #include #include - -#include - #ifdef USE_MESSAGING #include #endif -static void trimString(std::string &str) { - std::string::size_type pos = str.find_last_not_of(" \r\n"); - if (pos != std::string::npos) { - str.erase(pos + 1); - pos = str.find_first_not_of(" \r\n"); - if (pos != std::string::npos) { str.erase(0, pos); } - } else { str.erase(str.begin(), str.end()); } -} - -VCell::Expression *VCellSundialsSolver::readExpression(std::istream &inputstream) { - std::string exp; - getline(inputstream, exp); - trimString(exp); - if (*(exp.end() - 1) != ';') { throw VCell::Exception(BAD_EXPRESSION_MSG); } - return new VCell::Expression(exp); -} +/** * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Local Helper Function Prototypes + * * * * * * * * * * * * * * * * * * * * * * * * * * * **/ +static void trimString(std::string &str); +/** * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Instance Methods + * * * * * * * * * * * * * * * * * * * * * * * * * * * **/ VCellSundialsSolver::VCellSundialsSolver() { - NEQ = 0; - NPARAM = 0; + NUM_EQUATIONS = 0; + NUM_PARAMETERS = 0; STARTING_TIME = 0.0; ENDING_TIME = 0.0; RelativeTolerance = 0.0; @@ -70,7 +58,7 @@ VCellSundialsSolver::VCellSundialsSolver() { VCellSundialsSolver::~VCellSundialsSolver() { N_VDestroy_Serial(y); - for (int i = 0; i < NEQ; i++) { delete initialConditionExpressions[i]; } + for (int i = 0; i < NUM_EQUATIONS; i++) { delete initialConditionExpressions[i]; } delete[] initialConditionExpressions; delete[] variableNames; delete[] paramNames; @@ -99,21 +87,21 @@ VCellSundialsSolver::~VCellSundialsSolver() { eventExeList.clear(); } -void VCellSundialsSolver::updateTempRowData(double currTime) { - tempRowData[0] = currTime; - for (int i = 0; i < NEQ; i++) { tempRowData[i + 1] = NV_Ith_S(y, i); } -} - void VCellSundialsSolver::writeData(double currTime, FILE *outputFile) { updateTempRowData(currTime); odeResultSet->addRow(tempRowData); writeFileData(outputFile); } +void VCellSundialsSolver::updateTempRowData(double currTime) { + tempRowData[0] = currTime; + for (int i = 0; i < NUM_EQUATIONS; i++) { tempRowData[i + 1] = NV_Ith_S(y, i); } +} + void VCellSundialsSolver::writeFileData(FILE *outputFile) { if (outputFile != nullptr) { fprintf(outputFile, "%0.17E", tempRowData[0]); - for (int i = 1; i < NEQ + 1; i++) { fprintf(outputFile, "\t%0.17E", tempRowData[i]); } + for (int i = 1; i < NUM_EQUATIONS + 1; i++) { fprintf(outputFile, "\t%0.17E", tempRowData[i]); } fprintf(outputFile, "\n"); } } @@ -170,56 +158,121 @@ void VCellSundialsSolver::printProgress(double currTime, double &lastPercentile, } } -void VCellSundialsSolver::readInput(std::istream &inputstream) { +// void VCellSundialsSolver::readInput(std::istream &inputFileStream) { +// try { +// if (solver != nullptr) { throw "readInput should only be called once"; } +// while (!inputFileStream.eof()) { +// std::string name; +// inputFileStream >> name; +// if (name.empty()) { continue; } +// if (name == "SOLVER") { +// inputFileStream >> name; +// if (name != getSolverName()) { throw "Wrong solver"; } +// } else if (name == "STARTING_TIME") { inputFileStream >> STARTING_TIME; } else if ( +// name == "ENDING_TIME") { inputFileStream >> ENDING_TIME; } else if ( +// name == "RELATIVE_TOLERANCE") { inputFileStream >> RelativeTolerance; } else if ( +// name == "ABSOLUTE_TOLERANCE") { inputFileStream >> AbsoluteTolerance; } else if ( +// name == "MAX_TIME_STEP") { inputFileStream >> maxTimeStep; } else if (name == "KEEP_EVERY") { +// inputFileStream >> keepEvery; +// } else if (name == "OUTPUT_TIME_STEP") { +// double outputTimeStep = 0.0; +// inputFileStream >> outputTimeStep; +// double timePoint = 0.0; +// int count = 1; +// while (STARTING_TIME + count * outputTimeStep < ENDING_TIME + 1E-12 * ENDING_TIME) { +// timePoint = STARTING_TIME + count * outputTimeStep; +// outputTimes.push_back(timePoint); +// count++; +// } +// ENDING_TIME = outputTimes[outputTimes.size() - 1]; +// } else if (name == "OUTPUT_TIMES") { +// int totalNumTimePoints; +// double timePoint; +// inputFileStream >> totalNumTimePoints; +// for (int i = 0; i < totalNumTimePoints; i++) { +// inputFileStream >> timePoint; +// if (timePoint > STARTING_TIME && timePoint <= ENDING_TIME) { outputTimes.push_back(timePoint); } +// } +// if (outputTimes[outputTimes.size() - 1] < ENDING_TIME) { outputTimes.push_back(ENDING_TIME); } +// } else if (name == "DISCONTINUITIES") { readDiscontinuities(inputFileStream); } +// else if (name == "NUM_PARAMETERS") { +// inputFileStream >> NUM_PARAMETERS; +// paramNames = new std::string[NUM_PARAMETERS]; +// for (int i = 0; i < NUM_PARAMETERS; i++) { inputFileStream >> paramNames[i]; } +// } else if (name == "NUM_EQUATIONS") { +// inputFileStream >> NUM_EQUATIONS; +// variableNames = new std::string[NUM_EQUATIONS]; +// initialConditionExpressions = new VCell::Expression *[NUM_EQUATIONS]; +// readEquations(inputFileStream); +// } else if (name == "EVENTS") { readEvents(inputFileStream); } else { +// std::string msg = "Unexpected token \"" + name + "\" in the input file!"; +// throw VCell::Exception(msg); +// } +// } +// initialize(); +// } catch (char *ex) { throw VCell::Exception(std::string("VCellSundialsSolver::readInput() : ") + ex); } catch ( +// VCell::Exception &ex) { +// throw VCell::Exception(std::string("VCellSundialsSolver::readInput() : ") + ex.getMessage()); +// } +// } + +void VCellSundialsSolver::configureFromInput(VCellSolverInputBreakdown& inputBreakdown) { try { - if (solver != 0) { throw "readInput should only be called once"; } - std::string name; - while (!inputstream.eof()) { - name = ""; - inputstream >> name; - if (name.empty()) { continue; } - if (name == "SOLVER") { - inputstream >> name; - if (name != getSolverName()) { throw "Wrong solver"; } - } else if (name == "STARTING_TIME") { inputstream >> STARTING_TIME; } else if ( - name == "ENDING_TIME") { inputstream >> ENDING_TIME; } else if ( - name == "RELATIVE_TOLERANCE") { inputstream >> RelativeTolerance; } else if ( - name == "ABSOLUTE_TOLERANCE") { inputstream >> AbsoluteTolerance; } else if ( - name == "MAX_TIME_STEP") { inputstream >> maxTimeStep; } else if (name == "KEEP_EVERY") { - inputstream >> keepEvery; - } else if (name == "OUTPUT_TIME_STEP") { - double outputTimeStep = 0.0; - inputstream >> outputTimeStep; - double timePoint = 0.0; - int count = 1; - while (STARTING_TIME + count * outputTimeStep < ENDING_TIME + 1E-12 * ENDING_TIME) { - timePoint = STARTING_TIME + count * outputTimeStep; - outputTimes.push_back(timePoint); - count++; - } - ENDING_TIME = outputTimes[outputTimes.size() - 1]; - } else if (name == "OUTPUT_TIMES") { - int totalNumTimePoints; - double timePoint; - inputstream >> totalNumTimePoints; - for (int i = 0; i < totalNumTimePoints; i++) { - inputstream >> timePoint; - if (timePoint > STARTING_TIME && timePoint <= ENDING_TIME) { outputTimes.push_back(timePoint); } - } - if (outputTimes[outputTimes.size() - 1] < ENDING_TIME) { outputTimes.push_back(ENDING_TIME); } - } else if (name == "DISCONTINUITIES") { readDiscontinuities(inputstream); } else if ( - name == "NUM_PARAMETERS") { - inputstream >> NPARAM; - paramNames = new std::string[NPARAM]; - for (int i = 0; i < NPARAM; i++) { inputstream >> paramNames[i]; } - } else if (name == "NUM_EQUATIONS") { - inputstream >> NEQ; - variableNames = new std::string[NEQ]; - initialConditionExpressions = new VCell::Expression *[NEQ]; - readEquations(inputstream); - } else if (name == "EVENTS") { readEvents(inputstream); } else { - std::string msg = "Unexpected token \"" + name + "\" in the input file!"; - throw VCell::Exception(msg); + if (solver != nullptr) { throw "readInput should only be called once"; } + if (inputBreakdown.solverType != getSolverType()) throw VCell::Exception("Fatal Error: Solver mismatch detected!"); + STARTING_TIME = inputBreakdown.timeCourseSettings.STARTING_TIME; + ENDING_TIME = inputBreakdown.timeCourseSettings.ENDING_TIME; + RelativeTolerance = inputBreakdown.timeCourseSettings.RELATIVE_TOLERANCE; + AbsoluteTolerance = inputBreakdown.timeCourseSettings.ABSOLUTE_TOLERANCE; + maxTimeStep = inputBreakdown.timeCourseSettings.MAX_TIME_STEP; + keepEvery = inputBreakdown.timeCourseSettings.KEEP_EVERY; + ///TODO: replace "1e-12" with "SMALL_REAL" constant? Probably need to change outputTimes to `std::vector` + if (inputBreakdown.timeCourseSettings.OUTPUT_TIME_STEP != 0.0){ + for (int count = 1; STARTING_TIME + count * inputBreakdown.timeCourseSettings.OUTPUT_TIME_STEP < ENDING_TIME + 1E-12 * ENDING_TIME; count++) + outputTimes.push_back(STARTING_TIME + count * inputBreakdown.timeCourseSettings.OUTPUT_TIME_STEP); + ENDING_TIME = outputTimes.back(); + } else if (!inputBreakdown.timeCourseSettings.TIME_POINTS.empty()) { + for (auto elem : inputBreakdown.timeCourseSettings.TIME_POINTS) outputTimes.push_back(elem); + } + numDiscontinuities = inputBreakdown.discontinuitiesSettings.DISCONTINUITIES.size(); + odeDiscontinuities = new OdeDiscontinuity*[numDiscontinuities]; + for (int i = 0; i < numDiscontinuities; i++) { + odeDiscontinuities[i] = new OdeDiscontinuity(); + odeDiscontinuities[i]->discontinuitySymbol = inputBreakdown.discontinuitiesSettings.DISCONTINUITIES[i].symbol; + odeDiscontinuities[i]->discontinuityExpression = inputBreakdown.discontinuitiesSettings.DISCONTINUITIES[i].discontinuityExpression; + odeDiscontinuities[i]->rootFindingExpression = inputBreakdown.discontinuitiesSettings.DISCONTINUITIES[i].rootFindingExpression; + } + NUM_PARAMETERS = inputBreakdown.modelSettings.PARAMETER_NAMES.size(); + paramNames = new std::string[NUM_PARAMETERS]; + for (int i = 0; i < NUM_PARAMETERS; i++) {paramNames[i] = inputBreakdown.modelSettings.PARAMETER_NAMES[i];} + NUM_EQUATIONS = inputBreakdown.modelSettings.VARIABLE_NAMES.size(); + variableNames = new std::string[NUM_EQUATIONS]; + initialConditionExpressions = new VCell::Expression*[NUM_EQUATIONS]; + readEquations(inputBreakdown); + numEvents = inputBreakdown.eventSettings.EVENTS.size(); + events = new Event*[numEvents]; + for (int i = 0; i < inputBreakdown.eventSettings.EVENTS.size(); i++) { + events[i] = new Event(); + events[i]->name = inputBreakdown.eventSettings.EVENTS[i].eventName; + events[i]->bUseValuesAtTriggerTime = inputBreakdown.eventSettings.EVENTS[i].shouldUseValuesAtTriggerTime; + try { + events[i]->triggerExpression = new VCell::Expression(inputBreakdown.eventSettings.EVENTS[i].triggerExpression); + } catch (VCell::Exception &ex) { + throw VCell::Exception(std::string("trigger expression") + " " + ex.getMessage()); + } + + try { + if (!inputBreakdown.eventSettings.EVENTS[i].delayDurationExpression.empty()) + events[i]->delayDurationExpression = new VCell::Expression(inputBreakdown.eventSettings.EVENTS[i].delayDurationExpression); + } catch (VCell::Exception &ex) { + throw VCell::Exception(std::string("delay duration expression") + " " + ex.getMessage()); + } + + for (auto [varIndex, assignmentExpression]: inputBreakdown.eventSettings.EVENTS[i].eventAssignments) { + EventAssignment* eventAssignment = new EventAssignment(); + eventAssignment->varIndex = varIndex; + eventAssignment->assignmentExpression = new VCell::Expression(assignmentExpression); + events[i]->eventAssignmentsVec->emplace_back(eventAssignment); } } initialize(); @@ -229,59 +282,58 @@ void VCellSundialsSolver::readInput(std::istream &inputstream) { } } -void VCellSundialsSolver::readEvents(std::istream &inputstream) { - std::string token; - - inputstream >> numEvents; - events = new Event *[numEvents]; - for (int i = 0; i < numEvents; i++) { - events[i] = new Event(); - while (true) { // Break on "EVENTASSIGNMENTS" - inputstream >> token; - if (token == "EVENT") { inputstream >> events[i]->name; } else if (token == "TRIGGER") { - try { events[i]->triggerExpression = readExpression(inputstream); } catch (VCell::Exception &ex) { - throw VCell::Exception(std::string("trigger expression") + " " + ex.getMessage()); - } - } else if (token == "DELAY") { - inputstream >> token; - events[i]->bUseValuesAtTriggerTime = token == "true"; - try { events[i]->delayDurationExpression = readExpression(inputstream); } catch (VCell::Exception &ex) { - throw VCell::Exception(std::string("delay duration expression") + " " + ex.getMessage()); - } - } else if (token == "EVENTASSIGNMENTS") { - int numEventAssignments; - inputstream >> numEventAssignments; - for (int j = 0; j < numEventAssignments; j++) { - EventAssignment *ea = new EventAssignment(); - inputstream >> ea->varIndex; - try { ea->assignmentExpression = readExpression(inputstream); } catch (VCell::Exception &ex) { - throw VCell::Exception(std::string("event assignment expression") + " " + ex.getMessage()); - } - events[i]->eventAssignmentsVec->push_back(ea); - } - break; - } else { throw VCell::Exception("Unexpected token \"" + token + "\" in the input file!"); } - } - } -} +// void VCellSundialsSolver::readEvents(std::istream &inputstream) { +// std::string token; +// +// inputstream >> numEvents; +// events = new Event *[numEvents]; +// for (int i = 0; i < numEvents; i++) { +// events[i] = new Event(); +// +// while (true) { // Break on "EVENTASSIGNMENTS" +// inputstream >> token; +// if (token == "EVENT") { inputstream >> events[i]->name; } else if (token == "TRIGGER") { +// try { events[i]->triggerExpression = readExpression(inputstream); } catch (VCell::Exception &ex) { +// throw VCell::Exception(std::string("trigger expression") + " " + ex.getMessage()); +// } +// } else if (token == "DELAY") { +// inputstream >> token; +// events[i]->bUseValuesAtTriggerTime = token == "true"; +// try { events[i]->delayDurationExpression = readExpression(inputstream); } catch (VCell::Exception &ex) { +// throw VCell::Exception(std::string("delay duration expression") + " " + ex.getMessage()); +// } +// } else if (token == "EVENTASSIGNMENTS") { +// int numEventAssignments; +// inputstream >> numEventAssignments; +// for (int j = 0; j < numEventAssignments; j++) { +// EventAssignment *ea = new EventAssignment(); +// inputstream >> ea->varIndex; +// try { ea->assignmentExpression = readExpression(inputstream); } catch (VCell::Exception &ex) { +// throw VCell::Exception(std::string("event assignment expression") + " " + ex.getMessage()); +// } +// events[i]->eventAssignmentsVec->push_back(ea); +// } +// break; +// } else { throw VCell::Exception("Unexpected token \"" + token + "\" in the input file!"); } +// } +// } +// } void VCellSundialsSolver::readDiscontinuities(std::istream &inputstream) { inputstream >> numDiscontinuities; odeDiscontinuities = new OdeDiscontinuity *[numDiscontinuities]; - std::string line; - std::string exp; for (int i = 0; i < numDiscontinuities; i++) { OdeDiscontinuity *od = new OdeDiscontinuity; inputstream >> od->discontinuitySymbol; - line = ""; + std::string line = ""; getline(inputstream, line); std::string::size_type pos = line.find(";"); if (pos == std::string::npos) { std::string msg = std::string("discontinuity expression ") + BAD_EXPRESSION_MSG; throw VCell::Exception(msg); } - exp = line.substr(0, pos + 1); + std::string exp = line.substr(0, pos + 1); trimString(exp); od->discontinuityExpression = new VCell::Expression(exp); @@ -299,35 +351,35 @@ void VCellSundialsSolver::readDiscontinuities(std::istream &inputstream) { void VCellSundialsSolver::initialize() { // add parameters to symbol table - numAllSymbols = 1 + NEQ + NPARAM + numDiscontinuities; + numAllSymbols = 1 + NUM_EQUATIONS + NUM_PARAMETERS + numDiscontinuities; allSymbols = new std::string[numAllSymbols]; // t, variables, parameters, discontinuities //t allSymbols[0] = "t"; odeResultSet->addColumn("t"); // variables - for (int i = 0; i < NEQ; i++) { + for (int i = 0; i < NUM_EQUATIONS; i++) { allSymbols[1 + i] = variableNames[i]; odeResultSet->addColumn(variableNames[i]); } // parameters - for (int i = 0; i < NPARAM; i++) { allSymbols[1 + NEQ + i] = paramNames[i]; } + for (int i = 0; i < NUM_PARAMETERS; i++) { allSymbols[1 + NUM_EQUATIONS + i] = paramNames[i]; } //discontinuities for (int i = 0; i < numDiscontinuities; i++) { - allSymbols[1 + NEQ + NPARAM + i] = odeDiscontinuities[i]->discontinuitySymbol; + allSymbols[1 + NUM_EQUATIONS + NUM_PARAMETERS + i] = odeDiscontinuities[i]->discontinuitySymbol; } // default symbol table has variables, parameters and discontinuities. defaultSymbolTable = new SimpleSymbolTable(allSymbols, numAllSymbols); // initial condition can only be function of parameters. - initialConditionSymbolTable = new SimpleSymbolTable(allSymbols + 1 + NEQ, NPARAM); - for (int i = 0; i < NEQ; i++) { initialConditionExpressions[i]->bindExpression(initialConditionSymbolTable); } + initialConditionSymbolTable = new SimpleSymbolTable(allSymbols + 1 + NUM_EQUATIONS, NUM_PARAMETERS); + for (int i = 0; i < NUM_EQUATIONS; i++) { initialConditionExpressions[i]->bindExpression(initialConditionSymbolTable); } if (numDiscontinuities > 0) { rootsFound = new int[2 * numDiscontinuities]; discontinuityValues = new double[numDiscontinuities]; // discontinuities can't be function of discontinuity symbols - discontinuitySymbolTable = new SimpleSymbolTable(allSymbols, 1 + NEQ + NPARAM); + discontinuitySymbolTable = new SimpleSymbolTable(allSymbols, 1 + NUM_EQUATIONS + NUM_PARAMETERS); for (int i = 0; i < numDiscontinuities; i++) { odeDiscontinuities[i]->discontinuityExpression->bindExpression(discontinuitySymbolTable); odeDiscontinuities[i]->rootFindingExpression->bindExpression(discontinuitySymbolTable); @@ -335,10 +387,10 @@ void VCellSundialsSolver::initialize() { } if (numEvents > 0) { for (int i = 0; i < numEvents; i++) { events[i]->bind(defaultSymbolTable); } } - values = new realtype[1 + NEQ + NPARAM + numDiscontinuities]; - tempRowData = new realtype[1 + NEQ]; + values = new realtype[1 + NUM_EQUATIONS + NUM_PARAMETERS + numDiscontinuities]; + tempRowData = new realtype[1 + NUM_EQUATIONS]; - y = N_VNew_Serial(NEQ); + y = N_VNew_Serial(NUM_EQUATIONS); if (y == nullptr) { throw "Out of Memory"; } } @@ -373,7 +425,7 @@ bool VCellSundialsSolver::updateDiscontinuities(realtype t, bool bOnRootReturn) std::cout << std::endl; // copy discontinuity values to values to evaluate RHS - if (bUpdated) { memcpy(values + 1 + NEQ + NPARAM, discontinuityValues, numDiscontinuities * sizeof(double)); } + if (bUpdated) { memcpy(values + 1 + NUM_EQUATIONS + NUM_PARAMETERS, discontinuityValues, numDiscontinuities * sizeof(double)); } return bUpdated; } @@ -386,7 +438,7 @@ void VCellSundialsSolver::initDiscontinuities() { discontinuityValues[i] = odeDiscontinuities[i]->discontinuityExpression->evaluateVector(values); } // copy discontinuity values to values to evaluate RHS - memcpy(values + 1 + NEQ + NPARAM, discontinuityValues, numDiscontinuities * sizeof(double)); + memcpy(values + 1 + NUM_EQUATIONS + NUM_PARAMETERS, discontinuityValues, numDiscontinuities * sizeof(double)); } void VCellSundialsSolver::checkDiscontinuityConsistency() const { @@ -447,7 +499,7 @@ void VCellSundialsSolver::printVariableValues(realtype t) { updateTandVariableValues(t, y); std::cout << "variable values are" << std::endl; std::cout << "t " << values[0] << std::endl; - for (int i = 0; i < NEQ; i++) { std::cout << variableNames[i] << " " << values[i + 1] << std::endl; } + for (int i = 0; i < NUM_EQUATIONS; i++) { std::cout << variableNames[i] << " " << values[i + 1] << std::endl; } std::cout << std::endl; } @@ -564,3 +616,27 @@ void VCellSundialsSolver::checkStopRequested(double time, long numIterations) { if (SimulationMessaging::getInstVar()->isStopRequested()) { throw StoppedByUserException("stopped by user"); } #endif } + +/** * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Static Methods + * * * * * * * * * * * * * * * * * * * * * * * * * * * **/ + +VCell::Expression *VCellSundialsSolver::readExpression(std::istream &inputstream) { + std::string exp; + getline(inputstream, exp); + trimString(exp); + if (*(exp.end() - 1) != ';') { throw VCell::Exception(BAD_EXPRESSION_MSG); } + return new VCell::Expression(exp); +} + +/** * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Local Helper Functions + * * * * * * * * * * * * * * * * * * * * * * * * * * * **/ +static void trimString(std::string &str) { + std::string::size_type pos = str.find_last_not_of(" \r\n"); + if (pos != std::string::npos) { + str.erase(pos + 1); + pos = str.find_first_not_of(" \r\n"); + if (pos != std::string::npos) { str.erase(0, pos); } + } else { str.erase(str.begin(), str.end()); } +} diff --git a/IDAWin/VCellSundialsSolver.h b/IDAWin/VCellSundialsSolver.h index b3c26f638..8b5d3b170 100644 --- a/IDAWin/VCellSundialsSolver.h +++ b/IDAWin/VCellSundialsSolver.h @@ -6,90 +6,174 @@ #include #include - #include - - #include #include -class SymbolTable; -class OdeResultSet; +#include "VCellSolver.h" + +class SymbolTable; // Defined in SymbolTable.h +class OdeResultSet; // Defined in OdeResultSet.h +struct Event; +struct EventAssignment; +struct EventExecution; +struct OdeDiscontinuity; #define bytesPerSample 25 -#define MaxFileSizeBytes 1000000000 /* 1 gigabyte */ +#define MaxFileSizeBytes 1000000000 /* 1 gigabyte */ #define BAD_EXPRESSION_MSG " is not terminated by ';'" #define MAX_NUM_EVENTS_DISCONTINUITIES_EVAL 50 +class VCellSundialsSolver : public VCellSolver { + public: + VCellSundialsSolver(); + virtual ~VCellSundialsSolver(); + + // void readInput(std::istream &inputFileStream); + + void configureFromInput(VCellSolverInputBreakdown& inputBreakdown) override; + + OdeResultSet *getResultSet() const { return this->odeResultSet; } + [[nodiscard]] int getNumEquations() const { return this->NUM_EQUATIONS; } + VCell::Expression **getInitialConditionExpressions() const { return this->initialConditionExpressions; } + void setStartingTime(const realtype newStartingTime) { this->STARTING_TIME = newStartingTime; } + void setEndingTime(const realtype newEndingTime) { this->ENDING_TIME = newEndingTime; } + //void setOutputTimes(int count, double* newOutputTimes); + SymbolTable *getSymbolTable() const { return this->defaultSymbolTable; } + + static void checkStopRequested(double, long); + + protected: + // 0 : t + // 1 ~ N : variable values + // N+1 ~ N+NPARAM : parameter values + // N+NPARAM+1 ~ N+NPARAM+numDiscontinuites : discontinuity values + realtype *values; + // 0 ~ N-1 : equations + VCell::Expression **initialConditionExpressions; + SymbolTable *initialConditionSymbolTable; + OdeResultSet *odeResultSet; // mainly for parameter optimization use, but it also stores column names + + void *solver; // the memory for solver + std::string recoverableErrMsg; + + int NUM_EQUATIONS; + int NUM_PARAMETERS; + realtype STARTING_TIME; + realtype ENDING_TIME; + realtype RelativeTolerance; + realtype AbsoluteTolerance; + long keepEvery; + double maxTimeStep; + std::vector outputTimes; + double *tempRowData; // data for current time to be written to output file and to be added to odeResultSet + + int numDiscontinuities; + OdeDiscontinuity **odeDiscontinuities; + SymbolTable *discontinuitySymbolTable; + double *discontinuityValues; + int *rootsFound; + + std::string *paramNames; + std::string *variableNames; // variables + std::string *allSymbols; + int numAllSymbols; + SymbolTable *defaultSymbolTable; + N_Vector y; + + static VCell::Expression *readExpression(std::istream &inputstream); + + void writeData(double currTime, FILE *outputFile); + virtual void updateTempRowData(double currTime); + void writeFileData(FILE *outputFile); + void writeFileHeader(FILE *outputFile); + void printProgress(double currTime, double &lastPercentile, clock_t &lastTime, double increment, FILE *outputFile) const; + void readDiscontinuities(std::istream &inputstream); + virtual void readEquations(std::istream &inputstream) = 0; + virtual void readEquations(VCellSolverInputBreakdown& inputBreakdown) = 0; + virtual void initialize(); + void initDiscontinuities(); + bool updateDiscontinuities(realtype t, bool bOnRootReturn); + void checkDiscontinuityConsistency() const; + void solveInitialDiscontinuities(double t); + virtual bool fixInitialDiscontinuities(double t) =0; + void printVariableValues(realtype t); + void printDiscontinuityValues(); + virtual void updateTandVariableValues(realtype t, N_Vector y) =0; + int RootFn(realtype t, N_Vector y, realtype *gout); + virtual std::string getSolverName() =0; + virtual VCellSolverTypes getSolverType() =0; + bool executeEvents(realtype realTimeVar); + double getNextEventTime(); + + private: + Event **events; + int numEvents; + std::list eventExeList; + + // void readEvents(std::istream &inputstream); + void testEventTriggers(realtype Time); +}; + struct EventAssignment { int varIndex; - VCell::Expression* assignmentExpression; + VCell::Expression *assignmentExpression; - ~EventAssignment() { - delete assignmentExpression; - } - void bind(SymbolTable* symbolTable) const { - assignmentExpression->bindExpression(symbolTable); + ~EventAssignment() { delete assignmentExpression; } + void bind(SymbolTable *symbolTable) const { assignmentExpression->bindExpression(symbolTable); } +}; + +struct EventExecution { + realtype exeTime; + Event *event0; + double *targetValues; + + EventExecution(Event *e) { + this->exeTime = RCONST(0.0); + this->event0 = e; + this->targetValues = nullptr; } + + ~EventExecution() { delete this->targetValues; } }; struct Event { std::string name; - VCell::Expression* triggerExpression; + VCell::Expression *triggerExpression; bool bUseValuesAtTriggerTime; - VCell::Expression* delayDurationExpression; - std::vector* eventAssignmentsVec; - bool triggerValue; + VCell::Expression *delayDurationExpression; + std::vector *eventAssignmentsVec; + bool triggerValue; Event() { this->bUseValuesAtTriggerTime = false; this->triggerExpression = nullptr; this->triggerValue = false; this->delayDurationExpression = nullptr; - this->eventAssignmentsVec = new std::vector; + this->eventAssignmentsVec = new std::vector; } + ~Event() { delete this->triggerExpression; delete this->delayDurationExpression; - for (const EventAssignment* elem : *this->eventAssignmentsVec) delete elem; + for (const EventAssignment *elem: *this->eventAssignmentsVec) delete elem; this->eventAssignmentsVec->clear(); delete this->eventAssignmentsVec; } - [[nodiscard]] bool hasDelay() const { - return this->delayDurationExpression != nullptr; - } + [[nodiscard]] bool hasDelay() const { return this->delayDurationExpression != nullptr; } - void bind(SymbolTable* symbolTable) const { + void bind(SymbolTable *symbolTable) const { this->triggerExpression->bindExpression(symbolTable); - if (this->delayDurationExpression != nullptr) { - this->delayDurationExpression->bindExpression(symbolTable); - } - for (const EventAssignment* elem : *this->eventAssignmentsVec) { - elem->bind(symbolTable); - } - } -}; - -struct EventExecution { - realtype exeTime; - Event* event0; - double* targetValues; - - EventExecution(Event* e) { - this->exeTime = RCONST(0.0); - this->event0 = e; - this->targetValues = nullptr; - } - ~EventExecution() { - delete this->targetValues; + if (this->delayDurationExpression != nullptr) { this->delayDurationExpression->bindExpression(symbolTable); } + for (const EventAssignment *elem: *this->eventAssignmentsVec) { elem->bind(symbolTable); } } }; struct OdeDiscontinuity { std::string discontinuitySymbol; - VCell::Expression* discontinuityExpression; - VCell::Expression* rootFindingExpression; + VCell::Expression *discontinuityExpression; + VCell::Expression *rootFindingExpression; ~OdeDiscontinuity() { delete this->discontinuityExpression; @@ -97,96 +181,4 @@ struct OdeDiscontinuity { } }; -class VCellSundialsSolver { -public: - VCellSundialsSolver(); - virtual ~VCellSundialsSolver(); - - void readInput(std::istream& inputstream); - virtual void solve(double* paramValues=nullptr, bool bPrintProgress=false, FILE* outputFile=nullptr, void (*checkStopRequested)(double, long)=nullptr) = 0; - OdeResultSet* getResultSet() { return this->odeResultSet; } - [[nodiscard]] int getNumEquations() const { return this->NEQ; } - VCell::Expression** getInitialConditionExpressions() { return this->initialConditionExpressions; } - void setStartingTime(realtype newStartingTime) { this->STARTING_TIME = newStartingTime; } - void setEndingTime(realtype newEndingTime) { this->ENDING_TIME = newEndingTime; } - //void setOutputTimes(int count, double* newOutputTimes); - SymbolTable* getSymbolTable() { return this->defaultSymbolTable;} - - static void checkStopRequested(double, long); - -protected: - // 0 : t - // 1 ~ N : variable values - // N+1 ~ N+NPARAM : parameter values - // N+NPARAM+1 ~ N+NPARAM+numDiscontinuites : discontinuity values - realtype* values; - // 0 ~ N-1 : equations - VCell::Expression** initialConditionExpressions; - SymbolTable* initialConditionSymbolTable; - OdeResultSet* odeResultSet; // mainly for parameter optimization use but it also stores column names - - void* solver; // the memory for solver - std::string recoverableErrMsg; - - int NEQ; - int NPARAM; - realtype STARTING_TIME; - realtype ENDING_TIME; - realtype RelativeTolerance; - realtype AbsoluteTolerance; - long keepEvery; - double maxTimeStep; - std::vector outputTimes; - double* tempRowData; // data for current time to be written to output file and to be added to odeResultSet - - int numDiscontinuities; - OdeDiscontinuity** odeDiscontinuities; - SymbolTable* discontinuitySymbolTable; - double* discontinuityValues; - int* rootsFound; - - std::string* paramNames; - std::string* variableNames; // variables - std::string* allSymbols; - int numAllSymbols; - SymbolTable* defaultSymbolTable; - - N_Vector y; - void writeData(double currTime, FILE* outputFile); - virtual void updateTempRowData(double currTime); - void writeFileData(FILE* outputFile); - void writeFileHeader(FILE* outputFile); - void printProgress(double currTime, double& lastPercentile, clock_t& lastTime, double increment, FILE* outputFile) const; - - void readDiscontinuities(std::istream& inputstream); - virtual void readEquations(std::istream& inputstream) = 0; - virtual void initialize(); - - void initDiscontinuities(); - bool updateDiscontinuities(realtype t, bool bOnRootReturn); - void checkDiscontinuityConsistency() const; - - void solveInitialDiscontinuities(double t); - virtual bool fixInitialDiscontinuities(double t)=0; - - void printVariableValues(realtype t); - void printDiscontinuityValues(); - virtual void updateTandVariableValues(realtype t, N_Vector y)=0; - - int RootFn(realtype t, N_Vector y, realtype *gout); - virtual std::string getSolverName()=0; - - static VCell::Expression* readExpression(std::istream& inputstream); - bool executeEvents(realtype realTimeVar); - double getNextEventTime(); - -private: - Event** events; - int numEvents; - - void readEvents(std::istream& inputstream); - void testEventTriggers(realtype Time); - std::list eventExeList; -}; - #endif From 096ba48d7d29d430c587a6913946a22319e4ef7e Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Mon, 30 Jun 2025 10:28:56 -0400 Subject: [PATCH 05/26] more sprintf removal --- ExpressionParser/ASTIdNode.cpp | 7 ++-- ExpressionParser/ASTPowerNode.cpp | 40 +++++++++---------- .../ExpressionParserTokenManager.cpp | 13 +++--- ExpressionParser/ParseException.cpp | 5 ++- 4 files changed, 32 insertions(+), 33 deletions(-) diff --git a/ExpressionParser/ASTIdNode.cpp b/ExpressionParser/ASTIdNode.cpp index 5dd47fd44..9bbb6a4d8 100644 --- a/ExpressionParser/ASTIdNode.cpp +++ b/ExpressionParser/ASTIdNode.cpp @@ -1,6 +1,9 @@ #include #include "ASTIdNode.h" + +#include + #include "ExpressionException.h" #include "ExpressionBindingException.h" #include "RuntimeException.h" @@ -46,9 +49,7 @@ std::string ASTIdNode::infixString(int lang, NameScope* nameScope) return cbit.util.TokenMangler.getEscapedTokenJSCL(idName); } else { */ - char chrs[20]; - sprintf(chrs, "%d\0", lang); - throw RuntimeException(std::string("Lanaguage '") + chrs + " not supported"); + throw RuntimeException(std::format("Lanaguage '{}' not supported", lang)); } } diff --git a/ExpressionParser/ASTPowerNode.cpp b/ExpressionParser/ASTPowerNode.cpp index 45811536d..0b28a2a73 100644 --- a/ExpressionParser/ASTPowerNode.cpp +++ b/ExpressionParser/ASTPowerNode.cpp @@ -1,7 +1,10 @@ #include -#include +#include #include "ASTPowerNode.h" + +#include + #include "RuntimeException.h" #include "DivideByZeroException.h" #include "FunctionDomainException.h" @@ -21,9 +24,8 @@ ASTPowerNode::~ASTPowerNode() { std::string ASTPowerNode::infixString(int lang, NameScope* nameScope) { if (jjtGetNumChildren() != 2) { - char ch[20]; - sprintf(ch, "%d\0", jjtGetNumChildren()); - throw RuntimeException("There are" + std::string(ch) + " arguments for the power operator, expecting 2"); + std::string errMsg{std::format("There are {} arguments for the power operator, expecting 2", jjtGetNumChildren())}; + throw RuntimeException(errMsg); } std::string buffer; @@ -52,9 +54,8 @@ void ASTPowerNode::getStackElements(std::vector& elements) { double ASTPowerNode::evaluate(int evalType, double* values) { if (jjtGetNumChildren() != 2) { - char chrs[1000]; - sprintf(chrs, "ASTPowerNode: wrong number of arguments for '^' (%d), expected 2\0", jjtGetNumChildren()); - throw ExpressionException(chrs); + std::string errMsg{std::format("ASTPowerNode: wrong number of arguments for '^' ({}), expected 2", jjtGetNumChildren())}; + throw ExpressionException(errMsg); } // // see if there are any constant 0.0's, if there are simplify to 0.0 @@ -84,25 +85,22 @@ double ASTPowerNode::evaluate(int evalType, double* values) { if (exponentException == NULL && baseException == NULL) { if (baseValue == 0.0 && exponentValue < 0.0) { std::string childString = infixString(LANGUAGE_DEFAULT,0); - char problem[1000]; - sprintf(problem, "u^v and u=0 and v=%lf<0", exponentValue); + std::string problem{std::format("u^v and u=0 and v={}<0", exponentValue)}; std::string errorMsg = getFunctionDomainError(problem, values, "u", baseChild, "v", exponentChild); throw DivideByZeroException(errorMsg); - } else if (baseValue < 0.0 && exponentValue != MathUtil::round(exponentValue)) { - char problem[1000]; - sprintf(problem, "u^v and u=%lf<0 and v=%lf not an integer: undefined", baseValue, exponentValue); + } + if (baseValue < 0.0 && exponentValue != MathUtil::round(exponentValue)) { + std::string problem{std::format("u^v and u={}<0 and v={} not an integer: undefined", baseValue, exponentValue)}; + std::string errorMsg = getFunctionDomainError(problem, values, "u", baseChild, "v", exponentChild); + throw FunctionDomainException(errorMsg); + } + double result = pow(baseValue, exponentValue); + if (MathUtil::double_infinity == -result || MathUtil::double_infinity == result || result != result) { + std::string problem{std::format("u^v evaluated to {}, u={}, v={}", result, baseValue, exponentValue)}; std::string errorMsg = getFunctionDomainError(problem, values, "u", baseChild, "v", exponentChild); throw FunctionDomainException(errorMsg); - } else { - double result = pow(baseValue, exponentValue); - if (MathUtil::double_infinity == -result || MathUtil::double_infinity == result || result != result) { - char problem[1000]; - sprintf(problem, "u^v evaluated to %lf, u=%lf, v=%lf", result, baseValue); - std::string errorMsg = getFunctionDomainError(problem, values, "u", baseChild, "v", exponentChild); - throw FunctionDomainException(errorMsg); - } - return result; } + return result; } else if (exponentException == 0 && exponentValue == 0.0) { return 1.0; } else if (baseException == 0 && baseValue == 1.0) { diff --git a/ExpressionParser/ExpressionParserTokenManager.cpp b/ExpressionParser/ExpressionParserTokenManager.cpp index 1484ac744..7873eaa5e 100644 --- a/ExpressionParser/ExpressionParserTokenManager.cpp +++ b/ExpressionParser/ExpressionParserTokenManager.cpp @@ -525,9 +525,7 @@ void ExpressionParserTokenManager::ReInit(SimpleCharStream* stream, int lexState void ExpressionParserTokenManager::SwitchTo(int lexState) { if (lexState >= 1 || lexState < 0) { - char ex[20]; - sprintf(ex, "%d\0", lexState); - throw RuntimeException("Error: Ignoring invalid lexical state: " + std::string(ex) + ".State unchanged."); + throw RuntimeException("Error: Ignoring invalid lexical state: " + std::to_string(lexState) + ".State unchanged."); } else curLexState = lexState; @@ -609,16 +607,17 @@ Token* ExpressionParserTokenManager::getNextToken(void) input_stream->backup(1); error_after = curPos <= 1 ? "" : input_stream->GetImage(); } - char chrs[1000]; + + std::string errMsg; if (EOFSeen) - sprintf(chrs, "Lexical error at line %d, column %d. Encountered: \0", error_line, error_column); + errMsg = std::format("Lexical error at line {}, column {}. Encountered: ", error_line, error_column); else { std::string a = Exception::add_escapes(std::string(&curChar, 1)); std::string b = Exception::add_escapes(error_after); - sprintf(chrs, "Lexical error at line %d, column %d. Encountered: \"%s\" (%d) after : \"%s\"\0", error_line, error_column, a.c_str(), curChar, b.c_str()); + errMsg = std::format("Lexical error at line {}, column {}. Encountered: \"{}\" ({}) after : \"{}\"", error_line, error_column, a.c_str(), curChar, b.c_str()); } - throw RuntimeException(chrs); + throw RuntimeException(errMsg); } EOFLoop : return 0; diff --git a/ExpressionParser/ParseException.cpp b/ExpressionParser/ParseException.cpp index 1c1a77def..9893d989d 100644 --- a/ExpressionParser/ParseException.cpp +++ b/ExpressionParser/ParseException.cpp @@ -2,6 +2,8 @@ #include "ParseException.h" +#include + std::string ParseException::eol = string("\n"); ParseException::ParseException() : Exception("ParseException", "") @@ -70,8 +72,7 @@ std::string ParseException::getExactMessage(void) retval += add_escapes(tok->image); tok = tok->next; } - char chrs[128]; - sprintf(chrs, "\" at line %d, column %d\0", currentToken->next->beginLine, currentToken->next->beginColumn); + std::string chrs{std::format("\" at line {}, column {}", currentToken->next->beginLine, currentToken->next->beginColumn)}; retval += chrs; retval += "." + eol; if (numETS == 1) { From b90b008386e77a9ba7982d5b527a0f3c7e15b55a Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Thu, 13 Nov 2025 11:10:46 -0500 Subject: [PATCH 06/26] Migrated Tests to C++, fixed message param parsing --- .github/workflows/cd.yml | 19 +- .gitignore | 2 +- CMakeLists.txt | 24 +- Dockerfile | 10 +- IDAWin/CMakeLists.txt | 18 +- IDAWin/SundialsSolverInterface.cpp | 67 +++++ IDAWin/SundialsSolverInterface.h | 11 + IDAWin/SundialsSolverStandalone.cpp | 51 +--- IDAWin/VCellSolverFactory.cpp | 92 ++----- IDAWin/VCellSundialsSolver.h | 6 +- IDAWin/hello_test.cpp | 9 - IDAWin/tests/smoke/smoke.py | 72 ------ tests/CMakeLists.txt | 43 ++++ tests/unit/hello_test.cpp | 9 + .../resources}/SimID_1489333437_0_.cvodeInput | 0 .../resources}/SimID_1489333437_0_.functions | 0 .../SimID_1489333437_0_.ida.expected | 0 .../unit/resources}/SimID_1489333437_0_.log | 0 .../SimID_1489333437_0__0.simtask.xml | 0 .../resources/SimID_1607235431_0_.cvodeInput | 231 ++++++++++++++++++ .../resources/SimID_1607235431_0_.functions | 23 ++ tests/unit/resources/SimID_1607235431_0_.log | 4 + .../SimID_1607235431_0__0.simtask.xml | 162 ++++++++++++ .../resources/SimID_256118677_0_.cvodeInput | 24 ++ .../resources/SimID_256118677_0_.functions | 6 + .../resources/SimID_256118677_0_.ida.expected | 159 ++++++++++++ tests/unit/resources/SimID_256118677_0_.log | 4 + .../resources}/simpleModel_Network_orig.vcml | 0 tests/unit/smoke_test.cpp | 112 +++++++++ 29 files changed, 908 insertions(+), 250 deletions(-) create mode 100644 IDAWin/SundialsSolverInterface.cpp create mode 100644 IDAWin/SundialsSolverInterface.h delete mode 100644 IDAWin/hello_test.cpp delete mode 100755 IDAWin/tests/smoke/smoke.py create mode 100644 tests/CMakeLists.txt create mode 100644 tests/unit/hello_test.cpp rename {IDAWin/tests/smoke => tests/unit/resources}/SimID_1489333437_0_.cvodeInput (100%) rename {IDAWin/tests/smoke => tests/unit/resources}/SimID_1489333437_0_.functions (100%) rename {IDAWin/tests/smoke => tests/unit/resources}/SimID_1489333437_0_.ida.expected (100%) rename {IDAWin/tests/smoke => tests/unit/resources}/SimID_1489333437_0_.log (100%) rename {IDAWin/tests/smoke => tests/unit/resources}/SimID_1489333437_0__0.simtask.xml (100%) create mode 100644 tests/unit/resources/SimID_1607235431_0_.cvodeInput create mode 100644 tests/unit/resources/SimID_1607235431_0_.functions create mode 100644 tests/unit/resources/SimID_1607235431_0_.log create mode 100644 tests/unit/resources/SimID_1607235431_0__0.simtask.xml create mode 100644 tests/unit/resources/SimID_256118677_0_.cvodeInput create mode 100644 tests/unit/resources/SimID_256118677_0_.functions create mode 100644 tests/unit/resources/SimID_256118677_0_.ida.expected create mode 100644 tests/unit/resources/SimID_256118677_0_.log rename {IDAWin/tests/smoke => tests/unit/resources}/simpleModel_Network_orig.vcml (100%) create mode 100644 tests/unit/smoke_test.cpp diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 3428fe41e..855bcab39 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -9,8 +9,8 @@ jobs: strategy: fail-fast: false matrix: - platform: [windows-latest, ubuntu-latest, ubuntu-24.04-arm, macos-13, macos-latest] - #platform: [windows-latest, windows-11-arm, ubuntu-latest, ubuntu-24.04-arm, macos-13, macos-latest] + platform: [windows-latest, ubuntu-latest, ubuntu-24.04-arm, macos-15-intel, macos-latest] + #platform: [windows-latest, windows-11-arm, ubuntu-latest, ubuntu-24.04-arm, macos-15-intel, macos-latest] runs-on: ${{ matrix.platform }} @@ -51,7 +51,7 @@ jobs: cp conan-profiles/CI-CD/Windows-ARM64_profile.txt $CONAN_HOME/profiles/default - name: Install MacOS dependencies - if: matrix.platform == 'macos-13' + if: matrix.platform == 'macos-15-intel' shell: bash run: | brew install conan @@ -77,7 +77,7 @@ jobs: sudo apt upgrade -y sudo apt install -y wget wget --version - wget https://github.com/conan-io/conan/releases/download/2.17.0/conan-2.17.0-amd64.deb + wget https://github.com/conan-io/conan/releases/download/2.22.2/conan-2.22.2-amd64.deb sudo apt install -y ./conan-*.deb conan --version mkdir -p ~/.conan2/profiles/ @@ -101,7 +101,7 @@ jobs: conan install . --output-folder build --build=missing -o include_messaging=False - name: Install Dependencies through Conan on MacOS - if: matrix.platform == 'macos-13' || matrix.platform == 'macos-latest' + if: matrix.platform == 'macos-latest' || matrix.platform == 'macos-15-intel' shell: bash run: | conan install . --output-folder build --build=missing @@ -140,10 +140,9 @@ jobs: cmake --build . --config Release - name: Build Unix - if: matrix.platform == 'ubuntu-latest' || matrix.platform == 'ubuntu-24.04-arm' || matrix.platform == 'macos-13' || matrix.platform == 'macos-latest' + if: matrix.platform == 'ubuntu-latest' || matrix.platform == 'ubuntu-24.04-arm' || matrix.platform == 'macos-15-intel' || matrix.platform == 'macos-latest' run: | echo "working dir is $PWD" - cd build source conanbuild.sh cmake -B . -S .. -G "Ninja" -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" -DCMAKE_BUILD_TYPE=Release @@ -157,7 +156,7 @@ jobs: ./bin/SundialsSolverStandalone_x64 || true - name: Test Unix - if: matrix.platform == 'ubuntu-latest' || matrix.platform == 'ubuntu-24.04-arm' || matrix.platform == 'macos-13' || matrix.platform == 'macos-latest' + if: matrix.platform == 'ubuntu-latest' || matrix.platform == 'ubuntu-24.04-arm' || matrix.platform == 'macos-15-intel' || matrix.platform == 'macos-latest' run: | cd build @@ -168,7 +167,7 @@ jobs: - name: fix Macos shared object paths - if: matrix.platform == 'macos-13' || matrix.platform == 'macos-latest' + if: matrix.platform == 'macos-15-intel' || matrix.platform == 'macos-latest' shell: bash run: | mkdir build/upload @@ -213,7 +212,7 @@ jobs: cd ../.. - name: Upload Intel Macos binaries - if: matrix.platform == 'macos-13' + if: matrix.platform == 'macos-15-intel' uses: actions/upload-artifact@v4 with: name: macos_x86_64.tgz diff --git a/.gitignore b/.gitignore index f57332c81..6aa1b85b4 100644 --- a/.gitignore +++ b/.gitignore @@ -23,7 +23,7 @@ NFsim_v1.11/tests/smoke/SimID_273069657_0_.gdat NFsim_v1.11/tests/smoke/SimID_273069657_0_.species -IDAWin/tests/smoke/SimID_1489333437_0_.ida +*.ida CMakeUserPresets.json conan-build diff --git a/CMakeLists.txt b/CMakeLists.txt index 751592c4c..887bcec7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,6 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") endif () enable_language(CXX) enable_language(C) -#enable_language(Fortran) ############################################# # If this isn't explicitly set in cmake 3.x.x, we get warnings @@ -33,7 +32,6 @@ option(OPTION_EXTRA_CONFIG_INFO "Print useful cmake debug info while configuring # # Build 64bit binaries on Mac and target Macos 10.7 or later # -# set(CMAKE_Fortran_OSX_DEPLOYMENT_TARGET_FLAG "-mmacosx-version-min=10.7" CACHE PATH "") # set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7" CACHE PATH "") # # Choose 32bit or 64bit target arch on Linux @@ -51,13 +49,11 @@ if (LINUX) set (ARCH_64bit FALSE) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32") set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") -# set (CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -m32") endif() if (LINUX_64bit_BINARIES) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m64") set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m64") -# set (CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -m64") endif() endif() @@ -185,11 +181,11 @@ endif() ####################################### add_subdirectory(VCellMessaging) -message(STATUS "adding ExpressionParser") add_subdirectory(ExpressionParser) -message(STATUS "adding sundials") add_subdirectory(sundials) add_subdirectory(IDAWin) +enable_testing() +add_subdirectory(tests) set(DOWNLOAD_EXTRACT_TIMESTAMP true) cmake_policy(SET CMP0135 NEW) @@ -207,21 +203,7 @@ FetchContent_Declare( GIT_REPOSITORY https://github.com/p-ranav/argparse.git ) FetchContent_MakeAvailable(argparse) -############################################# -# GoogleTest -############################################## -FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG v1.17.0 -) -if (WINDOWS) - # For Windows: Prevent overriding the parent project's compiler/linker settings - set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) -endif() -FetchContent_MakeAvailable(googletest) -enable_testing() ############################################# # @@ -237,10 +219,8 @@ if (OPTION_EXTRA_CONFIG_INFO) include(CMakePrintHelpers) cmake_print_variables(OPTION_TARGET_MESSAGING OPTION_TARGET_DOCS) -# cmake_print_variables(CMAKE_CXX_FLAGS CMAKE_C_FLAGS CMAKE_Fortran_FLAGS) cmake_print_variables(CMAKE_CXX_FLAGS CMAKE_C_FLAGS) cmake_print_variables(CMAKE_SYSTEM_NAME WINDOWS WIN32 MINGW APPLE ARCH_64bit ARCH_32bit) -# cmake_print_variables(CMAKE_CPP_COMPILER CMAKE_C_COMPILER CMAKE_CXX_COMPILER CMAKE_Fortran_COMPILER) cmake_print_variables(CMAKE_CPP_COMPILER CMAKE_C_COMPILER CMAKE_CXX_COMPILER) endif () diff --git a/Dockerfile b/Dockerfile index f92d88759..f6476148b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,19 @@ -FROM debian:bookworm-slim AS build +FROM debian:trixie-slim AS build SHELL ["/bin/bash", "-c"] RUN apt-get -y update && apt-get install -y apt-utils && apt-get remove --purge gcc +<<<<<<< HEAD RUN apt-get install -y -qq -o=Dpkg::Use-Pty=0 clang mold ninja-build cmake libc++-dev libc++abi-dev libcurl4-openssl-dev \ git curl wget ca-certificates python3 python3-pip +======= +RUN apt-get install -y -qq -o=Dpkg::Use-Pty=0 mold ninja-build cmake clang-18 clang++-18 clang-tools-18 libc++-18-dev libc++abi-18-dev \ + libcurl4-openssl-dev git curl wget ca-certificates python3 python3-pip +>>>>>>> 88cc3915 (Fixed bad parsing when looking for messaging params) RUN rm $(which gcc) || true RUN rm $(which g++) || true RUN rm -rf /var/lib/apt/lists/* && rm /usr/bin/ld +#RUN ln -s $(which clang-18) /usr/bin/clang +#RUN ln -s $(which clang++-18) /usr/bin/clang++ RUN ln -s $(which mold) /usr/bin/ld COPY . /vcellroot @@ -14,6 +21,7 @@ COPY . /vcellroot RUN mkdir -p /vcellroot/build/bin WORKDIR /vcellroot/build +RUN clang++ --version RUN /usr/bin/cmake .. -G Ninja -DOPTION_TARGET_MESSAGING=ON -DOPTION_TARGET_DOCS=OFF RUN ninja --verbose RUN ctest -VV diff --git a/IDAWin/CMakeLists.txt b/IDAWin/CMakeLists.txt index d9480bc26..b6d66cc54 100644 --- a/IDAWin/CMakeLists.txt +++ b/IDAWin/CMakeLists.txt @@ -1,6 +1,7 @@ project(IDAWin) -set (SRC_FILES +set (SRC_FILES + SundialsSolverInterface.cpp OdeResultSet.cpp StoppedByUserException.cpp VCellCVodeSolver.cpp @@ -20,6 +21,7 @@ set (HEADER_FILES VCellCVodeSolver.h VCellIDASolver.h SteadyStateDriver.h + SundialsSolverInterface.h ) set (EXE_SRC_FILES @@ -33,19 +35,9 @@ if (ARCH_64bit) set(EXE_FILE ${EXE_FILE}_x64) endif() +target_include_directories(IDAWin PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) add_executable(${EXE_FILE} ${EXE_SRC_FILES}) target_link_libraries(${EXE_FILE} IDAWin) install(TARGETS ${EXE_FILE} RUNTIME DESTINATION ${OPTION_EXE_DIRECTORY}) -install(TARGETS IDAWin ARCHIVE DESTINATION bin) - -# # # # # # # # # # # # # # # # # -# smoke test as a python script, for bash test example, see NFsim/tests/smoke -# # # # # # # # # # # # # # # # # -enable_testing() -set(test_dir ${CMAKE_CURRENT_SOURCE_DIR}/tests/smoke) -include(GoogleTest) -add_test(NAME ${EXE_FILE}_smoke COMMAND ${python_cmd} ${test_dir}/smoke.py ${test_sundials_exe} WORKING_DIRECTORY ${test_dir}) -add_executable(hello_test hello_test.cpp) -target_link_libraries(hello_test GTest::gtest_main) -gtest_discover_tests(hello_test) \ No newline at end of file +install(TARGETS IDAWin ARCHIVE DESTINATION bin) \ No newline at end of file diff --git a/IDAWin/SundialsSolverInterface.cpp b/IDAWin/SundialsSolverInterface.cpp new file mode 100644 index 000000000..ae3b8fdeb --- /dev/null +++ b/IDAWin/SundialsSolverInterface.cpp @@ -0,0 +1,67 @@ +// +// Created by Logan Drescher on 11/12/25. +// +// Messaging +#include "SundialsSolverInterface.h" +#include "VCellSundialsSolver.h" +#include "VCellSolverFactory.h" + +// included referenced in commented out code: +#include +#include +#include + +#define CVODE_SOLVER "CVODE" +#define IDA_SOLVER "IDA" + +void activateSolver(std::ifstream& inputFileStream, FILE* outputFile, int taskID) { + // std::string solver; + // + // while (!inputFileStream.eof()) { // Note break statement if "SOLVER" encountered + // std::string nextToken; + // inputFileStream >> nextToken; + // if (nextToken.empty()) continue; + // if (nextToken[0] == '#') getline(inputFileStream, nextToken); + // else if (nextToken == "JMS_PARAM_BEGIN") { + // loadJMSInfo(inputFileStream, taskID); + // #ifdef USE_MESSAGING + // SimulationMessaging::getInstVar()->start(); // start the thread + // #endif + // } else if (nextToken == "SOLVER") { + // inputFileStream >> solver; + // break; + // } + // } + // #ifdef USE_MESSAGING + // // should only happen during testing for solver compiled with messaging but run locally. + // if (SimulationMessaging::getInstVar() == nullptr) { SimulationMessaging::create(); } + // #endif + // + // if (solver.empty()) { throw "Solver not defined "; } + // VCellSundialsSolver *vss = nullptr; + // + // if (solver == IDA_SOLVER) { + // vss = new VCellIDASolver(); + // } else if (solver == CVODE_SOLVER) { + // vss = new VCellCVodeSolver(); + // } else { + // std::stringstream ss; + // ss << "Solver " << solver << " not defined!"; + // throw ss.str(); + // } + // + // vss->solve(nullptr, true, outputFile, VCellSundialsSolver::checkStopRequested); + // delete vss; + + // Check if we have messaging and a taskID of -1; if so; fail now. + + #ifdef USE_MESSAGING + if (taskID < 0) { + throw std::runtime_error("task id of value: " + std::to_string(taskID) + " not acceptable when this library is built with messaging"); + } + #endif + + VCellSolver* targetSolver = VCellSolverFactory::produceVCellSolver(inputFileStream, taskID); + targetSolver->solve(nullptr, true, outputFile, VCellSundialsSolver::checkStopRequested); +} + diff --git a/IDAWin/SundialsSolverInterface.h b/IDAWin/SundialsSolverInterface.h new file mode 100644 index 000000000..a6cb969ff --- /dev/null +++ b/IDAWin/SundialsSolverInterface.h @@ -0,0 +1,11 @@ +// +// Created by Logan Drescher on 11/12/25. +// +#ifndef VCELL_ODE_NUMERICS_SUNDIALSSOLVERINTERFACE_H +#define VCELL_ODE_NUMERICS_SUNDIALSSOLVERINTERFACE_H + +#include + +void activateSolver(std::ifstream& inputFileStream, FILE* outputFile, int taskID); + +#endif //VCELL_ODE_NUMERICS_SUNDIALSSOLVERINTERFACE_H \ No newline at end of file diff --git a/IDAWin/SundialsSolverStandalone.cpp b/IDAWin/SundialsSolverStandalone.cpp index 07f4ecf8f..fb6da561b 100644 --- a/IDAWin/SundialsSolverStandalone.cpp +++ b/IDAWin/SundialsSolverStandalone.cpp @@ -8,19 +8,16 @@ #include #include // Local Includes -#include "VCellCVodeSolver.h" -#include "VCellIDASolver.h" +#include "SundialsSolverInterface.h" + #include "StoppedByUserException.h" #include #include -#include "VCellSolverFactory.h" #define CVODE_SOLVER "CVODE" #define IDA_SOLVER "IDA" int parseAndRunWithArgParse(int argc, char *argv[]); -void activateSolver(std::ifstream& inputFileStream, FILE* outputFile, int taskID); -void loadJMSInfo(std::istream &ifsInput, int taskID); void errExit(int returnCode, std::string &errorMsg); int main(int argc, char *argv[]) { @@ -87,50 +84,6 @@ int parseAndRunWithArgParse(int argc, char *argv[]) { return returnCode; } -void activateSolver(std::ifstream& inputFileStream, FILE* outputFile, int taskID) { - // std::string solver; - // - // while (!inputFileStream.eof()) { // Note break statement if "SOLVER" encountered - // std::string nextToken; - // inputFileStream >> nextToken; - // if (nextToken.empty()) continue; - // if (nextToken[0] == '#') getline(inputFileStream, nextToken); - // else if (nextToken == "JMS_PARAM_BEGIN") { - // loadJMSInfo(inputFileStream, taskID); - // #ifdef USE_MESSAGING - // SimulationMessaging::getInstVar()->start(); // start the thread - // #endif - // } else if (nextToken == "SOLVER") { - // inputFileStream >> solver; - // break; - // } - // } - // #ifdef USE_MESSAGING - // // should only happen during testing for solver compiled with messaging but run locally. - // if (SimulationMessaging::getInstVar() == nullptr) { SimulationMessaging::create(); } - // #endif - // - // if (solver.empty()) { throw "Solver not defined "; } - // VCellSundialsSolver *vss = nullptr; - // - // if (solver == IDA_SOLVER) { - // vss = new VCellIDASolver(); - // } else if (solver == CVODE_SOLVER) { - // vss = new VCellCVodeSolver(); - // } else { - // std::stringstream ss; - // ss << "Solver " << solver << " not defined!"; - // throw ss.str(); - // } - // - // vss->solve(nullptr, true, outputFile, VCellSundialsSolver::checkStopRequested); - // delete vss; - - - VCellSolver* targetSolver = VCellSolverFactory::produceVCellSolver(inputFileStream, taskID); - targetSolver->solve(nullptr, true, outputFile, VCellSundialsSolver::checkStopRequested); -} - void errExit(int returnCode, std::string &errorMsg) { #ifdef USE_MESSAGING if (returnCode != 0) { diff --git a/IDAWin/VCellSolverFactory.cpp b/IDAWin/VCellSolverFactory.cpp index 5a905d73e..558454e4b 100644 --- a/IDAWin/VCellSolverFactory.cpp +++ b/IDAWin/VCellSolverFactory.cpp @@ -14,7 +14,7 @@ #include "VCellCVodeSolver.h" #include "VCellIDASolver.h" #ifdef USE_MESSAGING -static void loadJMSInfo(std::istream &ifsInput, int taskID); +#include #endif // Forward Declarations static void readDiscontinuities (std::istream &inputStream, VCellSolverInputBreakdown& inputBreakdown); @@ -54,12 +54,7 @@ VCellSolverInputBreakdown VCellSolverFactory::parseInputFile(std::ifstream& inpu inputFileStream >> nextToken; if (nextToken.empty()) continue; if (nextToken[0] == '#') std::getline(inputFileStream, nextToken); - else if (nextToken == "JMS_PARAM_BEGIN") { - #ifdef USE_MESSAGING - loadJMSInfo(inputFileStream, taskID); - SimulationMessaging::getInstVar()->start(); // start the thread - #endif - } else if (nextToken == "SOLVER") { + else if (nextToken == "SOLVER") { std::string solverName; inputFileStream >> solverName; inputBreakdown.solverType = determineSolverType(solverName); @@ -112,10 +107,13 @@ VCellSolverInputBreakdown VCellSolverFactory::parseInputFile(std::ifstream& inpu } VCellSolverTypes VCellSolverFactory::determineSolverType(const std::string& solverName){ - if (solverName == "CVODE") return VCellSolverTypes::CVODE; - if (solverName == "IDA") return VCellSolverTypes::IDA; - if (solverName == "CSSS") return VCellSolverTypes::STEADY_STATE; // copasi-style steady-state - throw new std::runtime_error("Unknown solver type: `" + solverName + "`"); + if (solverName == "CVODE") + return VCellSolverTypes::CVODE; + if (solverName == "IDA") + return VCellSolverTypes::IDA; + if (solverName == "CSSS") + return VCellSolverTypes::STEADY_STATE; // copasi-style steady-state + throw std::runtime_error("Unknown solver type: `" + solverName + "`"); } void VCellSolverFactory::processEquations(std::ifstream& inputFileStream, VCellSolverInputBreakdown& inputBreakdown) { @@ -127,51 +125,6 @@ void VCellSolverFactory::processEquations(std::ifstream& inputFileStream, VCellS * Local Helper Functions * * * * * * * * * * * * * * * * * * * * * * * * * * * **/ -#ifdef USE_MESSAGING -static void loadJMSInfo(std::istream &ifsInput, int taskID) { - if (taskID < 0) { - SimulationMessaging::create(); - return; // No need to do any parsing - } - std::string broker; - std::string smqUserName; - std::string password; - std::string qName; - std::string topicName; - std::string vCellUsername; - int simKey, jobIndex; - - while (!ifsInput.eof()) { - std::string nextToken; - ifsInput >> nextToken; - if (nextToken.empty()) continue; - if (nextToken[0] == '#') { - // std::getline(ifsInput, nextToken); // Is this ignoring because of a comment? - ifsInput.ignore('\n'); - continue; - } - if (nextToken == "JMS_PARAM_END") { ifsInput.ignore(EOF); } else if ( - nextToken == "JMS_BROKER") { ifsInput >> broker; } else if ( - nextToken == "JMS_USER") { ifsInput >> smqUserName >> password; } else if ( - nextToken == "JMS_QUEUE") { ifsInput >> qName; } else if ( - nextToken == "JMS_TOPIC") { ifsInput >> topicName; } else if (nextToken == "VCELL_USER") { - ifsInput >> vCellUsername; - } else if (nextToken == "SIMULATION_KEY") { - ifsInput >> simKey; - continue; - } else if (nextToken == "JOB_INDEX") { - ifsInput >> jobIndex; - continue; - } - } - - SimulationMessaging::create(broker.c_str(), smqUserName.c_str(), - password.c_str(), qName.c_str(), topicName.c_str(), - vCellUsername.c_str(), simKey, jobIndex, taskID); - -} -#endif - static void readDiscontinuities(std::istream &inputStream, VCellSolverInputBreakdown& inputBreakdown) { VCellSolverInputBreakdown::DiscontinuityComponents discontinuityComponents; std::size_t numOfDiscontinuities; @@ -483,7 +436,10 @@ static void loadJMSInfo(std::istream &ifsInput, int taskID) { std::string vCellUsername; int simKey, jobIndex; - while (!ifsInput.eof()) { + while (true) { + if (ifsInput.eof()) + throw std::runtime_error("VCellSolverFactory::loadJMSInfo() reached end of file, but no `JMS_PARAM_END` reached!"); + std::string nextToken; ifsInput >> nextToken; if (nextToken.empty()) continue; @@ -492,19 +448,15 @@ static void loadJMSInfo(std::istream &ifsInput, int taskID) { ifsInput.ignore('\n'); continue; } - if (nextToken == "JMS_PARAM_END") { ifsInput.ignore(EOF); } else if ( - nextToken == "JMS_BROKER") { ifsInput >> broker; } else if ( - nextToken == "JMS_USER") { ifsInput >> smqUserName >> password; } else if ( - nextToken == "JMS_QUEUE") { ifsInput >> qName; } else if ( - nextToken == "JMS_TOPIC") { ifsInput >> topicName; } else if (nextToken == "VCELL_USER") { - ifsInput >> vCellUsername; - } else if (nextToken == "SIMULATION_KEY") { - ifsInput >> simKey; - continue; - } else if (nextToken == "JOB_INDEX") { - ifsInput >> jobIndex; - continue; - } + if (nextToken == "JMS_PARAM_END") break; // Non-error stop condition + + if (nextToken == "JMS_BROKER") ifsInput >> broker; + else if (nextToken == "JMS_USER") ifsInput >> smqUserName >> password; + else if (nextToken == "JMS_QUEUE") ifsInput >> qName; + else if (nextToken == "JMS_TOPIC") ifsInput >> topicName; + else if (nextToken == "VCELL_USER") ifsInput >> vCellUsername; + else if (nextToken == "SIMULATION_KEY") ifsInput >> simKey; + else if (nextToken == "JOB_INDEX") ifsInput >> jobIndex; } SimulationMessaging::create(broker.c_str(), smqUserName.c_str(), diff --git a/IDAWin/VCellSundialsSolver.h b/IDAWin/VCellSundialsSolver.h index 8b5d3b170..57a02d062 100644 --- a/IDAWin/VCellSundialsSolver.h +++ b/IDAWin/VCellSundialsSolver.h @@ -33,13 +33,13 @@ class VCellSundialsSolver : public VCellSolver { void configureFromInput(VCellSolverInputBreakdown& inputBreakdown) override; - OdeResultSet *getResultSet() const { return this->odeResultSet; } + [[nodiscard]] OdeResultSet *getResultSet() const { return this->odeResultSet; } [[nodiscard]] int getNumEquations() const { return this->NUM_EQUATIONS; } - VCell::Expression **getInitialConditionExpressions() const { return this->initialConditionExpressions; } + [[nodiscard]] VCell::Expression **getInitialConditionExpressions() const { return this->initialConditionExpressions; } void setStartingTime(const realtype newStartingTime) { this->STARTING_TIME = newStartingTime; } void setEndingTime(const realtype newEndingTime) { this->ENDING_TIME = newEndingTime; } //void setOutputTimes(int count, double* newOutputTimes); - SymbolTable *getSymbolTable() const { return this->defaultSymbolTable; } + [[nodiscard]] SymbolTable *getSymbolTable() const { return this->defaultSymbolTable; } static void checkStopRequested(double, long); diff --git a/IDAWin/hello_test.cpp b/IDAWin/hello_test.cpp deleted file mode 100644 index 5a57e138f..000000000 --- a/IDAWin/hello_test.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include - -// Demonstrate some basic assertions. -TEST(HelloTest, BasicAssertions) { - // Expect two strings not to be equal. - EXPECT_STRNE("hello", "world"); - // Expect equality. - EXPECT_EQ(7 * 6, 42); -} diff --git a/IDAWin/tests/smoke/smoke.py b/IDAWin/tests/smoke/smoke.py deleted file mode 100755 index 65f4d7fee..000000000 --- a/IDAWin/tests/smoke/smoke.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python3 - -import os -import posixpath -import subprocess -import sys -from pathlib import Path - - -# function to numerically compare the contents of two text files to determine approximate equality -# each line is a space delimited list of numbers, compare them to within 8 significant figures -def compare_files(file1: Path, file2: Path, tolerance: float): - with open(file1, 'r') as f1, open(file2, 'r') as f2: - for line1, line2 in zip(f1, f2): - if (line1 := line1.strip()) == (line2 := line2.strip()): - continue - array1 = [float(x) for x in line1.split()] - array2 = [float(x) for x in line2.split()] - for x, y in zip(array1, array2): - if x != y and abs(x - y) > tolerance * max(abs(x), abs(y)): - return False - return True - - -# get the directory of this script -test_dir = os.path.dirname(os.path.realpath(__file__)) -# in the path replace \ with /, D:\ with /d/ -test_dir = test_dir.replace("\\", "/") -# tell os.path.join to use / as the path separator -os.path.sep = "/" -exe = sys.argv[1] - -print(f"test_dir: {test_dir}") -print(f"exe: {exe}") - -input_file = posixpath.join(test_dir, "SimID_1489333437_0_.cvodeInput") -output_file = posixpath.join(test_dir, "SimID_1489333437_0_.ida") -expected_output_file = posixpath.join(test_dir, "SimID_1489333437_0_.ida.expected") - -if not posixpath.exists(exe): - print(f"SundialsSolverStandalone_x64 executable {exe} not found. Exiting...") - sys.exit(1) - -if not posixpath.exists(input_file): - print(f"Input file {input_file} not found. Exiting...") - sys.exit(1) - -if not posixpath.exists(expected_output_file): - print(f"Expected output file {expected_output_file} not found. Exiting...") - sys.exit(1) - -command = [exe, input_file, output_file] -print(" ".join(command)) - -try: - subprocess.check_call(command) -except subprocess.CalledProcessError: - print("SundialsSolverStandalone_x64 failed to run. Exiting...") - sys.exit(1) - -# verify that the output files exist -if not os.path.isfile(output_file): - print(f"Output file {output_file} not found. Exiting...") - sys.exit(1) - -# verify that the output files match the expected output files -if not compare_files(file1=Path(output_file), file2=Path(expected_output_file), tolerance=1e-8): - print(f"Output file {output_file} does not match expected output {expected_output_file}. Exiting...") - sys.exit(1) - -print("SundialsSolverStandalone_x64 solver completed and solution matched expected output. Exiting...") -sys.exit(0) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 000000000..686c973b3 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,43 @@ +############################################# +# GoogleTest +############################################## +include(FetchContent) +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG v1.17.0 +) +if (WINDOWS) + # For Windows: Prevent overriding the parent project's compiler/linker settings + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +endif() + +FetchContent_MakeAvailable(googletest) +include(GoogleTest) + +# Add test executables +add_executable(unit_tests + unit/smoke_test.cpp + unit/hello_test.cpp +) +target_link_libraries(unit_tests PRIVATE + IDAWin + GTest::gtest_main +) +gtest_discover_tests(unit_tests) + +target_compile_definitions(unit_tests PRIVATE + RESOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}/unit/resources" +) + + +#add_executable(integration_tests +# integration/test_integration.cpp +#) +#target_link_libraries(integration_tests PRIVATE +# IDAWin +# GTest::gtest_main +#) +#gtest_discover_tests(integration_tests) + + diff --git a/tests/unit/hello_test.cpp b/tests/unit/hello_test.cpp new file mode 100644 index 000000000..2d418cc0e --- /dev/null +++ b/tests/unit/hello_test.cpp @@ -0,0 +1,9 @@ +#include + +// Demonstrate some basic assertions. +TEST(HelloTest, BasicAssertions) { + // Expect two strings not to be equal. + EXPECT_STRNE("hello", "world"); + // Expect equality. + EXPECT_EQ(7 * 6, 42); +} \ No newline at end of file diff --git a/IDAWin/tests/smoke/SimID_1489333437_0_.cvodeInput b/tests/unit/resources/SimID_1489333437_0_.cvodeInput similarity index 100% rename from IDAWin/tests/smoke/SimID_1489333437_0_.cvodeInput rename to tests/unit/resources/SimID_1489333437_0_.cvodeInput diff --git a/IDAWin/tests/smoke/SimID_1489333437_0_.functions b/tests/unit/resources/SimID_1489333437_0_.functions similarity index 100% rename from IDAWin/tests/smoke/SimID_1489333437_0_.functions rename to tests/unit/resources/SimID_1489333437_0_.functions diff --git a/IDAWin/tests/smoke/SimID_1489333437_0_.ida.expected b/tests/unit/resources/SimID_1489333437_0_.ida.expected similarity index 100% rename from IDAWin/tests/smoke/SimID_1489333437_0_.ida.expected rename to tests/unit/resources/SimID_1489333437_0_.ida.expected diff --git a/IDAWin/tests/smoke/SimID_1489333437_0_.log b/tests/unit/resources/SimID_1489333437_0_.log similarity index 100% rename from IDAWin/tests/smoke/SimID_1489333437_0_.log rename to tests/unit/resources/SimID_1489333437_0_.log diff --git a/IDAWin/tests/smoke/SimID_1489333437_0__0.simtask.xml b/tests/unit/resources/SimID_1489333437_0__0.simtask.xml similarity index 100% rename from IDAWin/tests/smoke/SimID_1489333437_0__0.simtask.xml rename to tests/unit/resources/SimID_1489333437_0__0.simtask.xml diff --git a/tests/unit/resources/SimID_1607235431_0_.cvodeInput b/tests/unit/resources/SimID_1607235431_0_.cvodeInput new file mode 100644 index 000000000..fcd890744 --- /dev/null +++ b/tests/unit/resources/SimID_1607235431_0_.cvodeInput @@ -0,0 +1,231 @@ +SOLVER CVODE +STARTING_TIME 0.0 +ENDING_TIME 500.0 +RELATIVE_TOLERANCE 1.0E-9 +ABSOLUTE_TOLERANCE 1.0E-9 +MAX_TIME_STEP 1.0 +KEEP_EVERY 1 +DISCONTINUITIES 200 +__D_B_98 (t >= 250.0); (-250.0 + t); +__D_B_37 (t <= 187.75510204081633); (-187.75510204081633 + t); +__D_B_138 (t >= 41.83673469387755); (-41.83673469387755 + t); +__D_B_118 (t >= 22.448979591836736); (-22.448979591836736 + t); +__D_B_122 (t >= 26.3265306122449); (-26.3265306122449 + t); +__D_B_30 (t >= 180.6122448979592); (-180.6122448979592 + t); +__D_B_165 (t <= 68.01020408163264); (-68.01020408163264 + t); +__D_B_180 (t >= 82.55102040816327); (-82.55102040816327 + t); +__D_B_77 (t <= 228.57142857142856); (-228.57142857142856 + t); +__D_B_13 (t <= 163.26530612244898); (-163.26530612244898 + t); +__D_B_28 (t >= 178.57142857142858); (-178.57142857142858 + t); +__D_B_137 (t <= 40.867346938775505); (-40.867346938775505 + t); +__D_B_89 (t <= 240.81632653061223); (-240.81632653061223 + t); +__D_B_198 (t >= 100.0); (-100.0 + t); +__D_B_142 (t >= 45.714285714285715); (-45.714285714285715 + t); +__D_B_42 (t >= 192.85714285714286); (-192.85714285714286 + t); +__D_B_35 (t <= 185.71428571428572); (-185.71428571428572 + t); +__D_B_50 (t >= 201.0204081632653); (-201.0204081632653 + t); +__D_B_179 (t <= 81.58163265306122); (-81.58163265306122 + t); +__D_B_31 (t <= 181.6326530612245); (-181.6326530612245 + t); +__D_B_67 (t <= 218.3673469387755); (-218.3673469387755 + t); +__D_B_164 (t >= 67.0408163265306); (-67.0408163265306 + t); +__D_B_40 (t >= 190.81632653061223); (-190.81632653061223 + t); +__D_B_29 (t <= 179.59183673469389); (-179.59183673469389 + t); +__D_B_45 (t <= 195.91836734693877); (-195.91836734693877 + t); +__D_B_66 (t >= 217.3469387755102); (-217.3469387755102 + t); +__D_B_15 (t <= 165.30612244897958); (-165.30612244897958 + t); +__D_B_47 (t <= 197.9591836734694); (-197.9591836734694 + t); +__D_B_130 (t >= 34.08163265306122); (-34.08163265306122 + t); +__D_B_147 (t <= 50.56122448979591); (-50.56122448979591 + t); +__D_B_182 (t >= 84.48979591836735); (-84.48979591836735 + t); +__D_B_156 (t >= 59.285714285714285); (-59.285714285714285 + t); +__D_B_9 (t <= 159.18367346938774); (-159.18367346938774 + t); +__D_B_60 (t >= 211.22448979591837); (-211.22448979591837 + t); +__D_B_82 (t >= 233.67346938775512); (-233.67346938775512 + t); +__D_B_22 (t >= 172.44897959183675); (-172.44897959183675 + t); +__D_B_51 (t <= 202.0408163265306); (-202.0408163265306 + t); +__D_B_189 (t <= 91.27551020408163); (-91.27551020408163 + t); +__D_B_91 (t <= 242.85714285714286); (-242.85714285714286 + t); +__D_B_143 (t <= 46.68367346938775); (-46.68367346938775 + t); +__D_B_117 (t <= 21.479591836734684); (-21.479591836734684 + t); +__D_B_73 (t <= 224.48979591836735); (-224.48979591836735 + t); +__D_B_12 (t >= 162.24489795918367); (-162.24489795918367 + t); +__D_B_162 (t >= 65.10204081632654); (-65.10204081632654 + t); +__D_B_184 (t >= 86.42857142857143); (-86.42857142857143 + t); +__D_B_109 (t <= 13.724489795918359); (-13.724489795918359 + t); +__D_B_16 (t >= 166.3265306122449); (-166.3265306122449 + t); +__D_B_36 (t >= 186.73469387755102); (-186.73469387755102 + t); +__D_B_86 (t >= 237.75510204081633); (-237.75510204081633 + t); +__D_B_135 (t <= 38.92857142857142); (-38.92857142857142 + t); +__D_B_174 (t >= 76.73469387755102); (-76.73469387755102 + t); +__D_B_145 (t <= 48.62244897959183); (-48.62244897959183 + t); +__D_B_140 (t >= 43.775510204081634); (-43.775510204081634 + t); +__D_B_111 (t <= 15.663265306122442); (-15.663265306122442 + t); +__D_B_110 (t >= 14.693877551020408); (-14.693877551020408 + t); +__D_B_103 (t <= 7.908163265306115); (-7.908163265306115 + t); +__D_B_161 (t <= 64.13265306122449); (-64.13265306122449 + t); +__D_B_194 (t >= 96.12244897959184); (-96.12244897959184 + t); +__D_B_8 (t >= 158.16326530612244); (-158.16326530612244 + t); +__D_B_139 (t <= 42.806122448979586); (-42.806122448979586 + t); +__D_B_1 (t <= 151.0204081632653); (-151.0204081632653 + t); +__D_B_62 (t >= 213.26530612244898); (-213.26530612244898 + t); +__D_B_76 (t >= 227.55102040816325); (-227.55102040816325 + t); +__D_B_85 (t <= 236.73469387755102); (-236.73469387755102 + t); +__D_B_128 (t >= 32.14285714285714); (-32.14285714285714 + t); +__D_B_61 (t <= 212.24489795918367); (-212.24489795918367 + t); +__D_B_33 (t <= 183.6734693877551); (-183.6734693877551 + t); +__D_B_159 (t <= 62.1938775510204); (-62.1938775510204 + t); +__D_B_23 (t <= 173.46938775510205); (-173.46938775510205 + t); +__D_B_144 (t >= 47.6530612244898); (-47.6530612244898 + t); +__D_B_7 (t <= 157.14285714285714); (-157.14285714285714 + t); +__D_B_21 (t <= 171.42857142857142); (-171.42857142857142 + t); +__D_B_136 (t >= 39.89795918367347); (-39.89795918367347 + t); +__D_B_52 (t >= 203.0612244897959); (-203.0612244897959 + t); +__D_B_10 (t >= 160.20408163265307); (-160.20408163265307 + t); +__D_B_2 (t >= 152.0408163265306); (-152.0408163265306 + t); +__D_B_157 (t <= 60.25510204081632); (-60.25510204081632 + t); +__D_B_48 (t >= 198.9795918367347); (-198.9795918367347 + t); +__D_B_58 (t >= 209.18367346938777); (-209.18367346938777 + t); +__D_B_24 (t >= 174.48979591836735); (-174.48979591836735 + t); +__D_B_87 (t <= 238.77551020408163); (-238.77551020408163 + t); +__D_B_146 (t >= 49.59183673469388); (-49.59183673469388 + t); +__D_B_106 (t >= 10.816326530612244); (-10.816326530612244 + t); +__D_B_53 (t <= 204.0816326530612); (-204.0816326530612 + t); +__D_B_54 (t >= 205.10204081632654); (-205.10204081632654 + t); +__D_B_148 (t >= 51.53061224489796); (-51.53061224489796 + t); +__D_B_59 (t <= 210.20408163265307); (-210.20408163265307 + t); +__D_B_108 (t >= 12.755102040816325); (-12.755102040816325 + t); +__D_B_56 (t >= 207.14285714285714); (-207.14285714285714 + t); +__D_B_169 (t <= 71.88775510204081); (-71.88775510204081 + t); +__D_B_101 (t <= 5.969387755102034); (-5.969387755102034 + t); +__D_B_4 (t >= 154.08163265306123); (-154.08163265306123 + t); +__D_B_32 (t >= 182.6530612244898); (-182.6530612244898 + t); +__D_B_74 (t >= 225.51020408163265); (-225.51020408163265 + t); +__D_B_114 (t >= 18.57142857142857); (-18.57142857142857 + t); +__D_B_72 (t >= 223.46938775510205); (-223.46938775510205 + t); +__D_B_119 (t <= 23.41836734693877); (-23.41836734693877 + t); +__D_B_113 (t <= 17.60204081632652); (-17.60204081632652 + t); +__D_B_181 (t <= 83.5204081632653); (-83.5204081632653 + t); +__D_B_90 (t >= 241.83673469387756); (-241.83673469387756 + t); +__D_B_5 (t <= 155.10204081632654); (-155.10204081632654 + t); +__D_B_191 (t <= 93.21428571428571); (-93.21428571428571 + t); +__D_B_133 (t <= 36.989795918367335); (-36.989795918367335 + t); +__D_B_17 (t <= 167.3469387755102); (-167.3469387755102 + t); +__D_B_27 (t <= 177.55102040816325); (-177.55102040816325 + t); +__D_B_177 (t <= 79.64285714285714); (-79.64285714285714 + t); +__D_B_183 (t <= 85.45918367346938); (-85.45918367346938 + t); +__D_B_188 (t >= 90.3061224489796); (-90.3061224489796 + t); +__D_B_132 (t >= 36.0204081632653); (-36.0204081632653 + t); +__D_B_152 (t >= 55.40816326530612); (-55.40816326530612 + t); +__D_B_149 (t <= 52.49999999999999); (-52.49999999999999 + t); +__D_B_79 (t <= 230.6122448979592); (-230.6122448979592 + t); +__D_B_116 (t >= 20.51020408163265); (-20.51020408163265 + t); +__D_B_18 (t >= 168.3673469387755); (-168.3673469387755 + t); +__D_B_96 (t >= 247.9591836734694); (-247.9591836734694 + t); +__D_B_160 (t >= 63.16326530612245); (-63.16326530612245 + t); +__D_B_187 (t <= 89.33673469387755); (-89.33673469387755 + t); +__D_B_55 (t <= 206.12244897959184); (-206.12244897959184 + t); +__D_B_70 (t >= 221.42857142857144); (-221.42857142857144 + t); +__D_B_95 (t <= 246.9387755102041); (-246.9387755102041 + t); +__D_B_38 (t >= 188.77551020408163); (-188.77551020408163 + t); +__D_B_178 (t >= 80.61224489795919); (-80.61224489795919 + t); +__D_B_26 (t >= 176.53061224489795); (-176.53061224489795 + t); +__D_B_158 (t >= 61.224489795918366); (-61.224489795918366 + t); +__D_B_175 (t <= 77.70408163265306); (-77.70408163265306 + t); +__D_B_99 (t <= 251.0204081632653); (-251.0204081632653 + t); +__D_B_107 (t <= 11.785714285714278); (-11.785714285714278 + t); +__D_B_19 (t <= 169.3877551020408); (-169.3877551020408 + t); +__D_B_168 (t >= 70.91836734693878); (-70.91836734693878 + t); +__D_B_3 (t <= 153.0612244897959); (-153.0612244897959 + t); +__D_B_39 (t <= 189.79591836734693); (-189.79591836734693 + t); +__D_B_171 (t <= 73.8265306122449); (-73.8265306122449 + t); +__D_B_25 (t <= 175.51020408163265); (-175.51020408163265 + t); +__D_B_167 (t <= 69.94897959183673); (-69.94897959183673 + t); +__D_B_6 (t >= 156.12244897959184); (-156.12244897959184 + t); +__D_B_176 (t >= 78.6734693877551); (-78.6734693877551 + t); +__D_B_123 (t <= 27.295918367346932); (-27.295918367346932 + t); +__D_B_155 (t <= 58.31632653061224); (-58.31632653061224 + t); +__D_B_141 (t <= 44.74489795918367); (-44.74489795918367 + t); +__D_B_197 (t <= 99.03061224489795); (-99.03061224489795 + t); +__D_B_64 (t >= 215.30612244897958); (-215.30612244897958 + t); +__D_B_170 (t >= 72.85714285714286); (-72.85714285714286 + t); +__D_B_112 (t >= 16.632653061224488); (-16.632653061224488 + t); +__D_B_151 (t <= 54.438775510204074); (-54.438775510204074 + t); +__D_B_94 (t >= 245.9183673469388); (-245.9183673469388 + t); +__D_B_192 (t >= 94.18367346938776); (-94.18367346938776 + t); +__D_B_125 (t <= 29.234693877551013); (-29.234693877551013 + t); +__D_B_153 (t <= 56.377551020408156); (-56.377551020408156 + t); +__D_B_100 (t >= 5.0); (-5.0 + t); +__D_B_69 (t <= 220.40816326530611); (-220.40816326530611 + t); +__D_B_84 (t >= 235.71428571428572); (-235.71428571428572 + t); +__D_B_150 (t >= 53.46938775510204); (-53.46938775510204 + t); +__D_B_131 (t <= 35.051020408163254); (-35.051020408163254 + t); +__D_B_44 (t >= 194.89795918367346); (-194.89795918367346 + t); +__D_B_41 (t <= 191.83673469387753); (-191.83673469387753 + t); +__D_B_49 (t <= 200.0); (-200.0 + t); +__D_B_0 (t >= 150.0); (-150.0 + t); +__D_B_75 (t <= 226.53061224489795); (-226.53061224489795 + t); +__D_B_88 (t >= 239.79591836734693); (-239.79591836734693 + t); +__D_B_196 (t >= 98.06122448979592); (-98.06122448979592 + t); +__D_B_105 (t <= 9.846938775510196); (-9.846938775510196 + t); +__D_B_63 (t <= 214.28571428571428); (-214.28571428571428 + t); +__D_B_80 (t >= 231.6326530612245); (-231.6326530612245 + t); +__D_B_83 (t <= 234.69387755102042); (-234.69387755102042 + t); +__D_B_93 (t <= 244.89795918367346); (-244.89795918367346 + t); +__D_B_57 (t <= 208.16326530612244); (-208.16326530612244 + t); +__D_B_127 (t <= 31.173469387755095); (-31.173469387755095 + t); +__D_B_185 (t <= 87.39795918367346); (-87.39795918367346 + t); +__D_B_115 (t <= 19.540816326530603); (-19.540816326530603 + t); +__D_B_71 (t <= 222.44897959183675); (-222.44897959183675 + t); +__D_B_172 (t >= 74.79591836734694); (-74.79591836734694 + t); +__D_B_199 (t <= 100.96938775510203); (-100.96938775510203 + t); +__D_B_186 (t >= 88.36734693877551); (-88.36734693877551 + t); +__D_B_129 (t <= 33.11224489795917); (-33.11224489795917 + t); +__D_B_14 (t >= 164.28571428571428); (-164.28571428571428 + t); +__D_B_134 (t >= 37.95918367346939); (-37.95918367346939 + t); +__D_B_68 (t >= 219.3877551020408); (-219.3877551020408 + t); +__D_B_193 (t <= 95.15306122448979); (-95.15306122448979 + t); +__D_B_11 (t <= 161.22448979591837); (-161.22448979591837 + t); +__D_B_163 (t <= 66.07142857142857); (-66.07142857142857 + t); +__D_B_124 (t >= 28.26530612244898); (-28.26530612244898 + t); +__D_B_20 (t >= 170.40816326530611); (-170.40816326530611 + t); +__D_B_43 (t <= 193.87755102040816); (-193.87755102040816 + t); +__D_B_102 (t >= 6.938775510204081); (-6.938775510204081 + t); +__D_B_166 (t >= 68.9795918367347); (-68.9795918367347 + t); +__D_B_65 (t <= 216.32653061224488); (-216.32653061224488 + t); +__D_B_92 (t >= 243.87755102040816); (-243.87755102040816 + t); +__D_B_120 (t >= 24.387755102040817); (-24.387755102040817 + t); +__D_B_154 (t >= 57.3469387755102); (-57.3469387755102 + t); +__D_B_46 (t >= 196.9387755102041); (-196.9387755102041 + t); +__D_B_34 (t >= 184.69387755102042); (-184.69387755102042 + t); +__D_B_190 (t >= 92.24489795918367); (-92.24489795918367 + t); +__D_B_97 (t <= 248.9795918367347); (-248.9795918367347 + t); +__D_B_121 (t <= 25.35714285714285); (-25.35714285714285 + t); +__D_B_78 (t >= 229.59183673469389); (-229.59183673469389 + t); +__D_B_173 (t <= 75.76530612244898); (-75.76530612244898 + t); +__D_B_195 (t <= 97.09183673469387); (-97.09183673469387 + t); +__D_B_126 (t >= 30.20408163265306); (-30.20408163265306 + t); +__D_B_81 (t <= 232.6530612244898); (-232.6530612244898 + t); +__D_B_104 (t >= 8.877551020408163); (-8.877551020408163 + t); +EVENTS 2 +EVENT event1 +TRIGGER (((__D_B_0 == 1.0) && (__D_B_1 == 1.0)) || ((__D_B_2 == 1.0) && (__D_B_3 == 1.0)) || ((__D_B_4 == 1.0) && (__D_B_5 == 1.0)) || ((__D_B_6 == 1.0) && (__D_B_7 == 1.0)) || ((__D_B_8 == 1.0) && (__D_B_9 == 1.0)) || ((__D_B_10 == 1.0) && (__D_B_11 == 1.0)) || ((__D_B_12 == 1.0) && (__D_B_13 == 1.0)) || ((__D_B_14 == 1.0) && (__D_B_15 == 1.0)) || ((__D_B_16 == 1.0) && (__D_B_17 == 1.0)) || ((__D_B_18 == 1.0) && (__D_B_19 == 1.0)) || ((__D_B_20 == 1.0) && (__D_B_21 == 1.0)) || ((__D_B_22 == 1.0) && (__D_B_23 == 1.0)) || ((__D_B_24 == 1.0) && (__D_B_25 == 1.0)) || ((__D_B_26 == 1.0) && (__D_B_27 == 1.0)) || ((__D_B_28 == 1.0) && (__D_B_29 == 1.0)) || ((__D_B_30 == 1.0) && (__D_B_31 == 1.0)) || ((__D_B_32 == 1.0) && (__D_B_33 == 1.0)) || ((__D_B_34 == 1.0) && (__D_B_35 == 1.0)) || ((__D_B_36 == 1.0) && (__D_B_37 == 1.0)) || ((__D_B_38 == 1.0) && (__D_B_39 == 1.0)) || ((__D_B_40 == 1.0) && (__D_B_41 == 1.0)) || ((__D_B_42 == 1.0) && (__D_B_43 == 1.0)) || ((__D_B_44 == 1.0) && (__D_B_45 == 1.0)) || ((__D_B_46 == 1.0) && (__D_B_47 == 1.0)) || ((__D_B_48 == 1.0) && (__D_B_49 == 1.0)) || ((__D_B_50 == 1.0) && (__D_B_51 == 1.0)) || ((__D_B_52 == 1.0) && (__D_B_53 == 1.0)) || ((__D_B_54 == 1.0) && (__D_B_55 == 1.0)) || ((__D_B_56 == 1.0) && (__D_B_57 == 1.0)) || ((__D_B_58 == 1.0) && (__D_B_59 == 1.0)) || ((__D_B_60 == 1.0) && (__D_B_61 == 1.0)) || ((__D_B_62 == 1.0) && (__D_B_63 == 1.0)) || ((__D_B_64 == 1.0) && (__D_B_65 == 1.0)) || ((__D_B_66 == 1.0) && (__D_B_67 == 1.0)) || ((__D_B_68 == 1.0) && (__D_B_69 == 1.0)) || ((__D_B_70 == 1.0) && (__D_B_71 == 1.0)) || ((__D_B_72 == 1.0) && (__D_B_73 == 1.0)) || ((__D_B_74 == 1.0) && (__D_B_75 == 1.0)) || ((__D_B_76 == 1.0) && (__D_B_77 == 1.0)) || ((__D_B_78 == 1.0) && (__D_B_79 == 1.0)) || ((__D_B_80 == 1.0) && (__D_B_81 == 1.0)) || ((__D_B_82 == 1.0) && (__D_B_83 == 1.0)) || ((__D_B_84 == 1.0) && (__D_B_85 == 1.0)) || ((__D_B_86 == 1.0) && (__D_B_87 == 1.0)) || ((__D_B_88 == 1.0) && (__D_B_89 == 1.0)) || ((__D_B_90 == 1.0) && (__D_B_91 == 1.0)) || ((__D_B_92 == 1.0) && (__D_B_93 == 1.0)) || ((__D_B_94 == 1.0) && (__D_B_95 == 1.0)) || ((__D_B_96 == 1.0) && (__D_B_97 == 1.0)) || ((__D_B_98 == 1.0) && (__D_B_99 == 1.0))); +EVENTASSIGNMENTS 1 +0 (-5.0 + A); +EVENT event0 +TRIGGER (((__D_B_100 == 1.0) && (__D_B_101 == 1.0)) || ((__D_B_102 == 1.0) && (__D_B_103 == 1.0)) || ((__D_B_104 == 1.0) && (__D_B_105 == 1.0)) || ((__D_B_106 == 1.0) && (__D_B_107 == 1.0)) || ((__D_B_108 == 1.0) && (__D_B_109 == 1.0)) || ((__D_B_110 == 1.0) && (__D_B_111 == 1.0)) || ((__D_B_112 == 1.0) && (__D_B_113 == 1.0)) || ((__D_B_114 == 1.0) && (__D_B_115 == 1.0)) || ((__D_B_116 == 1.0) && (__D_B_117 == 1.0)) || ((__D_B_118 == 1.0) && (__D_B_119 == 1.0)) || ((__D_B_120 == 1.0) && (__D_B_121 == 1.0)) || ((__D_B_122 == 1.0) && (__D_B_123 == 1.0)) || ((__D_B_124 == 1.0) && (__D_B_125 == 1.0)) || ((__D_B_126 == 1.0) && (__D_B_127 == 1.0)) || ((__D_B_128 == 1.0) && (__D_B_129 == 1.0)) || ((__D_B_130 == 1.0) && (__D_B_131 == 1.0)) || ((__D_B_132 == 1.0) && (__D_B_133 == 1.0)) || ((__D_B_134 == 1.0) && (__D_B_135 == 1.0)) || ((__D_B_136 == 1.0) && (__D_B_137 == 1.0)) || ((__D_B_138 == 1.0) && (__D_B_139 == 1.0)) || ((__D_B_140 == 1.0) && (__D_B_141 == 1.0)) || ((__D_B_142 == 1.0) && (__D_B_143 == 1.0)) || ((__D_B_144 == 1.0) && (__D_B_145 == 1.0)) || ((__D_B_146 == 1.0) && (__D_B_147 == 1.0)) || ((__D_B_148 == 1.0) && (__D_B_149 == 1.0)) || ((__D_B_150 == 1.0) && (__D_B_151 == 1.0)) || ((__D_B_152 == 1.0) && (__D_B_153 == 1.0)) || ((__D_B_154 == 1.0) && (__D_B_155 == 1.0)) || ((__D_B_156 == 1.0) && (__D_B_157 == 1.0)) || ((__D_B_158 == 1.0) && (__D_B_159 == 1.0)) || ((__D_B_160 == 1.0) && (__D_B_161 == 1.0)) || ((__D_B_162 == 1.0) && (__D_B_163 == 1.0)) || ((__D_B_164 == 1.0) && (__D_B_165 == 1.0)) || ((__D_B_166 == 1.0) && (__D_B_167 == 1.0)) || ((__D_B_168 == 1.0) && (__D_B_169 == 1.0)) || ((__D_B_170 == 1.0) && (__D_B_171 == 1.0)) || ((__D_B_172 == 1.0) && (__D_B_173 == 1.0)) || ((__D_B_174 == 1.0) && (__D_B_175 == 1.0)) || ((__D_B_176 == 1.0) && (__D_B_177 == 1.0)) || ((__D_B_178 == 1.0) && (__D_B_179 == 1.0)) || ((__D_B_180 == 1.0) && (__D_B_181 == 1.0)) || ((__D_B_182 == 1.0) && (__D_B_183 == 1.0)) || ((__D_B_184 == 1.0) && (__D_B_185 == 1.0)) || ((__D_B_186 == 1.0) && (__D_B_187 == 1.0)) || ((__D_B_188 == 1.0) && (__D_B_189 == 1.0)) || ((__D_B_190 == 1.0) && (__D_B_191 == 1.0)) || ((__D_B_192 == 1.0) && (__D_B_193 == 1.0)) || ((__D_B_194 == 1.0) && (__D_B_195 == 1.0)) || ((__D_B_196 == 1.0) && (__D_B_197 == 1.0)) || ((__D_B_198 == 1.0) && (__D_B_199 == 1.0))); +EVENTASSIGNMENTS 1 +0 (5.0 + A); +NUM_EQUATIONS 6 +ODE A INIT 10.0; + RATE 0.0; +ODE OCT4 INIT 0.01; + RATE ((1000.0 * (1.0E-4 + A + (0.01 * OCT4_SOX2) + (0.2 * NANOG * OCT4_SOX2)) / (1000.0001 + (1.1 * A) + OCT4_SOX2 + (0.7 * NANOG * OCT4_SOX2))) - OCT4 - ((0.05 * OCT4 * SOX2) - (0.001 * OCT4_SOX2))); +ODE SOX2 INIT 0.01; + RATE ( - ((0.05 * OCT4 * SOX2) - (0.001 * OCT4_SOX2)) + (1000.0 * (1.0E-4 + A + (0.01 * OCT4_SOX2) + (0.2 * NANOG * OCT4_SOX2)) / (1000.0001 + (1.1 * A) + OCT4_SOX2 + (0.7 * NANOG * OCT4_SOX2))) - SOX2); +ODE NANOG INIT 0.01; + RATE (((1.0E-4 + (0.005 * OCT4_SOX2) + (0.1 * OCT4_SOX2 * NANOG)) / (1.0000001 + (9.95E-4 * OCT4_SOX2) + (0.001 * OCT4_SOX2 * NANOG))) - NANOG); +ODE OCT4_SOX2 INIT 0.1; + RATE ((0.05 * OCT4 * SOX2) - (0.001 * OCT4_SOX2) - (5.0 * OCT4_SOX2)); +ODE Protein INIT 0.0; + RATE ((1000.0 * (1.0E-4 + (0.1 * OCT4_SOX2)) / (1000.0001 + OCT4_SOX2 + (1000.0 * NANOG * OCT4_SOX2))) - (0.01 * Protein)); + diff --git a/tests/unit/resources/SimID_1607235431_0_.functions b/tests/unit/resources/SimID_1607235431_0_.functions new file mode 100644 index 000000000..94f84ae79 --- /dev/null +++ b/tests/unit/resources/SimID_1607235431_0_.functions @@ -0,0 +1,23 @@ +##--------------------------------------------- +## /Users/logandrescher/.vcell/simdata/temp/SimID_1607235431_0_.functions +##--------------------------------------------- + +Compartment::degradation; 0.0; ; Nonspatial_VariableType; false +event0.triggerFunction; (((t >= 5.0) && (t <= 5.969387755102034)) || ((t >= 6.938775510204081) && (t <= 7.908163265306115)) || ((t >= 8.877551020408163) && (t <= 9.846938775510196)) || ((t >= 10.816326530612244) && (t <= 11.785714285714278)) || ((t >= 12.755102040816325) && (t <= 13.724489795918359)) || ((t >= 14.693877551020408) && (t <= 15.663265306122442)) || ((t >= 16.632653061224488) && (t <= 17.60204081632652)) || ((t >= 18.57142857142857) && (t <= 19.540816326530603)) || ((t >= 20.51020408163265) && (t <= 21.479591836734684)) || ((t >= 22.448979591836736) && (t <= 23.41836734693877)) || ((t >= 24.387755102040817) && (t <= 25.35714285714285)) || ((t >= 26.3265306122449) && (t <= 27.295918367346932)) || ((t >= 28.26530612244898) && (t <= 29.234693877551013)) || ((t >= 30.20408163265306) && (t <= 31.173469387755095)) || ((t >= 32.14285714285714) && (t <= 33.11224489795917)) || ((t >= 34.08163265306122) && (t <= 35.051020408163254)) || ((t >= 36.0204081632653) && (t <= 36.989795918367335)) || ((t >= 37.95918367346939) && (t <= 38.92857142857142)) || ((t >= 39.89795918367347) && (t <= 40.867346938775505)) || ((t >= 41.83673469387755) && (t <= 42.806122448979586)) || ((t >= 43.775510204081634) && (t <= 44.74489795918367)) || ((t >= 45.714285714285715) && (t <= 46.68367346938775)) || ((t >= 47.6530612244898) && (t <= 48.62244897959183)) || ((t >= 49.59183673469388) && (t <= 50.56122448979591)) || ((t >= 51.53061224489796) && (t <= 52.49999999999999)) || ((t >= 53.46938775510204) && (t <= 54.438775510204074)) || ((t >= 55.40816326530612) && (t <= 56.377551020408156)) || ((t >= 57.3469387755102) && (t <= 58.31632653061224)) || ((t >= 59.285714285714285) && (t <= 60.25510204081632)) || ((t >= 61.224489795918366) && (t <= 62.1938775510204)) || ((t >= 63.16326530612245) && (t <= 64.13265306122449)) || ((t >= 65.10204081632654) && (t <= 66.07142857142857)) || ((t >= 67.0408163265306) && (t <= 68.01020408163264)) || ((t >= 68.9795918367347) && (t <= 69.94897959183673)) || ((t >= 70.91836734693878) && (t <= 71.88775510204081)) || ((t >= 72.85714285714286) && (t <= 73.8265306122449)) || ((t >= 74.79591836734694) && (t <= 75.76530612244898)) || ((t >= 76.73469387755102) && (t <= 77.70408163265306)) || ((t >= 78.6734693877551) && (t <= 79.64285714285714)) || ((t >= 80.61224489795919) && (t <= 81.58163265306122)) || ((t >= 82.55102040816327) && (t <= 83.5204081632653)) || ((t >= 84.48979591836735) && (t <= 85.45918367346938)) || ((t >= 86.42857142857143) && (t <= 87.39795918367346)) || ((t >= 88.36734693877551) && (t <= 89.33673469387755)) || ((t >= 90.3061224489796) && (t <= 91.27551020408163)) || ((t >= 92.24489795918367) && (t <= 93.21428571428571)) || ((t >= 94.18367346938776) && (t <= 95.15306122448979)) || ((t >= 96.12244897959184) && (t <= 97.09183673469387)) || ((t >= 98.06122448979592) && (t <= 99.03061224489795)) || ((t >= 100.0) && (t <= 100.96938775510203))); ; Nonspatial_VariableType; false +event1.triggerFunction; (((t >= 150.0) && (t <= 151.0204081632653)) || ((t >= 152.0408163265306) && (t <= 153.0612244897959)) || ((t >= 154.08163265306123) && (t <= 155.10204081632654)) || ((t >= 156.12244897959184) && (t <= 157.14285714285714)) || ((t >= 158.16326530612244) && (t <= 159.18367346938774)) || ((t >= 160.20408163265307) && (t <= 161.22448979591837)) || ((t >= 162.24489795918367) && (t <= 163.26530612244898)) || ((t >= 164.28571428571428) && (t <= 165.30612244897958)) || ((t >= 166.3265306122449) && (t <= 167.3469387755102)) || ((t >= 168.3673469387755) && (t <= 169.3877551020408)) || ((t >= 170.40816326530611) && (t <= 171.42857142857142)) || ((t >= 172.44897959183675) && (t <= 173.46938775510205)) || ((t >= 174.48979591836735) && (t <= 175.51020408163265)) || ((t >= 176.53061224489795) && (t <= 177.55102040816325)) || ((t >= 178.57142857142858) && (t <= 179.59183673469389)) || ((t >= 180.6122448979592) && (t <= 181.6326530612245)) || ((t >= 182.6530612244898) && (t <= 183.6734693877551)) || ((t >= 184.69387755102042) && (t <= 185.71428571428572)) || ((t >= 186.73469387755102) && (t <= 187.75510204081633)) || ((t >= 188.77551020408163) && (t <= 189.79591836734693)) || ((t >= 190.81632653061223) && (t <= 191.83673469387753)) || ((t >= 192.85714285714286) && (t <= 193.87755102040816)) || ((t >= 194.89795918367346) && (t <= 195.91836734693877)) || ((t >= 196.9387755102041) && (t <= 197.9591836734694)) || ((t >= 198.9795918367347) && (t <= 200.0)) || ((t >= 201.0204081632653) && (t <= 202.0408163265306)) || ((t >= 203.0612244897959) && (t <= 204.0816326530612)) || ((t >= 205.10204081632654) && (t <= 206.12244897959184)) || ((t >= 207.14285714285714) && (t <= 208.16326530612244)) || ((t >= 209.18367346938777) && (t <= 210.20408163265307)) || ((t >= 211.22448979591837) && (t <= 212.24489795918367)) || ((t >= 213.26530612244898) && (t <= 214.28571428571428)) || ((t >= 215.30612244897958) && (t <= 216.32653061224488)) || ((t >= 217.3469387755102) && (t <= 218.3673469387755)) || ((t >= 219.3877551020408) && (t <= 220.40816326530611)) || ((t >= 221.42857142857144) && (t <= 222.44897959183675)) || ((t >= 223.46938775510205) && (t <= 224.48979591836735)) || ((t >= 225.51020408163265) && (t <= 226.53061224489795)) || ((t >= 227.55102040816325) && (t <= 228.57142857142856)) || ((t >= 229.59183673469389) && (t <= 230.6122448979592)) || ((t >= 231.6326530612245) && (t <= 232.6530612244898)) || ((t >= 233.67346938775512) && (t <= 234.69387755102042)) || ((t >= 235.71428571428572) && (t <= 236.73469387755102)) || ((t >= 237.75510204081633) && (t <= 238.77551020408163)) || ((t >= 239.79591836734693) && (t <= 240.81632653061223)) || ((t >= 241.83673469387756) && (t <= 242.85714285714286)) || ((t >= 243.87755102040816) && (t <= 244.89795918367346)) || ((t >= 245.9183673469388) && (t <= 246.9387755102041)) || ((t >= 247.9591836734694) && (t <= 248.9795918367347)) || ((t >= 250.0) && (t <= 251.0204081632653))); ; Nonspatial_VariableType; false +Compartment::LumpedJ_J0; (1000.0 * (1.0E-4 + A + (0.01 * OCT4_SOX2) + (0.2 * NANOG * OCT4_SOX2)) / (1000.0001 + (1.1 * A) + OCT4_SOX2 + (0.7 * NANOG * OCT4_SOX2))); ; Nonspatial_VariableType; false +Compartment::LumpedJ_J1; OCT4; ; Nonspatial_VariableType; false +Compartment::LumpedJ_J2; ((1.0E-4 + (0.005 * OCT4_SOX2) + (0.1 * OCT4_SOX2 * NANOG)) / (1.0000001 + (9.95E-4 * OCT4_SOX2) + (0.001 * OCT4_SOX2 * NANOG))); ; Nonspatial_VariableType; false +Compartment::LumpedJ_J3; NANOG; ; Nonspatial_VariableType; false +Compartment::LumpedJ_J4; ((0.05 * OCT4 * SOX2) - (0.001 * OCT4_SOX2)); ; Nonspatial_VariableType; false +Compartment::LumpedJ_J5; (5.0 * OCT4_SOX2); ; Nonspatial_VariableType; false +Compartment::LumpedJ_J6; (1000.0 * (1.0E-4 + A + (0.01 * OCT4_SOX2) + (0.2 * NANOG * OCT4_SOX2)) / (1000.0001 + (1.1 * A) + OCT4_SOX2 + (0.7 * NANOG * OCT4_SOX2))); ; Nonspatial_VariableType; false +Compartment::LumpedJ_J7; SOX2; ; Nonspatial_VariableType; false +Compartment::LumpedJ_J8; (1000.0 * (1.0E-4 + (0.1 * OCT4_SOX2)) / (1000.0001 + OCT4_SOX2 + (1000.0 * NANOG * OCT4_SOX2))); ; Nonspatial_VariableType; false +Compartment::LumpedJ_J9; (0.01 * Protein); ; Nonspatial_VariableType; false +Compartment::NANOG_Gene; 0.0; ; Nonspatial_VariableType; false +Compartment::OCT4_Gene; 1.0; ; Nonspatial_VariableType; false +Compartment::p53; 0.0; ; Nonspatial_VariableType; false +Compartment::SOX2_Gene; 0.0; ; Nonspatial_VariableType; false +Compartment::targetGene; 0.01; ; Nonspatial_VariableType; false + diff --git a/tests/unit/resources/SimID_1607235431_0_.log b/tests/unit/resources/SimID_1607235431_0_.log new file mode 100644 index 000000000..89146ccf2 --- /dev/null +++ b/tests/unit/resources/SimID_1607235431_0_.log @@ -0,0 +1,4 @@ +IDAData logfile +IDAData text format version 1 +SimID_1607235431_0_.ida +KeepMost 1000 diff --git a/tests/unit/resources/SimID_1607235431_0__0.simtask.xml b/tests/unit/resources/SimID_1607235431_0__0.simtask.xml new file mode 100644 index 000000000..e41a76b03 --- /dev/null +++ b/tests/unit/resources/SimID_1607235431_0__0.simtask.xml @@ -0,0 +1,162 @@ + + + 96485.3321 + 9.64853321E-5 + 1.0E-9 + 6.02214179E11 + 3.141592653589793 + 8314.46261815 + 300.0 + 1.0 + 0.1 + 0.2 + 10.0 + 0.0 + 0.0011 + 0.001 + 7.0E-4 + 1.0 + 0.01 + 0.2 + 0.0011 + 0.001 + 7.0E-4 + 0.0 + 0.005 + 0.1 + 1.0E-4 + 1.0E-4 + 1.0E-4 + 1.0E-4 + 0.0 + 100.0 + 5.0 + 50.0 + 0.0 + 250.0 + 150.0 + 50.0 + 1000.0 + 0.001 + 9.95E-4 + 0.01 + 0.1 + 1.0 + 1.0 + 1.0 + 0.01 + 0.1 + 0.001 + 1.0 + 0.05 + 0.001 + 5.0 + 1000.0 + 0.001660538783162726 + 0.0 + 0.01 + 1.0 + 0.01 + 0.1 + 0.0 + 0.0 + 1.0 + 0.0 + 0.01 + 0.0 + 0.01 + + + + + + + degradation_init_l_1 + (((t >= event0.minTime) && (t <= (event0.minTime + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 1.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 1.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 2.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 2.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 3.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 3.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 4.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 4.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 5.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 5.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 6.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 6.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 7.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 7.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 8.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 8.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 9.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 9.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 10.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 10.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 11.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 11.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 12.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 12.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 13.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 13.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 14.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 14.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 15.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 15.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 16.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 16.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 17.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 17.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 18.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 18.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 19.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 19.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 20.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 20.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 21.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 21.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 22.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 22.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 23.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 23.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 24.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 24.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 25.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 25.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 26.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 26.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 27.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 27.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 28.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 28.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 29.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 29.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 30.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 30.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 31.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 31.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 32.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 32.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 33.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 33.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 34.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 34.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 35.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 35.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 36.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 36.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 37.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 37.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 38.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 38.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 39.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 39.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 40.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 40.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 41.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 41.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 42.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 42.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 43.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 43.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 44.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 44.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 45.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 45.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 46.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 46.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 47.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 47.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 48.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 48.0)) + 0.9693877551020336))) || ((t >= (event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 49.0))) && (t <= ((event0.minTime + (((event0.maxTime - event0.minTime) / (event0.numTimes + -1.0)) * 49.0)) + 0.9693877551020336)))) + (((t >= event1.minTime) && (t <= (event1.minTime + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 1.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 1.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 2.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 2.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 3.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 3.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 4.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 4.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 5.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 5.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 6.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 6.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 7.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 7.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 8.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 8.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 9.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 9.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 10.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 10.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 11.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 11.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 12.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 12.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 13.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 13.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 14.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 14.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 15.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 15.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 16.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 16.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 17.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 17.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 18.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 18.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 19.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 19.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 20.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 20.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 21.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 21.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 22.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 22.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 23.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 23.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 24.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 24.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 25.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 25.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 26.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 26.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 27.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 27.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 28.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 28.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 29.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 29.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 30.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 30.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 31.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 31.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 32.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 32.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 33.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 33.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 34.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 34.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 35.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 35.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 36.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 36.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 37.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 37.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 38.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 38.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 39.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 39.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 40.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 40.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 41.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 41.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 42.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 42.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 43.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 43.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 44.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 44.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 45.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 45.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 46.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 46.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 47.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 47.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 48.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 48.0)) + 1.0204081632653015))) || ((t >= (event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 49.0))) && (t <= ((event1.minTime + (((event1.maxTime - event1.minTime) / (event1.numTimes + -1.0)) * 49.0)) + 1.0204081632653015)))) + ((OCT4_Gene * f * ((A * a1) + (OCT4_SOX2 * a2) + (NANOG * OCT4_SOX2 * a3) + eta1)) / (eta1 + f + (A * b1 * f) + (OCT4_SOX2 * b2 * f) + (NANOG * OCT4_SOX2 * b3 * f))) + (OCT4 * gamma1) + ((eta5 + (e1 * OCT4_SOX2) + (e2 * OCT4_SOX2 * NANOG)) / (1.0 + (eta5 / f) + (f2 * OCT4_SOX2) + (f1 * OCT4_SOX2 * NANOG) + (f3 * p53))) + (NANOG * gamma2) + ((k1c * OCT4 * SOX2) - (k2c * OCT4_SOX2)) + (OCT4_SOX2 * k3c) + ((f * ((A * c1) + (OCT4_SOX2 * c2) + (NANOG * OCT4_SOX2 * c3) + eta3)) / (eta3 + f + (A * d1 * f) + (OCT4_SOX2 * d2 * f) + (NANOG * OCT4_SOX2 * d3 * f))) + (SOX2 * gamma3) + ((f * (eta7 + (OCT4_SOX2 * g1))) / (eta7 + f + (OCT4_SOX2 * f * h1) + (NANOG * OCT4_SOX2 * f * h2))) + (Protein * gamma4) + NANOG_Gene_init_l_1 + OCT4_Gene_init_l_1 + p53_init_l_1 + SOX2_Gene_init_l_1 + targetGene_init_l_1 + + + + + + + + + 0.0 + A_init_l_1 + + + ((LumpedJ_J0 / Size_compartment) - (LumpedJ_J1 / Size_compartment) - (LumpedJ_J4 / Size_compartment)) + OCT4_init_l_1 + + + ( - (LumpedJ_J4 / Size_compartment) + (LumpedJ_J6 / Size_compartment) - (LumpedJ_J7 / Size_compartment)) + SOX2_init_l_1 + + + ((LumpedJ_J2 / Size_compartment) - (LumpedJ_J3 / Size_compartment)) + NANOG_init_l_1 + + + ((LumpedJ_J4 / Size_compartment) - (LumpedJ_J5 / Size_compartment)) + OCT4_SOX2_init_l_1 + + + ((LumpedJ_J8 / Size_compartment) - (LumpedJ_J9 / Size_compartment)) + Protein_init_l_1 + + + + + + + + event1.triggerFunction + (A - 5.0) + + + event0.triggerFunction + (A + 5.0) + + + + + + + + + 1 + + + 0.01 + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/unit/resources/SimID_256118677_0_.cvodeInput b/tests/unit/resources/SimID_256118677_0_.cvodeInput new file mode 100644 index 000000000..f5786cddc --- /dev/null +++ b/tests/unit/resources/SimID_256118677_0_.cvodeInput @@ -0,0 +1,24 @@ +# JMS_Paramters +JMS_PARAM_BEGIN +JMS_BROKER vcell.cam.uchc.edu:30163 +JMS_USER clientUser dummy +JMS_QUEUE workerEvent +JMS_TOPIC serviceControl +VCELL_USER ldresche +SIMULATION_KEY 256118677 +JOB_INDEX 0 +JMS_PARAM_END + +SOLVER CVODE +STARTING_TIME 0.0 +ENDING_TIME 10.0 +RELATIVE_TOLERANCE 1.0E-9 +ABSOLUTE_TOLERANCE 1.0E-9 +MAX_TIME_STEP 1.0 +KEEP_EVERY 1 +NUM_EQUATIONS 2 +ODE s0 INIT 1.0; + RATE - ((0.5 * s0) - (0.2 * s1)); +ODE s1 INIT 0.0; + RATE ((0.5 * s0) - (0.2 * s1)); + diff --git a/tests/unit/resources/SimID_256118677_0_.functions b/tests/unit/resources/SimID_256118677_0_.functions new file mode 100644 index 000000000..380a43ce0 --- /dev/null +++ b/tests/unit/resources/SimID_256118677_0_.functions @@ -0,0 +1,6 @@ +##--------------------------------------------- +## /simdata/ldresche/SimID_256118677_0_.functions +##--------------------------------------------- + +Compartment::J_r0; ((0.5 * s0) - (0.2 * s1)); ; Nonspatial_VariableType; false + diff --git a/tests/unit/resources/SimID_256118677_0_.ida.expected b/tests/unit/resources/SimID_256118677_0_.ida.expected new file mode 100644 index 000000000..c0a71f95f --- /dev/null +++ b/tests/unit/resources/SimID_256118677_0_.ida.expected @@ -0,0 +1,159 @@ +t:s0:s1: +0.00000000000000000E+00 1.00000000000000000E+00 0.00000000000000000E+00 +1.02648488190150711E-10 9.99999999948675722E-01 5.13242440913875063E-11 +1.02658753038969712E-06 9.99999486706603657E-01 5.13293396373231300E-07 +1.12914363494047687E-05 9.99994354310467903E-01 5.64568953210529354E-06 +4.48211895830175010E-05 9.99977589784958321E-01 2.24102150417055293E-05 +1.17277047540880841E-04 9.99941363945291539E-01 5.86360547084540949E-05 +2.53272368309885381E-04 9.99873375142890652E-01 1.26624857109333797E-04 +4.96024984269423201E-04 9.99752030703600969E-01 2.47969296398973030E-04 +9.22698163574049712E-04 9.99538800058830001E-01 4.61199941169868684E-04 +1.68044365853622663E-03 9.99160272360433921E-01 8.39727639565850115E-04 +3.18862269929603417E-03 9.98407466672823207E-01 1.59253332717652126E-03 +5.45114110140219790E-03 9.97279622342691741E-01 2.72037765730797233E-03 +7.71365950350836119E-03 9.96153562661734271E-01 3.84643733826544416E-03 +9.97617790561452535E-03 9.95029284922585688E-01 4.97071507741405488E-03 +1.22386963077206895E-02 9.93906786345072457E-01 6.09321365492729479E-03 +1.45012147098268537E-02 9.92786064126930712E-01 7.21393587306905053E-03 +1.73952544462393256E-02 9.91355107543508862E-01 8.64489245649092790E-03 +2.02892941826517993E-02 9.89927047398444282E-01 1.00729526015555741E-02 +2.31833339190642730E-02 9.88501877601865031E-01 1.14981223981348198E-02 +2.60773736554767467E-02 9.87079592114147530E-01 1.29204078858523308E-02 +3.29068688347819438E-02 9.83734614715639721E-01 1.62653852843602023E-02 +3.97363640140871374E-02 9.80405590318767395E-01 1.95944096812325566E-02 +5.02957748456068915E-02 9.75289650084949389E-01 2.47103499150505036E-02 +6.08551856771266456E-02 9.70211385257129355E-01 2.97886147428706068E-02 +7.14145965086463996E-02 9.65170518189519044E-01 3.48294818104809070E-02 +8.19740073401661606E-02 9.60166773545884755E-01 3.98332264541152309E-02 +9.25334181716859216E-02 9.55199878101650190E-01 4.48001218983497956E-02 +1.09712253292456846E-01 9.47197402212381134E-01 5.28025977876188587E-02 +1.26891088413227771E-01 9.39290581299578320E-01 6.07094187004216662E-02 +1.44069923533998695E-01 9.31478272015571096E-01 6.85217279844288346E-02 +1.61248758654769619E-01 9.23759344630696533E-01 7.62406553693034117E-02 +1.89660049003499281E-01 9.11195316277009670E-01 8.88046837229902880E-02 +2.18071339352228943E-01 8.98878691567765453E-01 1.01121308432234533E-01 +2.46482629700958605E-01 8.86804598883420114E-01 1.13195401116579816E-01 +2.74893920049688267E-01 8.74968262536995778E-01 1.25031737463004083E-01 +3.03305210398417957E-01 8.63365000789078185E-01 1.36634999210921676E-01 +3.31716500747147647E-01 8.51990224040054867E-01 1.48009775959944967E-01 +3.60127791095877337E-01 8.40839433068397923E-01 1.59160566931601855E-01 +3.88539081444607026E-01 8.29908217266169590E-01 1.70091782733830132E-01 +4.41138445070370255E-01 8.10235627189399343E-01 1.89764372810600435E-01 +4.77567574856914034E-01 7.97029226508457089E-01 2.02970773491542689E-01 +5.13996704643457814E-01 7.84155337872014080E-01 2.15844662127985643E-01 +5.50425834430001593E-01 7.71605588560225097E-01 2.28394411439774653E-01 +5.86854964216545372E-01 7.59371816557104728E-01 2.40628183442894966E-01 +6.23284094003089151E-01 7.47446066126369102E-01 2.52553933873630565E-01 +6.59713223789632930E-01 7.35820582268969181E-01 2.64179417731030486E-01 +6.96142353576176709E-01 7.24487805053363076E-01 2.75512194946636535E-01 +7.58815310233661888E-01 7.05654481953680968E-01 2.94345518046318699E-01 +8.21488266891147068E-01 6.87629536154125143E-01 3.12370463845874413E-01 +8.84161223548632247E-01 6.70378268923642939E-01 3.29621731076356561E-01 +9.46834180206117426E-01 6.53867470964243247E-01 3.46132529035756198E-01 +1.00950713686360261E+00 6.38065359483856231E-01 3.61934640516143269E-01 +1.07218009352108767E+00 6.22941516449129762E-01 3.77058483550869794E-01 +1.13485305017857274E+00 6.08466829122856878E-01 3.91533170877142678E-01 +1.19752600683605781E+00 5.94613433892461130E-01 4.05386566107538426E-01 +1.26019896349354288E+00 5.81354663050361431E-01 4.18645336949638014E-01 +1.32287192015102795E+00 5.68664993725449097E-01 4.31335006274550459E-01 +1.38554487680851302E+00 5.56519998669646965E-01 4.43480001330352702E-01 +1.44821783346599808E+00 5.44896299070346424E-01 4.55103700929653243E-01 +1.51089079012348315E+00 5.33771519516928472E-01 4.66228480483071195E-01 +1.57356374678096822E+00 5.23124244999401800E-01 4.76875755000597867E-01 +1.63623670343845329E+00 5.12933979731394318E-01 4.87066020268605349E-01 +1.69890966009593836E+00 5.03181107681094364E-01 4.96818892318905247E-01 +1.76158261675342342E+00 4.93846854780748468E-01 5.06153145219251144E-01 +1.82425557341090849E+00 4.84913252781268178E-01 5.15086747218731489E-01 +1.88692853006839356E+00 4.76363104676305715E-01 5.23636895323693952E-01 +1.94960148672587863E+00 4.68179951606793432E-01 5.31820048393206291E-01 +2.01227444338336392E+00 4.60348041167775057E-01 5.39651958832224610E-01 +2.07494740004084921E+00 4.52852297094320178E-01 5.47147702905679489E-01 +2.13762035669833450E+00 4.45678290239669095E-01 5.54321709760330572E-01 +2.20029331335581979E+00 4.38812210792762725E-01 5.61187789207236998E-01 +2.26296627001330508E+00 4.32240841692172695E-01 5.67759158307827083E-01 +2.32563922667079037E+00 4.25951533186333664E-01 5.74048466813666169E-01 +2.38831218332827566E+00 4.19932178485779350E-01 5.80067821514220539E-01 +2.45098513998576095E+00 4.14171190457713889E-01 5.85828809542286000E-01 +2.51365809664324624E+00 4.08657479319653238E-01 5.91342520680346651E-01 +2.57633105330073153E+00 4.03380431291335495E-01 5.96619568708664394E-01 +2.63900400995821682E+00 3.98329888163913515E-01 6.01670111836086319E-01 +2.70167696661570211E+00 3.93496127746071123E-01 6.06503872253928655E-01 +2.76434992327318740E+00 3.88869845148941795E-01 6.11130154851058038E-01 +2.82702287993067269E+00 3.84442134874129537E-01 6.15557865125870296E-01 +2.88969583658815798E+00 3.80204473670731957E-01 6.19795526329267821E-01 +2.95236879324564327E+00 3.76148704128358324E-01 6.23851295871641454E-01 +3.01504174990312857E+00 3.72267018974363595E-01 6.27732981025636239E-01 +3.07771470656061386E+00 3.68551946044992529E-01 6.31448053955007249E-01 +3.14038766321809915E+00 3.64996333901570436E-01 6.35003666098429287E-01 +3.20306061987558444E+00 3.61593338064120084E-01 6.38406661935879582E-01 +3.26573357653306973E+00 3.58336407835898707E-01 6.41663592164100960E-01 +3.32840653319055502E+00 3.55219273693456239E-01 6.44780726306543484E-01 +3.39107948984804031E+00 3.52235935217927731E-01 6.47764064782072047E-01 +3.45375244650552560E+00 3.49380649544342170E-01 6.50619350455657663E-01 +3.51642540316301089E+00 3.46647920306724255E-01 6.53352079693275467E-01 +3.57909835982049618E+00 3.44032487057707448E-01 6.55967512942292386E-01 +3.64177131647798147E+00 3.41529315142283463E-01 6.58470684857716426E-01 +3.70444427313546676E+00 3.39133586006194754E-01 6.60866413993805191E-01 +3.76711722979295205E+00 3.36840687920315074E-01 6.63159312079684926E-01 +3.82979018645043734E+00 3.34646207103166171E-01 6.65353792896833829E-01 +3.89246314310792263E+00 3.32545919224480291E-01 6.67454080775519709E-01 +3.95513609976540792E+00 3.30535781273451845E-01 6.69464218726548155E-01 +4.01780905642289277E+00 3.28611923776025050E-01 6.71388076223975006E-01 +4.08048201308037761E+00 3.26770643346235901E-01 6.73229356653764155E-01 +4.14315496973786246E+00 3.25008395557270624E-01 6.74991604442729431E-01 +4.20582792639534730E+00 3.23321788118517361E-01 6.76678211881482694E-01 +4.26850088305283215E+00 3.21707574345476921E-01 6.78292425654523079E-01 +4.36259663226557493E+00 3.19413195181034726E-01 6.80586804818965274E-01 +4.45669238147831770E+00 3.17265070740067878E-01 6.82734929259932066E-01 +4.55078813069106047E+00 3.15253877871656929E-01 6.84746122128343071E-01 +4.64488387990380325E+00 3.13370887632055728E-01 6.86629112367944217E-01 +4.73897962911654602E+00 3.11607927741266999E-01 6.88392072258733001E-01 +4.83307537832928880E+00 3.09957347043537612E-01 6.90042652956462388E-01 +4.92717112754203157E+00 3.08411982061210621E-01 6.91588017938789434E-01 +5.02126687675477434E+00 3.06965125834067321E-01 6.93034874165932679E-01 +5.11536262596751712E+00 3.05610498904494543E-01 6.94389501095505457E-01 +5.20945837518025989E+00 3.04342222141284424E-01 6.95657777858715631E-01 +5.30355412439300267E+00 3.03154791209282182E-01 6.96845208790717763E-01 +5.39764987360574544E+00 3.02043052635592169E-01 6.97956947364407831E-01 +5.49174562281848821E+00 3.01002181432545757E-01 6.98997818567454243E-01 +5.58584137203123099E+00 3.00027660175653355E-01 6.99972339824346701E-01 +5.67993712124397376E+00 2.99115259411111445E-01 7.00884740588888611E-01 +5.77403287045671654E+00 2.98261019295870233E-01 7.01738980704129878E-01 +5.86812861966945931E+00 2.97461232403345710E-01 7.02538767596654345E-01 +5.96222436888220209E+00 2.96712427631590503E-01 7.03287572368409553E-01 +6.05632011809494486E+00 2.96011355142305865E-01 7.03988644857694190E-01 +6.15041586730768763E+00 2.95354972258738302E-01 7.04645027741261698E-01 +6.24451161652043041E+00 2.94740430259425534E-01 7.05259569740574466E-01 +6.33860736573317318E+00 2.94165062013088063E-01 7.05834937986911881E-01 +6.43270311494591596E+00 2.93626370403025250E-01 7.06373629596974695E-01 +6.52679886415865873E+00 2.93122017490281217E-01 7.06877982509718672E-01 +6.62089461337140150E+00 2.92649814367307026E-01 7.07350185632692807E-01 +6.71499036258414428E+00 2.92207711657821112E-01 7.07792288342178666E-01 +6.80908611179688705E+00 2.91793790622163152E-01 7.08206209377836515E-01 +6.90318186100962983E+00 2.91406254829892186E-01 7.08593745170107536E-01 +6.99727761022237260E+00 2.91043422363364113E-01 7.08956577636635665E-01 +7.09137335943511538E+00 2.90703718518221410E-01 7.09296281481778479E-01 +7.18546910864785815E+00 2.90385668969080357E-01 7.09614331030919643E-01 +7.27956485786060092E+00 2.90087893370859995E-01 7.09912106629139950E-01 +7.37366060707334370E+00 2.89809099368042500E-01 7.10190900631957334E-01 +7.46775635628608647E+00 2.89548076985837011E-01 7.10451923014162823E-01 +7.56185210549882925E+00 2.89303693378861304E-01 7.10696306621138585E-01 +7.65594785471157202E+00 2.89074887914547385E-01 7.10925112085452504E-01 +7.75004360392431479E+00 2.88860667569953156E-01 7.11139332430046678E-01 +7.84413935313705757E+00 2.88660102622012638E-01 7.11339897377987196E-01 +7.93823510234980034E+00 2.88472322612513654E-01 7.11527677387486235E-01 +8.08080669531667084E+00 2.88210359800015581E-01 7.11789640199984253E-01 +8.22337828828354134E+00 2.87973278978804759E-01 7.12026721021195130E-01 +8.36594988125041183E+00 2.87758716623703892E-01 7.12241283376295997E-01 +8.50852147421728233E+00 2.87564533611528172E-01 7.12435466388471661E-01 +8.65109306718415283E+00 2.87388794232750655E-01 7.12611205767249123E-01 +8.79366466015102333E+00 2.87229746815752529E-01 7.12770253184247249E-01 +8.93623625311789382E+00 2.87085806022613133E-01 7.12914193977386645E-01 +9.07880784608476432E+00 2.86955536978731940E-01 7.13044463021267783E-01 +9.22137943905163482E+00 2.86837641067565152E-01 7.13162358932434515E-01 +9.36395103201850532E+00 2.86730943062506516E-01 7.13269056937493207E-01 +9.50652262498537581E+00 2.86634379394257510E-01 7.13365620605742157E-01 +9.64909421795224631E+00 2.86546987502534933E-01 7.13453012497464734E-01 +9.79166581091911681E+00 2.86467896231261898E-01 7.13532103768737769E-01 +9.93423740388598731E+00 2.86396317164659742E-01 7.13603682835339925E-01 +1.00000000000000000E+01 2.86365632344379117E-01 7.13634367655620494E-01 diff --git a/tests/unit/resources/SimID_256118677_0_.log b/tests/unit/resources/SimID_256118677_0_.log new file mode 100644 index 000000000..afdb5aca5 --- /dev/null +++ b/tests/unit/resources/SimID_256118677_0_.log @@ -0,0 +1,4 @@ +IDAData logfile +IDAData text format version 1 +SimID_256118677_0_.ida +KeepMost 1000 diff --git a/IDAWin/tests/smoke/simpleModel_Network_orig.vcml b/tests/unit/resources/simpleModel_Network_orig.vcml similarity index 100% rename from IDAWin/tests/smoke/simpleModel_Network_orig.vcml rename to tests/unit/resources/simpleModel_Network_orig.vcml diff --git a/tests/unit/smoke_test.cpp b/tests/unit/smoke_test.cpp new file mode 100644 index 000000000..ac7485a7f --- /dev/null +++ b/tests/unit/smoke_test.cpp @@ -0,0 +1,112 @@ +// +// Created by Logan Drescher on 11/12/25. +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "SundialsSolverInterface.h" + +void compare(const std::filesystem::path& file1, const std::filesystem::path& file2, float tolerance); + + +TEST(SmokeTest, MessagingRequiresJobID) { + #ifndef USE_MESSAGING + return; // no need to test this + #endif + const std::filesystem::path RESOURCE_DIRECTORY{RESOURCE_DIR}; + const std::filesystem::path OUTPUT_TARGET{RESOURCE_DIRECTORY /"SimID_1489333437_0_.ida",}; + const std::array NECESSARY_FILES{ + RESOURCE_DIRECTORY /"SimID_1489333437_0_.cvodeInput", + RESOURCE_DIRECTORY /"SimID_1489333437_0_.ida.expected" + }; + for (const auto& file : NECESSARY_FILES) { + assert(std::filesystem::exists(file)); + } + FILE *outputFile = NULL; + std::ifstream inputFileStream{NECESSARY_FILES[0]}; + if (!inputFileStream.is_open()) { throw std::runtime_error("input file [" + NECESSARY_FILES[0].string() + "] doesn't exit!"); } + + // Open the output file... + if (NULL == (outputFile = fopen(OUTPUT_TARGET.string().c_str(), "w"))) { + throw std::runtime_error("Could not open output file[" + OUTPUT_TARGET.string() + "] for writing."); + } + + EXPECT_THROW(activateSolver(inputFileStream, outputFile, -1), std::runtime_error); + fclose(outputFile); +} + +TEST(SmokeTest, ConfirmExecution) { + #ifdef USE_MESSAGING + const int taskID = 2025, hashID = 256118677; + #else + const int taskID = -1, hashID = 1489333437; + #endif + const std::filesystem::path RESOURCE_DIRECTORY{RESOURCE_DIR}; + const std::filesystem::path OUTPUT_TARGET{RESOURCE_DIRECTORY /std::format("SimID_{}_0_.ida", hashID)}; + const std::array NECESSARY_FILES{ + RESOURCE_DIRECTORY /std::format("SimID_{}_0_.cvodeInput", hashID), + RESOURCE_DIRECTORY /std::format("SimID_{}_0_.ida.expected", hashID) + }; + for (const auto& file : NECESSARY_FILES) { + assert(std::filesystem::exists(file)); + } + FILE *outputFile = NULL; + std::ifstream inputFileStream{NECESSARY_FILES[0]}; + if (!inputFileStream.is_open()) { throw std::runtime_error("input file [" + NECESSARY_FILES[0].string() + "] doesn't exit!"); } + + // Open the output file... + if (NULL == (outputFile = fopen(OUTPUT_TARGET.string().c_str(), "w"))) { + throw std::runtime_error("Could not open output file[" + OUTPUT_TARGET.string() + "] for writing."); + } + + activateSolver(inputFileStream, outputFile, taskID); + fclose(outputFile); + + compare(OUTPUT_TARGET, NECESSARY_FILES[1], 1e-7); +} + +void compare(const std::filesystem::path& file1, const std::filesystem::path& file2, float tolerance) { + std::ifstream fileStream1(file1); + std::ifstream fileStream2(file2); + if (!fileStream1.is_open()) throw std::runtime_error("Could not file: `" + file1.string() + "`"); + if (!fileStream2.is_open()) throw std::runtime_error("Could not file: `" + file2.string() + "`"); + + std::string line1, line2; + bool eof1, eof2; + // we do **NOT** short circuit here, so we use `&` not `&&` + while ( + !((eof1 = !std::getline(fileStream1, line1))) + & + !((eof2 = !std::getline(fileStream2, line2))) + ){ + if (line1 == line2) continue; + double value; + std::istringstream lineStream1{line1}; + std::vector lineValues1; + while (lineStream1 >> value) lineValues1.push_back(value); + std::istringstream lineStream2{line2}; + std::vector lineValues2; + while (lineStream2 >> value) lineValues2.push_back(value); + if (lineValues1.size() != lineValues2.size()) throw std::runtime_error("Length of data does not match"); + + for (int i = 0; i < lineValues2.size(); i++) { + if (lineValues1[i] == lineValues2[i]) continue; + double adjustedTolerance = tolerance * std::max(std::abs(lineValues1[i]), std::abs(lineValues2[i])); + if (std::abs(lineValues1[i] - lineValues2[i]) > adjustedTolerance) + throw std::runtime_error("Values outside tolerance"); + } + } + + // last check; make sure they are both eof! + if (eof1 ^ eof2) { + throw std::runtime_error("Files do not contain same number of lines."); + } +} \ No newline at end of file From 802189242d8a83c6f32d618996655ad1bdaa3b2c Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Wed, 19 Nov 2025 13:20:00 -0500 Subject: [PATCH 07/26] Made messaging vs non-messaging more flexible --- IDAWin/SundialsSolverInterface.cpp | 46 -------------------- IDAWin/SundialsSolverStandalone.cpp | 2 +- IDAWin/VCellSolverFactory.cpp | 20 +++++---- conan-profiles/CI-CD/MacOS-AMD64_profile.txt | 2 +- tests/unit/smoke_test.cpp | 25 +++++------ 5 files changed, 24 insertions(+), 71 deletions(-) diff --git a/IDAWin/SundialsSolverInterface.cpp b/IDAWin/SundialsSolverInterface.cpp index ae3b8fdeb..e52cc2953 100644 --- a/IDAWin/SundialsSolverInterface.cpp +++ b/IDAWin/SundialsSolverInterface.cpp @@ -15,52 +15,6 @@ #define IDA_SOLVER "IDA" void activateSolver(std::ifstream& inputFileStream, FILE* outputFile, int taskID) { - // std::string solver; - // - // while (!inputFileStream.eof()) { // Note break statement if "SOLVER" encountered - // std::string nextToken; - // inputFileStream >> nextToken; - // if (nextToken.empty()) continue; - // if (nextToken[0] == '#') getline(inputFileStream, nextToken); - // else if (nextToken == "JMS_PARAM_BEGIN") { - // loadJMSInfo(inputFileStream, taskID); - // #ifdef USE_MESSAGING - // SimulationMessaging::getInstVar()->start(); // start the thread - // #endif - // } else if (nextToken == "SOLVER") { - // inputFileStream >> solver; - // break; - // } - // } - // #ifdef USE_MESSAGING - // // should only happen during testing for solver compiled with messaging but run locally. - // if (SimulationMessaging::getInstVar() == nullptr) { SimulationMessaging::create(); } - // #endif - // - // if (solver.empty()) { throw "Solver not defined "; } - // VCellSundialsSolver *vss = nullptr; - // - // if (solver == IDA_SOLVER) { - // vss = new VCellIDASolver(); - // } else if (solver == CVODE_SOLVER) { - // vss = new VCellCVodeSolver(); - // } else { - // std::stringstream ss; - // ss << "Solver " << solver << " not defined!"; - // throw ss.str(); - // } - // - // vss->solve(nullptr, true, outputFile, VCellSundialsSolver::checkStopRequested); - // delete vss; - - // Check if we have messaging and a taskID of -1; if so; fail now. - - #ifdef USE_MESSAGING - if (taskID < 0) { - throw std::runtime_error("task id of value: " + std::to_string(taskID) + " not acceptable when this library is built with messaging"); - } - #endif - VCellSolver* targetSolver = VCellSolverFactory::produceVCellSolver(inputFileStream, taskID); targetSolver->solve(nullptr, true, outputFile, VCellSundialsSolver::checkStopRequested); } diff --git a/IDAWin/SundialsSolverStandalone.cpp b/IDAWin/SundialsSolverStandalone.cpp index fb6da561b..0f7d28811 100644 --- a/IDAWin/SundialsSolverStandalone.cpp +++ b/IDAWin/SundialsSolverStandalone.cpp @@ -36,7 +36,7 @@ int parseAndRunWithArgParse(int argc, char *argv[]) { argumentParser.add_argument("input").help("path to directory with input files.").store_into(inputFilePath); argumentParser.add_argument("output").help("path to directory for output files.").store_into(outputFilePath); #ifdef USE_MESSAGING - argumentParser.add_argument("-tid").help("path to solver to run.").store_into(taskID); + argumentParser.add_argument("-tid").help("id of the job").store_into(taskID); #endif try { diff --git a/IDAWin/VCellSolverFactory.cpp b/IDAWin/VCellSolverFactory.cpp index 558454e4b..de756b035 100644 --- a/IDAWin/VCellSolverFactory.cpp +++ b/IDAWin/VCellSolverFactory.cpp @@ -103,6 +103,12 @@ VCellSolverInputBreakdown VCellSolverFactory::parseInputFile(std::ifstream& inpu else throw VCell::Exception("Unexpected token \"" + nextToken + "\" in the input file!"); } + + + #ifdef USE_MESSAGING + // Since messaging assumes we have a job requiring messaging, we should initialize a "default" messaging handler + if (NULL == SimulationMessaging::getInstVar()) SimulationMessaging::create(); + #endif return inputBreakdown; } @@ -191,7 +197,7 @@ static void readEvents(std::istream &inputStream, VCellSolverInputBreakdown& inp //eventComponents.eventAssignments.emplace_back(varIndex, assignmentExpression); // should try this in the future, more descriptive } break; - } else { throw VCell::Exception("Unexpected token \"" + token + "\" in the input file!"); } + } else { throw VCell::Exception("Unexpected event token \"" + token + "\" in the input file!"); } } inputBreakdown.eventSettings.EVENTS.push_back(std::move(eventComponents)); } @@ -421,13 +427,10 @@ static void collectSteadyStateTerms(std::ifstream& inputFileStream, VCellSolverI static void loadJMSInfo(std::istream &ifsInput, int taskID) { #ifndef USE_MESSAGING - return; // Only useful for messaging; let's not waste time! - #else - - if (taskID < 0) { - SimulationMessaging::create(); - return; // No need to do any parsing - } + // We'll still parse the section, as we can still execute the simulation; we'll just toss the values! + std::cerr << "WARNING: Input file expects messaging capabilities; this build does not support JMS messaging!" << std::endl; + #endif + std::string broker; std::string smqUserName; std::string password; @@ -459,6 +462,7 @@ static void loadJMSInfo(std::istream &ifsInput, int taskID) { else if (nextToken == "JOB_INDEX") ifsInput >> jobIndex; } + #ifdef USE_MESSAGING SimulationMessaging::create(broker.c_str(), smqUserName.c_str(), password.c_str(), qName.c_str(), topicName.c_str(), vCellUsername.c_str(), simKey, jobIndex, taskID); diff --git a/conan-profiles/CI-CD/MacOS-AMD64_profile.txt b/conan-profiles/CI-CD/MacOS-AMD64_profile.txt index 2d4556f59..bc4007fdf 100644 --- a/conan-profiles/CI-CD/MacOS-AMD64_profile.txt +++ b/conan-profiles/CI-CD/MacOS-AMD64_profile.txt @@ -4,5 +4,5 @@ build_type=Release compiler=apple-clang compiler.cppstd=20 compiler.libcxx=libc++ -compiler.version=13 +compiler.version=17 os=Macos \ No newline at end of file diff --git a/tests/unit/smoke_test.cpp b/tests/unit/smoke_test.cpp index ac7485a7f..6a6f66893 100644 --- a/tests/unit/smoke_test.cpp +++ b/tests/unit/smoke_test.cpp @@ -16,16 +16,13 @@ void compare(const std::filesystem::path& file1, const std::filesystem::path& file2, float tolerance); - -TEST(SmokeTest, MessagingRequiresJobID) { - #ifndef USE_MESSAGING - return; // no need to test this - #endif +TEST(SmokeTest, UserProvidesFilesWithoutJMS) { + constexpr int taskID = -1, hashID = 1489333437; const std::filesystem::path RESOURCE_DIRECTORY{RESOURCE_DIR}; - const std::filesystem::path OUTPUT_TARGET{RESOURCE_DIRECTORY /"SimID_1489333437_0_.ida",}; + const std::filesystem::path OUTPUT_TARGET{RESOURCE_DIRECTORY /std::format("SimID_{}_0_.ida", hashID)}; const std::array NECESSARY_FILES{ - RESOURCE_DIRECTORY /"SimID_1489333437_0_.cvodeInput", - RESOURCE_DIRECTORY /"SimID_1489333437_0_.ida.expected" + RESOURCE_DIRECTORY /std::format("SimID_{}_0_.cvodeInput", hashID), + RESOURCE_DIRECTORY /std::format("SimID_{}_0_.ida.expected", hashID) }; for (const auto& file : NECESSARY_FILES) { assert(std::filesystem::exists(file)); @@ -39,16 +36,14 @@ TEST(SmokeTest, MessagingRequiresJobID) { throw std::runtime_error("Could not open output file[" + OUTPUT_TARGET.string() + "] for writing."); } - EXPECT_THROW(activateSolver(inputFileStream, outputFile, -1), std::runtime_error); + activateSolver(inputFileStream, outputFile, taskID); fclose(outputFile); + + compare(OUTPUT_TARGET, NECESSARY_FILES[1], 1e-7); } -TEST(SmokeTest, ConfirmExecution) { - #ifdef USE_MESSAGING - const int taskID = 2025, hashID = 256118677; - #else - const int taskID = -1, hashID = 1489333437; - #endif +TEST(SmokeTest, UserProvidesFilesWithJMS) { + constexpr int taskID = 2025, hashID = 256118677; const std::filesystem::path RESOURCE_DIRECTORY{RESOURCE_DIR}; const std::filesystem::path OUTPUT_TARGET{RESOURCE_DIRECTORY /std::format("SimID_{}_0_.ida", hashID)}; const std::array NECESSARY_FILES{ From ed75ea25eb4566649579f9ad7633633aa73c9d2c Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Tue, 9 Dec 2025 13:12:55 -0500 Subject: [PATCH 08/26] Create separate functionality classes --- VCellMessaging/CMakeLists.txt | 17 +- .../include/VCELL/CurlProxyClasses.h | 90 ++++++++ VCellMessaging/include/VCELL/JobEventStatus.h | 29 +++ .../include/VCELL/MessageEventManager.h | 62 ++++++ VCellMessaging/include/VCELL/WorkerEvent.h | 51 +++++ VCellMessaging/src/CurlProxyClasses.cpp | 207 ++++++++++++++++++ VCellMessaging/src/JobEventStatus.cpp | 23 ++ VCellMessaging/src/MessageEventManager.cpp | 102 +++++++++ 8 files changed, 577 insertions(+), 4 deletions(-) create mode 100644 VCellMessaging/include/VCELL/CurlProxyClasses.h create mode 100644 VCellMessaging/include/VCELL/JobEventStatus.h create mode 100644 VCellMessaging/include/VCELL/MessageEventManager.h create mode 100644 VCellMessaging/include/VCELL/WorkerEvent.h create mode 100644 VCellMessaging/src/CurlProxyClasses.cpp create mode 100644 VCellMessaging/src/JobEventStatus.cpp create mode 100644 VCellMessaging/src/MessageEventManager.cpp diff --git a/VCellMessaging/CMakeLists.txt b/VCellMessaging/CMakeLists.txt index 35772800b..633117474 100644 --- a/VCellMessaging/CMakeLists.txt +++ b/VCellMessaging/CMakeLists.txt @@ -6,11 +6,18 @@ list(APPEND SOURCES GitDescribe.h) set(HEADER_FILES include/VCELL/SimulationMessaging.h include/VCELL/GitDescribe.h + include/VCELL/MessageEventManager.h + include/VCELL/CurlProxyClasses.h + include/VCELL/JobEventStatus.h + include/VCELL/WorkerEvent.h ) set(SRC_FILES src/SimulationMessaging.cpp - "${CMAKE_CURRENT_BINARY_DIR}/GitDescribe.cpp") + "${CMAKE_CURRENT_BINARY_DIR}/GitDescribe.cpp" + src/MessageEventManager.cpp + src/CurlProxyClasses.cpp + src/JobEventStatus.cpp) include_directories(include) @@ -20,9 +27,11 @@ target_include_directories(vcellmessaging INTERFACE $ $ # /include ) -if (${OPTION_TARGET_MESSAGING}) - message(STATUS "CURL_LIBRARIES = '${CURL_LIBRARIES}'") - message(STATUS "CURL_INCLUDE_DIR = '${CURL_INCLUDE_DIR}'") +if (OPTION_TARGET_MESSAGING) + if (OPTION_EXTRA_CONFIG_INFO) + message(STATUS "CURL_LIBRARIES = '${CURL_LIBRARIES}'") + message(STATUS "CURL_INCLUDE_DIR = '${CURL_INCLUDE_DIR}'") + endif () target_link_libraries(vcellmessaging ${CURL_LIBRARIES} Threads::Threads) target_compile_definitions(vcellmessaging PUBLIC USE_MESSAGING=1) diff --git a/VCellMessaging/include/VCELL/CurlProxyClasses.h b/VCellMessaging/include/VCELL/CurlProxyClasses.h new file mode 100644 index 000000000..54de41acc --- /dev/null +++ b/VCellMessaging/include/VCELL/CurlProxyClasses.h @@ -0,0 +1,90 @@ +// +// Created by Logan Drescher on 11/25/25. +// +#ifndef VCELL_ODE_NUMERICS_CURLPROXY_H +#define VCELL_ODE_NUMERICS_CURLPROXY_H +#include +#include + +#include "VCELL/WorkerEvent.h" + + +class AbstractCurlProxy { + public: + virtual ~AbstractCurlProxy() =default; + + virtual void sendStatus(WorkerEvent* event) = 0; + virtual void keepAlive() = 0; +}; + +class NullCurlProxy final : public AbstractCurlProxy { + public: + NullCurlProxy() =default; + ~NullCurlProxy()override =default; + void sendStatus(WorkerEvent* event) override {} + void keepAlive() override {} +}; + +#ifdef USE_MESSAGING +class CurlProxy final : public AbstractCurlProxy { + public: + CurlProxy(long simKey, int taskID, int jobIndex, const std::string& vcusername, const std::string& broker, int ttlLow, int ttlHigh); + ~CurlProxy() override; + + void sendStatus(WorkerEvent* event) override; + void keepAlive() override; + private: + static const char* TIMETOLIVE_PROPERTY; + static const char* DELIVERYMODE_PROPERTY; + static const char* DELIVERYMODE_PERSISTENT_VALUE; + static const char* DELIVERYMODE_NONPERSISTENT_VALUE; + static const char* PRIORITY_PROPERTY; + static const char* PRIORITY_DEFAULT_VALUE; + + static const char* MESSAGE_TYPE_PROPERTY; + static const char* MESSAGE_TYPE_WORKEREVENT_VALUE; + + static const char* USERNAME_PROPERTY; + static const char* HOSTNAME_PROPERTY; + static const char* SIMKEY_PROPERTY; + static const char* TASKID_PROPERTY; + static const char* JOBINDEX_PROPERTY; + static const char* WORKEREVENT_STATUS; + static const char* WORKEREVENT_PROGRESS; + static const char* WORKEREVENT_TIMEPOINT; + static const char* WORKEREVENT_STATUSMSG; + + int TTL_LOW_PRIORITY; + int TTL_HIGH_PRIORITY; + + + long simKey; + int taskID; + int jobIndex; + std::string hostname; + std::string broker; + std::string vcusername; + time_t lastTimeEventWasSent; +}; + + + +// This comment is transplanted from when this logic was a part of Simulation Messaging; originally written by Jim Schaff +// Documentation for the ActiveMQ restful API is missing, must see source code +// +// https://github.com/apache/activemq/blob/master/activemq-web/src/main/java/org/apache/activemq/web/MessageServlet.java +// https://github.com/apache/activemq/blob/master/activemq-web/src/main/java/org/apache/activemq/web/MessageServletSupport.java +// +// currently, the "web" api seems to use the same credentials as the "web console" ... defaults to admin:admin. +// TODO: pass in credentials, and protect them better (consider HTTPS). +// +/* + PROPERTIES="JMSDeliveryMode=persistent&JMSTimeToLive=3000" + PROPERTIES="${PROPERTIES}&SimKey=12446271133&JobIndex=0&TaskID=0&UserName=schaff" + PROPERTIES="${PROPERTIES}&MessageType=WorkerEvent&WorkerEvent_Status=1001&WorkerEvent_StatusMsg=Running" + PROPERTIES="${PROPERTIES}&WorkerEvent_TimePoint=2.0&WorkerEvent_Progress=0.4&HostName=localhost" + curl -XPOST "http://admin:admin@`hostname`:8165/api/message/workerEvent?type=queue&${PROPERTIES}" +*/ +#endif //USE_MESSAGING + +#endif //VCELL_ODE_NUMERICS_CURLPROXY_H \ No newline at end of file diff --git a/VCellMessaging/include/VCELL/JobEventStatus.h b/VCellMessaging/include/VCELL/JobEventStatus.h new file mode 100644 index 000000000..5c6648290 --- /dev/null +++ b/VCellMessaging/include/VCELL/JobEventStatus.h @@ -0,0 +1,29 @@ +// +// Created by Logan Drescher on 11/25/25. +// + +#ifndef VCELL_ODE_NUMERICS_JOBEVENTSTATUS_H +#define VCELL_ODE_NUMERICS_JOBEVENTSTATUS_H +#include +// enum JobEventStatus { +// JOB_STARTING = 999, +// JOB_DATA = 1000, +// JOB_PROGRESS = 1001, +// JOB_COMPLETED = 1003, +// JOB_FAILURE = 1002, +// JOB_ALIVE = 1004, +// }; + +namespace JobEvent { + enum Status { + JOB_STARTING = 999, + JOB_DATA = 1000, + JOB_PROGRESS = 1001, + JOB_COMPLETED = 1003, + JOB_FAILURE = 1002, + JOB_ALIVE = 1004, + }; + std::string toString(Status status); +} + +#endif //VCELL_ODE_NUMERICS_JOBEVENTSTATUS_H \ No newline at end of file diff --git a/VCellMessaging/include/VCELL/MessageEventManager.h b/VCellMessaging/include/VCELL/MessageEventManager.h new file mode 100644 index 000000000..d9d425ea9 --- /dev/null +++ b/VCellMessaging/include/VCELL/MessageEventManager.h @@ -0,0 +1,62 @@ +// +// Created by Logan Drescher on 11/24/25. +// +#ifndef VCELL_ODE_NUMERICS_MESSAGEEVENTQUEUE_H +#define VCELL_ODE_NUMERICS_MESSAGEEVENTQUEUE_H + +#include +#include +#include +#include +#include +#include "WorkerEvent.h" +/* + * We want to avoid threads being idle, while processing updates + * Components: + * 1) An "active" boolean (locked by a mutex) that indicates whether the queue is being processed. + * 1a) We must ensure that all relevant information a thread would use to ensure it's done, is always locked by the mutex. + * 2) A jthread dedicated to processing the queue when it has items, and sleeping when it does not + * 3) A condition variable used to ensure the jthread only runs when it needs to. + * + * LOCK ORDERING + * We are using two mutexes that could conflict with each other + * 1) Mutex for whether the worker is active + * 2) Mutex for access to the event queue + * + * There is a potential deadlock if the worker gets ownership of his isActive mutex, + * while another thread owns the event queue mutex. This is because before the worker decides to "clock out", + * they would check if they have work in the queue. Since the queue is owned, deadlock. + * *****ALWAYS LOCK THE IS_WORKER_ACTIVE MUTEX BEFORE THE QUEUE MUTEX******* + * + * The inverse is possible if the code is changed so that: the worker owns the queue the entire time its doing work, + * until it's empty. At the time of this warning, this is not the case. + */ +class MessageEventManager { + public: + explicit MessageEventManager(std::function sendUpdateFunction); + virtual ~MessageEventManager(); + void enqueue(JobEvent::Status status, double progress, double timepoint, const char *eventMessage); + void enqueue(JobEvent::Status status, double progress, double timepoint); + void enqueue(JobEvent::Status status, const char *eventMessage); + void requestStopAndWaitForIt(); + bool stopWasCalled(); + + private: + void processQueue(); + void processEvent(WorkerEvent* event); + void enqueue(WorkerEvent*); + + std::mutex timeClockMutex; + + bool stopRequested; + std::condition_variable requestedStopForeman; + std::mutex stopRequestedMutex; + std::queue eventQueue; + std::mutex queuetex; + + std::jthread eventQueueProcessingWorkerThread; + std::condition_variable needMessageProcessingForeman; + std::function sendUpdateFunction; +}; + +#endif //VCELL_ODE_NUMERICS_MESSAGEEVENTQUEUE_H \ No newline at end of file diff --git a/VCellMessaging/include/VCELL/WorkerEvent.h b/VCellMessaging/include/VCELL/WorkerEvent.h new file mode 100644 index 000000000..13e799090 --- /dev/null +++ b/VCellMessaging/include/VCELL/WorkerEvent.h @@ -0,0 +1,51 @@ +// +// Created by Logan Drescher on 11/25/25. +// + +#ifndef VCELL_ODE_NUMERICS_WORKEREVENT_H +#define VCELL_ODE_NUMERICS_WORKEREVENT_H +#include + +#include "JobEventStatus.h" + +struct WorkerEvent { + JobEvent::Status status; + double progress; + double timepoint; + std::string eventMessage; + + WorkerEvent(const WorkerEvent* aWorkerEvent) { + status = aWorkerEvent->status; + progress = aWorkerEvent->progress; + timepoint = aWorkerEvent->timepoint; + eventMessage = aWorkerEvent->eventMessage; + } + + WorkerEvent(JobEvent::Status status, double progress, double timepoint, const char *eventMessage) + :status(status), + progress(progress), + timepoint(timepoint), + eventMessage(eventMessage) {} + + WorkerEvent(JobEvent::Status status, double progress, double timepoint) + :status(status), + progress(progress), + timepoint(timepoint){} + + + WorkerEvent(JobEvent::Status arg_status, const char* eventMessage) + :status(arg_status), + progress(0), + timepoint(0), + eventMessage(eventMessage) {} + + bool equals(const WorkerEvent* aWorkerEvent) const { + return nullptr != aWorkerEvent + && this->status == aWorkerEvent->status + && this->progress == aWorkerEvent->progress + && this->timepoint == aWorkerEvent->timepoint + && this->eventMessage == aWorkerEvent->eventMessage; + } +}; + +#endif //VCELL_ODE_NUMERICS_WORKEREVENT_H \ No newline at end of file diff --git a/VCellMessaging/src/CurlProxyClasses.cpp b/VCellMessaging/src/CurlProxyClasses.cpp new file mode 100644 index 000000000..78f07f2ae --- /dev/null +++ b/VCellMessaging/src/CurlProxyClasses.cpp @@ -0,0 +1,207 @@ +// +// Created by Logan Drescher on 11/25/25. +// +#include "VCELL/CurlProxyClasses.h" +#include +#include + + +std::string trim(const std::string& str) { + std::string trimmedStr{str}; + auto whitespaceLambda = [](const unsigned char ch) { return !std::isspace(ch); }; + const auto endOfLeftmostWhitespace = std::ranges::find_if(trimmedStr, whitespaceLambda); + trimmedStr.erase(trimmedStr.begin(), endOfLeftmostWhitespace); + // need to one at a time, because positions will shift on `string::erase` + const auto startOfRightmostWhitespace = std::ranges::find_if(trimmedStr.rbegin(), trimmedStr.rend(), whitespaceLambda).base(); + trimmedStr.erase(startOfRightmostWhitespace, trimmedStr.end()); + return trimmedStr; +} + +#ifdef USE_MESSAGING +#include +#include +#include + +#include "VCELL/JobEventStatus.h" +#include "VCELL/SimulationMessaging.h" + +const char* CurlProxy::TIMETOLIVE_PROPERTY = "JMSTimeToLive"; +const char* CurlProxy::DELIVERYMODE_PROPERTY = "JMSDeliveryMode"; +const char* CurlProxy::DELIVERYMODE_PERSISTENT_VALUE = "persistent"; +const char* CurlProxy::DELIVERYMODE_NONPERSISTENT_VALUE = "nonpersistent"; +const char* CurlProxy::PRIORITY_PROPERTY = "JMSPriority"; +const char* CurlProxy::PRIORITY_DEFAULT_VALUE = "5"; + +const char* CurlProxy::MESSAGE_TYPE_PROPERTY = "MessageType"; +const char* CurlProxy::MESSAGE_TYPE_WORKEREVENT_VALUE = "WorkerEvent"; + +const char* CurlProxy::USERNAME_PROPERTY = "UserName"; +const char* CurlProxy::HOSTNAME_PROPERTY = "HostName"; +const char* CurlProxy::SIMKEY_PROPERTY = "SimKey"; +const char* CurlProxy::TASKID_PROPERTY = "TaskID"; +const char* CurlProxy::JOBINDEX_PROPERTY = "JobIndex"; +const char* CurlProxy::WORKEREVENT_STATUS = "WorkerEvent_Status"; +const char* CurlProxy::WORKEREVENT_PROGRESS = "WorkerEvent_Progress"; +const char* CurlProxy::WORKEREVENT_TIMEPOINT = "WorkerEvent_TimePoint"; +const char* CurlProxy::WORKEREVENT_STATUSMSG = "WorkerEvent_StatusMsg"; + +CurlProxy::CurlProxy(const long simKey, const int taskID, const int jobIndex, const std::string& vcusername, const std::string& broker, const int ttlLow, const int ttlHigh) { + curl_global_init(CURL_GLOBAL_ALL); + this->TTL_LOW_PRIORITY = ttlLow; + this->TTL_HIGH_PRIORITY = ttlHigh; + + this->simKey = simKey; + this->taskID = taskID; + this->jobIndex = jobIndex; + this->broker = broker; + this->vcusername = vcusername; + + std::array buf{}; + if (-1 == gethostname(buf.data(), buf.size())) + throw std::runtime_error("Unable to retrieve hostname"); + this->hostname = std::string(buf.data()); + + this->lastTimeEventWasSent = 0; + time(&this->lastTimeEventWasSent); // Update with current time +} + +CurlProxy::~CurlProxy() { + curl_global_cleanup(); +} + + +void CurlProxy::sendStatus(WorkerEvent* event) { + CURL *curlHandler = curl_easy_init(); + if (!curlHandler) { + #ifdef USE_MESSAGING + throw std::runtime_error("Unable to initialize CURL"); + #else + return; + #endif + } + std::stringstream ss_url; + ss_url << "http://" << "admin" << ":" << "admin" << "@" << this->broker << "/api/message/workerEvent?type=queue&"; + switch (event->status) { + case JobEvent::JOB_DATA: + case JobEvent::JOB_PROGRESS: + ss_url << PRIORITY_PROPERTY << "=" << PRIORITY_DEFAULT_VALUE << "&"; + ss_url << TIMETOLIVE_PROPERTY << "=" << this->TTL_LOW_PRIORITY << "&"; + ss_url << DELIVERYMODE_PROPERTY << "=" << DELIVERYMODE_NONPERSISTENT_VALUE << "&"; + break; + case JobEvent::JOB_STARTING: + case JobEvent::JOB_COMPLETED: + case JobEvent::JOB_FAILURE: + ss_url << PRIORITY_PROPERTY << "=" << PRIORITY_DEFAULT_VALUE << "&"; + ss_url << TIMETOLIVE_PROPERTY << "=" << this->TTL_HIGH_PRIORITY << "&"; + ss_url << DELIVERYMODE_PROPERTY << "=" << DELIVERYMODE_PERSISTENT_VALUE << "&"; + break; + case JobEvent::JOB_ALIVE: + throw std::runtime_error(std::format("SimulationMessaging::sendStatus: `{}` is not recognized",JobEvent::toString(event->status))); + } + + ss_url << MESSAGE_TYPE_PROPERTY << "=" << MESSAGE_TYPE_WORKEREVENT_VALUE << "&"; + ss_url << USERNAME_PROPERTY << "=" << this->vcusername << "&"; + ss_url << HOSTNAME_PROPERTY << "=" << this->hostname << "&"; + ss_url << SIMKEY_PROPERTY << "=" << this->simKey << "&"; + ss_url << TASKID_PROPERTY << "=" << this->taskID << "&"; + ss_url << JOBINDEX_PROPERTY << "=" << this->jobIndex << "&"; + + ss_url << WORKEREVENT_STATUS << "=" << event->status << "&"; + + std::string revisedMsg{event->eventMessage}; + if (!revisedMsg.empty()) { + revisedMsg = trim(revisedMsg); + //status message is only 2048 chars long in database, need null char + if (revisedMsg.size() > 2047) revisedMsg.resize(2047); + + for (char & i : revisedMsg) { + switch (i) { + // these characters are not valid both in database and in messages as a property + case '\r': + case '\n': + case '\'': + case '\"': + i = ' '; + break; + default: continue; + } + } + } + + if (!revisedMsg.empty()) { + if(char *output = curl_easy_escape(curlHandler, revisedMsg.c_str(), static_cast(revisedMsg.size()))) { + ss_url << WORKEREVENT_STATUSMSG << "=" << output << "&"; + curl_free(output); + } + } + + ss_url << WORKEREVENT_PROGRESS << "=" << event->progress << "&"; + ss_url << WORKEREVENT_TIMEPOINT << "=" << event->timepoint; + + std::string s_url = ss_url.str(); + const char* messaging_http_url = s_url.c_str(); + curl_easy_setopt(curlHandler, CURLOPT_URL, messaging_http_url); + + std::cout << "curl -XPOST " << messaging_http_url << std::endl; + + // print message to stdout + std::cout << "!!!SimulationMessaging::sendStatus [" << this->simKey << ":" << JobEvent::toString(event->status); + if (!revisedMsg.empty()) { + std::cout << ":" << revisedMsg; + } else { + std::cout << ":" << event->progress << ":" << event->timepoint; + } + std::cout << "]" << std::endl; + + + + // std::stringstream ss_body; + // ss_body << "empty message body"; // one way to force a POST verb with libcurl, probably better ways. + // std::string s_body = ss_body.str(); + // const char* postfields = s_body.c_str(); + curl_easy_setopt(curlHandler, CURLOPT_POSTFIELDS, ""); + + + // Perform the request, res will get the return code + // Check for errors + if(CURLcode res = curl_easy_perform(curlHandler); res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + + // always cleanup + curl_easy_cleanup(curlHandler); +} + +void CurlProxy::keepAlive() { + CURL *curlHandler = curl_easy_init(); + if (!curlHandler) { + #ifdef USE_MESSAGING + throw std::runtime_error("Unable to initialize CURL"); + #else + return; + #endif + } + // First set the URL that is about to receive our POST. This URL can + // just as well be a https:// URL if that is what should receive the + // data. + curl_easy_setopt(curlHandler, CURLOPT_URL, "http://localhost:6161/message/workerevent?readTimeout=20000&type=queue"); + + + // Now specify the POST data + std::stringstream ss; + ss << WORKEREVENT_STATUS << "=" << JobEvent::JOB_ALIVE; + + + curl_easy_setopt(curlHandler, CURLOPT_POSTFIELDS, ss.str().c_str()); + + // Perform the request, res will get the return code + + // Check for errors + //TODO: Potentially move this to outer scope using exceptions; we shouldn't be quiet here + if (const CURLcode res = curl_easy_perform(curlHandler); res != CURLE_OK) + std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl; + + curl_easy_cleanup(curlHandler); // always cleanup + time(&this->lastTimeEventWasSent); +} + +#endif \ No newline at end of file diff --git a/VCellMessaging/src/JobEventStatus.cpp b/VCellMessaging/src/JobEventStatus.cpp new file mode 100644 index 000000000..22281f510 --- /dev/null +++ b/VCellMessaging/src/JobEventStatus.cpp @@ -0,0 +1,23 @@ +// +// Created by Logan Drescher on 11/25/25. +// +#include + +std::string JobEvent::toString(const Status status) { + switch (status) { + case JOB_ALIVE: + return "JOB_ALIVE"; + case JOB_FAILURE: + return "JOB_DEAD"; + case JOB_PROGRESS: + return "JOB_PROGRESS"; + case JOB_COMPLETED: + return "JOB_COMPLETED"; + case JOB_DATA: + return "JOB_DATA"; + case JOB_STARTING: + return "JOB_STARTING"; + default: + throw std::runtime_error("Unknown status type"); + } +} diff --git a/VCellMessaging/src/MessageEventManager.cpp b/VCellMessaging/src/MessageEventManager.cpp new file mode 100644 index 000000000..4bc775328 --- /dev/null +++ b/VCellMessaging/src/MessageEventManager.cpp @@ -0,0 +1,102 @@ +// +// Created by Logan Drescher on 11/24/25. +// +#include +#include +#include + +MessageEventManager::MessageEventManager(std::function sendUpdateFunction): + stopRequested{false}, + sendUpdateFunction{std::move(sendUpdateFunction)}, + eventQueueProcessingWorkerThread([this]() {this->processQueue();}) // Should auto-start thread +{ + +} + +MessageEventManager::~MessageEventManager() { + if (!this->stopRequested) this->requestStopAndWaitForIt(); +} + +void MessageEventManager::enqueue(const JobEvent::Status status, const double progress, const double timepoint, const char *eventMessage) { + this->enqueue(new WorkerEvent(status, progress, timepoint, eventMessage)); +} + +void MessageEventManager::enqueue(const JobEvent::Status status, const double progress, const double timepoint) { + this->enqueue(status, progress, timepoint, ""); +} + +void MessageEventManager::enqueue(const JobEvent::Status status, const char *eventMessage) { + this->enqueue(status, 0, 0, eventMessage); +} + +void MessageEventManager::requestStopAndWaitForIt() { + std::unique_lock stopRequestedLock{this->stopRequestedMutex}; + this->stopRequested = true; + this->needMessageProcessingForeman.notify_all(); + this->requestedStopForeman.wait(stopRequestedLock, [this]()->bool{return this->eventQueue.empty();}); +} + +bool MessageEventManager::stopWasCalled() { + std::unique_lock stopRequestedLock{this->stopRequestedMutex}; + return this->stopRequested; + +} + +/////////////////////////////////////////////////// +/// Private Methods /// +/////////////////////////////////////////////////// + +void MessageEventManager::processQueue() { + while (true) { + WorkerEvent* event; + {// START Clock-out Scope // + // The order of the locks is important! See Header File + std::unique_lock checkIfTheresWorkLock{this->timeClockMutex}; // This is to prevent loop processing while `enqueue` is being called + std::unique_lock shouldBeActiveLock(this->queuetex); + if (this->eventQueue.empty()) { + { // START stop-requested Scope + std::unique_lock stopRequestedLock{this->stopRequestedMutex}; + if (this->stopRequested) { + this->requestedStopForeman.notify_all(); + return; // We're all done; since this should be done on a jthread, this should also trigger a join. + } + } // END stop-requested Scope + checkIfTheresWorkLock.unlock(); // Need to allow enqueuing, can't do that with this locked, and we can't rescope but unique_lock order matters above! + // The worker can stop working, and "get some sleep" + // Wait for the worker to be "prodded" (via `needMessagingForeman.notify_one()`), AND the event queue is not empty + // Note that this `wait()` call, by design, unlocks the queue mutex, until it is "prodded". + this->needMessageProcessingForeman.wait(shouldBeActiveLock); + if (this->eventQueue.empty()) continue; // Probably means we need to check if stop was requested again + } + event = this->eventQueue.front(); + this->eventQueue.pop(); + }// END Clock-out Scope // + + // Process Event + this->processEvent(event); + // Remember to delete the event! We need the memory back! + delete event; + } +} + +void MessageEventManager::processEvent(WorkerEvent* event) { + this->sendUpdateFunction(event); +} + +void MessageEventManager::enqueue(WorkerEvent* event) { + // The order of the locks is important! See Header File + std::lock_guard workerIsActiveLock{this->timeClockMutex}; // Need to lock to prevent worker from "clocking out" while we set this up + std::lock_guard stopRequestedLock{this->stopRequestedMutex}; // We scope this lock to the whole function; "last in the door" policy + if (this->stopRequested) { + std::cerr << "A new event was added to the messaging queue, but this queue has had `stop` requested!" << std::endl; + delete event; + return; // note: on this return function scope ends, and thus so too does out function-scoped locks + } + + {// START Emplace Scope // + std::lock_guard queueLock{this->queuetex}; + this->eventQueue.emplace(event); + }// END Emplace Scope // + + this->needMessageProcessingForeman.notify_one(); // Nudges one sleeping worker awake. If the worker is awake...this does nothing; as it should. +} \ No newline at end of file From 100e3024e7b4ab180c8dd8539521547da4af8245 Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Tue, 9 Dec 2025 13:13:49 -0500 Subject: [PATCH 09/26] Wire in new classes into workflow; add tests --- IDAWin/SundialsSolverInterface.cpp | 37 +- IDAWin/SundialsSolverStandalone.cpp | 45 +- IDAWin/VCellCVodeSolver.cpp | 2 +- IDAWin/VCellIDASolver.cpp | 2 +- IDAWin/VCellSolverFactory.cpp | 8 +- IDAWin/VCellSundialsSolver.cpp | 6 +- .../include/VCELL/SimulationMessaging.h | 291 ++---- VCellMessaging/src/SimulationMessaging.cpp | 864 ++++-------------- tests/CMakeLists.txt | 3 +- tests/unit/message_processing_test.cpp | 66 ++ 10 files changed, 367 insertions(+), 957 deletions(-) create mode 100644 tests/unit/message_processing_test.cpp diff --git a/IDAWin/SundialsSolverInterface.cpp b/IDAWin/SundialsSolverInterface.cpp index e52cc2953..7aadee250 100644 --- a/IDAWin/SundialsSolverInterface.cpp +++ b/IDAWin/SundialsSolverInterface.cpp @@ -10,12 +10,45 @@ #include #include #include +#include +#include "StoppedByUserException.h" #define CVODE_SOLVER "CVODE" #define IDA_SOLVER "IDA" +void errExit(int returnCode, const std::string &errorMsg); + void activateSolver(std::ifstream& inputFileStream, FILE* outputFile, int taskID) { - VCellSolver* targetSolver = VCellSolverFactory::produceVCellSolver(inputFileStream, taskID); - targetSolver->solve(nullptr, true, outputFile, VCellSundialsSolver::checkStopRequested); + try { + VCellSolver* targetSolver = VCellSolverFactory::produceVCellSolver(inputFileStream, taskID); + targetSolver->solve(nullptr, true, outputFile, VCellSundialsSolver::checkStopRequested); + } catch (const char *ex) { + errExit(-1, ex); + } catch (std::string &ex) { + errExit(-2, ex); + } catch (StoppedByUserException&) { + errExit(0, "Execution Stopped By User"); + } catch (VCell::Exception &ex) { + errExit(-3, ex.getMessage()); + } catch (const std::exception& err) { + errExit(-4, err.what()); + } catch (...) { + errExit(-5, "unknown error"); + } + + // cleanup + if (SimulationMessaging::getInstVar() != nullptr) { + SimulationMessaging::getInstVar()->waitUntilFinished(); + delete SimulationMessaging::getInstVar(); + } +} + +void errExit(int returnCode, const std::string &errorMsg) { + #ifdef USE_MESSAGING + if (returnCode && SimulationMessaging::getInstVar() && !SimulationMessaging::getInstVar()->isStopRequested()) { + SimulationMessaging::getInstVar()->setWorkerEvent(JobEvent::JOB_FAILURE, errorMsg.c_str()); + } + #endif + std::cerr << errorMsg << std::endl; } diff --git a/IDAWin/SundialsSolverStandalone.cpp b/IDAWin/SundialsSolverStandalone.cpp index 0f7d28811..ed5f9bbf6 100644 --- a/IDAWin/SundialsSolverStandalone.cpp +++ b/IDAWin/SundialsSolverStandalone.cpp @@ -1,8 +1,7 @@ // Messaging -#ifdef USE_MESSAGING + #include #include -#endif // Standard Includes #include #include @@ -18,7 +17,6 @@ #define IDA_SOLVER "IDA" int parseAndRunWithArgParse(int argc, char *argv[]); -void errExit(int returnCode, std::string &errorMsg); int main(int argc, char *argv[]) { std::cout << std::setprecision(20); @@ -58,46 +56,15 @@ int parseAndRunWithArgParse(int argc, char *argv[]) { throw std::runtime_error("Could not open output file[" + outputFilePath + "] for writing."); } activateSolver(inputFileStream, outputFile, taskID); - - } catch (const char *ex) { - errMsg += ex; - returnCode = -1; - } catch (std::string &ex) { - errMsg += ex; - returnCode = -1; - } catch (StoppedByUserException&) { - returnCode = 0; // stopped by user; - } catch (VCell::Exception &ex) { - errMsg += ex.getMessage(); - returnCode = -1; - } catch (const std::exception& err) { - errMsg += err.what(); - returnCode = -1; + } catch (const std::runtime_error& err) { + std::cerr << err.what() << std::endl; + returnCode = 5; } catch (...) { - errMsg += "unknown error"; - returnCode = -1; + std::cerr << "Unknown exception thrown." << std::endl; + returnCode = 255; } if (outputFile != NULL) { fclose(outputFile); } if (inputFileStream.is_open()) { inputFileStream.close(); } - errExit(returnCode, errMsg); return returnCode; } - -void errExit(int returnCode, std::string &errorMsg) { - #ifdef USE_MESSAGING - if (returnCode != 0) { - if (SimulationMessaging::getInstVar() != nullptr && !SimulationMessaging::getInstVar()->isStopRequested()) { - SimulationMessaging::getInstVar()->setWorkerEvent(new WorkerEvent(JOB_FAILURE, errorMsg.c_str())); - } - } - #endif - - if (returnCode != 0) std::cerr << errorMsg << std::endl; - #ifdef USE_MESSAGING - else if (SimulationMessaging::getInstVar() != nullptr) { - SimulationMessaging::getInstVar()->waitUntilFinished(); - delete SimulationMessaging::getInstVar(); - } - #endif -} diff --git a/IDAWin/VCellCVodeSolver.cpp b/IDAWin/VCellCVodeSolver.cpp index e37b7d418..1e09dd970 100644 --- a/IDAWin/VCellCVodeSolver.cpp +++ b/IDAWin/VCellCVodeSolver.cpp @@ -501,7 +501,7 @@ void VCellCVodeSolver::cvodeSolve(bool bPrintProgress, FILE* outputFile, void (* } } #ifdef USE_MESSAGING - SimulationMessaging::getInstVar()->setWorkerEvent(new WorkerEvent(JOB_COMPLETED, 1, Time)); + SimulationMessaging::getInstVar()->setWorkerEvent(JobEvent::JOB_COMPLETED, 1, Time); #endif } diff --git a/IDAWin/VCellIDASolver.cpp b/IDAWin/VCellIDASolver.cpp index 4d45a1a7f..70c70bf2b 100644 --- a/IDAWin/VCellIDASolver.cpp +++ b/IDAWin/VCellIDASolver.cpp @@ -718,7 +718,7 @@ void VCellIDASolver::idaSolve(bool bPrintProgress, FILE* outputFile, void (*chec } } #ifdef USE_MESSAGING - SimulationMessaging::getInstVar()->setWorkerEvent(new WorkerEvent(JOB_COMPLETED, 1, Time)); + SimulationMessaging::getInstVar()->setWorkerEvent(JobEvent::JOB_COMPLETED, 1, Time); #endif } diff --git a/IDAWin/VCellSolverFactory.cpp b/IDAWin/VCellSolverFactory.cpp index de756b035..3fe83577b 100644 --- a/IDAWin/VCellSolverFactory.cpp +++ b/IDAWin/VCellSolverFactory.cpp @@ -60,9 +60,9 @@ VCellSolverInputBreakdown VCellSolverFactory::parseInputFile(std::ifstream& inpu inputBreakdown.solverType = determineSolverType(solverName); } else if (nextToken == "JMS_PARAM_BEGIN") { loadJMSInfo(inputFileStream, taskID); - #ifdef USE_MESSAGING - SimulationMessaging::getInstVar()->start(); // start the thread - #endif + // #ifdef USE_MESSAGING + // SimulationMessaging::getInstVar()->start(); // start the thread + // #endif } else if (nextToken == "STARTING_TIME") { inputFileStream >> inputBreakdown.timeCourseSettings.STARTING_TIME; } else if (nextToken == "ENDING_TIME") { inputFileStream >> inputBreakdown.timeCourseSettings.ENDING_TIME; } @@ -430,7 +430,7 @@ static void loadJMSInfo(std::istream &ifsInput, int taskID) { // We'll still parse the section, as we can still execute the simulation; we'll just toss the values! std::cerr << "WARNING: Input file expects messaging capabilities; this build does not support JMS messaging!" << std::endl; #endif - + std::string broker; std::string smqUserName; std::string password; diff --git a/IDAWin/VCellSundialsSolver.cpp b/IDAWin/VCellSundialsSolver.cpp index 99286c686..3ff290601 100644 --- a/IDAWin/VCellSundialsSolver.cpp +++ b/IDAWin/VCellSundialsSolver.cpp @@ -122,7 +122,7 @@ void VCellSundialsSolver::printProgress(double currTime, double &lastPercentile, if (currTime == STARTING_TIME) { // print 0% #ifdef USE_MESSAGING - SimulationMessaging::getInstVar()->setWorkerEvent(new WorkerEvent(JOB_PROGRESS, lastPercentile, currTime)); + SimulationMessaging::getInstVar()->setWorkerEvent(JobEvent::JOB_PROGRESS, lastPercentile, currTime); #else printf("[[[progress:%lg%%]]]", lastPercentile*100.0); fflush(stdout); @@ -144,8 +144,8 @@ void VCellSundialsSolver::printProgress(double currTime, double &lastPercentile, if (lastPercentile != newPercentile) { #ifdef USE_MESSAGING SimulationMessaging::getInstVar()-> - setWorkerEvent(new WorkerEvent(JOB_PROGRESS, newPercentile, currTime)); - SimulationMessaging::getInstVar()->setWorkerEvent(new WorkerEvent(JOB_DATA, newPercentile, currTime)); + setWorkerEvent(JobEvent::JOB_PROGRESS, newPercentile, currTime); + SimulationMessaging::getInstVar()->setWorkerEvent(JobEvent::JOB_DATA, newPercentile, currTime); #else printf("[[[progress:%lg%%]]]", newPercentile*100.0); printf("[[[data:%lg]]]", currTime); diff --git a/VCellMessaging/include/VCELL/SimulationMessaging.h b/VCellMessaging/include/VCELL/SimulationMessaging.h index e5562b21a..d12be5dfa 100644 --- a/VCellMessaging/include/VCELL/SimulationMessaging.h +++ b/VCellMessaging/include/VCELL/SimulationMessaging.h @@ -1,234 +1,107 @@ #ifndef _SIMULATIONMESSAGING_H_ #define _SIMULATIONMESSAGING_H_ +#include #include -#ifdef USE_MESSAGING -#include -#include -#include - -#endif #include - -#if (defined(_WIN32) || defined(_WIN64) ) -#include -#else -#include -#include -#include #include -#endif +#include "CurlProxyClasses.h" +#include "MessageEventManager.h" +#include "WorkerEvent.h" #ifdef USE_MESSAGING -#if (!defined(_WIN32) && !defined(_WIN64) ) #include -#else -#include -#endif -static const int ONE_SECOND = 1000; -static const int ONE_MINUTE = 60 * ONE_SECOND; -static const int DEFAULT_TTL_HIGH = 10 * ONE_MINUTE; -static const int DEFAULT_TTL_LOW = ONE_MINUTE; +static constexpr int ONE_SECOND = 1000; +static constexpr int ONE_MINUTE = 60 * ONE_SECOND; +static constexpr int DEFAULT_TTL_HIGH = 10 * ONE_MINUTE; +static constexpr int DEFAULT_TTL_LOW = ONE_MINUTE; #endif -static const int WORKEREVENT_OUTPUT_MODE_STDOUT = 0; -static const int WORKEREVENT_OUTPUT_MODE_MESSAGING = 1; - -static const int JOB_STARTING = 999; -static const int JOB_DATA = 1000; -static const int JOB_PROGRESS = 1001; -static const int JOB_FAILURE = 1002; -static const int JOB_COMPLETED = 1003; -static const int JOB_WORKER_ALIVE = 1004; - -static const int DEFAULT_PRIORITY = 0; //range 0-127, the bigger number the higher priority -static const int Message_DEFAULT_PRIORITY = 0; - -struct WorkerEvent { - int status; - double progress; - double timepoint; - char* eventMessage; - - WorkerEvent(const WorkerEvent* aWorkerEvent) { - status = aWorkerEvent->status; - progress = aWorkerEvent->progress; - timepoint = aWorkerEvent->timepoint; - eventMessage = copy(aWorkerEvent->eventMessage); - } - - WorkerEvent(int arg_status, double arg_progress, double arg_timepoint, const char *arg_eventMessage) - :status(arg_status), - progress(arg_progress), - timepoint(arg_timepoint), - eventMessage(copy(arg_eventMessage)) {} - - WorkerEvent(int arg_status, double arg_progress, double arg_timepoint) - :status(arg_status), - progress(arg_progress), - timepoint(arg_timepoint), - eventMessage(0) {} - - - WorkerEvent(int arg_status, const char* arg_eventMessage) - :status(arg_status), - progress(0), - timepoint(0), - eventMessage(copy(arg_eventMessage)) {} -private: - static char *copy(const char *in) { - if (in != 0) { - size_t len = strlen(in) + 1; - char * c = new char[len + 1]; - memset(c, 0, len); - strcpy(c, in); - return c; - } - return 0; - } -public: - - bool equals(WorkerEvent* aWorkerEvent) { - if (status != aWorkerEvent->status || progress != aWorkerEvent->progress || timepoint != aWorkerEvent->timepoint) { - return false; - } - if (eventMessage != NULL && aWorkerEvent->eventMessage != NULL && strcmp(eventMessage, aWorkerEvent->eventMessage) != 0) { - return false; - } - - if (eventMessage != NULL && aWorkerEvent->eventMessage == NULL - || eventMessage == NULL && aWorkerEvent->eventMessage != NULL) { - return false; - } +enum WorkerEventOutputMode { + // ***Align values to bits*** + WORKEREVENT_OUTPUT_MODE_STDOUT = 1, + WORKEREVENT_OUTPUT_MODE_MESSAGING = 2, + // = 4 + // = 8 + WORKEREVENT_OUTPUT_MODE_ALL = 3 // This should be 2^n -1, where n is number of bits - return true; - } }; -class SimulationMessaging -{ -public: - virtual ~SimulationMessaging() throw(); - static SimulationMessaging* create(); - static SimulationMessaging* getInstVar(); - void start(); - void setWorkerEvent(WorkerEvent* workerEvent); +static constexpr int DEFAULT_PRIORITY = 0; //range 0-127, the bigger number the higher priority +static constexpr int Message_DEFAULT_PRIORITY = 0; - bool isStopRequested() { - return bStopRequested; - } - int getTaskID() { -#ifdef USE_MESSAGING - return m_taskID; -#else - return -1; -#endif - } +//TODO: Re-write so that there is a messaging handler abstract class with two children: stdOut and CUrl +class SimulationMessaging { + public: + virtual ~SimulationMessaging() noexcept; + static void initialize(); + static SimulationMessaging* create(); + static SimulationMessaging* getInstVar(); + void setWorkerEvent(JobEvent::Status status, const char *eventMessage); + void setWorkerEvent(JobEvent::Status status, double progress, double timepoint); + void setWorkerEvent(JobEvent::Status status, double progress, double timepoint, const char *eventMessage); - int getJobIndex() { -#ifdef USE_MESSAGING - return m_jobIndex; -#else - return 0; -#endif - } -#ifdef USE_MESSAGING - static SimulationMessaging* create(const char* broker, const char* smqusername, const char* passwd, const char* qname, const char* tname, - const char* vcusername, int simKey, int jobIndex, int taskID, int ttl_low=DEFAULT_TTL_LOW, int ttl_high=DEFAULT_TTL_HIGH); - void waitUntilFinished(); - friend void* startMessagingThread(void* param); -#endif + bool isStopRequested() { + return this->eventHandler.stopWasCalled(); + } -private: - SimulationMessaging(); - static SimulationMessaging *m_inst; - std::vector events; - int workerEventOutputMode; - - void sendStatus(); - bool bStopRequested; - /** - * is this a critical message type? - */ - bool criticalDelivery(const WorkerEvent & event) { - switch (event.status) { - case JOB_DATA: - case JOB_PROGRESS: - return false; - default: - return true; + [[nodiscard]] int getTaskID() const { // default = -1 + return this->taskID; } - } -#ifdef USE_MESSAGING - bool bStarted; - - SimulationMessaging(const char* broker, const char* smqusername, const char* passwd, const char* qname, const char*tname, const char* vcusername, int simKey, int jobIndex, int taskID, int ttl_low=DEFAULT_TTL_LOW, int ttl_high=DEFAULT_TTL_HIGH); - - void keepAlive(); - static char* trim(char* str); - void setupConnection (); //synchronized - void cleanup(); - - char *m_broker; - char *m_smqusername; - char *m_password; - char *m_qname; - char* m_tname; - char* m_vcusername; - int m_simKey; - int m_taskID; - int m_jobIndex; - - char* m_tListener; - - char m_hostname[256]; - int m_ttl_lowPriority; - int m_ttl_highPriority; - time_t lastSentEventTime; - - const char* getStatusString(int status); - - bool lockMessaging(); - void unlockMessaging(); - void delay(int duration); - - bool lockWorkerEvent(bool bTry=false); - void unlockWorkerEvent(); - struct WorkerEventLocker { - WorkerEventLocker(SimulationMessaging &sm_, bool bTry = false) - :sm(sm_), - locked( sm.lockWorkerEvent(false)) { - } - ~WorkerEventLocker( ) { - sm.unlockWorkerEvent(); - } - SimulationMessaging &sm; - const bool locked; - }; - -#ifdef WIN32 - CRITICAL_SECTION lockForMessaging; - CRITICAL_SECTION lockForWorkerEvent; - HANDLE hNewWorkerEvent; - HANDLE hMessagingThreadEndEvent; -#else // UNIX - pthread_t newWorkerEventThread; - pthread_mutex_t mutex_messaging; - pthread_mutex_t mutex_workerEvent; - pthread_mutex_t mutex_cond_workerEvent; - pthread_cond_t cond_workerEvent; - bool bNewWorkerEvent; -#endif + [[nodiscard]] int getJobIndex() const { // default = 0 + return m_jobIndex; + } + void waitUntilFinished(); + #ifdef USE_MESSAGING + static SimulationMessaging* create(const char* broker, const char* smqusername, const char* passwd, const char* qname, const char* tname, + const char* vcusername, int simKey, int jobIndex, int taskID, int ttl_low=DEFAULT_TTL_LOW, int ttl_high=DEFAULT_TTL_HIGH); + friend void* startMessagingThread(void* param); + #endif + + private: + // Statics + static void sendStdOutStatus(WorkerEvent*); + static bool criticalDelivery(const WorkerEvent & event) { + // is this a critical message type? + switch (event.status) { + case JobEvent::JOB_DATA: + case JobEvent::JOB_PROGRESS: + return false; + default: + return true; + } + } + static SimulationMessaging *m_inst; + + // Members + static bool isInitialized; + + SimulationMessaging(); + #ifdef USE_MESSAGING + SimulationMessaging(const char* broker, const char* vcusername, int simKey, int jobIndex, int taskID, int ttl_low=DEFAULT_TTL_LOW, int ttl_high=DEFAULT_TTL_HIGH); + #endif + + void sendStatus(WorkerEvent*); + void keepAlive(); + std::vector events; + MessageEventManager eventHandler; + AbstractCurlProxy *curlHandler; + WorkerEventOutputMode workerEventOutputMode; + + int taskID; + int m_jobIndex; + time_t lastSentEventTime; + + pthread_t newWorkerEventThread; + pthread_mutex_t mutex_messaging; + pthread_mutex_t mutex_workerEvent; + pthread_mutex_t mutex_cond_workerEvent; + pthread_cond_t cond_workerEvent; + bool bNewWorkerEvent; -#else - //NO MESSAGING compile compatibility variant - struct WorkerEventLocker { - WorkerEventLocker(SimulationMessaging &, bool bTry = false) {} - }; -#endif }; #endif // _SIMULATIONMESSAGING_H_ diff --git a/VCellMessaging/src/SimulationMessaging.cpp b/VCellMessaging/src/SimulationMessaging.cpp index e127d2b85..47d5c729e 100644 --- a/VCellMessaging/src/SimulationMessaging.cpp +++ b/VCellMessaging/src/SimulationMessaging.cpp @@ -1,749 +1,219 @@ #include #include #include +#include #include -#include +#include +#include -#include -#ifdef USE_MESSAGING -#if ( !defined(_WIN32) && !defined(_WIN64) ) // UNIX -#include -#include -#endif - -static const char* TIMETOLIVE_PROPERTY = "JMSTimeToLive"; -static const char* DELIVERYMODE_PROPERTY = "JMSDeliveryMode"; -static const char* DELIVERYMODE_PERSISTENT_VALUE = "persistent"; -static const char* DELIVERYMODE_NONPERSISTENT_VALUE = "nonpersistent"; -static const char* PRIORITY_PROPERTY = "JMSPriority"; -static const char* PRIORITY_DEFAULT_VALUE = "5"; -static const char* MESSAGE_TYPE_PROPERTY = "MessageType"; -static const char* MESSAGE_TYPE_WORKEREVENT_VALUE = "WorkerEvent"; - -static const char* USERNAME_PROPERTY = "UserName"; -static const char* HOSTNAME_PROPERTY = "HostName"; -static const char* SIMKEY_PROPERTY = "SimKey"; -static const char* TASKID_PROPERTY = "TaskID"; -static const char* JOBINDEX_PROPERTY = "JobIndex"; -static const char* WORKEREVENT_STATUS = "WorkerEvent_Status"; -static const char* WORKEREVENT_PROGRESS = "WorkerEvent_Progress"; -static const char* WORKEREVENT_TIMEPOINT = "WorkerEvent_TimePoint"; -static const char* WORKEREVENT_STATUSMSG = "WorkerEvent_StatusMsg"; static const double WORKEREVENT_MESSAGE_MIN_TIME_SECONDS = 15.0; +bool SimulationMessaging::isInitialized = false; -#endif SimulationMessaging *SimulationMessaging::m_inst = NULL; -SimulationMessaging::SimulationMessaging(){ - this->bStopRequested = false; -#ifdef USE_MESSAGING - this->m_taskID = -1; - this->bStarted = false; -#endif +SimulationMessaging::SimulationMessaging(): + eventHandler(std::bind(&SimulationMessaging::sendStatus, this, std::placeholders::_1)), + taskID{-1}, + m_jobIndex{-1} +{ + this->taskID = -1; this->workerEventOutputMode = WORKEREVENT_OUTPUT_MODE_STDOUT; -} - -#ifdef USE_MESSAGING -SimulationMessaging::SimulationMessaging(const char* broker, const char* smqusername, const char* passwd, const char*qname, - const char* tname, const char* vcusername, int simKey, int jobIndex, int taskID, int ttl_low, int ttl_high){ - m_broker = const_cast(broker); - m_smqusername = const_cast( smqusername ); - m_password = const_cast(passwd ); - m_qname = const_cast( qname ); - m_tname = const_cast( tname ); - - m_vcusername = const_cast( vcusername ); - m_simKey = simKey; - m_jobIndex = jobIndex; - m_taskID = taskID; - - m_ttl_lowPriority = ttl_low; - m_ttl_highPriority = ttl_high; - - curl_global_init(CURL_GLOBAL_ALL); - - { - memset(m_hostname, 0, 256); - - // get hostname -#if ( defined(_WIN32) || defined(_WIN64) ) - WSADATA wsaData; - WORD wVersionRequested = MAKEWORD( 2, 2 ); - WSAStartup( wVersionRequested, &wsaData ); -#endif - gethostname(m_hostname, 256); - //cout) << "hostname is " << m_hostname << endl; - } - - time(&lastSentEventTime); - - workerEventOutputMode = WORKEREVENT_OUTPUT_MODE_MESSAGING; - -#if ( defined(_WIN32) || defined(_WIN64) ) - InitializeCriticalSection(&lockForMessaging); - InitializeCriticalSection(&lockForWorkerEvent); -#else // UNIX - pthread_mutex_init(&mutex_messaging, NULL); - pthread_mutex_init(&mutex_workerEvent, NULL); - pthread_mutex_init(&mutex_cond_workerEvent, NULL); - pthread_cond_init(&cond_workerEvent, NULL); - bNewWorkerEvent = false; -#endif + this->curlHandler = new NullCurlProxy(); - bStarted = false; -} -#endif - -namespace { - void cleanupWorkerEvent(WorkerEvent *we) { - delete we; - } } -SimulationMessaging::~SimulationMessaging() throw() -{ - if (workerEventOutputMode == WORKEREVENT_OUTPUT_MODE_STDOUT) { - return; - } - std::for_each(events.begin(), events.end( ),cleanupWorkerEvent); - /* - if (workerEvent != NULL) { - delete workerEvent; - workerEvent = NULL; - } - */ #ifdef USE_MESSAGING - cleanup(); -#if ( defined(_WIN32) || defined(_WIN64) ) - DeleteCriticalSection(&lockForMessaging); - DeleteCriticalSection(&lockForWorkerEvent); -#else // UNIX - pthread_mutex_destroy(&mutex_messaging); - pthread_mutex_destroy(&mutex_workerEvent); - pthread_mutex_destroy(&mutex_cond_workerEvent); - pthread_cond_destroy(&cond_workerEvent); -#endif - curl_global_cleanup(); -#endif -} - -SimulationMessaging* SimulationMessaging::getInstVar() { - return m_inst; -} - -SimulationMessaging* SimulationMessaging::create() +SimulationMessaging::SimulationMessaging(const char* broker, const char* vcusername, int simKey, int jobIndex, int taskID, int ttl_low, int ttl_high): + eventHandler(std::bind(&SimulationMessaging::sendStatus, this, std::placeholders::_1)), + workerEventOutputMode{WORKEREVENT_OUTPUT_MODE_MESSAGING}, + taskID{taskID}, + m_jobIndex{jobIndex}, + bNewWorkerEvent{false} { - if (m_inst == 0){ - m_inst = new SimulationMessaging(); - } - return(m_inst); + this->curlHandler = new CurlProxy(simKey, taskID, jobIndex, vcusername, broker, ttl_low, ttl_high); + time(&this->lastSentEventTime); } - -void SimulationMessaging::sendStatus() { - WorkerEvent * workerEvent = 0; //set to null pointer - for (;;) { - { //scope for locking mutex - WorkerEventLocker locker(*this); - if (events.size( ) > 0 ) { - workerEvent = events.front( ); - if (!events.empty()) - { - events.erase(events.begin()); - } - } - else { - return; - } - } //unlocks mutex - - if (workerEventOutputMode == WORKEREVENT_OUTPUT_MODE_STDOUT) { - switch (workerEvent->status) { - case JOB_DATA: - printf("[[[data:%lg]]]", workerEvent->timepoint); - fflush(stdout); - break; - case JOB_PROGRESS: - printf("[[[progress:%lg%%]]]", workerEvent->progress * 100.0); - fflush(stdout); - break; - case JOB_STARTING: - std::cout<< workerEvent->eventMessage << std::endl; - break; - case JOB_COMPLETED: - std::cerr << "Simulation Complete in Main() ... " << std::endl; - break; - case JOB_FAILURE: - std::cerr << workerEvent->eventMessage << std::endl; - break; - } - return; - - } -#ifdef USE_MESSAGING - /* get a curl handle */ - CURL* curl = curl_easy_init(); - if(curl) { - // Documentation for the ActiveMQ restful API is missing, must see source code - // - // https://github.com/apache/activemq/blob/master/activemq-web/src/main/java/org/apache/activemq/web/MessageServlet.java - // https://github.com/apache/activemq/blob/master/activemq-web/src/main/java/org/apache/activemq/web/MessageServletSupport.java - // - // currently, the "web" api seems to use the same credentials as the "web console" ... defaults to admin:admin. - // TODO: pass in credentials, and protect them better (consider HTTPS). - // - /* - PROPERTIES="JMSDeliveryMode=persistent&JMSTimeToLive=3000" - PROPERTIES="${PROPERTIES}&SimKey=12446271133&JobIndex=0&TaskID=0&UserName=schaff" - PROPERTIES="${PROPERTIES}&MessageType=WorkerEvent&WorkerEvent_Status=1001&WorkerEvent_StatusMsg=Running" - PROPERTIES="${PROPERTIES}&WorkerEvent_TimePoint=2.0&WorkerEvent_Progress=0.4&HostName=localhost" - curl -XPOST "http://admin:admin@`hostname`:8165/api/message/workerEvent?type=queue&${PROPERTIES}" - */ - std::stringstream ss_url; - - // ss_url << "http://" << m_smqusername << ":" << m_password << "@" << m_broker << "/api/message/workerEvent?type=queue&"; - ss_url << "http://" << "admin" << ":" << "admin" << "@" << m_broker << "/api/message/workerEvent?type=queue&"; - - switch (workerEvent->status) { - case JOB_DATA: - ss_url << PRIORITY_PROPERTY << "=" << PRIORITY_DEFAULT_VALUE << "&"; - ss_url << TIMETOLIVE_PROPERTY << "=" << m_ttl_lowPriority << "&"; - ss_url << DELIVERYMODE_PROPERTY << "=" << DELIVERYMODE_NONPERSISTENT_VALUE << "&"; - break; - case JOB_PROGRESS: - ss_url << PRIORITY_PROPERTY << "=" << PRIORITY_DEFAULT_VALUE << "&"; - ss_url << TIMETOLIVE_PROPERTY << "=" << m_ttl_lowPriority << "&"; - ss_url << DELIVERYMODE_PROPERTY << "=" << DELIVERYMODE_NONPERSISTENT_VALUE << "&"; - break; - case JOB_STARTING: - ss_url << PRIORITY_PROPERTY << "=" << PRIORITY_DEFAULT_VALUE << "&"; - ss_url << TIMETOLIVE_PROPERTY << "=" << m_ttl_highPriority << "&"; - ss_url << DELIVERYMODE_PROPERTY << "=" << DELIVERYMODE_PERSISTENT_VALUE << "&"; - break; - case JOB_COMPLETED: - ss_url << PRIORITY_PROPERTY << "=" << PRIORITY_DEFAULT_VALUE << "&"; - ss_url << TIMETOLIVE_PROPERTY << "=" << m_ttl_highPriority << "&"; - ss_url << DELIVERYMODE_PROPERTY << "=" << DELIVERYMODE_PERSISTENT_VALUE << "&"; - break; - case JOB_FAILURE: - ss_url << PRIORITY_PROPERTY << "=" << PRIORITY_DEFAULT_VALUE << "&"; - ss_url << TIMETOLIVE_PROPERTY << "=" << m_ttl_highPriority << "&"; - ss_url << DELIVERYMODE_PROPERTY << "=" << DELIVERYMODE_PERSISTENT_VALUE << "&"; - break; - } - - ss_url << MESSAGE_TYPE_PROPERTY << "=" << MESSAGE_TYPE_WORKEREVENT_VALUE << "&"; - ss_url << USERNAME_PROPERTY << "=" << m_vcusername << "&"; - ss_url << HOSTNAME_PROPERTY << "=" << m_hostname << "&"; - ss_url << SIMKEY_PROPERTY << "=" << m_simKey << "&"; - ss_url << TASKID_PROPERTY << "=" << m_taskID << "&"; - ss_url << JOBINDEX_PROPERTY << "=" << m_jobIndex << "&"; - - ss_url << WORKEREVENT_STATUS << "=" << workerEvent->status << "&"; - - char* revisedMsg = workerEvent->eventMessage; - if (revisedMsg != NULL) { - revisedMsg = trim(revisedMsg); - if (strlen(revisedMsg) > 2048) { - revisedMsg[2047] = 0; //status message is only 2048 chars long in database - } - - for (int i = 0; i < (int) strlen(revisedMsg); i++) { - switch (revisedMsg[i]) { - case '\r': - case '\n': - case '\'': - case '\"': - revisedMsg[i] = ' '; - break; - // these characters are not valid both in database and in messages as a property - } - } - } - if (revisedMsg != NULL) { - char *output = curl_easy_escape(curl, revisedMsg, strlen(revisedMsg)); - if(output) { - ss_url << WORKEREVENT_STATUSMSG << "=" << output << "&"; - curl_free(output); - } - } - - ss_url << WORKEREVENT_PROGRESS << "=" << workerEvent->progress << "&"; - ss_url << WORKEREVENT_TIMEPOINT << "=" << workerEvent->timepoint; - - std::string s_url = ss_url.str(); - const char* messaging_http_url = s_url.c_str(); - curl_easy_setopt(curl, CURLOPT_URL, messaging_http_url); - - std::cout << "curl -XPOST " << messaging_http_url << std::endl; - - - // - // print message to stdout - // - std::cout << "!!!SimulationMessaging::sendStatus [" << (long)m_simKey << ":" << getStatusString(workerEvent->status); - if (revisedMsg != NULL) { - std::cout << ":" << revisedMsg; - } else { - std::cout << ":" << workerEvent->progress << ":" << workerEvent->timepoint; - } - std::cout << "]" << std::endl; - - - - // std::stringstream ss_body; - // ss_body << "empty message body"; // one way to force a POST verb with libcurl, probably better ways. - // std::string s_body = ss_body.str(); - // const char* postfields = s_body.c_str(); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ""); - - - // Perform the request, res will get the return code - CURLcode res = curl_easy_perform(curl); - // Check for errors - if(res != CURLE_OK) - fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); - - // always cleanup - curl_easy_cleanup(curl); - } - - - - time(&lastSentEventTime); - if (workerEvent->status == JOB_COMPLETED || workerEvent->status == JOB_FAILURE) { -#if ( defined(_WIN32) || defined(_WIN64) ) - SetEvent(hMessagingThreadEndEvent); -#else // UNIX - std::cout << "!!!thread exiting" << std::endl; - pthread_exit(NULL); -#endif - } -#else - throw "OUPUT_MODE_STANDOUT must be using if not using messaging!"; #endif - } - delete workerEvent; -} - -void SimulationMessaging::setWorkerEvent(WorkerEvent* arg_workerEvent) { - if (m_inst == 0) { - throw "SimulationMessaging is not initialized"; - } - if (workerEventOutputMode == WORKEREVENT_OUTPUT_MODE_STDOUT) { - events.push_back(arg_workerEvent); - sendStatus(); - return; - } -#ifdef USE_MESSAGING - else { - bool ifset = true; - bool iftry = false; - bool critical = criticalDelivery(*arg_workerEvent); - if (!critical) { - iftry = true; - // - // for portability, if not POSIX, time_t not guaranteed to be in seconds - // add WORKEREVENT_MESSAGE_MIN_TIME_SECONDS to last event time - // - struct tm nextMessageTime_tm = *localtime( &lastSentEventTime); - nextMessageTime_tm.tm_sec += WORKEREVENT_MESSAGE_MIN_TIME_SECONDS; // add MIN_TIME seconds to the time - time_t nextMessageTime = mktime( &nextMessageTime_tm); // normalize it - time_t currTime; - time(&currTime); - if (currTime <= nextMessageTime){ - ifset = false; - } - } - - if (ifset) { - { //scope for lock - WorkerEventLocker locker(*this,iftry); - if (locker.locked) { - events.push_back(arg_workerEvent); - } - } //unlock worker event -#if ( defined(_WIN32) || defined(_WIN64) ) - SetEvent(hNewWorkerEvent); -#else //UNIX - pthread_mutex_lock(&mutex_cond_workerEvent); - bNewWorkerEvent = true; - pthread_cond_signal(&cond_workerEvent); - pthread_mutex_unlock(&mutex_cond_workerEvent); -#endif - } - } -#else - throw "OUPUT_MODE_STANDOUT must be using if not using messaging!"; -#endif -} -#ifdef USE_MESSAGING -/** - * Keep trying to start the connection. Once it is established, - * setup the message handlers and publishers. (This is synchronized - * because it can be called from the main thread, and from the - * onException handler.) - */ -void SimulationMessaging::setupConnection () { //synchronized - // lockMessaging(); - // unlockMessaging(); +SimulationMessaging::~SimulationMessaging() noexcept{ + delete this->curlHandler; } -void SimulationMessaging::cleanup(){ +SimulationMessaging* SimulationMessaging::getInstVar() { + return SimulationMessaging::m_inst; } +SimulationMessaging* SimulationMessaging::create(){ + if (SimulationMessaging::m_inst == NULL) SimulationMessaging::m_inst = new SimulationMessaging(); -/** - * Suspend the current thread for "duration" milliseconds. - */ -void SimulationMessaging::delay(int duration) -{ -#if ( defined(_WIN32) || defined(_WIN64) ) - Sleep(duration); -#else // UNIX - sleep(duration/1000); -#endif + return SimulationMessaging::m_inst; } -bool SimulationMessaging::lockMessaging() -{ -#if ( defined(_WIN32) || defined(_WIN64) ) - EnterCriticalSection(&lockForMessaging); - return true; -#else // UNIX - if (pthread_mutex_lock(&mutex_messaging)) { - std::cout << "Cannot acquire mutex, fatal error." << std::endl; - exit(1); - } -#endif -} +void SimulationMessaging::sendStatus(WorkerEvent* event) { + if (WORKEREVENT_OUTPUT_MODE_STDOUT & workerEventOutputMode) SimulationMessaging::sendStdOutStatus(event); + if (WORKEREVENT_OUTPUT_MODE_MESSAGING & workerEventOutputMode) this->curlHandler->sendStatus(event); -void SimulationMessaging::unlockMessaging() -{ -#if ( defined(_WIN32) || defined(_WIN64) ) - LeaveCriticalSection(&lockForMessaging); -#else // UNIX - pthread_mutex_unlock(&mutex_workerEvent); -#endif + time(&lastSentEventTime); // Update the time var } +void SimulationMessaging::keepAlive() { + if (workerEventOutputMode & WORKEREVENT_OUTPUT_MODE_MESSAGING) this->curlHandler->keepAlive(); -bool SimulationMessaging::lockWorkerEvent(bool bTry) -{ -#if ( defined(_WIN32) || defined(_WIN64) ) - if (bTry) { - return TryEnterCriticalSection(&lockForWorkerEvent)!=0; - } - try { - EnterCriticalSection(&lockForWorkerEvent); - } catch (...) { - return false; - } - return true; -#else // UNIX - if (bTry) { - return (pthread_mutex_trylock(&mutex_workerEvent) == 0); - } - if (pthread_mutex_lock(&mutex_workerEvent) != 0) { - return false; - } - return true; -#endif -} - -void SimulationMessaging::unlockWorkerEvent() -{ -#if ( defined(_WIN32) || defined(_WIN64) ) - LeaveCriticalSection(&lockForWorkerEvent); -#else // UNIX - pthread_mutex_unlock(&mutex_workerEvent); -#endif + time(&lastSentEventTime); // Update the time var } -char* SimulationMessaging::trim(char* str) { - int leftIndex, rightIndex; - int len = (int)strlen(str); - for (leftIndex = 0; leftIndex < len; leftIndex ++) { // remove leading spaces - char c = str[leftIndex]; - if (c != ' ' && c != '\n' && c != '\r') { +void SimulationMessaging::sendStdOutStatus(WorkerEvent* workerEvent) { + switch (workerEvent->status) { + case JobEvent::JOB_DATA: + std::cout << std::format("[[[data:{}]]])", workerEvent->timepoint) << std::endl; + // printf("[[[data:%lg]]]", workerEvent->timepoint); + // fflush(stdout); break; - } - } - for (rightIndex = len - 1; rightIndex >= 0; rightIndex --) { // remove trailing spaces and new line and carriage return - char c = str[rightIndex]; - if (c != ' ' && c != '\n' && c != '\r') { + case JobEvent::JOB_PROGRESS: + std::cout << std::format("[[[progress:{}%]]]", workerEvent->progress * 100.0) << std::endl; + // printf("[[[progress:%lg%%]]]", workerEvent->progress * 100.0); + // fflush(stdout); break; - } - } - - len = rightIndex - leftIndex + 2; - if (len <= 0) { - return 0; - } - - char* newstr = new char[len]; - memset(newstr, 0, len * sizeof(char)); - strncpy(newstr, str + leftIndex, len - 1); - - return newstr; + case JobEvent::JOB_STARTING: + std::cout<< workerEvent->eventMessage << std::endl; + break; + case JobEvent::JOB_COMPLETED: + std::cerr << "Simulation Complete in Main() ... " << std::endl; + break; + case JobEvent::JOB_FAILURE: + std::cerr << workerEvent->eventMessage << std::endl; + break; + default: + throw std::runtime_error(std::format("SimulationMessaging::sendStatus: `{}` is not recognized",JobEvent::toString(workerEvent->status))); + } +} + +void SimulationMessaging::setWorkerEvent(JobEvent::Status status, const char *eventMessage) { + this->eventHandler.enqueue(status, eventMessage); +} + +void SimulationMessaging::setWorkerEvent(const JobEvent::Status status, const double progress, const double timepoint) { + this->eventHandler.enqueue(status, progress, timepoint); + // bool critical = criticalDelivery(*arg_workerEvent); + // if (!critical) { + // iftry = true; + // // + // // for portability, if not POSIX, time_t not guaranteed to be in seconds + // // add WORKEREVENT_MESSAGE_MIN_TIME_SECONDS to last event time + // // + // tm nextMessageTime_tm = *localtime( &lastSentEventTime); + // nextMessageTime_tm.tm_sec += WORKEREVENT_MESSAGE_MIN_TIME_SECONDS; // add MIN_TIME seconds to the time + // time_t nextMessageTime = mktime( &nextMessageTime_tm); // normalize it + // time_t currTime; + // time(&currTime); + // if (currTime <= nextMessageTime){ + // isTimeForNextMessage = false; + // } + // } + // + // if (isTimeForNextMessage) { + // { //scope for lock + // WorkerEventLocker locker(*this,iftry); + // if (locker.locked) { + // events.push_back(arg_workerEvent); + // } + // } //unlock worker event + // pthread_mutex_lock(&mutex_cond_workerEvent); + // bNewWorkerEvent = true; + // pthread_cond_signal(&cond_workerEvent); + // pthread_mutex_unlock(&mutex_cond_workerEvent); + // } +} + +void SimulationMessaging::setWorkerEvent(JobEvent::Status status, const double progress, const double timepoint, const char *eventMessage) { + this->eventHandler.enqueue(status, progress, timepoint, eventMessage); } -void SimulationMessaging::keepAlive() { - if (workerEventOutputMode == WORKEREVENT_OUTPUT_MODE_MESSAGING) { - - - CURL* curl = curl_easy_init(); - if(curl) { - // First set the URL that is about to receive our POST. This URL can - // just as well be a https:// URL if that is what should receive the - // data. - curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:6161/message/workerevent?readTimeout=20000&type=queue"); - - - // Now specify the POST data - std::stringstream ss; - - //status - // msg->setIntProperty(WORKEREVENT_STATUS, JOB_WORKER_ALIVE); - ss << WORKEREVENT_STATUS << "=" << JOB_WORKER_ALIVE; - - std::string x = ss.str(); - const char* postfields = x.c_str(); - - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postfields); // e.g. postfields="name=daniel&project=curl" - - // Perform the request, res will get the return code - CURLcode res = curl_easy_perform(curl); - // Check for errors - if(res != CURLE_OK) - fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); - - // always cleanup - curl_easy_cleanup(curl); - } - - time(&lastSentEventTime); - } +void SimulationMessaging::waitUntilFinished() { + this->eventHandler.requestStopAndWaitForIt(); + // if (workerEventOutputMode == WORKEREVENT_OUTPUT_MODE_STDOUT) return; + // #ifdef USE_MESSAGING + // std::cout << "!!!waiting for thread to exit" << std::endl; + // pthread_join(newWorkerEventThread, NULL); + // std::cout << "!!Threads joined successfully" << std::endl; + // #endif } -const char* SimulationMessaging::getStatusString(int status) { - switch (status) { - case JOB_STARTING: - return "JOB_STARTING"; - - case JOB_PROGRESS: - return "JOB_PROGRESS"; - - case JOB_DATA: - return "JOB_DATA"; - - case JOB_COMPLETED: - return "JOB_COMPLETED"; - - case JOB_WORKER_ALIVE: - return "JOB_WORKER_ALIVE"; - - case JOB_FAILURE: - return "JOB_FAILURE"; - - default: - return "Unknown status"; - } -} +#ifdef USE_MESSAGING -SimulationMessaging* SimulationMessaging::create(const char* broker, const char* smqusername, const char* passwd, const char* qname, const char* tname, const char* vcusername, int simKey, int jobIndex, int taskID, int ttl_low, int ttl_high) -{ +SimulationMessaging* SimulationMessaging::create(const char* broker, const char* smqusername, const char* passwd, const char* qname, const char* tname, const char* vcusername, int simKey, int jobIndex, int taskID, int ttl_low, int ttl_high){ if (m_inst != NULL && m_inst->workerEventOutputMode == WORKEREVENT_OUTPUT_MODE_STDOUT) { delete m_inst; m_inst = NULL; } if (m_inst == NULL){ - m_inst = new SimulationMessaging(broker, smqusername, passwd, qname, tname, vcusername, simKey, jobIndex, taskID, ttl_low, ttl_high); + m_inst = new SimulationMessaging(broker, vcusername, simKey, jobIndex, taskID, ttl_low, ttl_high); } return(m_inst); } -void SimulationMessaging::waitUntilFinished() { - if (workerEventOutputMode == WORKEREVENT_OUTPUT_MODE_STDOUT) { - return; - } -#if ( defined(_WIN32) || defined(_WIN64) ) - DWORD dwWaitResult = WaitForSingleObject(hMessagingThreadEndEvent, INFINITE); - switch (dwWaitResult) { - case WAIT_OBJECT_0: - case WAIT_TIMEOUT: - break; - - default: - cout << "Wait error: " << GetLastError() << endl; - break; - } -#else - std::cout << "!!!waiting for thread to exit" << std::endl; - pthread_join(newWorkerEventThread, NULL); -#endif -} - -void* startMessagingThread(void* param); - -void SimulationMessaging::start() { - if (workerEventOutputMode == WORKEREVENT_OUTPUT_MODE_STDOUT) { - return; - } - if (bStarted) { - return; - } - bStarted = true; - -#if ( defined(_WIN32) || defined(_WIN64) ) - HANDLE hThread; - DWORD IDThread; - - char messagingThreadEndEventName[256]; - char newWorkerEventName[256]; - sprintf(messagingThreadEndEventName, "MessagingThreadEndEvent_%ld_%d_%d", m_simKey, m_jobIndex, m_taskID); - sprintf(newWorkerEventName, "NewWorkerEvent_%ld_%d_%d", m_simKey, m_jobIndex, m_taskID); - - // Create a manual-reset event object. The master thread sets - // this to nonsignaled when it writes to the shared buffer. - hMessagingThreadEndEvent = CreateEvent( - NULL, // no security attributes - true, // manual-reset event - false, // initial state is nonsignaled - messagingThreadEndEventName // object name - ); - - if (hMessagingThreadEndEvent == NULL) { - throw "CreateEvent failed for new worker event"; - } - - if (GetLastError() == ERROR_ALREADY_EXISTS) { - throw "Event for messagingThreadEndEvent already exists"; - } - - hNewWorkerEvent = CreateEvent( - NULL, // no security attributes - true, // manual-reset event - false, // initial state is nonsignaled - newWorkerEventName // object name - ); - - if (hNewWorkerEvent == NULL) { - throw "CreateEvent failed for new worker event"; - } - - if (GetLastError() == ERROR_ALREADY_EXISTS) { - throw "Event for newWorkerEvent already exists"; - } - - // Create multiple threads and an auto-reset event object - // for each thread. Each thread sets its event object to - // signaled when it is not reading from the shared buffer. - - hThread = CreateThread(NULL, 0, - (LPTHREAD_START_ROUTINE) startMessagingThread, - &hNewWorkerEvent, // pass event handle - 0, &IDThread); - if (hThread == NULL) - { - throw "CreateThread failed for messaging thread"; - } -#else // UNIX - int retv = pthread_create(&newWorkerEventThread, NULL, startMessagingThread, this); -#endif -} - -void* startMessagingThread(void* lpParam){ - SimulationMessaging::getInstVar()->setupConnection(); - -#if ( defined(_WIN32) || defined(_WIN64) ) - DWORD dwWaitResult; - HANDLE hEvent; - - hEvent = *(HANDLE*)lpParam; // thread's read event - - while (true) { - dwWaitResult = WaitForSingleObject(hEvent, 5*60*1000); // 5 min wait - - switch (dwWaitResult) { - case WAIT_OBJECT_0: - { - WorkerEvent* sentWorkerEvent = SimulationMessaging::getInstVar()->sendStatus(); - SimulationMessaging::getInstVar()->lockWorkerEvent(); - if (sentWorkerEvent != NULL - && SimulationMessaging::getInstVar()->getWorkerEvent() != NULL - && sentWorkerEvent->equals(SimulationMessaging::getInstVar()->getWorkerEvent())) { - ResetEvent(hEvent); - } - SimulationMessaging::getInstVar()->unlockWorkerEvent(); - delete sentWorkerEvent; - } - break; - - // An error occurred. - case WAIT_TIMEOUT: - SimulationMessaging::getInstVar()->keepAlive(); - break; - - default: - std::cout<< "Wait error: " << GetLastError() << endl; - break; - } - } -#else // UNIX - int waitReturn = 0; - struct timespec timeout; - struct timeval now; - struct timeval start; - SimulationMessaging* simMessaging = (SimulationMessaging*)lpParam; - - while (true) { - waitReturn = 1; - gettimeofday(&start, NULL); - timeout.tv_sec = start.tv_sec; - // condition might be signalled before this thread gets blocked - // so this thread might miss signal and doesn't get wakened - // if this happens don't want to wait too long to check if there - // is new worker event. This is the reason for the loop and 2 sec timeout - pthread_mutex_lock(&simMessaging->mutex_cond_workerEvent); - while (timeout.tv_sec - start.tv_sec < 5 * 60) { - // at this point, there are two possibilities - // 1. this thread has been awakened, waited < 2 secs, waitReturn is 0 - // 2. this thread has waited full 2 sec timout, waitReturn is non zero - // in either case, check if there is new worker event. - if (simMessaging->bNewWorkerEvent) { - simMessaging->bNewWorkerEvent = false; - waitReturn = 0; - break; - } - gettimeofday(&now, NULL); - timeout.tv_sec = now.tv_sec + 2; - timeout.tv_nsec = now.tv_usec * 1000; - waitReturn = pthread_cond_timedwait(&simMessaging->cond_workerEvent, &simMessaging->mutex_cond_workerEvent, &timeout); - } - pthread_mutex_unlock(&simMessaging->mutex_cond_workerEvent); - - switch (waitReturn) { - case 0: { // new event - simMessaging->sendStatus(); - break; - } - - case ETIMEDOUT: // time out - simMessaging->keepAlive(); - break; - - default: - std::cout<< "Wait error: " << waitReturn << std::endl; - break; - } - } -#endif -} +// void SimulationMessaging::start() { +// if (workerEventOutputMode == WORKEREVENT_OUTPUT_MODE_STDOUT) { +// return; +// } +// if (bStarted) { +// return; +// } +// bStarted = true; +// int retv = pthread_create(&newWorkerEventThread, NULL, startMessagingThread, this); +// } + +// void* startMessagingThread(void* lpParam){ +// int waitReturn = 0; +// struct timespec timeout; +// struct timeval now; +// struct timeval start; +// SimulationMessaging* simMessaging = (SimulationMessaging*)lpParam; +// +// while (true) { +// waitReturn = 1; +// gettimeofday(&start, NULL); +// timeout.tv_sec = start.tv_sec; +// // condition might be signalled before this thread gets blocked +// // so this thread might miss signal and doesn't get wakened +// // if this happens don't want to wait too long to check if there +// // is new worker event. This is the reason for the loop and 2 sec timeout +// pthread_mutex_lock(&simMessaging->mutex_cond_workerEvent); +// while (timeout.tv_sec - start.tv_sec < 5 * 60) { +// // at this point, there are two possibilities +// // 1. this thread has been awakened, waited < 2 secs, waitReturn is 0 +// // 2. this thread has waited full 2 sec timout, waitReturn is non zero +// // in either case, check if there is new worker event. +// if (simMessaging->bNewWorkerEvent) { +// simMessaging->bNewWorkerEvent = false; +// waitReturn = 0; +// break; +// } +// gettimeofday(&now, NULL); +// timeout.tv_sec = now.tv_sec + 2; +// timeout.tv_nsec = now.tv_usec * 1000; +// waitReturn = pthread_cond_timedwait(&simMessaging->cond_workerEvent, &simMessaging->mutex_cond_workerEvent, &timeout); +// } +// pthread_mutex_unlock(&simMessaging->mutex_cond_workerEvent); +// +// switch (waitReturn) { +// case 0: { // new event +// simMessaging->sendStatus(); +// break; +// } +// +// case ETIMEDOUT: // time out +// simMessaging->keepAlive(); +// break; +// +// default: +// std::cout<< "Wait error: " << waitReturn << std::endl; +// break; +// } +// } +// } #endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 686c973b3..9ee8867de 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -7,7 +7,7 @@ FetchContent_Declare( GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG v1.17.0 ) -if (WINDOWS) +if (WIN32) # For Windows: Prevent overriding the parent project's compiler/linker settings set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) endif() @@ -19,6 +19,7 @@ include(GoogleTest) add_executable(unit_tests unit/smoke_test.cpp unit/hello_test.cpp + unit/message_processing_test.cpp ) target_link_libraries(unit_tests PRIVATE IDAWin diff --git a/tests/unit/message_processing_test.cpp b/tests/unit/message_processing_test.cpp new file mode 100644 index 000000000..088c06189 --- /dev/null +++ b/tests/unit/message_processing_test.cpp @@ -0,0 +1,66 @@ +// +// Created by Logan Drescher on 12/1/25. +// +#include +#include +#include +#include + +#include "VCellSundialsSolver.h" + +static std::vector eventTracker; + +void appendNextIndex(const WorkerEvent* event); +std::vector generateFibonacci(int length); +long sequenceComparison(const std::vector &sequence1, const std::vector &sequence2); + +TEST(MessageProcessingTest, MessagesAreProcessed) { + + MessageEventManager eventManager{[](WorkerEvent* event)->void{appendNextIndex(event);}}; + const int NUM_ITERATIONS = 100; + for (int i = 0; i < NUM_ITERATIONS; ++i) { + JobEvent::Status status; + switch (i + 1) { + case 1: status = JobEvent::JOB_STARTING; break; + case NUM_ITERATIONS: status = JobEvent::JOB_COMPLETED; break; + default: status = JobEvent::JOB_PROGRESS; break; + } + eventManager.enqueue(status, (i + 1) / 100.0, i, std::to_string(i).c_str()); + } + eventManager.requestStopAndWaitForIt(); + std::vector expectedResults = generateFibonacci(NUM_ITERATIONS); + ASSERT_TRUE(!sequenceComparison(eventTracker, expectedResults)); +} + +void appendNextIndex(const WorkerEvent* event) { + if (eventTracker.empty()) { eventTracker.emplace_back("0"); return; } + if (1 == eventTracker.size()) { eventTracker.emplace_back("1"); return; } + + const long firstValue = std::stol(eventTracker[eventTracker.size() - 2]); + const long secondValue = std::stol(eventTracker[eventTracker.size() - 1]); + const std::string nextValue{std::to_string(firstValue + secondValue)}; + eventTracker.push_back(nextValue); +} + +std::vector generateFibonacci(const int length) { + std::vector sequence; + for (int i = 0; i < length; ++i) { + if (i == 0) { sequence.emplace_back("0"); continue; } + if (i == 1) { sequence.emplace_back("1"); continue; } + const long firstValue = std::stol(sequence[sequence.size() - 2]); + const long secondValue = std::stol(sequence[sequence.size() - 1]); + const std::string nextValue{std::to_string(firstValue + secondValue)}; + sequence.push_back(nextValue); + } + return sequence; +} + +long sequenceComparison(const std::vector &sequence1, const std::vector &sequence2) { + if (const size_t comp = sequence2.size() - sequence1.size(); comp) return static_cast(comp); + for (size_t i = 0; i < sequence1.size(); ++i) { + const long firstValue = std::stol(sequence1[i]); + const long secondValue = std::stol(sequence2[i]); + if (const long comp = secondValue - firstValue; comp) return comp; + } + return 0; +} \ No newline at end of file From a8cc4ed724a69a0510bf493461ae35df4090192e Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Tue, 9 Dec 2025 13:37:44 -0500 Subject: [PATCH 10/26] downgraded j-thread for better compatability --- Dockerfile | 29 ++++++++++--------- .../include/VCELL/MessageEventManager.h | 2 +- .../include/VCELL/SimulationMessaging.h | 10 +------ VCellMessaging/src/JobEventStatus.cpp | 1 + VCellMessaging/src/MessageEventManager.cpp | 1 + VCellMessaging/src/SimulationMessaging.cpp | 4 +-- 6 files changed, 21 insertions(+), 26 deletions(-) diff --git a/Dockerfile b/Dockerfile index f6476148b..5565be3ba 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,29 +2,32 @@ FROM debian:trixie-slim AS build SHELL ["/bin/bash", "-c"] RUN apt-get -y update && apt-get install -y apt-utils && apt-get remove --purge gcc -<<<<<<< HEAD -RUN apt-get install -y -qq -o=Dpkg::Use-Pty=0 clang mold ninja-build cmake libc++-dev libc++abi-dev libcurl4-openssl-dev \ - git curl wget ca-certificates python3 python3-pip -======= -RUN apt-get install -y -qq -o=Dpkg::Use-Pty=0 mold ninja-build cmake clang-18 clang++-18 clang-tools-18 libc++-18-dev libc++abi-18-dev \ - libcurl4-openssl-dev git curl wget ca-certificates python3 python3-pip ->>>>>>> 88cc3915 (Fixed bad parsing when looking for messaging params) +# line 1 => build dependencies +# line 2 => build tools +# line 3 => dependencies +RUN apt-get install -y -qq -o=Dpkg::Use-Pty=0 \ + git curl wget ca-certificates python3 python3-pip \ + mold ninja-build cmake clang-19 clang++-19 clang-tools-19 libc++-19-dev libc++abi-19-dev \ + libspdlog-dev libcurl4-openssl-dev + RUN rm $(which gcc) || true RUN rm $(which g++) || true RUN rm -rf /var/lib/apt/lists/* && rm /usr/bin/ld -#RUN ln -s $(which clang-18) /usr/bin/clang -#RUN ln -s $(which clang++-18) /usr/bin/clang++ RUN ln -s $(which mold) /usr/bin/ld - +RUN clang-19 --version +RUN ln -s $(which clang-19) /usr/bin/clang +RUN clang++-19 --version +RUN ln -s $(which clang++-19) /usr/bin/clang++ COPY . /vcellroot RUN mkdir -p /vcellroot/build/bin WORKDIR /vcellroot/build -RUN clang++ --version -RUN /usr/bin/cmake .. -G Ninja -DOPTION_TARGET_MESSAGING=ON -DOPTION_TARGET_DOCS=OFF +RUN /usr/bin/cmake .. -G Ninja -DOPTION_TARGET_MESSAGING=ON -DOPTION_TARGET_DOCS=OFF -DOPTION_TEST_WITH_LOCALHOST=ON +#RUN /usr/bin/cmake .. -G Ninja -DOPTION_TARGET_MESSAGING=ON -DOPTION_TARGET_DOCS=OFF -DCMAKE_CXX_FLAGS="-fexperimental-library" # `experimental-library` needed for new j-thread feature! RUN ninja --verbose -RUN ctest -VV +RUN ctest -VV --output-on-failure +RUN rm /vcellroot/build/bin/unit_tests WORKDIR /vcellroot/build/bin ENV PATH="/vcellroot/build/bin:${PATH}" \ No newline at end of file diff --git a/VCellMessaging/include/VCELL/MessageEventManager.h b/VCellMessaging/include/VCELL/MessageEventManager.h index d9d425ea9..b00470f15 100644 --- a/VCellMessaging/include/VCELL/MessageEventManager.h +++ b/VCellMessaging/include/VCELL/MessageEventManager.h @@ -54,7 +54,7 @@ class MessageEventManager { std::queue eventQueue; std::mutex queuetex; - std::jthread eventQueueProcessingWorkerThread; + std::thread eventQueueProcessingWorkerThread; //TODO: make a `std::jthread` once compilers catch up with standard std::condition_variable needMessageProcessingForeman; std::function sendUpdateFunction; }; diff --git a/VCellMessaging/include/VCELL/SimulationMessaging.h b/VCellMessaging/include/VCELL/SimulationMessaging.h index d12be5dfa..92a672e04 100644 --- a/VCellMessaging/include/VCELL/SimulationMessaging.h +++ b/VCellMessaging/include/VCELL/SimulationMessaging.h @@ -1,10 +1,8 @@ #ifndef _SIMULATIONMESSAGING_H_ #define _SIMULATIONMESSAGING_H_ -#include #include -#include -#include + #include "CurlProxyClasses.h" #include "MessageEventManager.h" @@ -94,12 +92,6 @@ class SimulationMessaging { int taskID; int m_jobIndex; time_t lastSentEventTime; - - pthread_t newWorkerEventThread; - pthread_mutex_t mutex_messaging; - pthread_mutex_t mutex_workerEvent; - pthread_mutex_t mutex_cond_workerEvent; - pthread_cond_t cond_workerEvent; bool bNewWorkerEvent; }; diff --git a/VCellMessaging/src/JobEventStatus.cpp b/VCellMessaging/src/JobEventStatus.cpp index 22281f510..bff233567 100644 --- a/VCellMessaging/src/JobEventStatus.cpp +++ b/VCellMessaging/src/JobEventStatus.cpp @@ -2,6 +2,7 @@ // Created by Logan Drescher on 11/25/25. // #include +#include std::string JobEvent::toString(const Status status) { switch (status) { diff --git a/VCellMessaging/src/MessageEventManager.cpp b/VCellMessaging/src/MessageEventManager.cpp index 4bc775328..fae2ab342 100644 --- a/VCellMessaging/src/MessageEventManager.cpp +++ b/VCellMessaging/src/MessageEventManager.cpp @@ -15,6 +15,7 @@ MessageEventManager::MessageEventManager(std::function sendU MessageEventManager::~MessageEventManager() { if (!this->stopRequested) this->requestStopAndWaitForIt(); + this->eventQueueProcessingWorkerThread.join(); } void MessageEventManager::enqueue(const JobEvent::Status status, const double progress, const double timepoint, const char *eventMessage) { diff --git a/VCellMessaging/src/SimulationMessaging.cpp b/VCellMessaging/src/SimulationMessaging.cpp index 47d5c729e..5dea90ff4 100644 --- a/VCellMessaging/src/SimulationMessaging.cpp +++ b/VCellMessaging/src/SimulationMessaging.cpp @@ -1,9 +1,7 @@ #include #include -#include #include -#include -#include +#include #include From c1c0294b5b2e1fc7d8ad714753095a3ba9fd42e2 Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Wed, 10 Dec 2025 13:32:49 -0500 Subject: [PATCH 11/26] Fixed remaining memory leaks! Now leak free! --- IDAWin/SundialsSolverInterface.cpp | 60 ++++++++++++++++++++++++++---- IDAWin/VCellSolverFactory.cpp | 8 +++- 2 files changed, 59 insertions(+), 9 deletions(-) diff --git a/IDAWin/SundialsSolverInterface.cpp b/IDAWin/SundialsSolverInterface.cpp index 7aadee250..09fcebcb8 100644 --- a/IDAWin/SundialsSolverInterface.cpp +++ b/IDAWin/SundialsSolverInterface.cpp @@ -19,21 +19,63 @@ void errExit(int returnCode, const std::string &errorMsg); void activateSolver(std::ifstream& inputFileStream, FILE* outputFile, int taskID) { + int returnCode = 0; + std::string errorMsg; + VCellSolver* targetSolver; + // First try block - create the solver; failure => no need to delete targetSolver! try { - VCellSolver* targetSolver = VCellSolverFactory::produceVCellSolver(inputFileStream, taskID); - targetSolver->solve(nullptr, true, outputFile, VCellSundialsSolver::checkStopRequested); + targetSolver = VCellSolverFactory::produceVCellSolver(inputFileStream, taskID); } catch (const char *ex) { - errExit(-1, ex); + returnCode = -1; + errorMsg = ex; + targetSolver = nullptr; } catch (std::string &ex) { - errExit(-2, ex); + returnCode = -2; + errorMsg = ex; + targetSolver = nullptr; } catch (StoppedByUserException&) { - errExit(0, "Execution Stopped By User"); + returnCode = 0; + errorMsg = "Execution Stopped By User"; + targetSolver = nullptr; } catch (VCell::Exception &ex) { - errExit(-3, ex.getMessage()); + returnCode = -3; + errorMsg = ex.getMessage(); + targetSolver = nullptr; } catch (const std::exception& err) { - errExit(-4, err.what()); + returnCode = -4; + errorMsg = err.what(); + targetSolver = nullptr; } catch (...) { - errExit(-5, "unknown error"); + returnCode = -5; + errorMsg = "Unknown Error Detected"; + targetSolver = nullptr; + } + // second try block - solver is created; must delete it! + if (nullptr != targetSolver) { + try { + targetSolver->solve(nullptr, true, outputFile, VCellSundialsSolver::checkStopRequested); + } catch (const char *ex) { + returnCode = -1; + errorMsg = ex; + // errExit(-1, ex); + } catch (std::string &ex) { + returnCode = -2; + errorMsg = ex; + } catch (StoppedByUserException&) { + returnCode = 0; + errorMsg = "Execution Stopped By User"; + } catch (VCell::Exception &ex) { + returnCode = -3; + errorMsg = ex.getMessage(); + } catch (const std::exception& err) { + returnCode = -4; + errorMsg = err.what(); + } catch (...) { + returnCode = -5; + errorMsg = "Unknown Error Detected"; + } + // !!! DELETE THE SOLVER !!! + delete targetSolver; } // cleanup @@ -41,6 +83,8 @@ void activateSolver(std::ifstream& inputFileStream, FILE* outputFile, int taskID SimulationMessaging::getInstVar()->waitUntilFinished(); delete SimulationMessaging::getInstVar(); } + + if (!errorMsg.empty()) errExit(returnCode, errorMsg); } void errExit(int returnCode, const std::string &errorMsg) { diff --git a/IDAWin/VCellSolverFactory.cpp b/IDAWin/VCellSolverFactory.cpp index 3fe83577b..212508f2e 100644 --- a/IDAWin/VCellSolverFactory.cpp +++ b/IDAWin/VCellSolverFactory.cpp @@ -43,7 +43,13 @@ VCellSolver* VCellSolverFactory::produceVCellSolver(std::ifstream& inputFileStre default: throw new VCell::Exception("Unknown VCellSolver type"); } - desiredSolver->configureFromInput(inputBreakdown); + try { + desiredSolver->configureFromInput(inputBreakdown); + } catch (...) { + delete desiredSolver; + throw; + } + return desiredSolver; } From 180ddd8a1efdf2c28237a4befddcc69e0b3d806a Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Tue, 16 Dec 2025 11:01:23 -0500 Subject: [PATCH 12/26] Added catch to messaging loop, should give better message and stay alive --- .../include/VCELL/MessageEventManager.h | 1 + VCellMessaging/src/MessageEventManager.cpp | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/VCellMessaging/include/VCELL/MessageEventManager.h b/VCellMessaging/include/VCELL/MessageEventManager.h index b00470f15..a167e4748 100644 --- a/VCellMessaging/include/VCELL/MessageEventManager.h +++ b/VCellMessaging/include/VCELL/MessageEventManager.h @@ -42,6 +42,7 @@ class MessageEventManager { bool stopWasCalled(); private: + void performQueueProcessing(); void processQueue(); void processEvent(WorkerEvent* event); void enqueue(WorkerEvent*); diff --git a/VCellMessaging/src/MessageEventManager.cpp b/VCellMessaging/src/MessageEventManager.cpp index fae2ab342..20e3024ab 100644 --- a/VCellMessaging/src/MessageEventManager.cpp +++ b/VCellMessaging/src/MessageEventManager.cpp @@ -4,11 +4,14 @@ #include #include #include +#include +#include +#include MessageEventManager::MessageEventManager(std::function sendUpdateFunction): stopRequested{false}, sendUpdateFunction{std::move(sendUpdateFunction)}, - eventQueueProcessingWorkerThread([this]() {this->processQueue();}) // Should auto-start thread + eventQueueProcessingWorkerThread([this]() {this->performQueueProcessing();}) // Should auto-start thread { } @@ -32,6 +35,7 @@ void MessageEventManager::enqueue(const JobEvent::Status status, const char *eve void MessageEventManager::requestStopAndWaitForIt() { std::unique_lock stopRequestedLock{this->stopRequestedMutex}; + if (this->stopRequested) return; this->stopRequested = true; this->needMessageProcessingForeman.notify_all(); this->requestedStopForeman.wait(stopRequestedLock, [this]()->bool{return this->eventQueue.empty();}); @@ -47,6 +51,18 @@ bool MessageEventManager::stopWasCalled() { /// Private Methods /// /////////////////////////////////////////////////// +void MessageEventManager::performQueueProcessing() { + try { + processQueue(); + } catch (std::system_error e) { + std::cerr << std::format("System Error (Code {} - {}): {}", std::to_string(e.code().value()), e.code().category().name(), e.what()) << std::endl; + } catch (std::exception& e) { + std::cerr << e.what() << std::endl; + } catch (...) { + std::cerr << "An unknown exception occurred while processing the event queue!" << std::endl; + } +} + void MessageEventManager::processQueue() { while (true) { WorkerEvent* event; From d2fd72924f475d33a3417c254f7d53ad2ab2691b Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Tue, 16 Dec 2025 11:25:41 -0500 Subject: [PATCH 13/26] TSAN caught data race; added protective locks --- VCellMessaging/src/MessageEventManager.cpp | 2 ++ tests/unit/message_processing_test.cpp | 24 +++++++++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/VCellMessaging/src/MessageEventManager.cpp b/VCellMessaging/src/MessageEventManager.cpp index 20e3024ab..5f9bedef7 100644 --- a/VCellMessaging/src/MessageEventManager.cpp +++ b/VCellMessaging/src/MessageEventManager.cpp @@ -83,8 +83,10 @@ void MessageEventManager::processQueue() { // Wait for the worker to be "prodded" (via `needMessagingForeman.notify_one()`), AND the event queue is not empty // Note that this `wait()` call, by design, unlocks the queue mutex, until it is "prodded". this->needMessageProcessingForeman.wait(shouldBeActiveLock); + std::unique_lock stopRequestedLock{this->stopRequestedMutex}; if (this->eventQueue.empty()) continue; // Probably means we need to check if stop was requested again } + std::unique_lock stopRequestedLock{this->stopRequestedMutex}; event = this->eventQueue.front(); this->eventQueue.pop(); }// END Clock-out Scope // diff --git a/tests/unit/message_processing_test.cpp b/tests/unit/message_processing_test.cpp index 088c06189..548c83738 100644 --- a/tests/unit/message_processing_test.cpp +++ b/tests/unit/message_processing_test.cpp @@ -3,12 +3,15 @@ // #include #include +#include +#include #include #include #include "VCellSundialsSolver.h" static std::vector eventTracker; +static std::mutex eventTrackerMutex; void appendNextIndex(const WorkerEvent* event); std::vector generateFibonacci(int length); @@ -25,7 +28,9 @@ TEST(MessageProcessingTest, MessagesAreProcessed) { case NUM_ITERATIONS: status = JobEvent::JOB_COMPLETED; break; default: status = JobEvent::JOB_PROGRESS; break; } + eventTrackerMutex.lock(); eventManager.enqueue(status, (i + 1) / 100.0, i, std::to_string(i).c_str()); + eventTrackerMutex.unlock(); } eventManager.requestStopAndWaitForIt(); std::vector expectedResults = generateFibonacci(NUM_ITERATIONS); @@ -33,13 +38,18 @@ TEST(MessageProcessingTest, MessagesAreProcessed) { } void appendNextIndex(const WorkerEvent* event) { - if (eventTracker.empty()) { eventTracker.emplace_back("0"); return; } - if (1 == eventTracker.size()) { eventTracker.emplace_back("1"); return; } - - const long firstValue = std::stol(eventTracker[eventTracker.size() - 2]); - const long secondValue = std::stol(eventTracker[eventTracker.size() - 1]); - const std::string nextValue{std::to_string(firstValue + secondValue)}; - eventTracker.push_back(nextValue); + eventTrackerMutex.lock(); + if (eventTracker.empty()) { + eventTracker.emplace_back("0"); + } else if (1 == eventTracker.size()) { + eventTracker.emplace_back("1"); + } else { + const long firstValue = std::stol(eventTracker[eventTracker.size() - 2]); + const long secondValue = std::stol(eventTracker[eventTracker.size() - 1]); + const std::string nextValue{std::to_string(firstValue + secondValue)}; + eventTracker.push_back(nextValue); + } + eventTrackerMutex.unlock(); } std::vector generateFibonacci(const int length) { From 8f69da7c007b7dc96ffd5cfb09d959d88cd030e1 Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Wed, 17 Dec 2025 13:54:03 -0500 Subject: [PATCH 14/26] simplified event-queue, and fixed mutex-access-before-constructor-finished bug --- .../include/VCELL/MessageEventManager.h | 11 ++-- .../include/VCELL/SimulationMessaging.h | 14 ++--- VCellMessaging/src/MessageEventManager.cpp | 47 ++++++--------- VCellMessaging/src/SimulationMessaging.cpp | 57 ++++++------------- 4 files changed, 45 insertions(+), 84 deletions(-) diff --git a/VCellMessaging/include/VCELL/MessageEventManager.h b/VCellMessaging/include/VCELL/MessageEventManager.h index a167e4748..33bd2381e 100644 --- a/VCellMessaging/include/VCELL/MessageEventManager.h +++ b/VCellMessaging/include/VCELL/MessageEventManager.h @@ -47,17 +47,18 @@ class MessageEventManager { void processEvent(WorkerEvent* event); void enqueue(WorkerEvent*); - std::mutex timeClockMutex; + std::thread eventQueueProcessingWorkerThread; //TODO: make a `std::jthread` once compilers catch up with standard + std::function sendUpdateFunction; + // Critical Resources / Regions + // -> CR #1 - Whether stop has been requested or not bool stopRequested; std::condition_variable requestedStopForeman; std::mutex stopRequestedMutex; + // -> CR #2 - The Event Queue std::queue eventQueue; + std::condition_variable eventQueueForeman; std::mutex queuetex; - - std::thread eventQueueProcessingWorkerThread; //TODO: make a `std::jthread` once compilers catch up with standard - std::condition_variable needMessageProcessingForeman; - std::function sendUpdateFunction; }; #endif //VCELL_ODE_NUMERICS_MESSAGEEVENTQUEUE_H \ No newline at end of file diff --git a/VCellMessaging/include/VCELL/SimulationMessaging.h b/VCellMessaging/include/VCELL/SimulationMessaging.h index 92a672e04..ab6cd04cf 100644 --- a/VCellMessaging/include/VCELL/SimulationMessaging.h +++ b/VCellMessaging/include/VCELL/SimulationMessaging.h @@ -33,10 +33,8 @@ static constexpr int Message_DEFAULT_PRIORITY = 0; //TODO: Re-write so that there is a messaging handler abstract class with two children: stdOut and CUrl class SimulationMessaging { public: - virtual ~SimulationMessaging() noexcept; - static void initialize(); - static SimulationMessaging* create(); static SimulationMessaging* getInstVar(); + static void cleanupInstanceVar(); void setWorkerEvent(JobEvent::Status status, const char *eventMessage); void setWorkerEvent(JobEvent::Status status, double progress, double timepoint); void setWorkerEvent(JobEvent::Status status, double progress, double timepoint, const char *eventMessage); @@ -54,11 +52,12 @@ class SimulationMessaging { } void waitUntilFinished(); #ifdef USE_MESSAGING - static SimulationMessaging* create(const char* broker, const char* smqusername, const char* passwd, const char* qname, const char* tname, - const char* vcusername, int simKey, int jobIndex, int taskID, int ttl_low=DEFAULT_TTL_LOW, int ttl_high=DEFAULT_TTL_HIGH); - friend void* startMessagingThread(void* param); + void initialize_curl_messaging(bool alsoPrintToStdOut, const char* broker, const char* vcusername, int simKey, int jobIndex, int taskID, int ttl_low=DEFAULT_TTL_LOW, int ttl_high=DEFAULT_TTL_HIGH); #endif + protected: + virtual ~SimulationMessaging() noexcept; + private: // Statics static void sendStdOutStatus(WorkerEvent*); @@ -78,9 +77,6 @@ class SimulationMessaging { static bool isInitialized; SimulationMessaging(); - #ifdef USE_MESSAGING - SimulationMessaging(const char* broker, const char* vcusername, int simKey, int jobIndex, int taskID, int ttl_low=DEFAULT_TTL_LOW, int ttl_high=DEFAULT_TTL_HIGH); - #endif void sendStatus(WorkerEvent*); void keepAlive(); diff --git a/VCellMessaging/src/MessageEventManager.cpp b/VCellMessaging/src/MessageEventManager.cpp index 5f9bedef7..100bbb1a9 100644 --- a/VCellMessaging/src/MessageEventManager.cpp +++ b/VCellMessaging/src/MessageEventManager.cpp @@ -10,10 +10,9 @@ MessageEventManager::MessageEventManager(std::function sendUpdateFunction): stopRequested{false}, - sendUpdateFunction{std::move(sendUpdateFunction)}, - eventQueueProcessingWorkerThread([this]() {this->performQueueProcessing();}) // Should auto-start thread + sendUpdateFunction{std::move(sendUpdateFunction)} { - + this->eventQueueProcessingWorkerThread = std::thread([this]() {this->performQueueProcessing();}); // Should auto-start thread } MessageEventManager::~MessageEventManager() { @@ -37,14 +36,16 @@ void MessageEventManager::requestStopAndWaitForIt() { std::unique_lock stopRequestedLock{this->stopRequestedMutex}; if (this->stopRequested) return; this->stopRequested = true; - this->needMessageProcessingForeman.notify_all(); - this->requestedStopForeman.wait(stopRequestedLock, [this]()->bool{return this->eventQueue.empty();}); + this->eventQueueForeman.notify_all(); // We want any sleeping workers to wake up, so they check if a stop was requested. + this->requestedStopForeman.wait(stopRequestedLock, [this]()->bool { + std::unique_lock queueLock{this->queuetex}; + return this->eventQueue.empty(); + }); } bool MessageEventManager::stopWasCalled() { std::unique_lock stopRequestedLock{this->stopRequestedMutex}; return this->stopRequested; - } /////////////////////////////////////////////////// @@ -66,35 +67,27 @@ void MessageEventManager::performQueueProcessing() { void MessageEventManager::processQueue() { while (true) { WorkerEvent* event; - {// START Clock-out Scope // - // The order of the locks is important! See Header File - std::unique_lock checkIfTheresWorkLock{this->timeClockMutex}; // This is to prevent loop processing while `enqueue` is being called + {// START Queuetex Scope // std::unique_lock shouldBeActiveLock(this->queuetex); if (this->eventQueue.empty()) { { // START stop-requested Scope std::unique_lock stopRequestedLock{this->stopRequestedMutex}; if (this->stopRequested) { - this->requestedStopForeman.notify_all(); - return; // We're all done; since this should be done on a jthread, this should also trigger a join. + this->requestedStopForeman.notify_all(); // Tell all stop-requesters that we've registered a stop. + return; // We're all done } } // END stop-requested Scope - checkIfTheresWorkLock.unlock(); // Need to allow enqueuing, can't do that with this locked, and we can't rescope but unique_lock order matters above! - // The worker can stop working, and "get some sleep" - // Wait for the worker to be "prodded" (via `needMessagingForeman.notify_one()`), AND the event queue is not empty - // Note that this `wait()` call, by design, unlocks the queue mutex, until it is "prodded". - this->needMessageProcessingForeman.wait(shouldBeActiveLock); - std::unique_lock stopRequestedLock{this->stopRequestedMutex}; + // If the code gets to here, the worker can stop working, and "get some sleep" + this->eventQueueForeman.wait(shouldBeActiveLock); // Note that this `wait()` call, by design, unlocks the queue mutex, until it is "prodded". if (this->eventQueue.empty()) continue; // Probably means we need to check if stop was requested again } - std::unique_lock stopRequestedLock{this->stopRequestedMutex}; event = this->eventQueue.front(); this->eventQueue.pop(); - }// END Clock-out Scope // + }// END Queuetex Scope // // Process Event this->processEvent(event); - // Remember to delete the event! We need the memory back! - delete event; + delete event; // Remember to delete the event! We need the memory back! } } @@ -103,19 +96,13 @@ void MessageEventManager::processEvent(WorkerEvent* event) { } void MessageEventManager::enqueue(WorkerEvent* event) { - // The order of the locks is important! See Header File - std::lock_guard workerIsActiveLock{this->timeClockMutex}; // Need to lock to prevent worker from "clocking out" while we set this up + std::lock_guard workerIsActiveLock{this->queuetex}; // Need to lock to prevent worker from checking if it has more work while we make the request. std::lock_guard stopRequestedLock{this->stopRequestedMutex}; // We scope this lock to the whole function; "last in the door" policy if (this->stopRequested) { std::cerr << "A new event was added to the messaging queue, but this queue has had `stop` requested!" << std::endl; delete event; return; // note: on this return function scope ends, and thus so too does out function-scoped locks } - - {// START Emplace Scope // - std::lock_guard queueLock{this->queuetex}; - this->eventQueue.emplace(event); - }// END Emplace Scope // - - this->needMessageProcessingForeman.notify_one(); // Nudges one sleeping worker awake. If the worker is awake...this does nothing; as it should. + this->eventQueue.emplace(event); + this->eventQueueForeman.notify_one(); // Nudges one sleeping worker awake. If the worker is awake...this does nothing; as it should. } \ No newline at end of file diff --git a/VCellMessaging/src/SimulationMessaging.cpp b/VCellMessaging/src/SimulationMessaging.cpp index 5dea90ff4..45e04a982 100644 --- a/VCellMessaging/src/SimulationMessaging.cpp +++ b/VCellMessaging/src/SimulationMessaging.cpp @@ -4,50 +4,37 @@ #include #include - - -static const double WORKEREVENT_MESSAGE_MIN_TIME_SECONDS = 15.0; +static constexpr double WORKEREVENT_MESSAGE_MIN_TIME_SECONDS = 15.0; bool SimulationMessaging::isInitialized = false; -SimulationMessaging *SimulationMessaging::m_inst = NULL; +SimulationMessaging *SimulationMessaging::m_inst = nullptr; SimulationMessaging::SimulationMessaging(): eventHandler(std::bind(&SimulationMessaging::sendStatus, this, std::placeholders::_1)), + workerEventOutputMode{WORKEREVENT_OUTPUT_MODE_STDOUT}, taskID{-1}, - m_jobIndex{-1} -{ - this->taskID = -1; - this->workerEventOutputMode = WORKEREVENT_OUTPUT_MODE_STDOUT; - this->curlHandler = new NullCurlProxy(); - -} - -#ifdef USE_MESSAGING -SimulationMessaging::SimulationMessaging(const char* broker, const char* vcusername, int simKey, int jobIndex, int taskID, int ttl_low, int ttl_high): - eventHandler(std::bind(&SimulationMessaging::sendStatus, this, std::placeholders::_1)), - workerEventOutputMode{WORKEREVENT_OUTPUT_MODE_MESSAGING}, - taskID{taskID}, - m_jobIndex{jobIndex}, + m_jobIndex{-1}, bNewWorkerEvent{false} { - this->curlHandler = new CurlProxy(simKey, taskID, jobIndex, vcusername, broker, ttl_low, ttl_high); + this->curlHandler = new NullCurlProxy(); time(&this->lastSentEventTime); } -#endif SimulationMessaging::~SimulationMessaging() noexcept{ delete this->curlHandler; } SimulationMessaging* SimulationMessaging::getInstVar() { + if (nullptr == SimulationMessaging::m_inst) SimulationMessaging::m_inst = new SimulationMessaging(); return SimulationMessaging::m_inst; } -SimulationMessaging* SimulationMessaging::create(){ - if (SimulationMessaging::m_inst == NULL) SimulationMessaging::m_inst = new SimulationMessaging(); - - return SimulationMessaging::m_inst; +void SimulationMessaging::cleanupInstanceVar() { + if (nullptr == SimulationMessaging::m_inst) return; + SimulationMessaging::m_inst->waitUntilFinished(); + delete SimulationMessaging::m_inst; + SimulationMessaging::m_inst = nullptr; } void SimulationMessaging::sendStatus(WorkerEvent* event) { @@ -132,26 +119,16 @@ void SimulationMessaging::setWorkerEvent(JobEvent::Status status, const double p void SimulationMessaging::waitUntilFinished() { this->eventHandler.requestStopAndWaitForIt(); - // if (workerEventOutputMode == WORKEREVENT_OUTPUT_MODE_STDOUT) return; - // #ifdef USE_MESSAGING - // std::cout << "!!!waiting for thread to exit" << std::endl; - // pthread_join(newWorkerEventThread, NULL); - // std::cout << "!!Threads joined successfully" << std::endl; - // #endif } #ifdef USE_MESSAGING -SimulationMessaging* SimulationMessaging::create(const char* broker, const char* smqusername, const char* passwd, const char* qname, const char* tname, const char* vcusername, int simKey, int jobIndex, int taskID, int ttl_low, int ttl_high){ - if (m_inst != NULL && m_inst->workerEventOutputMode == WORKEREVENT_OUTPUT_MODE_STDOUT) { - delete m_inst; - m_inst = NULL; - } - if (m_inst == NULL){ - m_inst = new SimulationMessaging(broker, vcusername, simKey, jobIndex, taskID, ttl_low, ttl_high); - } - - return(m_inst); +void SimulationMessaging::initialize_curl_messaging(bool alsoPrintToStdOut, const char* broker, const char* vcusername, int simKey, int jobIndex, int givenTaskID, int ttl_low, int ttl_high){ + this->workerEventOutputMode = alsoPrintToStdOut ? WORKEREVENT_OUTPUT_MODE_ALL : WORKEREVENT_OUTPUT_MODE_MESSAGING; + this->taskID = givenTaskID; + this->m_jobIndex = jobIndex; + delete this->curlHandler; // get rid of the null one we make by default + this->curlHandler = new CurlProxy(simKey, taskID, jobIndex, vcusername, broker, ttl_low, ttl_high); } // void SimulationMessaging::start() { From f1f1cb72fdec208dd2a5b5ead33aec96d9345c62 Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Wed, 17 Dec 2025 13:54:22 -0500 Subject: [PATCH 15/26] rearranged some test code to use same body --- tests/unit/message_processing_test.cpp | 86 +++++++++++++++++--------- tests/unit/smoke_test.cpp | 44 ++++++------- 2 files changed, 76 insertions(+), 54 deletions(-) diff --git a/tests/unit/message_processing_test.cpp b/tests/unit/message_processing_test.cpp index 548c83738..b8840d567 100644 --- a/tests/unit/message_processing_test.cpp +++ b/tests/unit/message_processing_test.cpp @@ -3,53 +3,81 @@ // #include #include -#include -#include +// #include +// #include +#include +#include #include #include #include "VCellSundialsSolver.h" static std::vector eventTracker; -static std::mutex eventTrackerMutex; + void appendNextIndex(const WorkerEvent* event); std::vector generateFibonacci(int length); long sequenceComparison(const std::vector &sequence1, const std::vector &sequence2); -TEST(MessageProcessingTest, MessagesAreProcessed) { +static std::unordered_map nameMap; + +// std::mutex& getMeyersMutex() { +// static std::mutex eventMutex; +// return eventMutex; +// } - MessageEventManager eventManager{[](WorkerEvent* event)->void{appendNextIndex(event);}}; - const int NUM_ITERATIONS = 100; - for (int i = 0; i < NUM_ITERATIONS; ++i) { - JobEvent::Status status; - switch (i + 1) { - case 1: status = JobEvent::JOB_STARTING; break; - case NUM_ITERATIONS: status = JobEvent::JOB_COMPLETED; break; - default: status = JobEvent::JOB_PROGRESS; break; +TEST(MessageProcessingTest, MessagesAreProcessed) { + try { + nameMap[std::this_thread::get_id()] = "Main Thread"; + MessageEventManager eventManager{[](WorkerEvent* event)->void{appendNextIndex(event);}}; + const int NUM_ITERATIONS = 100; + for (int i = 0; i < NUM_ITERATIONS; ++i) { + //std::cerr << "Main PID = " << getpid() << "\n"; + JobEvent::Status status; + switch (i + 1) { + case 1: status = JobEvent::JOB_STARTING; break; + case NUM_ITERATIONS: status = JobEvent::JOB_COMPLETED; break; + default: status = JobEvent::JOB_PROGRESS; break; + } + //std::cerr << "Locking mutex at " << &getMeyersMutex << " on thread " << nameMap[std::this_thread::get_id()] << "\n"; + // getMeyersMutex().lock();//eventTrackerMutex->lock(); + //std::cerr << "Obtained mutex at " << &getMeyersMutex << " on thread " << nameMap[std::this_thread::get_id()] << "\n"; + eventManager.enqueue(status, (i + 1) / 100.0, i, std::to_string(i).c_str()); + //std::cerr << "Unlocking mutex at " << &getMeyersMutex << " on thread " << nameMap[std::this_thread::get_id()] << "\n"; + // getMeyersMutex().unlock();//eventTrackerMutex->unlock(); } - eventTrackerMutex.lock(); - eventManager.enqueue(status, (i + 1) / 100.0, i, std::to_string(i).c_str()); - eventTrackerMutex.unlock(); + eventManager.requestStopAndWaitForIt(); + std::vector expectedResults = generateFibonacci(NUM_ITERATIONS); + // delete eventTrackerMutex; + ASSERT_TRUE(!sequenceComparison(eventTracker, expectedResults)); + } catch (const std::exception& e) { + std::cerr << "Caught exception in main test body: " << e.what() << std::endl; } - eventManager.requestStopAndWaitForIt(); - std::vector expectedResults = generateFibonacci(NUM_ITERATIONS); - ASSERT_TRUE(!sequenceComparison(eventTracker, expectedResults)); } void appendNextIndex(const WorkerEvent* event) { - eventTrackerMutex.lock(); - if (eventTracker.empty()) { - eventTracker.emplace_back("0"); - } else if (1 == eventTracker.size()) { - eventTracker.emplace_back("1"); - } else { - const long firstValue = std::stol(eventTracker[eventTracker.size() - 2]); - const long secondValue = std::stol(eventTracker[eventTracker.size() - 1]); - const std::string nextValue{std::to_string(firstValue + secondValue)}; - eventTracker.push_back(nextValue); + //std::cerr << "Event PID = " << getpid() << "\n"; + if (!nameMap.contains(std::this_thread::get_id())) nameMap[std::this_thread::get_id()] = "Event Thread"; + try { + //std::cerr << "Locking mutex at " << &getMeyersMutex << " on thread " << nameMap[std::this_thread::get_id()] << "\n"; + // getMeyersMutex().lock();//eventTrackerMutex->lock(); + //std::cerr << "Obtained mutex at " << &getMeyersMutex << " on thread " << nameMap[std::this_thread::get_id()] << "\n"; + if (eventTracker.empty()) { + eventTracker.emplace_back("0"); + } else if (1 == eventTracker.size()) { + eventTracker.emplace_back("1"); + } else { + const long firstValue = std::stol(eventTracker[eventTracker.size() - 2]); + const long secondValue = std::stol(eventTracker[eventTracker.size() - 1]); + const std::string nextValue{std::to_string(firstValue + secondValue)}; + eventTracker.push_back(nextValue); + } + //std::cerr << "Unlocking mutex at " << &getMeyersMutex << " on thread " << nameMap[std::this_thread::get_id()] << "\n"; + // getMeyersMutex().unlock();//eventTrackerMutex->unlock(); + } catch (const std::exception& e) { + std::cerr << "Caught exception in event loop: " << e.what() << std::endl; + exit(2); } - eventTrackerMutex.unlock(); } std::vector generateFibonacci(const int length) { diff --git a/tests/unit/smoke_test.cpp b/tests/unit/smoke_test.cpp index 6a6f66893..18d94e1ee 100644 --- a/tests/unit/smoke_test.cpp +++ b/tests/unit/smoke_test.cpp @@ -16,8 +16,7 @@ void compare(const std::filesystem::path& file1, const std::filesystem::path& file2, float tolerance); -TEST(SmokeTest, UserProvidesFilesWithoutJMS) { - constexpr int taskID = -1, hashID = 1489333437; +void performSmokeTest(const int taskID, const int hashID) { const std::filesystem::path RESOURCE_DIRECTORY{RESOURCE_DIR}; const std::filesystem::path OUTPUT_TARGET{RESOURCE_DIRECTORY /std::format("SimID_{}_0_.ida", hashID)}; const std::array NECESSARY_FILES{ @@ -36,36 +35,31 @@ TEST(SmokeTest, UserProvidesFilesWithoutJMS) { throw std::runtime_error("Could not open output file[" + OUTPUT_TARGET.string() + "] for writing."); } - activateSolver(inputFileStream, outputFile, taskID); + try { + activateSolver(inputFileStream, outputFile, taskID); + } catch (const std::exception& e) { + std::cerr << "Error caught in test: " << e.what() << std::endl; + exit(EXIT_FAILURE); + } fclose(outputFile); compare(OUTPUT_TARGET, NECESSARY_FILES[1], 1e-7); } -TEST(SmokeTest, UserProvidesFilesWithJMS) { - constexpr int taskID = 2025, hashID = 256118677; - const std::filesystem::path RESOURCE_DIRECTORY{RESOURCE_DIR}; - const std::filesystem::path OUTPUT_TARGET{RESOURCE_DIRECTORY /std::format("SimID_{}_0_.ida", hashID)}; - const std::array NECESSARY_FILES{ - RESOURCE_DIRECTORY /std::format("SimID_{}_0_.cvodeInput", hashID), - RESOURCE_DIRECTORY /std::format("SimID_{}_0_.ida.expected", hashID) - }; - for (const auto& file : NECESSARY_FILES) { - assert(std::filesystem::exists(file)); - } - FILE *outputFile = NULL; - std::ifstream inputFileStream{NECESSARY_FILES[0]}; - if (!inputFileStream.is_open()) { throw std::runtime_error("input file [" + NECESSARY_FILES[0].string() + "] doesn't exit!"); } - - // Open the output file... - if (NULL == (outputFile = fopen(OUTPUT_TARGET.string().c_str(), "w"))) { - throw std::runtime_error("Could not open output file[" + OUTPUT_TARGET.string() + "] for writing."); - } +TEST(SmokeTest, UserProvidesFilesWithoutJMS) { + constexpr int taskID = -1, hashID = 1489333437; + performSmokeTest(taskID, hashID); +} - activateSolver(inputFileStream, outputFile, taskID); - fclose(outputFile); +TEST(SmokeTest, UserProvidesFilesWithJMS) { + constexpr int taskID = 2025; + #ifdef TEST_WITH_LOCALHOST + constexpr int hashID = 886118677; + #else + constexpr int hashID = 256118677; + #endif - compare(OUTPUT_TARGET, NECESSARY_FILES[1], 1e-7); + performSmokeTest(taskID, hashID); } void compare(const std::filesystem::path& file1, const std::filesystem::path& file2, float tolerance) { From 1a306e581fdb52b799b4eceb8afa4a74dc8cc8af Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Wed, 17 Dec 2025 13:54:39 -0500 Subject: [PATCH 16/26] Fixed undefined behavior bug --- IDAWin/SundialsSolverInterface.cpp | 5 +---- IDAWin/VCellSolverFactory.cpp | 14 +++++--------- IDAWin/VCellSundialsSolver.cpp | 4 +++- tests/unit/message_processing_test.cpp | 24 ------------------------ 4 files changed, 9 insertions(+), 38 deletions(-) diff --git a/IDAWin/SundialsSolverInterface.cpp b/IDAWin/SundialsSolverInterface.cpp index 09fcebcb8..774fa2b31 100644 --- a/IDAWin/SundialsSolverInterface.cpp +++ b/IDAWin/SundialsSolverInterface.cpp @@ -79,10 +79,7 @@ void activateSolver(std::ifstream& inputFileStream, FILE* outputFile, int taskID } // cleanup - if (SimulationMessaging::getInstVar() != nullptr) { - SimulationMessaging::getInstVar()->waitUntilFinished(); - delete SimulationMessaging::getInstVar(); - } + SimulationMessaging::cleanupInstanceVar(); if (!errorMsg.empty()) errExit(returnCode, errorMsg); } diff --git a/IDAWin/VCellSolverFactory.cpp b/IDAWin/VCellSolverFactory.cpp index 212508f2e..648cd9300 100644 --- a/IDAWin/VCellSolverFactory.cpp +++ b/IDAWin/VCellSolverFactory.cpp @@ -109,12 +109,6 @@ VCellSolverInputBreakdown VCellSolverFactory::parseInputFile(std::ifstream& inpu else throw VCell::Exception("Unexpected token \"" + nextToken + "\" in the input file!"); } - - - #ifdef USE_MESSAGING - // Since messaging assumes we have a job requiring messaging, we should initialize a "default" messaging handler - if (NULL == SimulationMessaging::getInstVar()) SimulationMessaging::create(); - #endif return inputBreakdown; } @@ -469,8 +463,10 @@ static void loadJMSInfo(std::istream &ifsInput, int taskID) { } #ifdef USE_MESSAGING - SimulationMessaging::create(broker.c_str(), smqUserName.c_str(), - password.c_str(), qName.c_str(), topicName.c_str(), - vCellUsername.c_str(), simKey, jobIndex, taskID); + // SimulationMessaging::getInstVar()->initialize_curl_messaging(broker.c_str(), smqUserName.c_str(), + // password.c_str(), qName.c_str(), topicName.c_str(), + // vCellUsername.c_str(), simKey, jobIndex, taskID); + SimulationMessaging::getInstVar()->initialize_curl_messaging(false, broker.c_str(), + vCellUsername.c_str(), simKey, jobIndex, taskID); #endif } diff --git a/IDAWin/VCellSundialsSolver.cpp b/IDAWin/VCellSundialsSolver.cpp index 3ff290601..168793ad0 100644 --- a/IDAWin/VCellSundialsSolver.cpp +++ b/IDAWin/VCellSundialsSolver.cpp @@ -613,7 +613,9 @@ double VCellSundialsSolver::getNextEventTime() { void VCellSundialsSolver::checkStopRequested(double time, long numIterations) { #ifdef USE_MESSAGING - if (SimulationMessaging::getInstVar()->isStopRequested()) { throw StoppedByUserException("stopped by user"); } + auto instVar = SimulationMessaging::getInstVar(); + if (nullptr == instVar) return; + if (instVar->isStopRequested()) { throw StoppedByUserException("stopped by user"); } #endif } diff --git a/tests/unit/message_processing_test.cpp b/tests/unit/message_processing_test.cpp index b8840d567..5b7463cf4 100644 --- a/tests/unit/message_processing_test.cpp +++ b/tests/unit/message_processing_test.cpp @@ -3,10 +3,7 @@ // #include #include -// #include -// #include #include -#include #include #include @@ -14,37 +11,23 @@ static std::vector eventTracker; - void appendNextIndex(const WorkerEvent* event); std::vector generateFibonacci(int length); long sequenceComparison(const std::vector &sequence1, const std::vector &sequence2); -static std::unordered_map nameMap; - -// std::mutex& getMeyersMutex() { -// static std::mutex eventMutex; -// return eventMutex; -// } TEST(MessageProcessingTest, MessagesAreProcessed) { try { - nameMap[std::this_thread::get_id()] = "Main Thread"; MessageEventManager eventManager{[](WorkerEvent* event)->void{appendNextIndex(event);}}; const int NUM_ITERATIONS = 100; for (int i = 0; i < NUM_ITERATIONS; ++i) { - //std::cerr << "Main PID = " << getpid() << "\n"; JobEvent::Status status; switch (i + 1) { case 1: status = JobEvent::JOB_STARTING; break; case NUM_ITERATIONS: status = JobEvent::JOB_COMPLETED; break; default: status = JobEvent::JOB_PROGRESS; break; } - //std::cerr << "Locking mutex at " << &getMeyersMutex << " on thread " << nameMap[std::this_thread::get_id()] << "\n"; - // getMeyersMutex().lock();//eventTrackerMutex->lock(); - //std::cerr << "Obtained mutex at " << &getMeyersMutex << " on thread " << nameMap[std::this_thread::get_id()] << "\n"; eventManager.enqueue(status, (i + 1) / 100.0, i, std::to_string(i).c_str()); - //std::cerr << "Unlocking mutex at " << &getMeyersMutex << " on thread " << nameMap[std::this_thread::get_id()] << "\n"; - // getMeyersMutex().unlock();//eventTrackerMutex->unlock(); } eventManager.requestStopAndWaitForIt(); std::vector expectedResults = generateFibonacci(NUM_ITERATIONS); @@ -56,12 +39,7 @@ TEST(MessageProcessingTest, MessagesAreProcessed) { } void appendNextIndex(const WorkerEvent* event) { - //std::cerr << "Event PID = " << getpid() << "\n"; - if (!nameMap.contains(std::this_thread::get_id())) nameMap[std::this_thread::get_id()] = "Event Thread"; try { - //std::cerr << "Locking mutex at " << &getMeyersMutex << " on thread " << nameMap[std::this_thread::get_id()] << "\n"; - // getMeyersMutex().lock();//eventTrackerMutex->lock(); - //std::cerr << "Obtained mutex at " << &getMeyersMutex << " on thread " << nameMap[std::this_thread::get_id()] << "\n"; if (eventTracker.empty()) { eventTracker.emplace_back("0"); } else if (1 == eventTracker.size()) { @@ -72,8 +50,6 @@ void appendNextIndex(const WorkerEvent* event) { const std::string nextValue{std::to_string(firstValue + secondValue)}; eventTracker.push_back(nextValue); } - //std::cerr << "Unlocking mutex at " << &getMeyersMutex << " on thread " << nameMap[std::this_thread::get_id()] << "\n"; - // getMeyersMutex().unlock();//eventTrackerMutex->unlock(); } catch (const std::exception& e) { std::cerr << "Caught exception in event loop: " << e.what() << std::endl; exit(2); From dd2eddfb64e5ea04f33abe1fe724b982707bac36 Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Thu, 13 Nov 2025 12:18:37 -0500 Subject: [PATCH 17/26] trying libcurl downgrade --- .github/workflows/cd.yml | 6 ++++-- ExpressionParser/ExpressionParserTokenManager.cpp | 1 + ExpressionParser/ExpressionParserTokenManager.h | 1 - IDAWin/VCellIDASolver.cpp | 1 + IDAWin/VCellSundialsSolver.cpp | 2 ++ 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 855bcab39..f59ca393a 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -54,7 +54,9 @@ jobs: if: matrix.platform == 'macos-15-intel' shell: bash run: | - brew install conan + wget https://github.com/conan-io/conan/releases/download/2.22.2/conan-2.22.2-macos-x86_64.tgz + tar -xvzf conan-2.22.2-macos-x86_64.tgz + cp ./bin/conan /usr/local/bin conan --version mkdir -p ~/.conan2/profiles/ touch ~/.conan2/profiles/default # if we don't make a file first, cp thinks its a folder that doesn't exist @@ -115,7 +117,7 @@ jobs: sudo apt install mold libc++-dev libc++abi-dev sudo rm /usr/bin/ld sudo ln -s $(which mold) /usr/bin/ld - conan install . --output-folder build --build=missing + conan install -v trace . --output-folder build --build=missing - name: Install Dependencies through Conan on Linux if: matrix.platform == 'ubuntu-24.04-arm' diff --git a/ExpressionParser/ExpressionParserTokenManager.cpp b/ExpressionParser/ExpressionParserTokenManager.cpp index 7873eaa5e..52260642b 100644 --- a/ExpressionParser/ExpressionParserTokenManager.cpp +++ b/ExpressionParser/ExpressionParserTokenManager.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "ExpressionParserTokenManager.h" #include "RuntimeException.h" diff --git a/ExpressionParser/ExpressionParserTokenManager.h b/ExpressionParser/ExpressionParserTokenManager.h index 1c4bec14d..ea461ccfa 100644 --- a/ExpressionParser/ExpressionParserTokenManager.h +++ b/ExpressionParser/ExpressionParserTokenManager.h @@ -5,7 +5,6 @@ #include - #include "Token.h" #include "SimpleCharStream.h" diff --git a/IDAWin/VCellIDASolver.cpp b/IDAWin/VCellIDASolver.cpp index 70c70bf2b..28a3decc6 100644 --- a/IDAWin/VCellIDASolver.cpp +++ b/IDAWin/VCellIDASolver.cpp @@ -16,6 +16,7 @@ //#include #include #include +#include #ifdef USE_MESSAGING #include #endif diff --git a/IDAWin/VCellSundialsSolver.cpp b/IDAWin/VCellSundialsSolver.cpp index 168793ad0..81dd8d483 100644 --- a/IDAWin/VCellSundialsSolver.cpp +++ b/IDAWin/VCellSundialsSolver.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #ifdef USE_MESSAGING #include From 3a0cdb309496789303674e3cbfc58dfb00b651ef Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Thu, 13 Nov 2025 14:56:00 -0500 Subject: [PATCH 18/26] changing intel-mac conan installation to python --- .github/workflows/cd.yml | 4 +--- ExpressionParser/ASTFuncNode.cpp | 3 +-- conan-profiles/CI-CD/MacOS-AMD64_profile.txt | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index f59ca393a..46e1b5dc8 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -54,9 +54,7 @@ jobs: if: matrix.platform == 'macos-15-intel' shell: bash run: | - wget https://github.com/conan-io/conan/releases/download/2.22.2/conan-2.22.2-macos-x86_64.tgz - tar -xvzf conan-2.22.2-macos-x86_64.tgz - cp ./bin/conan /usr/local/bin + python3 -m pip install conan conan --version mkdir -p ~/.conan2/profiles/ touch ~/.conan2/profiles/default # if we don't make a file first, cp thinks its a folder that doesn't exist diff --git a/ExpressionParser/ASTFuncNode.cpp b/ExpressionParser/ASTFuncNode.cpp index 59bf3a0e6..75669e157 100644 --- a/ExpressionParser/ASTFuncNode.cpp +++ b/ExpressionParser/ASTFuncNode.cpp @@ -2,10 +2,9 @@ #include #include - +#include #include "ASTFuncNode.h" -#include #include "RuntimeException.h" #include "ExpressionException.h" #include "MathUtil.h" diff --git a/conan-profiles/CI-CD/MacOS-AMD64_profile.txt b/conan-profiles/CI-CD/MacOS-AMD64_profile.txt index bc4007fdf..cfb9c0c52 100644 --- a/conan-profiles/CI-CD/MacOS-AMD64_profile.txt +++ b/conan-profiles/CI-CD/MacOS-AMD64_profile.txt @@ -4,5 +4,5 @@ build_type=Release compiler=apple-clang compiler.cppstd=20 compiler.libcxx=libc++ -compiler.version=17 +compiler.version=15 os=Macos \ No newline at end of file From 2ded3e3cf5ec8da9786235eda352930eb9c404d7 Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Thu, 13 Nov 2025 15:29:03 -0500 Subject: [PATCH 19/26] intel mac 13 too old; upgrading to MacOS15! --- .github/workflows/cd.yml | 4 ++-- conan-profiles/CI-CD/MacOS-AMD64_profile.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 46e1b5dc8..3c237162d 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -54,7 +54,7 @@ jobs: if: matrix.platform == 'macos-15-intel' shell: bash run: | - python3 -m pip install conan + brew install conan conan --version mkdir -p ~/.conan2/profiles/ touch ~/.conan2/profiles/default # if we don't make a file first, cp thinks its a folder that doesn't exist @@ -139,7 +139,7 @@ jobs: cmake -B . -S .. -G "Ninja" -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" -DCMAKE_BUILD_TYPE=Release cmake --build . --config Release - - name: Build Unix + - name: Build Non-Intel-Mac Unix if: matrix.platform == 'ubuntu-latest' || matrix.platform == 'ubuntu-24.04-arm' || matrix.platform == 'macos-15-intel' || matrix.platform == 'macos-latest' run: | echo "working dir is $PWD" diff --git a/conan-profiles/CI-CD/MacOS-AMD64_profile.txt b/conan-profiles/CI-CD/MacOS-AMD64_profile.txt index cfb9c0c52..bc4007fdf 100644 --- a/conan-profiles/CI-CD/MacOS-AMD64_profile.txt +++ b/conan-profiles/CI-CD/MacOS-AMD64_profile.txt @@ -4,5 +4,5 @@ build_type=Release compiler=apple-clang compiler.cppstd=20 compiler.libcxx=libc++ -compiler.version=15 +compiler.version=17 os=Macos \ No newline at end of file From c2ffa5dbc890e88b3ce7c1d62ddf378c7f829ea8 Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Tue, 9 Dec 2025 13:11:37 -0500 Subject: [PATCH 20/26] Updating build configurations --- .gitignore | 28 +++++++++---------- CMakeLists.txt | 24 ++++++---------- .../include/VCELL/CurlProxyClasses.h | 4 +-- 3 files changed, 23 insertions(+), 33 deletions(-) diff --git a/.gitignore b/.gitignore index 6aa1b85b4..4792b8fbe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,31 +1,29 @@ -/build-linux64-server/ -/build-dockcross-win64/ + /.project /.cproject -/build-win64/ /.DS_Store -/build-linux64/ -/build-macos/ /packages/ -/build_archive/ -/build-linux64-ubuntu/ + /conan/ /singularity/ -/cmake-build* -/build* -/debug-build* /.idea/ -/nfsim/ -/all_solvers/* .DS_Store -NFsim_v1.11/tests/smoke/SimID_273069657_0_.gdat -NFsim_v1.11/tests/smoke/SimID_273069657_0_.species - *.ida CMakeUserPresets.json + +/build-linux64-server/ +/build-dockcross-win64/ +/build-win64/ +/build-linux64/ +/build-macos/ +/build_archive/ +/build-linux64-ubuntu/ +/build* +/cmake-build* +/debug-build* conan-build conan-build/* conan_provider.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 887bcec7a..c45fc2750 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ if(POLICY CMP0057) cmake_policy(SET CMP0057 NEW) endif() -if (WINDOWS) +if (WIN32) cmake_policy(SET CMP0091 NEW) endif () @@ -89,7 +89,6 @@ endif() option(OPTION_TARGET_MESSAGING "Messaging (requires libcurl)" off) option(OPTION_TARGET_DOCS "Generate Doxygen documentation" off) - if (${OPTION_TARGET_DOCS}) # if (DOXYGEN_FOUND AND ${CMAKE_VERSION} GREATER_EQUAL 3.9) @@ -117,34 +116,27 @@ endif() set(HDF5_BUILD_CPP_LIB OFF CACHE BOOL "" ) -set(LINUX FALSE) -if (${CMAKE_SYSTEM_NAME} MATCHES Linux) - set(LINUX TRUE) -endif() -set(WINDOWS FALSE) -if (${CMAKE_SYSTEM_NAME} MATCHES Windows) - set(WINDOWS TRUE) - set(WIN32 TRUE) - set(MINGW TRUE) -endif() +#if (WIN32) # Only linux is designed at this point to support messaging, but unix should work too (and our devs generally work on macs) +# set(OPTION_TARGET_MESSAGING OFF) +#endif() set (ARCH_64bit FALSE) if (CMAKE_SIZEOF_VOID_P EQUAL 8) set (ARCH_64bit TRUE) endif() -if (NOT APPLE AND NOT LINUX AND NOT WINDOWS) +if (NOT APPLE AND NOT LINUX AND NOT WIN32) message(FATAL_ERROR "Unsupported Operating System") endif() #--------------------------- # IDE SUPPORT #--------------------------- -if (WINDOWS) +if (WIN32) set(NETBEANS_WINDOWS TRUE) set(CMAKE_RC_FLAGS "-DGCC_WINDRES") -endif(WINDOWS) +endif(WIN32) #add_definitions(-DFORTRAN_UNDERSCORE) @@ -220,7 +212,7 @@ if (OPTION_EXTRA_CONFIG_INFO) include(CMakePrintHelpers) cmake_print_variables(OPTION_TARGET_MESSAGING OPTION_TARGET_DOCS) cmake_print_variables(CMAKE_CXX_FLAGS CMAKE_C_FLAGS) - cmake_print_variables(CMAKE_SYSTEM_NAME WINDOWS WIN32 MINGW APPLE ARCH_64bit ARCH_32bit) + cmake_print_variables(CMAKE_SYSTEM_NAME WIN32 APPLE LINUX ARCH_64bit ARCH_32bit) cmake_print_variables(CMAKE_CPP_COMPILER CMAKE_C_COMPILER CMAKE_CXX_COMPILER) endif () diff --git a/VCellMessaging/include/VCELL/CurlProxyClasses.h b/VCellMessaging/include/VCELL/CurlProxyClasses.h index 54de41acc..450a7eec9 100644 --- a/VCellMessaging/include/VCELL/CurlProxyClasses.h +++ b/VCellMessaging/include/VCELL/CurlProxyClasses.h @@ -4,8 +4,6 @@ #ifndef VCELL_ODE_NUMERICS_CURLPROXY_H #define VCELL_ODE_NUMERICS_CURLPROXY_H #include -#include - #include "VCELL/WorkerEvent.h" @@ -26,6 +24,8 @@ class NullCurlProxy final : public AbstractCurlProxy { }; #ifdef USE_MESSAGING +#include + class CurlProxy final : public AbstractCurlProxy { public: CurlProxy(long simKey, int taskID, int jobIndex, const std::string& vcusername, const std::string& broker, int ttlLow, int ttlHigh); From 0802000ba2a06f367df03ef5c8d7af632983d503 Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Tue, 9 Dec 2025 15:00:49 -0500 Subject: [PATCH 21/26] Added build argument to select whether to test with localhost or not --- .github/workflows/docker-deploy.yml | 6 + CMakeLists.txt | 5 + VCellMessaging/src/CurlProxyClasses.cpp | 1 + tests/CMakeLists.txt | 9 +- .../resources/SimID_886118677_0_.cvodeInput | 24 +++ .../resources/SimID_886118677_0_.functions | 6 + .../resources/SimID_886118677_0_.ida.expected | 159 ++++++++++++++++++ tests/unit/resources/SimID_886118677_0_.log | 4 + tests/unit/smoke_test.cpp | 7 +- 9 files changed, 212 insertions(+), 9 deletions(-) create mode 100644 tests/unit/resources/SimID_886118677_0_.cvodeInput create mode 100644 tests/unit/resources/SimID_886118677_0_.functions create mode 100644 tests/unit/resources/SimID_886118677_0_.ida.expected create mode 100644 tests/unit/resources/SimID_886118677_0_.log diff --git a/.github/workflows/docker-deploy.yml b/.github/workflows/docker-deploy.yml index dda5774d5..db2f16f66 100644 --- a/.github/workflows/docker-deploy.yml +++ b/.github/workflows/docker-deploy.yml @@ -9,6 +9,12 @@ env: jobs: build-and-push-image: runs-on: ubuntu-latest + services: + activemq: + image: rmohr/activemq:latest # Or a specific version like rmohr/activemq:5.15.9 + ports: + - 30163:30163 # JMS port + - 8161:8161 # Web console port permissions: contents: read packages: write diff --git a/CMakeLists.txt b/CMakeLists.txt index c45fc2750..1be1bd6bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,6 +89,7 @@ endif() option(OPTION_TARGET_MESSAGING "Messaging (requires libcurl)" off) option(OPTION_TARGET_DOCS "Generate Doxygen documentation" off) +option(OPTION_TEST_WITH_LOCALHOST "Use localhost as server rather than vcell.cam.uchc.edu" off) if (${OPTION_TARGET_DOCS}) # if (DOXYGEN_FOUND AND ${CMAKE_VERSION} GREATER_EQUAL 3.9) @@ -166,6 +167,10 @@ if (OPTION_TARGET_MESSAGING) add_definitions(-DUSE_MESSAGING) endif() +if (OPTION_TEST_WITH_LOCALHOST) + add_definitions(-DTEST_WITH_LOCALHOST) +endif() + ###################################### # # Add subdirectories diff --git a/VCellMessaging/src/CurlProxyClasses.cpp b/VCellMessaging/src/CurlProxyClasses.cpp index 78f07f2ae..8bd1a591a 100644 --- a/VCellMessaging/src/CurlProxyClasses.cpp +++ b/VCellMessaging/src/CurlProxyClasses.cpp @@ -21,6 +21,7 @@ std::string trim(const std::string& str) { #include #include #include +#include #include "VCELL/JobEventStatus.h" #include "VCELL/SimulationMessaging.h" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9ee8867de..824aacb0b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -27,9 +27,12 @@ target_link_libraries(unit_tests PRIVATE ) gtest_discover_tests(unit_tests) -target_compile_definitions(unit_tests PRIVATE - RESOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}/unit/resources" -) +target_compile_definitions(unit_tests PRIVATE RESOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}/unit/resources") +if (OPTION_TEST_WITH_LOCALHOST) + target_compile_definitions(unit_tests PRIVATE TEST_WITH_LOCALHOST=1) +else() + target_compile_definitions(unit_tests PRIVATE TEST_WITH_LOCALHOST=0) +endif () #add_executable(integration_tests diff --git a/tests/unit/resources/SimID_886118677_0_.cvodeInput b/tests/unit/resources/SimID_886118677_0_.cvodeInput new file mode 100644 index 000000000..61ee66db3 --- /dev/null +++ b/tests/unit/resources/SimID_886118677_0_.cvodeInput @@ -0,0 +1,24 @@ +# JMS_Paramters +JMS_PARAM_BEGIN +JMS_BROKER localhost:30163 +JMS_USER clientUser dummy +JMS_QUEUE workerEvent +JMS_TOPIC serviceControl +VCELL_USER ldresche +SIMULATION_KEY 886118677 +JOB_INDEX 0 +JMS_PARAM_END + +SOLVER CVODE +STARTING_TIME 0.0 +ENDING_TIME 10.0 +RELATIVE_TOLERANCE 1.0E-9 +ABSOLUTE_TOLERANCE 1.0E-9 +MAX_TIME_STEP 1.0 +KEEP_EVERY 1 +NUM_EQUATIONS 2 +ODE s0 INIT 1.0; + RATE - ((0.5 * s0) - (0.2 * s1)); +ODE s1 INIT 0.0; + RATE ((0.5 * s0) - (0.2 * s1)); + diff --git a/tests/unit/resources/SimID_886118677_0_.functions b/tests/unit/resources/SimID_886118677_0_.functions new file mode 100644 index 000000000..d82a8d6eb --- /dev/null +++ b/tests/unit/resources/SimID_886118677_0_.functions @@ -0,0 +1,6 @@ +##--------------------------------------------- +## /simdata/ldresche/SimID_886118677_0_.functions +##--------------------------------------------- + +Compartment::J_r0; ((0.5 * s0) - (0.2 * s1)); ; Nonspatial_VariableType; false + diff --git a/tests/unit/resources/SimID_886118677_0_.ida.expected b/tests/unit/resources/SimID_886118677_0_.ida.expected new file mode 100644 index 000000000..c0a71f95f --- /dev/null +++ b/tests/unit/resources/SimID_886118677_0_.ida.expected @@ -0,0 +1,159 @@ +t:s0:s1: +0.00000000000000000E+00 1.00000000000000000E+00 0.00000000000000000E+00 +1.02648488190150711E-10 9.99999999948675722E-01 5.13242440913875063E-11 +1.02658753038969712E-06 9.99999486706603657E-01 5.13293396373231300E-07 +1.12914363494047687E-05 9.99994354310467903E-01 5.64568953210529354E-06 +4.48211895830175010E-05 9.99977589784958321E-01 2.24102150417055293E-05 +1.17277047540880841E-04 9.99941363945291539E-01 5.86360547084540949E-05 +2.53272368309885381E-04 9.99873375142890652E-01 1.26624857109333797E-04 +4.96024984269423201E-04 9.99752030703600969E-01 2.47969296398973030E-04 +9.22698163574049712E-04 9.99538800058830001E-01 4.61199941169868684E-04 +1.68044365853622663E-03 9.99160272360433921E-01 8.39727639565850115E-04 +3.18862269929603417E-03 9.98407466672823207E-01 1.59253332717652126E-03 +5.45114110140219790E-03 9.97279622342691741E-01 2.72037765730797233E-03 +7.71365950350836119E-03 9.96153562661734271E-01 3.84643733826544416E-03 +9.97617790561452535E-03 9.95029284922585688E-01 4.97071507741405488E-03 +1.22386963077206895E-02 9.93906786345072457E-01 6.09321365492729479E-03 +1.45012147098268537E-02 9.92786064126930712E-01 7.21393587306905053E-03 +1.73952544462393256E-02 9.91355107543508862E-01 8.64489245649092790E-03 +2.02892941826517993E-02 9.89927047398444282E-01 1.00729526015555741E-02 +2.31833339190642730E-02 9.88501877601865031E-01 1.14981223981348198E-02 +2.60773736554767467E-02 9.87079592114147530E-01 1.29204078858523308E-02 +3.29068688347819438E-02 9.83734614715639721E-01 1.62653852843602023E-02 +3.97363640140871374E-02 9.80405590318767395E-01 1.95944096812325566E-02 +5.02957748456068915E-02 9.75289650084949389E-01 2.47103499150505036E-02 +6.08551856771266456E-02 9.70211385257129355E-01 2.97886147428706068E-02 +7.14145965086463996E-02 9.65170518189519044E-01 3.48294818104809070E-02 +8.19740073401661606E-02 9.60166773545884755E-01 3.98332264541152309E-02 +9.25334181716859216E-02 9.55199878101650190E-01 4.48001218983497956E-02 +1.09712253292456846E-01 9.47197402212381134E-01 5.28025977876188587E-02 +1.26891088413227771E-01 9.39290581299578320E-01 6.07094187004216662E-02 +1.44069923533998695E-01 9.31478272015571096E-01 6.85217279844288346E-02 +1.61248758654769619E-01 9.23759344630696533E-01 7.62406553693034117E-02 +1.89660049003499281E-01 9.11195316277009670E-01 8.88046837229902880E-02 +2.18071339352228943E-01 8.98878691567765453E-01 1.01121308432234533E-01 +2.46482629700958605E-01 8.86804598883420114E-01 1.13195401116579816E-01 +2.74893920049688267E-01 8.74968262536995778E-01 1.25031737463004083E-01 +3.03305210398417957E-01 8.63365000789078185E-01 1.36634999210921676E-01 +3.31716500747147647E-01 8.51990224040054867E-01 1.48009775959944967E-01 +3.60127791095877337E-01 8.40839433068397923E-01 1.59160566931601855E-01 +3.88539081444607026E-01 8.29908217266169590E-01 1.70091782733830132E-01 +4.41138445070370255E-01 8.10235627189399343E-01 1.89764372810600435E-01 +4.77567574856914034E-01 7.97029226508457089E-01 2.02970773491542689E-01 +5.13996704643457814E-01 7.84155337872014080E-01 2.15844662127985643E-01 +5.50425834430001593E-01 7.71605588560225097E-01 2.28394411439774653E-01 +5.86854964216545372E-01 7.59371816557104728E-01 2.40628183442894966E-01 +6.23284094003089151E-01 7.47446066126369102E-01 2.52553933873630565E-01 +6.59713223789632930E-01 7.35820582268969181E-01 2.64179417731030486E-01 +6.96142353576176709E-01 7.24487805053363076E-01 2.75512194946636535E-01 +7.58815310233661888E-01 7.05654481953680968E-01 2.94345518046318699E-01 +8.21488266891147068E-01 6.87629536154125143E-01 3.12370463845874413E-01 +8.84161223548632247E-01 6.70378268923642939E-01 3.29621731076356561E-01 +9.46834180206117426E-01 6.53867470964243247E-01 3.46132529035756198E-01 +1.00950713686360261E+00 6.38065359483856231E-01 3.61934640516143269E-01 +1.07218009352108767E+00 6.22941516449129762E-01 3.77058483550869794E-01 +1.13485305017857274E+00 6.08466829122856878E-01 3.91533170877142678E-01 +1.19752600683605781E+00 5.94613433892461130E-01 4.05386566107538426E-01 +1.26019896349354288E+00 5.81354663050361431E-01 4.18645336949638014E-01 +1.32287192015102795E+00 5.68664993725449097E-01 4.31335006274550459E-01 +1.38554487680851302E+00 5.56519998669646965E-01 4.43480001330352702E-01 +1.44821783346599808E+00 5.44896299070346424E-01 4.55103700929653243E-01 +1.51089079012348315E+00 5.33771519516928472E-01 4.66228480483071195E-01 +1.57356374678096822E+00 5.23124244999401800E-01 4.76875755000597867E-01 +1.63623670343845329E+00 5.12933979731394318E-01 4.87066020268605349E-01 +1.69890966009593836E+00 5.03181107681094364E-01 4.96818892318905247E-01 +1.76158261675342342E+00 4.93846854780748468E-01 5.06153145219251144E-01 +1.82425557341090849E+00 4.84913252781268178E-01 5.15086747218731489E-01 +1.88692853006839356E+00 4.76363104676305715E-01 5.23636895323693952E-01 +1.94960148672587863E+00 4.68179951606793432E-01 5.31820048393206291E-01 +2.01227444338336392E+00 4.60348041167775057E-01 5.39651958832224610E-01 +2.07494740004084921E+00 4.52852297094320178E-01 5.47147702905679489E-01 +2.13762035669833450E+00 4.45678290239669095E-01 5.54321709760330572E-01 +2.20029331335581979E+00 4.38812210792762725E-01 5.61187789207236998E-01 +2.26296627001330508E+00 4.32240841692172695E-01 5.67759158307827083E-01 +2.32563922667079037E+00 4.25951533186333664E-01 5.74048466813666169E-01 +2.38831218332827566E+00 4.19932178485779350E-01 5.80067821514220539E-01 +2.45098513998576095E+00 4.14171190457713889E-01 5.85828809542286000E-01 +2.51365809664324624E+00 4.08657479319653238E-01 5.91342520680346651E-01 +2.57633105330073153E+00 4.03380431291335495E-01 5.96619568708664394E-01 +2.63900400995821682E+00 3.98329888163913515E-01 6.01670111836086319E-01 +2.70167696661570211E+00 3.93496127746071123E-01 6.06503872253928655E-01 +2.76434992327318740E+00 3.88869845148941795E-01 6.11130154851058038E-01 +2.82702287993067269E+00 3.84442134874129537E-01 6.15557865125870296E-01 +2.88969583658815798E+00 3.80204473670731957E-01 6.19795526329267821E-01 +2.95236879324564327E+00 3.76148704128358324E-01 6.23851295871641454E-01 +3.01504174990312857E+00 3.72267018974363595E-01 6.27732981025636239E-01 +3.07771470656061386E+00 3.68551946044992529E-01 6.31448053955007249E-01 +3.14038766321809915E+00 3.64996333901570436E-01 6.35003666098429287E-01 +3.20306061987558444E+00 3.61593338064120084E-01 6.38406661935879582E-01 +3.26573357653306973E+00 3.58336407835898707E-01 6.41663592164100960E-01 +3.32840653319055502E+00 3.55219273693456239E-01 6.44780726306543484E-01 +3.39107948984804031E+00 3.52235935217927731E-01 6.47764064782072047E-01 +3.45375244650552560E+00 3.49380649544342170E-01 6.50619350455657663E-01 +3.51642540316301089E+00 3.46647920306724255E-01 6.53352079693275467E-01 +3.57909835982049618E+00 3.44032487057707448E-01 6.55967512942292386E-01 +3.64177131647798147E+00 3.41529315142283463E-01 6.58470684857716426E-01 +3.70444427313546676E+00 3.39133586006194754E-01 6.60866413993805191E-01 +3.76711722979295205E+00 3.36840687920315074E-01 6.63159312079684926E-01 +3.82979018645043734E+00 3.34646207103166171E-01 6.65353792896833829E-01 +3.89246314310792263E+00 3.32545919224480291E-01 6.67454080775519709E-01 +3.95513609976540792E+00 3.30535781273451845E-01 6.69464218726548155E-01 +4.01780905642289277E+00 3.28611923776025050E-01 6.71388076223975006E-01 +4.08048201308037761E+00 3.26770643346235901E-01 6.73229356653764155E-01 +4.14315496973786246E+00 3.25008395557270624E-01 6.74991604442729431E-01 +4.20582792639534730E+00 3.23321788118517361E-01 6.76678211881482694E-01 +4.26850088305283215E+00 3.21707574345476921E-01 6.78292425654523079E-01 +4.36259663226557493E+00 3.19413195181034726E-01 6.80586804818965274E-01 +4.45669238147831770E+00 3.17265070740067878E-01 6.82734929259932066E-01 +4.55078813069106047E+00 3.15253877871656929E-01 6.84746122128343071E-01 +4.64488387990380325E+00 3.13370887632055728E-01 6.86629112367944217E-01 +4.73897962911654602E+00 3.11607927741266999E-01 6.88392072258733001E-01 +4.83307537832928880E+00 3.09957347043537612E-01 6.90042652956462388E-01 +4.92717112754203157E+00 3.08411982061210621E-01 6.91588017938789434E-01 +5.02126687675477434E+00 3.06965125834067321E-01 6.93034874165932679E-01 +5.11536262596751712E+00 3.05610498904494543E-01 6.94389501095505457E-01 +5.20945837518025989E+00 3.04342222141284424E-01 6.95657777858715631E-01 +5.30355412439300267E+00 3.03154791209282182E-01 6.96845208790717763E-01 +5.39764987360574544E+00 3.02043052635592169E-01 6.97956947364407831E-01 +5.49174562281848821E+00 3.01002181432545757E-01 6.98997818567454243E-01 +5.58584137203123099E+00 3.00027660175653355E-01 6.99972339824346701E-01 +5.67993712124397376E+00 2.99115259411111445E-01 7.00884740588888611E-01 +5.77403287045671654E+00 2.98261019295870233E-01 7.01738980704129878E-01 +5.86812861966945931E+00 2.97461232403345710E-01 7.02538767596654345E-01 +5.96222436888220209E+00 2.96712427631590503E-01 7.03287572368409553E-01 +6.05632011809494486E+00 2.96011355142305865E-01 7.03988644857694190E-01 +6.15041586730768763E+00 2.95354972258738302E-01 7.04645027741261698E-01 +6.24451161652043041E+00 2.94740430259425534E-01 7.05259569740574466E-01 +6.33860736573317318E+00 2.94165062013088063E-01 7.05834937986911881E-01 +6.43270311494591596E+00 2.93626370403025250E-01 7.06373629596974695E-01 +6.52679886415865873E+00 2.93122017490281217E-01 7.06877982509718672E-01 +6.62089461337140150E+00 2.92649814367307026E-01 7.07350185632692807E-01 +6.71499036258414428E+00 2.92207711657821112E-01 7.07792288342178666E-01 +6.80908611179688705E+00 2.91793790622163152E-01 7.08206209377836515E-01 +6.90318186100962983E+00 2.91406254829892186E-01 7.08593745170107536E-01 +6.99727761022237260E+00 2.91043422363364113E-01 7.08956577636635665E-01 +7.09137335943511538E+00 2.90703718518221410E-01 7.09296281481778479E-01 +7.18546910864785815E+00 2.90385668969080357E-01 7.09614331030919643E-01 +7.27956485786060092E+00 2.90087893370859995E-01 7.09912106629139950E-01 +7.37366060707334370E+00 2.89809099368042500E-01 7.10190900631957334E-01 +7.46775635628608647E+00 2.89548076985837011E-01 7.10451923014162823E-01 +7.56185210549882925E+00 2.89303693378861304E-01 7.10696306621138585E-01 +7.65594785471157202E+00 2.89074887914547385E-01 7.10925112085452504E-01 +7.75004360392431479E+00 2.88860667569953156E-01 7.11139332430046678E-01 +7.84413935313705757E+00 2.88660102622012638E-01 7.11339897377987196E-01 +7.93823510234980034E+00 2.88472322612513654E-01 7.11527677387486235E-01 +8.08080669531667084E+00 2.88210359800015581E-01 7.11789640199984253E-01 +8.22337828828354134E+00 2.87973278978804759E-01 7.12026721021195130E-01 +8.36594988125041183E+00 2.87758716623703892E-01 7.12241283376295997E-01 +8.50852147421728233E+00 2.87564533611528172E-01 7.12435466388471661E-01 +8.65109306718415283E+00 2.87388794232750655E-01 7.12611205767249123E-01 +8.79366466015102333E+00 2.87229746815752529E-01 7.12770253184247249E-01 +8.93623625311789382E+00 2.87085806022613133E-01 7.12914193977386645E-01 +9.07880784608476432E+00 2.86955536978731940E-01 7.13044463021267783E-01 +9.22137943905163482E+00 2.86837641067565152E-01 7.13162358932434515E-01 +9.36395103201850532E+00 2.86730943062506516E-01 7.13269056937493207E-01 +9.50652262498537581E+00 2.86634379394257510E-01 7.13365620605742157E-01 +9.64909421795224631E+00 2.86546987502534933E-01 7.13453012497464734E-01 +9.79166581091911681E+00 2.86467896231261898E-01 7.13532103768737769E-01 +9.93423740388598731E+00 2.86396317164659742E-01 7.13603682835339925E-01 +1.00000000000000000E+01 2.86365632344379117E-01 7.13634367655620494E-01 diff --git a/tests/unit/resources/SimID_886118677_0_.log b/tests/unit/resources/SimID_886118677_0_.log new file mode 100644 index 000000000..11b4c8711 --- /dev/null +++ b/tests/unit/resources/SimID_886118677_0_.log @@ -0,0 +1,4 @@ +IDAData logfile +IDAData text format version 1 +SimID_886118677_0_.ida +KeepMost 1000 diff --git a/tests/unit/smoke_test.cpp b/tests/unit/smoke_test.cpp index 18d94e1ee..b003a75b9 100644 --- a/tests/unit/smoke_test.cpp +++ b/tests/unit/smoke_test.cpp @@ -35,12 +35,7 @@ void performSmokeTest(const int taskID, const int hashID) { throw std::runtime_error("Could not open output file[" + OUTPUT_TARGET.string() + "] for writing."); } - try { - activateSolver(inputFileStream, outputFile, taskID); - } catch (const std::exception& e) { - std::cerr << "Error caught in test: " << e.what() << std::endl; - exit(EXIT_FAILURE); - } + activateSolver(inputFileStream, outputFile, taskID); fclose(outputFile); compare(OUTPUT_TARGET, NECESSARY_FILES[1], 1e-7); From 91ee797dc733ec8fee04876a8421a5d930e9644e Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Wed, 10 Dec 2025 13:33:07 -0500 Subject: [PATCH 22/26] Added logging library --- CMakeLists.txt | 14 ++++++++++++++ IDAWin/CMakeLists.txt | 2 +- VCellMessaging/CMakeLists.txt | 2 +- conanfile.py | 1 + tests/CMakeLists.txt | 2 ++ 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1be1bd6bf..6f8c6faa4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,10 @@ endif () ############################################## option(OPTION_EXTRA_CONFIG_INFO "Print useful cmake debug info while configuring" OFF) +if (MINGW) + message("ERROR: Due to the requirements of the project, mingw builds are currently disabled. Native windows builds should still work.") + return() +endif () ############################################# # # Build 64bit binaries on Mac and target Macos 10.7 or later @@ -144,6 +148,16 @@ endif(WIN32) ############################################# # installation directories ############################################# +# You can install spdlog with the following: +# > Debian: sudo apt install libspdlog-dev +# > Homebrew: brew install spdlog +# > Conan: conan install --requires=spdlog/[*] ( it may be easier just to add to the conan file!) +# > vcpkg: vcpkg install spdlog +if(NOT TARGET spdlog) + # Stand-alone build + find_package(spdlog REQUIRED) +endif() + if (NOT OPTION_TARGET_MESSAGING) set(OPTION_EXE_DIRECTORY "bin" CACHE PATH "installation directory") else (NOT OPTION_TARGET_MESSAGING) diff --git a/IDAWin/CMakeLists.txt b/IDAWin/CMakeLists.txt index b6d66cc54..32a3a3801 100644 --- a/IDAWin/CMakeLists.txt +++ b/IDAWin/CMakeLists.txt @@ -29,7 +29,7 @@ set (EXE_SRC_FILES ) add_library(IDAWin STATIC ${SRC_FILES} ${HEADER_FILES}) -target_link_libraries(IDAWin sundials ExpressionParser vcellmessaging argparse) +target_link_libraries(IDAWin sundials ExpressionParser vcellmessaging argparse spdlog::spdlog) # if MinGW is enabled, add `$<$:ws2_32>` set(EXE_FILE SundialsSolverStandalone) if (ARCH_64bit) set(EXE_FILE ${EXE_FILE}_x64) diff --git a/VCellMessaging/CMakeLists.txt b/VCellMessaging/CMakeLists.txt index 633117474..3e59227b9 100644 --- a/VCellMessaging/CMakeLists.txt +++ b/VCellMessaging/CMakeLists.txt @@ -33,7 +33,7 @@ if (OPTION_TARGET_MESSAGING) message(STATUS "CURL_INCLUDE_DIR = '${CURL_INCLUDE_DIR}'") endif () - target_link_libraries(vcellmessaging ${CURL_LIBRARIES} Threads::Threads) + target_link_libraries(vcellmessaging ${CURL_LIBRARIES} Threads::Threads spdlog::spdlog) # if MinGW is enabled, add `$<$:ws2_32>` target_compile_definitions(vcellmessaging PUBLIC USE_MESSAGING=1) target_include_directories(vcellmessaging PUBLIC ${CURL_INCLUDE_DIR}) endif() diff --git a/conanfile.py b/conanfile.py index 2e7c89975..c069ba23d 100644 --- a/conanfile.py +++ b/conanfile.py @@ -31,6 +31,7 @@ def build(self): def requirements(self): self.requires("argparse/[>=3.2 <4.0]") + self.requires("spdlog/[>=1.16.0 <2.0]") if self.options.include_messaging: self.requires("libcurl/[<9.0]") diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 824aacb0b..731325ee4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -24,6 +24,8 @@ add_executable(unit_tests target_link_libraries(unit_tests PRIVATE IDAWin GTest::gtest_main + spdlog::spdlog + # if MinGW is enabled, add `$<$:ws2_32>` ) gtest_discover_tests(unit_tests) From 9581e7a334cee60a9cfb621550aed24cb95ec6ac Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Wed, 10 Dec 2025 14:12:59 -0500 Subject: [PATCH 23/26] fixed upload paths --- .github/workflows/cd.yml | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 3c237162d..61b3db769 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -216,46 +216,45 @@ jobs: uses: actions/upload-artifact@v4 with: name: macos_x86_64.tgz - path: build/upload/mac_x86_64.tgz + path: build/upload/mac64.tgz - name: Upload ARM Macos binaries if: matrix.platform == 'macos-latest' uses: actions/upload-artifact@v4 with: name: macos_arm64.tgz - path: build/upload/mac_arm_64.tgz + path: build/upload/mac64.tgz - name: Upload Windows (x86_64) binaries if: matrix.platform == 'windows-latest' uses: actions/upload-artifact@v4 with: - name: win64.zip - path: build/upload/win_x86_64.zip + name: win64_x86_64.zip + path: build/upload/win64.zip - name: Upload Windows (ARMv8) binaries if: matrix.platform == 'windows-11-arm' uses: actions/upload-artifact@v4 with: - name: win64.zip - path: build/upload/win_ARMv8_64.zip + name: win64_arm64.zip + path: build/upload/win64.zip - name: Upload Linux (x86_64) binaries if: matrix.platform == 'ubuntu-latest' uses: actions/upload-artifact@v4 with: - name: linux64.tgz - path: build/upload/linux_x86_64.tgz + name: linux64_x86_64.tgz + path: build/upload/linux64.tgz - name: Upload Linux (ARMv8) binaries if: matrix.platform == 'ubuntu-24.04-arm' uses: actions/upload-artifact@v4 with: - name: linux64.tgz - path: build/upload/linux_ARMv8_64.tgz + name: linux64_arm64.tgz + path: build/upload/linux64.tgz - name: Setup tmate session if: ${{ failure() }} uses: mxschmitt/action-tmate@v3 with: limit-access-to-actor: false - From fd95e2413457661d967a47469415baa293eefc3e Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Thu, 11 Dec 2025 15:55:17 -0500 Subject: [PATCH 24/26] updating universal libraries code --- .github/scripts/install_name_tool_macos.sh | 5 +- .github/workflows/cd.yml | 373 ++++++++++++++------- CMakeLists.txt | 32 +- IDAWin/CMakeLists.txt | 23 +- 4 files changed, 298 insertions(+), 135 deletions(-) diff --git a/.github/scripts/install_name_tool_macos.sh b/.github/scripts/install_name_tool_macos.sh index 9cd9d1d03..4f54f49ec 100755 --- a/.github/scripts/install_name_tool_macos.sh +++ b/.github/scripts/install_name_tool_macos.sh @@ -8,11 +8,12 @@ shopt -s -o nounset for exe in `ls *_x64` do echo "fixing paths in ${exe}" - for libpath in `otool -L ${exe} | grep "/" | grep -v "System" | awk '{print $1}'` + for libpath in `otool -L ${exe} | grep "/" | grep -v "/usr/lib" | grep -v "System" | awk '{print $1}'` do libfilename=${libpath##*/} echo install_name_tool -change $libpath @executable_path/$libfilename $exe install_name_tool -change $libpath @executable_path/$libfilename $exe + cp libpath "$(pwd)" done done @@ -29,7 +30,7 @@ do echo install_name_tool -id "@loader_path/$libfilename" $libfilename install_name_tool -id "@loader_path/$libfilename" $libfilename - for dependentlibpath in `otool -L ${libfilename} | grep "/" | grep -v "System" | awk '{print $1}'` + for dependentlibpath in `otool -L ${libfilename} | grep "/" | grep -v "/usr/lib" | grep -v "System" | awk '{print $1}'` do dependentlibfilename=${dependentlibpath##*/} # diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 61b3db769..f44c660e0 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -3,29 +3,115 @@ name: CD on: [push, workflow_dispatch] jobs: - native-build: - name: - native-build-${{ matrix.platform }} + MacOS: + name: native-build-${{ matrix.platform }} + runs-on: ${{ matrix.platform }} strategy: fail-fast: false matrix: - platform: [windows-latest, ubuntu-latest, ubuntu-24.04-arm, macos-15-intel, macos-latest] - #platform: [windows-latest, windows-11-arm, ubuntu-latest, ubuntu-24.04-arm, macos-15-intel, macos-latest] + platform: [ macos-15-intel, macos-latest ] - runs-on: ${{ matrix.platform }} + steps: + - name: checkout vcell-ode repo + uses: actions/checkout@v4 + + - name: Install MacOS dependencies + if: matrix.platform == 'macos-15-intel' + shell: bash + run: | + brew install conan + conan --version + mkdir -p ~/.conan2/profiles/ + touch ~/.conan2/profiles/default # if we don't make a file first, cp thinks its a folder that doesn't exist + cp conan-profiles/CI-CD/MacOS-AMD64_profile.txt ~/.conan2/profiles/default + + - name: Install MacOS dependencies + if: matrix.platform == 'macos-latest' + shell: bash + run: | + brew install conan + conan --version + mkdir -p ~/.conan2/profiles/ + touch ~/.conan2/profiles/default # if we don't make a file first, cp thinks its a folder that doesn't exist + cp conan-profiles/CI-CD/MacOS-ARM64_profile.txt ~/.conan2/profiles/default + + - name: Install Dependencies through Conan on MacOS + if: matrix.platform == 'macos-latest' || matrix.platform == 'macos-15-intel' + shell: bash + run: | + conan install . --output-folder build --build=missing + + - name: Build Mac + run: | + echo "working dir is $PWD" + cd build + source conanbuild.sh + cmake -B . -S .. -G "Ninja" -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" -DCMAKE_BUILD_TYPE=Release + cmake --build . --config Release + + - name: Test Mac + run: | + cd build + + ctest -VV + + echo "------ running SundialsSolverStandalone_x64 ------" + ./bin/SundialsSolverStandalone_x64 || true + + + - name: Make MacOS Shared Object Paths Relative + shell: bash + run: | + mkdir build/upload + cd build/bin + rm unit_tests + ls *_x64 | awk '{print $1}' | xargs -I '{}' otool -L '{}' | grep ")" | grep -v "build" | grep -v "System" | awk '{print $1}' | xargs -I '{}' cp -vn '{}' . || true + ls *.dylib | awk '{print $1}' | xargs -I '{}' otool -L '{}' | grep ")" | grep -v "build" | grep -v "System" | awk '{print $1}' | xargs -I '{}' cp -vn '{}' . || true + ls *.dylib | awk '{print $1}' | xargs -I '{}' otool -L '{}' | grep ")" | grep -v "build" | grep -v "System" | awk '{print $1}' | xargs -I '{}' cp -vn '{}' . || true + chmod u+w,+x * + tar czvf ../upload/mac64_bad_paths.tgz . + ../../.github/scripts/install_name_tool_macos.sh + tar czvf ../upload/mac64.tgz --dereference . + + - name: Upload Intel Macos binaries + if: matrix.platform == 'macos-15-intel' + uses: actions/upload-artifact@v4 + with: + name: macos_x86_64 + path: build/upload/mac64.tgz + + - name: Upload ARM Macos binaries + if: matrix.platform == 'macos-latest' + uses: actions/upload-artifact@v4 + with: + name: macos_arm64 + path: build/upload/mac64.tgz + - name: Setup tmate session + if: ${{ failure() }} + uses: mxschmitt/action-tmate@v3 + with: + limit-access-to-actor: false + + + WindowsOS: + name: native-build-${{ matrix.platform }} + runs-on: ${{ matrix.platform }} + strategy: + fail-fast: false + matrix: + #platform: [ windows-latest, windows-11-arm ] + platform: [ windows-latest ] steps: - name: checkout vcell-ode repo uses: actions/checkout@v4 - name: Install Windows Dependencies (Part 0 - Setup LLVM-style) - if: matrix.platform == 'windows-latest' || matrix.platform == 'windows-11-arm' uses: llvm/actions/setup-windows@main with: arch: amd64 - name: Install Windows Dependencies (Part 1 - Configure Conan ...and zip) - if: matrix.platform == 'windows-latest' || matrix.platform == 'windows-11-arm' shell: powershell run: | choco install zip -y @@ -50,25 +136,71 @@ jobs: run: | cp conan-profiles/CI-CD/Windows-ARM64_profile.txt $CONAN_HOME/profiles/default - - name: Install MacOS dependencies - if: matrix.platform == 'macos-15-intel' - shell: bash + - name: Install Dependencies through Conan on Windows + shell: powershell run: | - brew install conan - conan --version - mkdir -p ~/.conan2/profiles/ - touch ~/.conan2/profiles/default # if we don't make a file first, cp thinks its a folder that doesn't exist - cp conan-profiles/CI-CD/MacOS-AMD64_profile.txt ~/.conan2/profiles/default + conan install . --output-folder build --build=missing -o include_messaging=False - - name: Install MacOS dependencies - if: matrix.platform == 'macos-latest' + - name: Build Windows + shell: powershell + run: | + cd build + ./conanbuild.ps1 + cmake -B . -S .. -G "Ninja" -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" -DCMAKE_BUILD_TYPE=Release + cmake --build . --config Release + + - name: Test Windows + run: | + cd build + ctest -VV + ./bin/SundialsSolverStandalone_x64 || true + + - name: Make Windows Shared Object Paths Relative shell: bash run: | - brew install conan - conan --version - mkdir -p ~/.conan2/profiles/ - touch ~/.conan2/profiles/default # if we don't make a file first, cp thinks its a folder that doesn't exist - cp conan-profiles/CI-CD/MacOS-ARM64_profile.txt ~/.conan2/profiles/default + mkdir build/upload + cd build/bin + rm unit_tests* *.pdb || true + ls *.exe | awk '{print $1}' | xargs -I '{}' ldd '{}' | grep '=> /' | grep -v build | grep -iv windows | awk '{print $3}' | xargs -I '{}' cp -vn '{}' . || true + # Currently, Sundials only requires system32 dlls! + # ls *.dll | awk '{print $1}' | xargs -I '{}' ldd '{}' | grep '=> /' | grep -v build | grep -iv windows | awk '{print $3}' | xargs -I '{}' cp -vn '{}' . || true + # ls *.dll | awk '{print $1}' | xargs -I '{}' ldd '{}' | grep '=> /' | grep -v build | grep -iv windows | awk '{print $3}' | xargs -I '{}' cp -vn '{}' . || true + chmod u+w,+x * + zip ../upload/win64.zip ./* + cd ../.. + # fi + + - name: Upload Windows (x86_64) binaries + if: matrix.platform == 'windows-latest' + uses: actions/upload-artifact@v4 + with: + name: win64_x86_64 + path: build/upload/win64.zip + + - name: Upload Windows (ARMv8) binaries + if: matrix.platform == 'windows-11-arm' + uses: actions/upload-artifact@v4 + with: + name: win64_arm64 + path: build/upload/win64.zip + + - name: Setup tmate session + if: ${{ failure() }} + uses: mxschmitt/action-tmate@v3 + with: + limit-access-to-actor: false + + GNULinux: + name: native-build-${{ matrix.platform }} + runs-on: ${{ matrix.platform }} + strategy: + fail-fast: false + matrix: + platform: [ ubuntu-latest, ubuntu-24.04-arm ] + + steps: + - name: checkout vcell-ode repo + uses: actions/checkout@v4 - name: Install Linux Dependencies if: matrix.platform == 'ubuntu-latest' @@ -94,17 +226,6 @@ jobs: touch ~/.conan2/profiles/default # if we don't make a file first, cp thinks its a folder that doesn't exist cp conan-profiles/CI-CD/Linux-ARM64_profile.txt ~/.conan2/profiles/default - - name: Install Dependencies through Conan on Windows - if: matrix.platform == 'windows-latest' || matrix.platform == 'windows-11-arm' - shell: powershell - run: | - conan install . --output-folder build --build=missing -o include_messaging=False - - - name: Install Dependencies through Conan on MacOS - if: matrix.platform == 'macos-latest' || matrix.platform == 'macos-15-intel' - shell: bash - run: | - conan install . --output-folder build --build=missing - name: Install Dependencies through Conan on Linux if: matrix.platform == 'ubuntu-latest' @@ -130,17 +251,7 @@ jobs: sudo ln -s $(which mold) /usr/bin/ld conan install . --output-folder build --build=missing - - name: Build Windows - if: matrix.platform == 'windows-latest' || matrix.platform == 'windows-11-arm' - shell: powershell - run: | - cd build - ./conanbuild.ps1 - cmake -B . -S .. -G "Ninja" -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" -DCMAKE_BUILD_TYPE=Release - cmake --build . --config Release - - - name: Build Non-Intel-Mac Unix - if: matrix.platform == 'ubuntu-latest' || matrix.platform == 'ubuntu-24.04-arm' || matrix.platform == 'macos-15-intel' || matrix.platform == 'macos-latest' + - name: Build Linux run: | echo "working dir is $PWD" cd build @@ -148,62 +259,22 @@ jobs: cmake -B . -S .. -G "Ninja" -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" -DCMAKE_BUILD_TYPE=Release cmake --build . --config Release - - name: Test Windows - if: matrix.platform == 'windows-latest' || matrix.platform == 'windows-11-arm' - run: | - cd build - ctest -VV - ./bin/SundialsSolverStandalone_x64 || true - - - name: Test Unix - if: matrix.platform == 'ubuntu-latest' || matrix.platform == 'ubuntu-24.04-arm' || matrix.platform == 'macos-15-intel' || matrix.platform == 'macos-latest' + - name: Test Linux run: | cd build ctest -VV - + echo "------ running SundialsSolverStandalone_x64 ------" ./bin/SundialsSolverStandalone_x64 || true - - name: fix Macos shared object paths - if: matrix.platform == 'macos-15-intel' || matrix.platform == 'macos-latest' - shell: bash - run: | - mkdir build/upload - cd build/bin - rm hello_test TestVCellStoch testzip ziptool || true - ls *_x64 | awk '{print $1}' | xargs -I '{}' otool -L '{}' | grep ")" | grep -v "build" | grep -v "System" | awk '{print $1}' | xargs -I '{}' cp -vn '{}' . || true - ls *.dylib | awk '{print $1}' | xargs -I '{}' otool -L '{}' | grep ")" | grep -v "build" | grep -v "System" | awk '{print $1}' | xargs -I '{}' cp -vn '{}' . || true - ls *.dylib | awk '{print $1}' | xargs -I '{}' otool -L '{}' | grep ")" | grep -v "build" | grep -v "System" | awk '{print $1}' | xargs -I '{}' cp -vn '{}' . || true - chmod u+w,+x * - tar czvf ../upload/mac64_bad_paths.tgz . - ../../.github/scripts/install_name_tool_macos.sh - tar czvf ../upload/mac64.tgz --dereference . - - - name: handle shared object paths for Windows native build - if: matrix.platform == 'windows-latest' || matrix.platform == 'windows-11-arm' - shell: bash - run: | - mkdir build/upload - cd build/bin - rm hello_test* *.pdb || true - ls *.exe | awk '{print $1}' | xargs -I '{}' ldd '{}' | grep '=> /' | grep -v build | grep -iv windows | awk '{print $3}' | xargs -I '{}' cp -vn '{}' . || true - # Currently, Sundials only requires system32 dlls! - # ls *.dll | awk '{print $1}' | xargs -I '{}' ldd '{}' | grep '=> /' | grep -v build | grep -iv windows | awk '{print $3}' | xargs -I '{}' cp -vn '{}' . || true - # ls *.dll | awk '{print $1}' | xargs -I '{}' ldd '{}' | grep '=> /' | grep -v build | grep -iv windows | awk '{print $3}' | xargs -I '{}' cp -vn '{}' . || true - chmod u+w,+x * - zip ../upload/win64.zip ./* - cd ../.. - # fi - - - name: handle shared object paths for Linux native build - if: matrix.platform == 'ubuntu-latest' || matrix.platform == 'ubuntu-24.04-arm' + - name: Make Linux Shared Object Paths Relative shell: bash run: | mkdir build/upload cd build/bin - rm hello_test TestVCellStoch testzip ziptool || true + rm unit_tests ls *_x64 | awk '{print $1}' | xargs -I '{}' ldd '{}' | grep "=> /" | grep -v "build" | awk '{print $3}' | xargs -I '{}' cp -vn '{}' . || true ls *.so | awk '{print $1}' | xargs -I '{}' ldd '{}' | grep "=> /" | grep -v "build" | awk '{print $3}' | xargs -I '{}' cp -vn '{}' . || true ls *.so | awk '{print $1}' | xargs -I '{}' ldd '{}' | grep "=> /" | grep -v "build" | awk '{print $3}' | xargs -I '{}' cp -vn '{}' . || true @@ -211,50 +282,118 @@ jobs: tar czvf ../upload/linux64.tgz --dereference . cd ../.. - - name: Upload Intel Macos binaries - if: matrix.platform == 'macos-15-intel' + - name: Upload Linux (x86_64) binaries + if: matrix.platform == 'ubuntu-latest' uses: actions/upload-artifact@v4 with: - name: macos_x86_64.tgz - path: build/upload/mac64.tgz + name: linux64_x86_64 + path: build/upload/linux64.tgz - - name: Upload ARM Macos binaries - if: matrix.platform == 'macos-latest' + - name: Upload Linux (ARMv8) binaries + if: matrix.platform == 'ubuntu-24.04-arm' uses: actions/upload-artifact@v4 with: - name: macos_arm64.tgz - path: build/upload/mac64.tgz + name: linux64_arm64 + path: build/upload/linux64.tgz - - name: Upload Windows (x86_64) binaries - if: matrix.platform == 'windows-latest' - uses: actions/upload-artifact@v4 + - name: Setup tmate session + if: ${{ failure() }} + uses: mxschmitt/action-tmate@v3 with: - name: win64_x86_64.zip - path: build/upload/win64.zip + limit-access-to-actor: false - - name: Upload Windows (ARMv8) binaries - if: matrix.platform == 'windows-11-arm' - uses: actions/upload-artifact@v4 + + MacOS-Universal: + name: Create Universal macOS Binary + runs-on: macos-latest + needs: + - MacOS + + + steps: + # The binaries are "named" (ID'd) different as artifacts, but not as filenames (`mac64.tgz`). + # As soon as we download one of them we need to rename it to avoid overwriting the other one. + - name: Download macOS-Arm64 binary + uses: actions/download-artifact@v4 with: - name: win64_arm64.zip - path: build/upload/win64.zip + name: macos_arm64 + path: binaries - - name: Upload Linux (x86_64) binaries - if: matrix.platform == 'ubuntu-latest' - uses: actions/upload-artifact@v4 + - name: Unpack & Rename macOS-Arm64 binary + run: | + mkdir binaries/arm + tar xzvf binaries/mac64.tgz -C binaries/arm + rm binaries/mac64.tgz + + - name: Download macOS-x86_64 binary + uses: actions/download-artifact@v4 with: - name: linux64_x86_64.tgz - path: build/upload/linux64.tgz + name: macos_x86_64 + path: binaries - - name: Upload Linux (ARMv8) binaries - if: matrix.platform == 'ubuntu-24.04-arm' + - name: Unpack & Rename macOS-x86_64 binary + run: | + mkdir binaries/intel + tar xzvf binaries/mac64.tgz -C binaries/intel + rm binaries/mac64.tgz + + # this will go in the log of the run, useful for debugging + - name: Inspect binaries directory + run: | + echo "Contents of binaries/:" + ls -Rlh binaries/ + + # - name: Setup tmate session for inspection + # uses: mxschmitt/action-tmate@v3 + + - name: Combine with lipo + run: | + mkdir binaries/universal + for exe in `ls binaries/arm/ | grep ".*x64"` + do + echo "fixing ${exe}..." + lipo -create binaries/arm/$exe binaries/intel/${exe} \ + -output binaries/universal/${exe} + done + + for dylib in `ls binaries/arm/ | grep ".*\.dylib"` + do + echo "fixing ${dylib}..." + lipo -create binaries/arm/$dylib binaries/intel/${dylib} \ + -output binaries/universal/${dylib} + done + for entry in `ls binaries/universal/` + do + echo "confirming ${entry}..." + lipo -archs binaries/universal/${entry} + done + mv binaries/universal binaries/mac64 + tar czvf binaries/macos-universal.tgz binaries/mac64 + + - name: Inspect binaries directory + run: | + echo "Contents of binaries/:" + ls -Rlh binaries/ + + - name: Upload MacOS (Universal) binaries uses: actions/upload-artifact@v4 with: - name: linux64_arm64.tgz - path: build/upload/linux64.tgz + name: macos64_universal + path: binaries/macos-universal.tgz + +# - name: Upload universal binary to release +# if: github.event_name == 'release' +# uses: actions/upload-release-asset@v1 +# env: +# GITHUB_TOKEN: ${{ github.token }} +# with: +# upload_url: ${{ github.event.release.upload_url }} +# asset_path: binaries/macos-universal.tgz +# asset_name: macos-universal +# asset_content_type: application/octet-stream - name: Setup tmate session if: ${{ failure() }} uses: mxschmitt/action-tmate@v3 with: - limit-access-to-actor: false + limit-access-to-actor: false \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f8c6faa4..9a001958b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,14 +24,30 @@ if (WIN32) endif () ############################################# -# Display extra CMAKE info while configuring +# Declare Options ############################################## +option(OPTION_TARGET_MESSAGING "Messaging (requires libcurl)" OFF) +option(OPTION_TARGET_DOCS "Generate Doxygen documentation" OFF) +option(OPTION_TEST_WITH_LOCALHOST "Use localhost as server rather than vcell.cam.uchc.edu" OFF) option(OPTION_EXTRA_CONFIG_INFO "Print useful cmake debug info while configuring" OFF) +option(OPTION_STATICALLY_LINK "Decided whether to statically link, or use (and generate) dynamic shared libs" OFF) + +############################################# +# MinGW check +############################################## if (MINGW) message("ERROR: Due to the requirements of the project, mingw builds are currently disabled. Native windows builds should still work.") return() endif () + +if (OPTION_STATICALLY_LINK) + #set(BUILD_SHARED_LIBS OFF) + set(CMAKE_EXE_LINKER_FLAGS "-static") +else () + #set(BUILD_SHARED_LIBS ON) +endif () + ############################################# # # Build 64bit binaries on Mac and target Macos 10.7 or later @@ -80,20 +96,6 @@ else() SET(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.") endif() -#include (CMakeTestCCompiler) -#include (CheckCSourceCompiles) -#include (CheckCXXSourceCompiles) -#include (CheckStructHasMember) -#include (CheckLibraryExists) -#include (CheckFunctionExists) -#include (CheckCCompilerFlag) -#include (CheckCSourceRuns) -#include (CheckSymbolExists) -#include (CheckTypeSize) - -option(OPTION_TARGET_MESSAGING "Messaging (requires libcurl)" off) -option(OPTION_TARGET_DOCS "Generate Doxygen documentation" off) -option(OPTION_TEST_WITH_LOCALHOST "Use localhost as server rather than vcell.cam.uchc.edu" off) if (${OPTION_TARGET_DOCS}) # if (DOXYGEN_FOUND AND ${CMAKE_VERSION} GREATER_EQUAL 3.9) diff --git a/IDAWin/CMakeLists.txt b/IDAWin/CMakeLists.txt index 32a3a3801..a677ed793 100644 --- a/IDAWin/CMakeLists.txt +++ b/IDAWin/CMakeLists.txt @@ -29,7 +29,28 @@ set (EXE_SRC_FILES ) add_library(IDAWin STATIC ${SRC_FILES} ${HEADER_FILES}) -target_link_libraries(IDAWin sundials ExpressionParser vcellmessaging argparse spdlog::spdlog) # if MinGW is enabled, add `$<$:ws2_32>` +if (OPTION_STATICALLY_LINK) + target_link_libraries( + IDAWin -static + sundials -static + ExpressionParser -static + vcellmessaging -static + argparse -static + spdlog::spdlog -static + # if MinGW is enabled, add `$<$:ws2_32>` + ) +else () + target_link_libraries( + IDAWin + sundials + ExpressionParser + vcellmessaging + argparse + spdlog::spdlog + # if MinGW is enabled, add `$<$:ws2_32>` + ) +endif () + set(EXE_FILE SundialsSolverStandalone) if (ARCH_64bit) set(EXE_FILE ${EXE_FILE}_x64) From 313158dd83ee293c6d6943c17eead6771e95c258 Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Wed, 17 Dec 2025 13:52:59 -0500 Subject: [PATCH 25/26] Upgrading cmake to allow for all sanatizers + removed extra folder uploaded --- .github/workflows/cd.yml | 3 ++- CMakeLists.txt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index f44c660e0..dcb8131a8 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -368,7 +368,8 @@ jobs: lipo -archs binaries/universal/${entry} done mv binaries/universal binaries/mac64 - tar czvf binaries/macos-universal.tgz binaries/mac64 + cd binaries + tar czvf macos-universal.tgz mac64 - name: Inspect binaries directory run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a001958b..6c9581dca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ # Top Level CMake File # ############################################## -cmake_minimum_required(VERSION 3.14) +cmake_minimum_required(VERSION 3.16) project(vcell-ode-numerics) set(CMAKE_CXX_STANDARD 20) if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") From baf7586fd65a6a7b7633f4e8567e41e26b1be5cb Mon Sep 17 00:00:00 2001 From: Logan Drescher Date: Thu, 18 Dec 2025 13:05:52 -0500 Subject: [PATCH 26/26] removed int-overflow in test --- tests/unit/message_processing_test.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/unit/message_processing_test.cpp b/tests/unit/message_processing_test.cpp index 5b7463cf4..1932291fc 100644 --- a/tests/unit/message_processing_test.cpp +++ b/tests/unit/message_processing_test.cpp @@ -13,13 +13,13 @@ static std::vector eventTracker; void appendNextIndex(const WorkerEvent* event); std::vector generateFibonacci(int length); -long sequenceComparison(const std::vector &sequence1, const std::vector &sequence2); +long long sequenceComparison(const std::vector &sequence1, const std::vector &sequence2); TEST(MessageProcessingTest, MessagesAreProcessed) { try { MessageEventManager eventManager{[](WorkerEvent* event)->void{appendNextIndex(event);}}; - const int NUM_ITERATIONS = 100; + const int NUM_ITERATIONS = 50; for (int i = 0; i < NUM_ITERATIONS; ++i) { JobEvent::Status status; switch (i + 1) { @@ -45,8 +45,8 @@ void appendNextIndex(const WorkerEvent* event) { } else if (1 == eventTracker.size()) { eventTracker.emplace_back("1"); } else { - const long firstValue = std::stol(eventTracker[eventTracker.size() - 2]); - const long secondValue = std::stol(eventTracker[eventTracker.size() - 1]); + const long long firstValue = std::stoll(eventTracker[eventTracker.size() - 2]); + const long long secondValue = std::stoll(eventTracker[eventTracker.size() - 1]); const std::string nextValue{std::to_string(firstValue + secondValue)}; eventTracker.push_back(nextValue); } @@ -61,20 +61,20 @@ std::vector generateFibonacci(const int length) { for (int i = 0; i < length; ++i) { if (i == 0) { sequence.emplace_back("0"); continue; } if (i == 1) { sequence.emplace_back("1"); continue; } - const long firstValue = std::stol(sequence[sequence.size() - 2]); - const long secondValue = std::stol(sequence[sequence.size() - 1]); + const long long firstValue = std::stoll(sequence[sequence.size() - 2]); + const long long secondValue = std::stoll(sequence[sequence.size() - 1]); const std::string nextValue{std::to_string(firstValue + secondValue)}; sequence.push_back(nextValue); } return sequence; } -long sequenceComparison(const std::vector &sequence1, const std::vector &sequence2) { +long long sequenceComparison(const std::vector &sequence1, const std::vector &sequence2) { if (const size_t comp = sequence2.size() - sequence1.size(); comp) return static_cast(comp); for (size_t i = 0; i < sequence1.size(); ++i) { - const long firstValue = std::stol(sequence1[i]); - const long secondValue = std::stol(sequence2[i]); - if (const long comp = secondValue - firstValue; comp) return comp; + const long long firstValue = std::stoll(sequence1[i]); + const long long secondValue = std::stoll(sequence2[i]); + if (const long long comp = secondValue - firstValue; comp) return comp; } return 0; } \ No newline at end of file