From c704dcd4d1f31f114f3729404eee83a0d26157b0 Mon Sep 17 00:00:00 2001 From: arsLibera Date: Sat, 11 Jan 2025 07:52:05 -0500 Subject: [PATCH 1/4] Update test.yml --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 10f4273..01b94c7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,7 +4,7 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: build solver shell: bash run: | From c7fbb25e1b15f4acbb2f5a1b0ef79d0fb544bde2 Mon Sep 17 00:00:00 2001 From: arsLibera Date: Sat, 11 Jan 2025 11:23:41 -0500 Subject: [PATCH 2/4] Add gtest and example --- .github/workflows/test.yml | 6 +- .gitignore | 1 + CMakeLists.txt | 70 ++- Code/Source/cvOneDOptionsLegacySerializer.cxx | 404 ++++++++++++++++++ Code/Source/cvOneDOptionsLegacySerializer.h | 48 +++ Code/Source/main.cxx | 356 +-------------- {tests => Tests/SystemTests}/__init__.py | 0 .../cases/02_simpleArtery_Resistance.in | 0 .../cases/04_simpleArtery_Pressure.in | 0 .../SystemTests}/cases/05_bifurcation_RCR.in | 0 .../SystemTests}/cases/12_AortoFem_Pulse_R.in | 0 .../cases/14_AortoFem_Pulse_RCR.in | 0 ...ary_inlet_pressure_wave_outlet_coronary.in | 0 .../SystemTests}/cases/bifurcation_P.in | 0 .../SystemTests}/cases/bifurcation_R.in | 0 .../SystemTests}/cases/bifurcation_RCR.in | 0 .../cases/bifurcation_RCR_staticFunc.in | 0 .../SystemTests}/cases/bifurcation_R_stab.in | 0 .../SystemTests}/cases/tube_pressure.in | 0 .../SystemTests}/cases/tube_pressure_wave.in | 0 {tests => Tests/SystemTests}/cases/tube_r.in | 0 .../SystemTests}/cases/tube_r_Pd.in | 0 .../SystemTests}/cases/tube_r_stab.in | 0 .../SystemTests}/cases/tube_rcr.in | 0 .../SystemTests}/cases/tube_rcr_Pd.in | 0 .../SystemTests}/cases/tube_stenosis_r.in | 0 {tests => Tests/SystemTests}/conftest.py | 0 {tests => Tests/SystemTests}/plots.py | 0 .../SystemTests}/test_integration.py | 0 Tests/UnitTests/ExampleTest.cxx | 21 + Tests/UnitTests/TestFiles/ExampleFile.txt | 1 + Tests/UnitTests/TestFiles/SimpleArtery.in | 125 ++++++ Tests/UnitTests/TestLegacySerializer.cxx | 16 + 33 files changed, 693 insertions(+), 355 deletions(-) create mode 100644 Code/Source/cvOneDOptionsLegacySerializer.cxx create mode 100644 Code/Source/cvOneDOptionsLegacySerializer.h rename {tests => Tests/SystemTests}/__init__.py (100%) rename {tests => Tests/SystemTests}/cases/02_simpleArtery_Resistance.in (100%) rename {tests => Tests/SystemTests}/cases/04_simpleArtery_Pressure.in (100%) rename {tests => Tests/SystemTests}/cases/05_bifurcation_RCR.in (100%) rename {tests => Tests/SystemTests}/cases/12_AortoFem_Pulse_R.in (100%) rename {tests => Tests/SystemTests}/cases/14_AortoFem_Pulse_RCR.in (100%) rename {tests => Tests/SystemTests}/cases/Left_coronary_inlet_pressure_wave_outlet_coronary.in (100%) rename {tests => Tests/SystemTests}/cases/bifurcation_P.in (100%) rename {tests => Tests/SystemTests}/cases/bifurcation_R.in (100%) rename {tests => Tests/SystemTests}/cases/bifurcation_RCR.in (100%) rename {tests => Tests/SystemTests}/cases/bifurcation_RCR_staticFunc.in (100%) rename {tests => Tests/SystemTests}/cases/bifurcation_R_stab.in (100%) rename {tests => Tests/SystemTests}/cases/tube_pressure.in (100%) rename {tests => Tests/SystemTests}/cases/tube_pressure_wave.in (100%) rename {tests => Tests/SystemTests}/cases/tube_r.in (100%) rename {tests => Tests/SystemTests}/cases/tube_r_Pd.in (100%) rename {tests => Tests/SystemTests}/cases/tube_r_stab.in (100%) rename {tests => Tests/SystemTests}/cases/tube_rcr.in (100%) rename {tests => Tests/SystemTests}/cases/tube_rcr_Pd.in (100%) rename {tests => Tests/SystemTests}/cases/tube_stenosis_r.in (100%) rename {tests => Tests/SystemTests}/conftest.py (100%) rename {tests => Tests/SystemTests}/plots.py (100%) rename {tests => Tests/SystemTests}/test_integration.py (100%) create mode 100644 Tests/UnitTests/ExampleTest.cxx create mode 100644 Tests/UnitTests/TestFiles/ExampleFile.txt create mode 100755 Tests/UnitTests/TestFiles/SimpleArtery.in create mode 100644 Tests/UnitTests/TestLegacySerializer.cxx diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 01b94c7..6aa6a79 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,4 +19,8 @@ jobs: pip install numpy - name: result tests run: | - pytest + pytest Tests/SystemTests + - name: result unit tests + run: | + cd build_skyline + ctest diff --git a/.gitignore b/.gitignore index 45d7081..d36e03d 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ build .cproject .project .settings/ +.vscode/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index c3dc347..cdbd84a 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12) +CMAKE_MINIMUM_REQUIRED(VERSION 3.11) # COMMON SETTINGS PROJECT(OneDSolver) @@ -9,6 +9,7 @@ ENABLE_LANGUAGE(C CXX) OPTION(BUILD_SV_INSTALLER "Build SimVascular Installer" OFF) OPTION(buildPy "Build Python Interface" OFF) OPTION(buildDocs "Build Documentation" OFF) +OPTION(ENABLE_UNIT_TEST "Enable unit tests" ON) SET(sparseSolverType "skyline" CACHE STRING "Use Sparse Solver") SET_PROPERTY(CACHE sparseSolverType PROPERTY STRINGS skyline superlu csparse) @@ -74,7 +75,7 @@ ENDIF() # FILE(COPY "${CMAKE_CURRENT_SOURCE_DIR}/tests" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") # COMPILER FLAGS -SET(CMAKE_CXX_FLAGS "-g -m64 -O3 -std=c++0x -fPIC") +SET(CMAKE_CXX_FLAGS "-g -m64 -O3 -std=c++20 -fPIC") # PLACE EXECUTABLE IN BIN FOLDER SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) @@ -109,3 +110,68 @@ ENDIF() if (BUILD_SV_INSTALLER) add_subdirectory("${CMAKE_SOURCE_DIR}/Distribution") ENDIF() + +# Unit tests and Google Test +if(ENABLE_UNIT_TEST) + + # Link pthread on ubuntu20 + find_package(Threads REQUIRED) + + # Install Google Test + include(FetchContent) + FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/refs/heads/main.zip + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + ) + FetchContent_MakeAvailable(googletest) + + enable_testing() + include(GoogleTest) + + set(TESTBIN_DIRECTORY ${CMAKE_BINARY_DIR}/testbin) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${TESTBIN_DIRECTORY}) + + # Copy test files into the build directory so tests can access them + file(GLOB TEST_FILES "${CMAKE_CURRENT_SOURCE_DIR}/Tests/UnitTests/TestFiles/*") + foreach(TEST_FILE ${TEST_FILES}) + # Print the file being copied for debugging + file(COPY ${TEST_FILE} DESTINATION ${TESTBIN_DIRECTORY}/TestFiles/) + endforeach() + + # For now, we're filtering out "main" so we can include all the + # other source files in the same way as the main executable. + # + # It would probably be better if we refactored to avoid including + # main in the overall list of source files. Instead, we could just + # include main only when we're building the executable. That could + # be a good first refactoring to avoid this hacky post-processing + # step. + set(SRC_C_FOR_TESTS ${SRC_C}) + list(FILTER SRC_C_FOR_TESTS EXCLUDE REGEX "Source/main.cxx") + + set(SRC_H_FOR_TESTS ${SRC_H}) + list(FILTER SRC_H_FOR_TESTS EXCLUDE REGEX "Source/main.h") + + # Find all .cpp files in the UnitTests folder and subfolders + file(GLOB TEST_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/Tests/UnitTests/*.cxx") + + # Combine all test files into a single list + add_executable(UnitTestsExecutable ${TEST_SOURCES} ${SRC_C_FOR_TESTS} ${SRC_H_FOR_TESTS}) + + # For some solvers, we may need to link against additional libraries here. + # When we bump up against unit tests that require that, we should consider + # changing the strategy for how we generate executables in cmakelists to + # handle both use cases. + target_link_libraries(UnitTestsExecutable gtest gtest_main pthread) + + # It's likely we'll need to include additional directories for some + # versions of the unit tests. That can be updated when/if we update + # the general strategy. + target_include_directories(UnitTestsExecutable PRIVATE ${SRCS_DIR}) + + # Add the test to CTest, we're setting the working directory to testbin + # because we're copying the input files there for now + add_test(NAME UnitTests COMMAND UnitTestsExecutable WORKING_DIRECTORY ${TESTBIN_DIRECTORY}) + +endif() diff --git a/Code/Source/cvOneDOptionsLegacySerializer.cxx b/Code/Source/cvOneDOptionsLegacySerializer.cxx new file mode 100644 index 0000000..7f65ec2 --- /dev/null +++ b/Code/Source/cvOneDOptionsLegacySerializer.cxx @@ -0,0 +1,404 @@ +/* Copyright (c) Stanford University, The Regents of the University of + * California, and others. + * + * All Rights Reserved. + * + * See Copyright-SimVascular.txt for additional details. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cvOneDOptionsLegacySerializer.h" + +#include +#include +#include + +#include "cvOneDUtility.h" +#include "cvOneDGlobal.h" + +namespace cvOneD{ + +// ====================== +// READ SINGLE MODEL FILE +// ====================== +void readSingleOptionsLegacyFormat(string inputFile, cvOneDOptions* opts, cvStringVec includedFiles){ + + // Message + printf("\n"); + printf("Reading file: %s ... \n",inputFile.c_str()); + + // Declare + cvStringVec tokenizedString; + cvLongVec tempIntVec; + string matType; + cvDoubleVec temp; + bool doInclude = false; + + // Declare input File + ifstream infile; + infile.open(inputFile); + if(infile.fail()){ + throw cvException("ERROR: Input file does not exist.\n"); + } + int lineCount = 1; + int reminder = 0; + int totSegments = 0; + + // Read Data From File + std::string buffer; + while (std::getline(infile,buffer)){ + + // Trim String + buffer = trim_string(buffer); + + // Tokenize String + tokenizedString = split_string(buffer, " ,\t"); + if (tokenizedString.size() == 0) { + continue; + } + + // Check for Empty buffer + if(!buffer.empty()){ + // CHECK THE ELEMENT TYPE + if(upper_string(tokenizedString[0]) == "MODEL"){ + //printf("Found Model.\n"); + if(opts->modelNameDefined){ + throw cvException("ERROR: Model name already defined\n"); + } + if(tokenizedString.size() > 2){ + throw cvException(string("ERROR: Too many parameters for MODEL token. Line " + to_string(lineCount) + "\n").c_str()); + }else if(tokenizedString.size() < 2){ + throw cvException(string("ERROR: Not enough parameters for MODEL token. Line " + to_string(lineCount) + "\n").c_str()); + } + try{ + opts->modelName = tokenizedString[1]; + }catch(...){ + throw cvException(string("ERROR: Invalid Model Name. Line " + to_string(lineCount) + "\n").c_str()); + } + opts->modelNameDefined = true; + }else if(upper_string(tokenizedString[0]) == "NODE"){ + // printf("Found Joint.\n"); + if(tokenizedString.size() > 5){ + throw cvException(string("ERROR: Too many parameters for NODE token. Line " + to_string(lineCount) + "\n").c_str()); + }else if(tokenizedString.size() < 5){ + throw cvException(string("ERROR: Not enough parameters for NODE token. Line " + to_string(lineCount) + "\n").c_str()); + } + try{ + // Get Node Name + opts->nodeName.push_back(tokenizedString[1]); + opts->nodeXcoord.push_back(atof(tokenizedString[2].c_str())); + opts->nodeYcoord.push_back(atof(tokenizedString[3].c_str())); + opts->nodeZcoord.push_back(atof(tokenizedString[4].c_str())); + }catch(...){ + throw cvException(string("ERROR: Invalid NODE Format. Line " + to_string(lineCount) + "\n").c_str()); + } + }else if(upper_string(tokenizedString[0]) == "JOINT"){ + // printf("Found Joint.\n"); + if(tokenizedString.size() > 5){ + throw cvException(string("ERROR: Too many parameters for JOINT token. Line " + to_string(lineCount) + "\n").c_str()); + }else if(tokenizedString.size() < 5){ + throw cvException(string("ERROR: Not enough parameters for JOINT token. Line " + to_string(lineCount) + "\n").c_str()); + } + try{ + // Get Joint Name + opts->jointName.push_back(tokenizedString[1]); +// opts->jointNode.push_back(atof(tokenizedString[2].c_str())); + opts->jointNode.push_back(tokenizedString[2]); + opts->jointInletName.push_back(tokenizedString[3]); + opts->jointOutletName.push_back(tokenizedString[4]); + }catch(...){ + throw cvException(string("ERROR: Invalid JOINT Format. Line " + to_string(lineCount) + "\n").c_str()); + } + }else if(upper_string(tokenizedString[0]) == "JOINTINLET"){ + // printf("Found JointInlet.\n"); + if(tokenizedString.size() < 3){ + throw cvException(string("ERROR: Not enough parameters for JOINTINLET token. Line " + to_string(lineCount) + "\n").c_str()); + } + try{ + // Get Joint Name + opts->jointInletListNames.push_back(tokenizedString[1]); + totSegments = atoi(tokenizedString[2].c_str()); + opts->jointInletListNumber.push_back(totSegments); + tempIntVec.clear(); + if(totSegments > 0){ + for(size_t loopA=3;loopAjointInletList.push_back(tempIntVec); + }catch(...){ + throw cvException(string("ERROR: Invalid JOINTINLET Format. Line " + to_string(lineCount) + "\n").c_str()); + } + }else if(upper_string(tokenizedString[0]) == "JOINTOUTLET"){ + // printf("Found JointOutlet.\n"); + if(tokenizedString.size() < 3){ + throw cvException(string("ERROR: Not enough parameters for JOINTOUTLET token. Line " + to_string(lineCount) + "\n").c_str()); + } + try{ + // Get Joint Name + opts->jointOutletListNames.push_back(tokenizedString[1]); + totSegments = atoi(tokenizedString[2].c_str()); + opts->jointOutletListNumber.push_back(totSegments); + tempIntVec.clear(); + if(totSegments > 0){ + for(size_t loopA=3;loopAjointOutletList.push_back(tempIntVec); + }catch(...){ + throw cvException(string("ERROR: Invalid JOINTOUTLET Format. Line " + to_string(lineCount) + "\n").c_str()); + } + }else if(upper_string(tokenizedString[0]) == "SEGMENT"){ + // printf("Found Segment.\n"); + if(tokenizedString.size() > 17){ + throw cvException(string("ERROR: Too many parameters for SEGMENT token. Line " + to_string(lineCount) + "\n").c_str()); + }else if(tokenizedString.size() < 17){ + throw cvException(string("ERROR: Not enough parameters for SEGMENT token. Line " + to_string(lineCount) + "\n").c_str()); + } + try{ + // char* segName, + opts->segmentName.push_back(tokenizedString[1]); + // long segID, + opts->segmentID.push_back(atoi(tokenizedString[2].c_str())); + // double segLen, + opts->segmentLength.push_back(atof(tokenizedString[3].c_str())); + // long numEls, + opts->segmentTotEls.push_back(atoi(tokenizedString[4].c_str())); + // long inNode, + opts->segmentInNode.push_back(atoi(tokenizedString[5].c_str())); + // long outNode, + opts->segmentOutNode.push_back(atoi(tokenizedString[6].c_str())); + // double InitialInletArea, + opts->segmentInInletArea.push_back(atof(tokenizedString[7].c_str())); + // double InitialOutletArea, + opts->segmentInOutletArea.push_back(atof(tokenizedString[8].c_str())); + // double InitialFlow, + opts->segmentInFlow.push_back(atof(tokenizedString[9].c_str())); + // int matID, + opts->segmentMatName.push_back(tokenizedString[10].c_str()); + // char* lossType, + opts->segmentLossType.push_back(tokenizedString[11]); + // double branchAngle, + opts->segmentBranchAngle.push_back(atof(tokenizedString[12].c_str())); + // int upstreamSegment, + opts->segmentUpstreamSegment.push_back(atoi(tokenizedString[13].c_str())); + // int branchSegment, + opts->segmentBranchSegment.push_back(atoi(tokenizedString[14].c_str())); + // char* boundType, + opts->segmentBoundType.push_back(tokenizedString[15]); + // Curve ID Instead of num,value,time + // double* value, + // double* time, + // int num + opts->segmentDataTableName.push_back(tokenizedString[16]); + }catch(...){ + throw cvException(string("ERROR: Invalid SEGMENT Format. Line " + to_string(lineCount) + "\n").c_str()); + } + }else if(upper_string(tokenizedString[0]) == "SOLVEROPTIONS"){ + // printf("Found Solver Options.\n"); + if(opts->solverOptionDefined){ + throw cvException("ERROR: SOLVEROPTIONS already defined\n"); + } + if(tokenizedString.size() > 10){ + throw cvException(string("ERROR: Too many parameters for SOLVEROPTIONS token. Line " + to_string(lineCount) + "\n").c_str()); + }else if(tokenizedString.size() < 10){ + throw cvException(string("ERROR: Not enough parameters for SOLVEROPTIONS token. Line " + to_string(lineCount) + "\n").c_str()); + } + try{ + // double dt, + opts->timeStep = atof(tokenizedString[1].c_str()); + // long stepSize, + opts->stepSize = atof(tokenizedString[2].c_str()); + // long maxStep, + opts->maxStep = atof(tokenizedString[3].c_str()); + // long quadPoints, + opts->quadPoints = atoi(tokenizedString[4].c_str()); + // int CurveID, + opts->inletDataTableName = tokenizedString[5].c_str(); + // char* boundType, + opts->boundaryType = tokenizedString[6]; + // double conv, + opts->convergenceTolerance = atof(tokenizedString[7].c_str()); + // int useIV, + opts->useIV = atoi(tokenizedString[8].c_str()); + // int usestab + opts->useStab = atoi(tokenizedString[9].c_str()); + }catch(...){ + throw cvException(string("ERROR: Invalid SOLVEROPTIONS Format. Line " + to_string(lineCount) + "\n").c_str()); + } + opts->solverOptionDefined = true; + }else if(upper_string(tokenizedString[0]) == std::string("OUTPUT")){ + if(tokenizedString.size() > 3){ + throw cvException(string("ERROR: Too many parameters for OUTPUT token. Line " + to_string(lineCount) + "\n").c_str()); + }else if(tokenizedString.size() < 2){ + throw cvException(string("ERROR: Not enough parameters for OUTPUT token. Line " + to_string(lineCount) + "\n").c_str()); + } + // Output Type + if(upper_string(tokenizedString[1]) == "TEXT"){ + cvOneDGlobal::outputType = OutputTypeScope::OUTPUT_TEXT; + }else if(upper_string(tokenizedString[1]) == "VTK"){ + cvOneDGlobal::outputType = OutputTypeScope::OUTPUT_VTK; + }else if(upper_string(tokenizedString[1]) == "BOTH"){ + cvOneDGlobal::outputType = OutputTypeScope::OUTPUT_BOTH; + }else{ + throw cvException("ERROR: Invalid OUTPUT Type.\n"); + } + if(tokenizedString.size() > 2){ + cvOneDGlobal::vtkOutputType = atoi(tokenizedString[2].c_str()); + if(cvOneDGlobal::vtkOutputType > 1){ + throw cvException("ERROR: Invalid OUTPUT VTK Type.\n"); + } + } + }else if(upper_string(tokenizedString[0]) == std::string("DATATABLE")){ + // printf("Found Data Table.\n"); + try{ + + // Get Datatable Name + opts->dataTableName.push_back(tokenizedString[1]); + // Add the type of the datatable + opts->dataTableType.push_back(tokenizedString[2]); + + bool foundEnd = false; + temp.clear(); + while(!foundEnd){ + std::getline(infile,buffer); + lineCount++; + // Trim String + buffer = trim_string(buffer); + // Tokenize String + tokenizedString = split_string(buffer, " ,\t"); + if (tokenizedString.size() == 0) { + break; + } + // Check for Empty buffer + if(!buffer.empty()){ + if(upper_string(tokenizedString[0]) == std::string("ENDDATATABLE")){ + foundEnd = true; + }else{ + for(int loopA=0;loopAdataTableVals.push_back(temp); + }catch(...){ + throw cvException(string("ERROR: Invalid DATATABLE Format. Line " + to_string(lineCount) + "\n").c_str()); + } + }else if(upper_string(tokenizedString[0]) == std::string("MATERIAL")){ + // printf("Found Material.\n"); + if(tokenizedString.size() > 10){ + throw cvException(string("ERROR: Too many parameters for MATERIAL token. Line " + to_string(lineCount) + "\n").c_str()); + }else if(tokenizedString.size() < 8){ + throw cvException(string("ERROR: Not enough parameters for MATERIAL token. Line " + to_string(lineCount) + "\n").c_str()); + } + try{ + // Material Name + opts->materialName.push_back(tokenizedString[1]); + // Material Type + matType = tokenizedString[2]; + opts->materialType.push_back(matType); + // Density + opts->materialDensity.push_back(atof(tokenizedString[3].c_str())); + // Dynamic Viscosity + opts->materialViscosity.push_back(atof(tokenizedString[4].c_str())); + // Reference Pressure + opts->materialPRef.push_back(atof(tokenizedString[5].c_str())); + // Material Exponent + opts->materialExponent.push_back(atof(tokenizedString[6].c_str())); + // Extra Material Parameters + if(upper_string(matType) == "OLUFSEN"){ + opts->materialParam1.push_back(atof(tokenizedString[7].c_str())); + opts->materialParam2.push_back(atof(tokenizedString[8].c_str())); + opts->materialParam3.push_back(atof(tokenizedString[9].c_str())); + }else if(upper_string(matType) == "LINEAR"){ + opts->materialParam1.push_back(atof(tokenizedString[7].c_str())); + opts->materialParam2.push_back(0.0); + opts->materialParam3.push_back(0.0); + }else{ + throw cvException(string("ERROR: Invalid MATERIAL Type. Line " + to_string(lineCount) + "\n").c_str()); + } + }catch(...){ + throw cvException("ERROR: Invalid MATERIAL Format.\n"); + } + }else if(upper_string(tokenizedString[0]) == std::string("INCLUDE")){ + // Check if the file is active + if(upper_string(tokenizedString[2]) == "TRUE"){ + doInclude = true; + }else if(upper_string(tokenizedString[2]) == "FALSE"){ + doInclude = false; + }else{ + throw cvException(string("ERROR: Invalid INCLUDE switch format. Line " + to_string(lineCount) + "\n").c_str()); + } + try{ + // If active include file in list + if(doInclude){ + includedFiles.push_back(tokenizedString[1]); + } + }catch(...){ + throw cvException(string("ERROR: Invalid INCLUDE Format. Line " + to_string(lineCount) + "\n").c_str()); + } + }else if((tokenizedString.size() == 0)||(tokenizedString[0].at(0) == '#')||(tokenizedString[0].find_first_not_of(' ') == std::string::npos)){ + // printf("Found Blank.\n"); + // COMMENT OR BLANK LINE: DO NOTHING + }else{ + // TOKEN NOT RECOGNIZED + throw cvException(string("ERROR: Invalid Token in input file, line: " + to_string(lineCount) + "\n").c_str()); + } + } + // printf("Line: %d, Buffer: %s\n",lineCount,buffer.c_str()); + // getchar(); + + // Increment Line Count + lineCount++; + } + // Close File + infile.close(); +} + +void readNestedOptionsLegacyFormat(string inputFile, cvOneDOptions* opts){ + + // List of included Files + cvStringVec includedFiles; + string currentFile; + + // Read First File + readSingleOptionsLegacyFormat(inputFile,opts,includedFiles); + + //Read Nested Files + while(includedFiles.size() > 0){ + + // Get the first file Name + currentFile = includedFiles[0]; + // Delete the First element + includedFiles.erase(includedFiles.begin()); + // Read the file and store new included files + readSingleOptionsLegacyFormat(inputFile,opts,includedFiles); + } +} + +} // namespace cvOneD \ No newline at end of file diff --git a/Code/Source/cvOneDOptionsLegacySerializer.h b/Code/Source/cvOneDOptionsLegacySerializer.h new file mode 100644 index 0000000..e7913c7 --- /dev/null +++ b/Code/Source/cvOneDOptionsLegacySerializer.h @@ -0,0 +1,48 @@ +/* Copyright (c) Stanford University, The Regents of the University of + * California, and others. + * + * All Rights Reserved. + * + * See Copyright-SimVascular.txt for additional details. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject + * to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CVONEDOPTIONSLEGACYSERIALIZER_H +#define CVONEDOPTIONSLEGACYSERIALIZER_H + +#include "cvOneDOptions.h" + +using namespace std; + +namespace cvOneD{ + +void readSingleOptionsLegacyFormat(string inputFile, cvOneDOptions* opts, cvStringVec includedFiles); + +void readNestedOptionsLegacyFormat(string inputFile, cvOneDOptions* opts); + +} // namespace cvOneD + +#endif // CVONEDOPTIONSLEGACYSERIALIZER_H + diff --git a/Code/Source/main.cxx b/Code/Source/main.cxx index c82cb7d..436a62f 100644 --- a/Code/Source/main.cxx +++ b/Code/Source/main.cxx @@ -32,6 +32,8 @@ #include "main.h" #include +#include "cvOneDOptionsLegacySerializer.h" + using namespace std; // ==================== @@ -301,364 +303,14 @@ void createAndRunModel(cvOneDOptions* opts){ // READ SINGLE MODEL FILE // ====================== void readModelFile(string inputFile, cvOneDOptions* opts, cvStringVec includedFiles){ - - // Message - printf("\n"); - printf("Reading file: %s ... \n",inputFile.c_str()); - - // Declare - cvStringVec tokenizedString; - cvLongVec tempIntVec; - string matType; - cvDoubleVec temp; - bool doInclude = false; - - // Declare input File - ifstream infile; - infile.open(inputFile); - if(infile.fail()){ - throw cvException("ERROR: Input file does not exist.\n"); - } - int lineCount = 1; - int reminder = 0; - int totSegments = 0; - - // Read Data From File - std::string buffer; - while (std::getline(infile,buffer)){ - - // Trim String - buffer = trim_string(buffer); - - // Tokenize String - tokenizedString = split_string(buffer, " ,\t"); - if (tokenizedString.size() == 0) { - continue; - } - - // Check for Empty buffer - if(!buffer.empty()){ - // CHECK THE ELEMENT TYPE - if(upper_string(tokenizedString[0]) == "MODEL"){ - //printf("Found Model.\n"); - if(opts->modelNameDefined){ - throw cvException("ERROR: Model name already defined\n"); - } - if(tokenizedString.size() > 2){ - throw cvException(string("ERROR: Too many parameters for MODEL token. Line " + to_string(lineCount) + "\n").c_str()); - }else if(tokenizedString.size() < 2){ - throw cvException(string("ERROR: Not enough parameters for MODEL token. Line " + to_string(lineCount) + "\n").c_str()); - } - try{ - opts->modelName = tokenizedString[1]; - }catch(...){ - throw cvException(string("ERROR: Invalid Model Name. Line " + to_string(lineCount) + "\n").c_str()); - } - opts->modelNameDefined = true; - }else if(upper_string(tokenizedString[0]) == "NODE"){ - // printf("Found Joint.\n"); - if(tokenizedString.size() > 5){ - throw cvException(string("ERROR: Too many parameters for NODE token. Line " + to_string(lineCount) + "\n").c_str()); - }else if(tokenizedString.size() < 5){ - throw cvException(string("ERROR: Not enough parameters for NODE token. Line " + to_string(lineCount) + "\n").c_str()); - } - try{ - // Get Node Name - opts->nodeName.push_back(tokenizedString[1]); - opts->nodeXcoord.push_back(atof(tokenizedString[2].c_str())); - opts->nodeYcoord.push_back(atof(tokenizedString[3].c_str())); - opts->nodeZcoord.push_back(atof(tokenizedString[4].c_str())); - }catch(...){ - throw cvException(string("ERROR: Invalid NODE Format. Line " + to_string(lineCount) + "\n").c_str()); - } - }else if(upper_string(tokenizedString[0]) == "JOINT"){ - // printf("Found Joint.\n"); - if(tokenizedString.size() > 5){ - throw cvException(string("ERROR: Too many parameters for JOINT token. Line " + to_string(lineCount) + "\n").c_str()); - }else if(tokenizedString.size() < 5){ - throw cvException(string("ERROR: Not enough parameters for JOINT token. Line " + to_string(lineCount) + "\n").c_str()); - } - try{ - // Get Joint Name - opts->jointName.push_back(tokenizedString[1]); -// opts->jointNode.push_back(atof(tokenizedString[2].c_str())); - opts->jointNode.push_back(tokenizedString[2]); - opts->jointInletName.push_back(tokenizedString[3]); - opts->jointOutletName.push_back(tokenizedString[4]); - }catch(...){ - throw cvException(string("ERROR: Invalid JOINT Format. Line " + to_string(lineCount) + "\n").c_str()); - } - }else if(upper_string(tokenizedString[0]) == "JOINTINLET"){ - // printf("Found JointInlet.\n"); - if(tokenizedString.size() < 3){ - throw cvException(string("ERROR: Not enough parameters for JOINTINLET token. Line " + to_string(lineCount) + "\n").c_str()); - } - try{ - // Get Joint Name - opts->jointInletListNames.push_back(tokenizedString[1]); - totSegments = atoi(tokenizedString[2].c_str()); - opts->jointInletListNumber.push_back(totSegments); - tempIntVec.clear(); - if(totSegments > 0){ - for(size_t loopA=3;loopAjointInletList.push_back(tempIntVec); - }catch(...){ - throw cvException(string("ERROR: Invalid JOINTINLET Format. Line " + to_string(lineCount) + "\n").c_str()); - } - }else if(upper_string(tokenizedString[0]) == "JOINTOUTLET"){ - // printf("Found JointOutlet.\n"); - if(tokenizedString.size() < 3){ - throw cvException(string("ERROR: Not enough parameters for JOINTOUTLET token. Line " + to_string(lineCount) + "\n").c_str()); - } - try{ - // Get Joint Name - opts->jointOutletListNames.push_back(tokenizedString[1]); - totSegments = atoi(tokenizedString[2].c_str()); - opts->jointOutletListNumber.push_back(totSegments); - tempIntVec.clear(); - if(totSegments > 0){ - for(size_t loopA=3;loopAjointOutletList.push_back(tempIntVec); - }catch(...){ - throw cvException(string("ERROR: Invalid JOINTOUTLET Format. Line " + to_string(lineCount) + "\n").c_str()); - } - }else if(upper_string(tokenizedString[0]) == "SEGMENT"){ - // printf("Found Segment.\n"); - if(tokenizedString.size() > 17){ - throw cvException(string("ERROR: Too many parameters for SEGMENT token. Line " + to_string(lineCount) + "\n").c_str()); - }else if(tokenizedString.size() < 17){ - throw cvException(string("ERROR: Not enough parameters for SEGMENT token. Line " + to_string(lineCount) + "\n").c_str()); - } - try{ - // char* segName, - opts->segmentName.push_back(tokenizedString[1]); - // long segID, - opts->segmentID.push_back(atoi(tokenizedString[2].c_str())); - // double segLen, - opts->segmentLength.push_back(atof(tokenizedString[3].c_str())); - // long numEls, - opts->segmentTotEls.push_back(atoi(tokenizedString[4].c_str())); - // long inNode, - opts->segmentInNode.push_back(atoi(tokenizedString[5].c_str())); - // long outNode, - opts->segmentOutNode.push_back(atoi(tokenizedString[6].c_str())); - // double InitialInletArea, - opts->segmentInInletArea.push_back(atof(tokenizedString[7].c_str())); - // double InitialOutletArea, - opts->segmentInOutletArea.push_back(atof(tokenizedString[8].c_str())); - // double InitialFlow, - opts->segmentInFlow.push_back(atof(tokenizedString[9].c_str())); - // int matID, - opts->segmentMatName.push_back(tokenizedString[10].c_str()); - // char* lossType, - opts->segmentLossType.push_back(tokenizedString[11]); - // double branchAngle, - opts->segmentBranchAngle.push_back(atof(tokenizedString[12].c_str())); - // int upstreamSegment, - opts->segmentUpstreamSegment.push_back(atoi(tokenizedString[13].c_str())); - // int branchSegment, - opts->segmentBranchSegment.push_back(atoi(tokenizedString[14].c_str())); - // char* boundType, - opts->segmentBoundType.push_back(tokenizedString[15]); - // Curve ID Instead of num,value,time - // double* value, - // double* time, - // int num - opts->segmentDataTableName.push_back(tokenizedString[16]); - }catch(...){ - throw cvException(string("ERROR: Invalid SEGMENT Format. Line " + to_string(lineCount) + "\n").c_str()); - } - }else if(upper_string(tokenizedString[0]) == "SOLVEROPTIONS"){ - // printf("Found Solver Options.\n"); - if(opts->solverOptionDefined){ - throw cvException("ERROR: SOLVEROPTIONS already defined\n"); - } - if(tokenizedString.size() > 10){ - throw cvException(string("ERROR: Too many parameters for SOLVEROPTIONS token. Line " + to_string(lineCount) + "\n").c_str()); - }else if(tokenizedString.size() < 10){ - throw cvException(string("ERROR: Not enough parameters for SOLVEROPTIONS token. Line " + to_string(lineCount) + "\n").c_str()); - } - try{ - // double dt, - opts->timeStep = atof(tokenizedString[1].c_str()); - // long stepSize, - opts->stepSize = atof(tokenizedString[2].c_str()); - // long maxStep, - opts->maxStep = atof(tokenizedString[3].c_str()); - // long quadPoints, - opts->quadPoints = atoi(tokenizedString[4].c_str()); - // int CurveID, - opts->inletDataTableName = tokenizedString[5].c_str(); - // char* boundType, - opts->boundaryType = tokenizedString[6]; - // double conv, - opts->convergenceTolerance = atof(tokenizedString[7].c_str()); - // int useIV, - opts->useIV = atoi(tokenizedString[8].c_str()); - // int usestab - opts->useStab = atoi(tokenizedString[9].c_str()); - }catch(...){ - throw cvException(string("ERROR: Invalid SOLVEROPTIONS Format. Line " + to_string(lineCount) + "\n").c_str()); - } - opts->solverOptionDefined = true; - }else if(upper_string(tokenizedString[0]) == std::string("OUTPUT")){ - if(tokenizedString.size() > 3){ - throw cvException(string("ERROR: Too many parameters for OUTPUT token. Line " + to_string(lineCount) + "\n").c_str()); - }else if(tokenizedString.size() < 2){ - throw cvException(string("ERROR: Not enough parameters for OUTPUT token. Line " + to_string(lineCount) + "\n").c_str()); - } - // Output Type - if(upper_string(tokenizedString[1]) == "TEXT"){ - cvOneDGlobal::outputType = OutputTypeScope::OUTPUT_TEXT; - }else if(upper_string(tokenizedString[1]) == "VTK"){ - cvOneDGlobal::outputType = OutputTypeScope::OUTPUT_VTK; - }else if(upper_string(tokenizedString[1]) == "BOTH"){ - cvOneDGlobal::outputType = OutputTypeScope::OUTPUT_BOTH; - }else{ - throw cvException("ERROR: Invalid OUTPUT Type.\n"); - } - if(tokenizedString.size() > 2){ - cvOneDGlobal::vtkOutputType = atoi(tokenizedString[2].c_str()); - if(cvOneDGlobal::vtkOutputType > 1){ - throw cvException("ERROR: Invalid OUTPUT VTK Type.\n"); - } - } - }else if(upper_string(tokenizedString[0]) == std::string("DATATABLE")){ - // printf("Found Data Table.\n"); - try{ - - // Get Datatable Name - opts->dataTableName.push_back(tokenizedString[1]); - // Add the type of the datatable - opts->dataTableType.push_back(tokenizedString[2]); - - bool foundEnd = false; - temp.clear(); - while(!foundEnd){ - std::getline(infile,buffer); - lineCount++; - // Trim String - buffer = trim_string(buffer); - // Tokenize String - tokenizedString = split_string(buffer, " ,\t"); - if (tokenizedString.size() == 0) { - break; - } - // Check for Empty buffer - if(!buffer.empty()){ - if(upper_string(tokenizedString[0]) == std::string("ENDDATATABLE")){ - foundEnd = true; - }else{ - for(int loopA=0;loopAdataTableVals.push_back(temp); - }catch(...){ - throw cvException(string("ERROR: Invalid DATATABLE Format. Line " + to_string(lineCount) + "\n").c_str()); - } - }else if(upper_string(tokenizedString[0]) == std::string("MATERIAL")){ - // printf("Found Material.\n"); - if(tokenizedString.size() > 10){ - throw cvException(string("ERROR: Too many parameters for MATERIAL token. Line " + to_string(lineCount) + "\n").c_str()); - }else if(tokenizedString.size() < 8){ - throw cvException(string("ERROR: Not enough parameters for MATERIAL token. Line " + to_string(lineCount) + "\n").c_str()); - } - try{ - // Material Name - opts->materialName.push_back(tokenizedString[1]); - // Material Type - matType = tokenizedString[2]; - opts->materialType.push_back(matType); - // Density - opts->materialDensity.push_back(atof(tokenizedString[3].c_str())); - // Dynamic Viscosity - opts->materialViscosity.push_back(atof(tokenizedString[4].c_str())); - // Reference Pressure - opts->materialPRef.push_back(atof(tokenizedString[5].c_str())); - // Material Exponent - opts->materialExponent.push_back(atof(tokenizedString[6].c_str())); - // Extra Material Parameters - if(upper_string(matType) == "OLUFSEN"){ - opts->materialParam1.push_back(atof(tokenizedString[7].c_str())); - opts->materialParam2.push_back(atof(tokenizedString[8].c_str())); - opts->materialParam3.push_back(atof(tokenizedString[9].c_str())); - }else if(upper_string(matType) == "LINEAR"){ - opts->materialParam1.push_back(atof(tokenizedString[7].c_str())); - opts->materialParam2.push_back(0.0); - opts->materialParam3.push_back(0.0); - }else{ - throw cvException(string("ERROR: Invalid MATERIAL Type. Line " + to_string(lineCount) + "\n").c_str()); - } - }catch(...){ - throw cvException("ERROR: Invalid MATERIAL Format.\n"); - } - }else if(upper_string(tokenizedString[0]) == std::string("INCLUDE")){ - // Check if the file is active - if(upper_string(tokenizedString[2]) == "TRUE"){ - doInclude = true; - }else if(upper_string(tokenizedString[2]) == "FALSE"){ - doInclude = false; - }else{ - throw cvException(string("ERROR: Invalid INCLUDE switch format. Line " + to_string(lineCount) + "\n").c_str()); - } - try{ - // If active include file in list - if(doInclude){ - includedFiles.push_back(tokenizedString[1]); - } - }catch(...){ - throw cvException(string("ERROR: Invalid INCLUDE Format. Line " + to_string(lineCount) + "\n").c_str()); - } - }else if((tokenizedString.size() == 0)||(tokenizedString[0].at(0) == '#')||(tokenizedString[0].find_first_not_of(' ') == std::string::npos)){ - // printf("Found Blank.\n"); - // COMMENT OR BLANK LINE: DO NOTHING - }else{ - // TOKEN NOT RECOGNIZED - throw cvException(string("ERROR: Invalid Token in input file, line: " + to_string(lineCount) + "\n").c_str()); - } - } - // printf("Line: %d, Buffer: %s\n",lineCount,buffer.c_str()); - // getchar(); - - // Increment Line Count - lineCount++; - } - // Close File - infile.close(); + cvOneD::readSingleOptionsLegacyFormat(inputFile,opts,includedFiles); } // ==================== // READ MODEL FROM FILE // ==================== void readModel(string inputFile, cvOneDOptions* opts){ - - // List of included Files - cvStringVec includedFiles; - string currentFile; - - // Read First File - readModelFile(inputFile,opts,includedFiles); - - //Read Nested Files - while(includedFiles.size() > 0){ - - // Get the first file Name - currentFile = includedFiles[0]; - // Delete the First element - includedFiles.erase(includedFiles.begin()); - // Read the file and store new included files - readModelFile(inputFile,opts,includedFiles); - } + cvOneD::readNestedOptionsLegacyFormat(inputFile,opts); } // ============== diff --git a/tests/__init__.py b/Tests/SystemTests/__init__.py similarity index 100% rename from tests/__init__.py rename to Tests/SystemTests/__init__.py diff --git a/tests/cases/02_simpleArtery_Resistance.in b/Tests/SystemTests/cases/02_simpleArtery_Resistance.in similarity index 100% rename from tests/cases/02_simpleArtery_Resistance.in rename to Tests/SystemTests/cases/02_simpleArtery_Resistance.in diff --git a/tests/cases/04_simpleArtery_Pressure.in b/Tests/SystemTests/cases/04_simpleArtery_Pressure.in similarity index 100% rename from tests/cases/04_simpleArtery_Pressure.in rename to Tests/SystemTests/cases/04_simpleArtery_Pressure.in diff --git a/tests/cases/05_bifurcation_RCR.in b/Tests/SystemTests/cases/05_bifurcation_RCR.in similarity index 100% rename from tests/cases/05_bifurcation_RCR.in rename to Tests/SystemTests/cases/05_bifurcation_RCR.in diff --git a/tests/cases/12_AortoFem_Pulse_R.in b/Tests/SystemTests/cases/12_AortoFem_Pulse_R.in similarity index 100% rename from tests/cases/12_AortoFem_Pulse_R.in rename to Tests/SystemTests/cases/12_AortoFem_Pulse_R.in diff --git a/tests/cases/14_AortoFem_Pulse_RCR.in b/Tests/SystemTests/cases/14_AortoFem_Pulse_RCR.in similarity index 100% rename from tests/cases/14_AortoFem_Pulse_RCR.in rename to Tests/SystemTests/cases/14_AortoFem_Pulse_RCR.in diff --git a/tests/cases/Left_coronary_inlet_pressure_wave_outlet_coronary.in b/Tests/SystemTests/cases/Left_coronary_inlet_pressure_wave_outlet_coronary.in similarity index 100% rename from tests/cases/Left_coronary_inlet_pressure_wave_outlet_coronary.in rename to Tests/SystemTests/cases/Left_coronary_inlet_pressure_wave_outlet_coronary.in diff --git a/tests/cases/bifurcation_P.in b/Tests/SystemTests/cases/bifurcation_P.in similarity index 100% rename from tests/cases/bifurcation_P.in rename to Tests/SystemTests/cases/bifurcation_P.in diff --git a/tests/cases/bifurcation_R.in b/Tests/SystemTests/cases/bifurcation_R.in similarity index 100% rename from tests/cases/bifurcation_R.in rename to Tests/SystemTests/cases/bifurcation_R.in diff --git a/tests/cases/bifurcation_RCR.in b/Tests/SystemTests/cases/bifurcation_RCR.in similarity index 100% rename from tests/cases/bifurcation_RCR.in rename to Tests/SystemTests/cases/bifurcation_RCR.in diff --git a/tests/cases/bifurcation_RCR_staticFunc.in b/Tests/SystemTests/cases/bifurcation_RCR_staticFunc.in similarity index 100% rename from tests/cases/bifurcation_RCR_staticFunc.in rename to Tests/SystemTests/cases/bifurcation_RCR_staticFunc.in diff --git a/tests/cases/bifurcation_R_stab.in b/Tests/SystemTests/cases/bifurcation_R_stab.in similarity index 100% rename from tests/cases/bifurcation_R_stab.in rename to Tests/SystemTests/cases/bifurcation_R_stab.in diff --git a/tests/cases/tube_pressure.in b/Tests/SystemTests/cases/tube_pressure.in similarity index 100% rename from tests/cases/tube_pressure.in rename to Tests/SystemTests/cases/tube_pressure.in diff --git a/tests/cases/tube_pressure_wave.in b/Tests/SystemTests/cases/tube_pressure_wave.in similarity index 100% rename from tests/cases/tube_pressure_wave.in rename to Tests/SystemTests/cases/tube_pressure_wave.in diff --git a/tests/cases/tube_r.in b/Tests/SystemTests/cases/tube_r.in similarity index 100% rename from tests/cases/tube_r.in rename to Tests/SystemTests/cases/tube_r.in diff --git a/tests/cases/tube_r_Pd.in b/Tests/SystemTests/cases/tube_r_Pd.in similarity index 100% rename from tests/cases/tube_r_Pd.in rename to Tests/SystemTests/cases/tube_r_Pd.in diff --git a/tests/cases/tube_r_stab.in b/Tests/SystemTests/cases/tube_r_stab.in similarity index 100% rename from tests/cases/tube_r_stab.in rename to Tests/SystemTests/cases/tube_r_stab.in diff --git a/tests/cases/tube_rcr.in b/Tests/SystemTests/cases/tube_rcr.in similarity index 100% rename from tests/cases/tube_rcr.in rename to Tests/SystemTests/cases/tube_rcr.in diff --git a/tests/cases/tube_rcr_Pd.in b/Tests/SystemTests/cases/tube_rcr_Pd.in similarity index 100% rename from tests/cases/tube_rcr_Pd.in rename to Tests/SystemTests/cases/tube_rcr_Pd.in diff --git a/tests/cases/tube_stenosis_r.in b/Tests/SystemTests/cases/tube_stenosis_r.in similarity index 100% rename from tests/cases/tube_stenosis_r.in rename to Tests/SystemTests/cases/tube_stenosis_r.in diff --git a/tests/conftest.py b/Tests/SystemTests/conftest.py similarity index 100% rename from tests/conftest.py rename to Tests/SystemTests/conftest.py diff --git a/tests/plots.py b/Tests/SystemTests/plots.py similarity index 100% rename from tests/plots.py rename to Tests/SystemTests/plots.py diff --git a/tests/test_integration.py b/Tests/SystemTests/test_integration.py similarity index 100% rename from tests/test_integration.py rename to Tests/SystemTests/test_integration.py diff --git a/Tests/UnitTests/ExampleTest.cxx b/Tests/UnitTests/ExampleTest.cxx new file mode 100644 index 0000000..948733e --- /dev/null +++ b/Tests/UnitTests/ExampleTest.cxx @@ -0,0 +1,21 @@ +#include +#include +#include + +TEST(ExampleTest, ExampleTest) { + const std::string filePath = "TestFiles/ExampleFile.txt"; + std::ifstream file(filePath); + + // Verify the file was successfully opened + ASSERT_TRUE(file.is_open()) << "Failed to open file: " << filePath; + + // Read the contents of the file + std::string content; + std::getline(file, content); + + // Verify that the file is not empty (example assumption that the file should contain some text) + EXPECT_TRUE(content.find("Example text") != std::string::npos); + + // Close the file + file.close(); +} diff --git a/Tests/UnitTests/TestFiles/ExampleFile.txt b/Tests/UnitTests/TestFiles/ExampleFile.txt new file mode 100644 index 0000000..9854092 --- /dev/null +++ b/Tests/UnitTests/TestFiles/ExampleFile.txt @@ -0,0 +1 @@ +Example text \ No newline at end of file diff --git a/Tests/UnitTests/TestFiles/SimpleArtery.in b/Tests/UnitTests/TestFiles/SimpleArtery.in new file mode 100755 index 0000000..8d1570b --- /dev/null +++ b/Tests/UnitTests/TestFiles/SimpleArtery.in @@ -0,0 +1,125 @@ +# ========== +# MODEL CARD +# ========== +# - Name of the model (string) + +MODEL simpleArtery_Res_ + + +# ========== +# NODE CARD +# ========== +# - Node Name (double) +# - Node X Coordinate (double) +# - Node Y Coordinate (double) +# - Node Z Coordinate (double) + +NODE 0 0.0 0.0 0.0 +NODE 1 0.0 0.0 -20.0 + + +# ========== +# JOINT CARD +# ========== +# - Joint Name (string) +# - Joint Node (double) +# - Joint Inlet Name (string) +# - Joint Outlet Name (string) + + +# ================================ +# JOINTINLET AND JOINTOUTLET CARDS +# ================================ +# - Inlet/Outlet Name (string) +# - Total Number of segments (int) +# - List of segments (list of int) + +# NO JOINTS IN THIS MODEL + + +# ============ +# SEGMENT CARD +# ============ +# - Segment Name (string) +# - Segment ID (int) +# - Segment Length (double) +# - Total Finite Elements in Segment (int) +# - Segment Inlet Node (int) +# - Segment Outlet Node (int) +# - Segment Inlet Area (double) +# - Segment Outlet Area (double) +# - Segment Inflow Value (double) +# - Segment Material (string) +# - Type of Loss (string - 'NONE','STENOSIS','BRANCH_THROUGH_DIVIDING','BRANCH_SIDE_DIVIDING','BRANCH_THROUGH_CONVERGING', +# 'BRANCH_SIDE_CONVERGING','BIFURCATION_BRANCH') +# - Branch Angle (double) +# - Upstream Segment ID (int) +# - Branch Segment ID (int) +# - Boundary Condition Type (string - 'NOBOUND','PRESSURE','AREA','FLOW','RESISTANCE','RESISTANCE_TIME','PRESSURE_WAVE', +# 'WAVE','RCR','CORONARY','IMPEDANCE','PULMONARY') +# - Data Table Name (string) + +SEGMENT ARTERY 0 20.0 50 0 1 2.0 2.0 0.0 MAT1 NONE 0.0 0 0 RESISTANCE RESTABLE + + +# ============== +# DATATABLE CARD +# ============== +# - Data Table Name (string) +# - Data Table Type (string - 'LIST','IMPEDANCE','RCRIMPEDANCE','MORPHIMPEDANCE','ADMITTANCE','RCRADMITTANCE','MORPHADMITTANCE' +# - LIST OPTION - List of times and Values (e.g., 'time0 value0 time1 value1 ...' list of alternating times and values) +# - IMPEDANCE and ADMITTANCE OPTIONS - Number of time steps, Length to Radius Ratio, Radius at Root, Period, Scale Factor, Number of Fourier modes +# - RCRIMPEDANCE and RCRADMITTANCE OPTIONS - Number of time steps, RCR distal resistance, RCR proximal resistance, Period, RCR capacitance, Scale factor, Number of Fourier modes +# - MORPHIMPEDANCE and MORPHADMITTANCE OPTIONS - Number of time steps, Min vessel order, Radius at Root, Period, Number of Fourier modes +# - ENDDATATABLE - Keyword for termination of table data + +DATATABLE RESTABLE LIST +0.0 100.0 +ENDDATATABLE + +DATATABLE INLETDATA LIST +0.0 200.0 +10.0 200.0 +ENDDATATABLE + + +# ============= +# MATERIAL CARD +# ============= +# - Material Name (string) +# - Material Type (string - 'LINEAR','OLUFSEN') +# - Material Density (double) +# - Material Viscosity (double) +# - Referemce Pressure (double, CGS units) +# - Material Exponent (double) +# - Material Parameter 1 (double) +# - Material Parameter 2 (double, unused for LINEAR type) +# - Material Parameter 3 (double, unused for LINEAR type) + +# Ref Pressure 1333.22*85 where 85 is in mmHg +MATERIAL MAT1 OLUFSEN 1.06 0.04 113324.0 1.0 2.0e7 -22.5267 8.65e5 + + +# ================== +# SOLVEROPTIONS CARD +# ================== +# - Solver Time Step (double), +# - Steps Between Saves (int), +# - Max Number of Save Steps (int) +# - Number of quadrature points for finite elements (int), +# - Name of Datatable for inlet conditions (string) +# - Type of boundary condition (string - 'NOBOUND','PRESSURE','AREA','FLOW','RESISTANCE','RESISTANCE_TIME','PRESSURE_WAVE', +# 'WAVE','RCR','CORONARY','IMPEDANCE','PULMONARY') +# - Convergence tolerance (double), +# - Formulation Type (int - 0 Advective, 1 Conservative), +# - Stabilization (int - 0 No stabilization, 1 With stabilization) + +SOLVEROPTIONS 0.01 10 1000 4 INLETDATA FLOW 1.0e-6 1 1 + + +# =========== +# OUTPUT CARD +# =========== +# - Output type (string - 'TEXT', 'VTK') + +OUTPUT TEXT diff --git a/Tests/UnitTests/TestLegacySerializer.cxx b/Tests/UnitTests/TestLegacySerializer.cxx new file mode 100644 index 0000000..e35598e --- /dev/null +++ b/Tests/UnitTests/TestLegacySerializer.cxx @@ -0,0 +1,16 @@ +#include +#include +#include + +#include "cvOneDOptions.h" +#include "cvOneDOptionsLegacySerializer.h" + +// Test case for the add function +TEST(LegacySerializer, ParseSimpleFile) { + // Path to the file in the TestFiles directory + const std::string filePath = "TestFiles/SimpleArtery.in"; + + std::ifstream file(filePath); + + file.close(); +} From cd1685b5f202f08f2dcf58ca5ddc20d73e471196 Mon Sep 17 00:00:00 2001 From: arsLibera Date: Sat, 8 Feb 2025 14:23:39 -0500 Subject: [PATCH 3/4] Remove (nonfunctional) nested options deserializer. Add unit tests for legacy deserialization --- .github/workflows/build.yml | 69 ++-- CMakeLists.txt | 6 +- Code/Source/cvOneDOptionsLegacySerializer.cxx | 40 +-- Code/Source/cvOneDOptionsLegacySerializer.h | 4 +- Code/Source/main.cxx | 20 +- Tests/UnitTests/ExampleTest.cxx | 2 + Tests/UnitTests/TestFiles/BifurcationR.in | 107 ++++++ Tests/UnitTests/TestLegacySerializer.cxx | 319 +++++++++++++++++- 8 files changed, 467 insertions(+), 100 deletions(-) create mode 100644 Tests/UnitTests/TestFiles/BifurcationR.in diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 074355f..b88752b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -81,38 +81,41 @@ jobs: asset_name: svOneDSolver-${{github.ref_name}}-Ubuntu.deb tag: ${{ github.ref }} - build-windows: - runs-on: windows-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Install Cygwin - uses: cygwin/cygwin-install-action@master - with: - install-dir: C:\cygwin64 - packages: make tcl zip gzip tar patch wget - - name: Install Visual Studio BuildTools - run: | - choco install visualstudio2017buildtools visualstudio2017-workload-vctools - - name: Build - run: | - cd BuildWithMake - dos2unix ./windows-build.sh - C:\cygwin64\bin\bash.exe -c "./build_windows_github.bat" - cp Release/sim*.msi ../svOneDSolver-Windows.msi - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: Windows Installer - path: svOneDSolver-Windows.msi - if-no-files-found: error - - name: Upload release asset - if: startsWith(github.ref, 'refs/tags/') - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: svOneDSolver-Windows.msi - asset_name: svOneDSolver-${{github.ref_name}}-Windows.msi - tag: ${{ github.ref }} +# TODO: There's an issue for fixing the Windows build. +# Right now it's failing. +# +# build-windows: +# runs-on: windows-latest +# steps: +# - name: Checkout +# uses: actions/checkout@v3 +# - name: Install Cygwin +# uses: cygwin/cygwin-install-action@master +# with: +# install-dir: C:\cygwin64 +# packages: make tcl zip gzip tar patch wget +# - name: Install Visual Studio BuildTools +# run: | +# choco install visualstudio2017buildtools visualstudio2017-workload-vctools +# - name: Build +# run: | +# cd BuildWithMake +# dos2unix ./windows-build.sh +# C:\cygwin64\bin\bash.exe -c "./build_windows_github.bat" +# cp Release/sim*.msi ../svOneDSolver-Windows.msi +# - name: Upload artifact +# uses: actions/upload-artifact@v3 +# with: +# name: Windows Installer +# path: svOneDSolver-Windows.msi +# if-no-files-found: error +# - name: Upload release asset +# if: startsWith(github.ref, 'refs/tags/') +# uses: svenstaro/upload-release-action@v2 +# with: +# repo_token: ${{ secrets.GITHUB_TOKEN }} +# file: svOneDSolver-Windows.msi +# asset_name: svOneDSolver-${{github.ref_name}}-Windows.msi +# tag: ${{ github.ref }} diff --git a/CMakeLists.txt b/CMakeLists.txt index cdbd84a..09df300 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,7 +130,6 @@ if(ENABLE_UNIT_TEST) include(GoogleTest) set(TESTBIN_DIRECTORY ${CMAKE_BINARY_DIR}/testbin) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${TESTBIN_DIRECTORY}) # Copy test files into the build directory so tests can access them file(GLOB TEST_FILES "${CMAKE_CURRENT_SOURCE_DIR}/Tests/UnitTests/TestFiles/*") @@ -170,6 +169,11 @@ if(ENABLE_UNIT_TEST) # the general strategy. target_include_directories(UnitTestsExecutable PRIVATE ${SRCS_DIR}) + # Set the output directory for the executable + set_target_properties(UnitTestsExecutable PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${TESTBIN_DIRECTORY} + ) + # Add the test to CTest, we're setting the working directory to testbin # because we're copying the input files there for now add_test(NAME UnitTests COMMAND UnitTestsExecutable WORKING_DIRECTORY ${TESTBIN_DIRECTORY}) diff --git a/Code/Source/cvOneDOptionsLegacySerializer.cxx b/Code/Source/cvOneDOptionsLegacySerializer.cxx index 7f65ec2..5b00767 100644 --- a/Code/Source/cvOneDOptionsLegacySerializer.cxx +++ b/Code/Source/cvOneDOptionsLegacySerializer.cxx @@ -43,7 +43,7 @@ namespace cvOneD{ // ====================== // READ SINGLE MODEL FILE // ====================== -void readSingleOptionsLegacyFormat(string inputFile, cvOneDOptions* opts, cvStringVec includedFiles){ +void readOptionsLegacyFormat(string inputFile, cvOneDOptions* opts){ // Message printf("\n"); @@ -345,23 +345,6 @@ void readSingleOptionsLegacyFormat(string inputFile, cvOneDOptions* opts, cvStri }catch(...){ throw cvException("ERROR: Invalid MATERIAL Format.\n"); } - }else if(upper_string(tokenizedString[0]) == std::string("INCLUDE")){ - // Check if the file is active - if(upper_string(tokenizedString[2]) == "TRUE"){ - doInclude = true; - }else if(upper_string(tokenizedString[2]) == "FALSE"){ - doInclude = false; - }else{ - throw cvException(string("ERROR: Invalid INCLUDE switch format. Line " + to_string(lineCount) + "\n").c_str()); - } - try{ - // If active include file in list - if(doInclude){ - includedFiles.push_back(tokenizedString[1]); - } - }catch(...){ - throw cvException(string("ERROR: Invalid INCLUDE Format. Line " + to_string(lineCount) + "\n").c_str()); - } }else if((tokenizedString.size() == 0)||(tokenizedString[0].at(0) == '#')||(tokenizedString[0].find_first_not_of(' ') == std::string::npos)){ // printf("Found Blank.\n"); // COMMENT OR BLANK LINE: DO NOTHING @@ -380,25 +363,4 @@ void readSingleOptionsLegacyFormat(string inputFile, cvOneDOptions* opts, cvStri infile.close(); } -void readNestedOptionsLegacyFormat(string inputFile, cvOneDOptions* opts){ - - // List of included Files - cvStringVec includedFiles; - string currentFile; - - // Read First File - readSingleOptionsLegacyFormat(inputFile,opts,includedFiles); - - //Read Nested Files - while(includedFiles.size() > 0){ - - // Get the first file Name - currentFile = includedFiles[0]; - // Delete the First element - includedFiles.erase(includedFiles.begin()); - // Read the file and store new included files - readSingleOptionsLegacyFormat(inputFile,opts,includedFiles); - } -} - } // namespace cvOneD \ No newline at end of file diff --git a/Code/Source/cvOneDOptionsLegacySerializer.h b/Code/Source/cvOneDOptionsLegacySerializer.h index e7913c7..f9f95c8 100644 --- a/Code/Source/cvOneDOptionsLegacySerializer.h +++ b/Code/Source/cvOneDOptionsLegacySerializer.h @@ -38,9 +38,7 @@ using namespace std; namespace cvOneD{ -void readSingleOptionsLegacyFormat(string inputFile, cvOneDOptions* opts, cvStringVec includedFiles); - -void readNestedOptionsLegacyFormat(string inputFile, cvOneDOptions* opts); +void readOptionsLegacyFormat(string inputFile, cvOneDOptions* opts); } // namespace cvOneD diff --git a/Code/Source/main.cxx b/Code/Source/main.cxx index 436a62f..9ab5564 100644 --- a/Code/Source/main.cxx +++ b/Code/Source/main.cxx @@ -299,30 +299,14 @@ void createAndRunModel(cvOneDOptions* opts){ delete [] inletCurveValue; } -// ====================== -// READ SINGLE MODEL FILE -// ====================== -void readModelFile(string inputFile, cvOneDOptions* opts, cvStringVec includedFiles){ - cvOneD::readSingleOptionsLegacyFormat(inputFile,opts,includedFiles); -} - -// ==================== -// READ MODEL FROM FILE -// ==================== -void readModel(string inputFile, cvOneDOptions* opts){ - cvOneD::readNestedOptionsLegacyFormat(inputFile,opts); -} - // ============== // RUN ONEDSOLVER // ============== void runOneDSolver(string inputFile){ - // Create Options + // Create options from legacy format file cvOneDOptions* opts = new cvOneDOptions(); - - // Read Model From File - readModel(inputFile,opts); + cvOneD::readOptionsLegacyFormat(inputFile,opts); // Model Checking opts->check(); diff --git a/Tests/UnitTests/ExampleTest.cxx b/Tests/UnitTests/ExampleTest.cxx index 948733e..4c313e2 100644 --- a/Tests/UnitTests/ExampleTest.cxx +++ b/Tests/UnitTests/ExampleTest.cxx @@ -2,6 +2,8 @@ #include #include +// This test verifies that the unit test framework is available and +// that we can access files in the "TestFiles" directory. TEST(ExampleTest, ExampleTest) { const std::string filePath = "TestFiles/ExampleFile.txt"; std::ifstream file(filePath); diff --git a/Tests/UnitTests/TestFiles/BifurcationR.in b/Tests/UnitTests/TestFiles/BifurcationR.in new file mode 100644 index 0000000..0088dda --- /dev/null +++ b/Tests/UnitTests/TestFiles/BifurcationR.in @@ -0,0 +1,107 @@ +# Modelled from aortic bifurcation test case from +# Xiao, N., Alastruey, J., Figueroa, C.A. A systematic comparison between 1-D and 3-D hemodynamics in compliant arterial models. Int J Numer Meth Bio, 2014; 30:204-231 + +MODEL results_bifurcation_R_ + +NODE 0 0.0 0.0 0.0 +NODE 1 0.0 0.0 -8.6 +NODE 2 0.0 -3.25280917510326 -7.85297602634594 +NODE 3 0.0 3.25280917510326 -7.85297602634594 + +JOINT JOINT1 1 INSEGS OUTSEGS +JOINTINLET INSEGS 1 0 +JOINTOUTLET OUTSEGS 2 1 2 + +SEGMENT seg0 0 8.6 50 0 1 2.32352192659501 2.32352192659501 0.0 MAT1 NONE 0.0 0 0 NOBOUND NONE +SEGMENT seg1 1 8.5 50 1 3 1.13097335529233 1.13097335529233 0.0 MAT1 NONE 0.0 0 0 RESISTANCE R_VALS +SEGMENT seg2 2 8.5 50 1 2 1.13097335529233 1.13097335529233 0.0 MAT1 NONE 0.0 0 0 RESISTANCE R_VALS + +DATATABLE R_VALS LIST +0.0 991.36 +ENDDATATABLE + +DATATABLE STEADY_FLOW LIST +0.0 7.985 +1.0 7.985 +ENDDATATABLE + +DATATABLE PULS_FLOW LIST +0.0 0.0 +0.019668108360095 -4.11549971450822 +0.055247073448669 -7.16517105402019 +0.089913757381125 -1.16130916560675 +0.113633067440174 9.27967911020849 +0.133703252874754 20.4428655623545 +0.150124313684865 32.8883708080991 +0.162896249870507 43.9606301469767 +0.173843623743914 54.6495650354764 +0.186615559929556 67.3593157640345 +0.204861183051901 79.6320314281343 +0.234784004972547 86.0097422945109 +0.26872086398011 78.7468811589053 +0.294264736351393 64.7319679994526 +0.314334921785973 52.023318573387 +0.32893142028385 41.4040199668672 +0.34535248109396 29.7287601931208 +0.363598104216306 17.3938305987427 +0.380019165026417 6.42196693062957 +0.396440225836527 -5.11194051566898 +0.422389556499419 -18.901261909892 +0.455347523345814 -23.8602212809087 +0.496182965572016 -18.2411583475954 +0.533485128399922 -10.4792705793446 +0.575247332435512 -4.00466780439001 +0.640931575675956 -2.79835885098868 +0.682440368279291 -5.30400645008189 +0.726077816913567 -8.52809746163143 +0.762721110017611 -9.03919130533637 +0.802405340308712 -7.30944473123762 +0.846498929521047 -5.55578067364513 +0.885944229033165 -5.87317544184486 +0.925563296384544 -7.14939949266443 +0.968258054490832 -6.10169723438909 +1.00949316274733 -4.10721983893183 +1.05237037708484 -3.31554531122748 +1.087 -1.77083891076532 +ENDDATATABLE + + +# Ref Pressure 1333.22*85 where 85 is in mmHg +# Rigid for now, but can be elastic with the following parameters: +# h_0 = 1.032mm, E_0 = 500 kPa, h_1 = h_2 = 0.72 mm, E_1 = E_2 = 700 kPa +MATERIAL MAT1 OLUFSEN 1.06 0.04 0 2.0 1.0e15 -20 1e9 + +SOLVEROPTIONS 0.001087 50 1000 2 STEADY_FLOW FLOW 1.0e-6 1 1 + +OUTPUT TEXT + + +# analytical solution + +# parameters +# viscosity mu 0.04 +# seg0 length L_0 8.6 +# seg0 radius r_0 0.86 +# seg1 length L_1 8.5 (same as seg2 length) +# seg1 radius r_1 0.6 (same as seg2 radius) +# resistance BC R 991.36 +# steady Inlet Flow Q_0 7.985 +# Distal pressure Pd 0 + +# reference solution +# assumes no pressure losses at bifurcation and purely parallel resistances +# total 1D model resistance Rtot = (R_0 + 0.5*(R_2 + R) +# Pressure at junction P_0_out = P_1_in = P_2_in + +# Results to be checked +# P_0_in 3997.46433118937 +# P_0_out 3984.67700709878 +# P_1_in 3984.67700709878 (same as P_2_in) +# P_1_out 3958.0048 (same as P_2_out because symmetric) +# Q_1_in 3.9925 (same as Q_1_out) + + + + + + diff --git a/Tests/UnitTests/TestLegacySerializer.cxx b/Tests/UnitTests/TestLegacySerializer.cxx index e35598e..48f27e8 100644 --- a/Tests/UnitTests/TestLegacySerializer.cxx +++ b/Tests/UnitTests/TestLegacySerializer.cxx @@ -5,12 +5,319 @@ #include "cvOneDOptions.h" #include "cvOneDOptionsLegacySerializer.h" -// Test case for the add function -TEST(LegacySerializer, ParseSimpleFile) { - // Path to the file in the TestFiles directory - const std::string filePath = "TestFiles/SimpleArtery.in"; +cvOneDOptions simpleArteryOptions() { + cvOneDOptions options; - std::ifstream file(filePath); + // MODEL + options.modelName = "simpleArtery_Res_"; + options.modelNameDefined = true; - file.close(); + // NODE + options.nodeName = {"0", "1"}; + options.nodeXcoord = {0.0, 0.0}; + options.nodeYcoord = {0.0, 0.0}; + options.nodeZcoord = {0.0, -20.0}; + + // JOINT + options.jointName = {}; + options.jointNode = {}; + options.jointInletName = {}; + options.jointOutletName = {}; + + // JOINTINLET AND JOINTOUTLET + options.jointInletListNames = {}; + options.jointInletListNumber = {}; + options.jointInletList = {}; + options.jointOutletListNames = {}; + options.jointOutletListNumber = {}; + options.jointOutletList = {}; + + // SEGMENT + options.segmentName = {"ARTERY"}; + options.segmentID = {0}; + options.segmentLength = {20.0}; + options.segmentTotEls = {50}; + options.segmentInNode = {0}; + options.segmentOutNode = {1}; + options.segmentInInletArea = {2.0}; + options.segmentInOutletArea = {2.0}; + options.segmentInFlow = {0.0}; + options.segmentMatName = {"MAT1"}; + options.segmentLossType = {"NONE"}; + options.segmentBranchAngle = {0.0}; + options.segmentUpstreamSegment = {0}; + options.segmentBranchSegment = {0}; + options.segmentBoundType = {"RESISTANCE"}; + options.segmentDataTableName = {"RESTABLE"}; + + // DATATABLE + options.dataTableName = {"RESTABLE", "INLETDATA"}; + options.dataTableType = {"LIST", "LIST"}; + options.dataTableVals = { + {0.0, 100.0}, // RESTABLE + {0.0, 200.0, 10.0, 200.0} // INLETDATA + }; + + // MATERIAL + options.materialName = {"MAT1"}; + options.materialType = {"OLUFSEN"}; + options.materialDensity = {1.06}; + options.materialViscosity = {0.04}; + options.materialPRef = {113324.0}; + options.materialExponent = {1.0}; + options.materialParam1 = {2.0e7}; + options.materialParam2 = {-22.5267}; + options.materialParam3 = {8.65e5}; + + // SOLVER OPTIONS + options.timeStep = 0.01; + options.stepSize = 10; + options.maxStep = 1000; + options.quadPoints = 4; + options.inletDataTableName = "INLETDATA"; + options.boundaryType = "FLOW"; + options.convergenceTolerance = 1.0e-6; + options.useIV = 1; + options.useStab = 1; + options.outputType = "TEXT"; + options.solverOptionDefined = true; + + return options; +} + +cvOneDOptions bifurcationOptions() { + cvOneDOptions options; + + // MODEL + options.modelName = "results_bifurcation_R_"; + options.modelNameDefined = true; + + // NODE + options.nodeName = {"0", "1", "2", "3"}; + options.nodeXcoord = {0.0, 0.0, 0.0, 0.0}; + options.nodeYcoord = {0.0, 0.0, -3.25280917510326, 3.25280917510326}; + options.nodeZcoord = {0.0, -8.6, -7.85297602634594, -7.85297602634594}; + + // JOINT DATA + options.jointName = {"JOINT1"}; + options.jointNode = {"1"}; + + // The joint XYZ coords are not recorded. + // TODO: it's likely going to make sense to refacotr + // these in the future. + options.jointXcoord = {}; + options.jointYcoord = {}; + options.jointZcoord = {}; + + // Inlet and Outlet Connections for the Joint + options.jointInletName = {"INSEGS"}; + options.jointOutletName = {"OUTSEGS"}; + + // Inlet and Outlet Lists + options.jointInletListNames = {"INSEGS"}; + options.jointInletListNumber = {1}; + options.jointInletList = {{0}}; + + options.jointOutletListNames = {"OUTSEGS"}; + options.jointOutletListNumber = {2}; + options.jointOutletList = {{1, 2}}; + + // SEGMENT + options.segmentName = {"seg0", "seg1", "seg2"}; + options.segmentID = {0, 1, 2}; + options.segmentLength = {8.6, 8.5, 8.5}; + options.segmentTotEls = {50, 50, 50}; + options.segmentInNode = {0, 1, 1}; + options.segmentOutNode = {1, 3, 2}; + options.segmentInInletArea = {2.32352192659501, 1.13097335529233, 1.13097335529233}; + options.segmentInOutletArea = {2.32352192659501, 1.13097335529233, 1.13097335529233}; + options.segmentInFlow = {0.0, 0.0, 0.0}; + options.segmentMatName = {"MAT1", "MAT1", "MAT1"}; + options.segmentLossType = {"NONE", "NONE", "NONE"}; + options.segmentBranchAngle = {0.0, 0.0, 0.0}; + options.segmentUpstreamSegment = {0, 0, 0}; + options.segmentBranchSegment = {0, 0, 0}; + options.segmentBoundType = {"NOBOUND", "RESISTANCE", "RESISTANCE"}; + options.segmentDataTableName = {"NONE", "R_VALS", "R_VALS"}; + + // DATATABLE + options.dataTableName = {"R_VALS", "STEADY_FLOW", "PULS_FLOW"}; + options.dataTableType = {"LIST", "LIST", "LIST"}; + options.dataTableVals = { + {0.0, 991.36}, // R_VALS + {0.0, 7.985, 1.0, 7.985}, // STEADY_FLOW + { + 0.0, 0.0, + 0.019668108360095, -4.11549971450822, + 0.055247073448669, -7.16517105402019, + 0.089913757381125, -1.16130916560675, + 0.113633067440174, 9.27967911020849, + 0.133703252874754, 20.4428655623545, + 0.150124313684865, 32.8883708080991, + 0.162896249870507, 43.9606301469767, + 0.173843623743914, 54.6495650354764, + 0.186615559929556, 67.3593157640345, + 0.204861183051901, 79.6320314281343, + 0.234784004972547, 86.0097422945109, + 0.26872086398011, 78.7468811589053, + 0.294264736351393, 64.7319679994526, + 0.314334921785973, 52.023318573387, + 0.32893142028385, 41.4040199668672, + 0.34535248109396, 29.7287601931208, + 0.363598104216306, 17.3938305987427, + 0.380019165026417, 6.42196693062957, + 0.396440225836527, -5.11194051566898, + 0.422389556499419, -18.901261909892, + 0.455347523345814, -23.8602212809087, + 0.496182965572016, -18.2411583475954, + 0.533485128399922, -10.4792705793446, + 0.575247332435512, -4.00466780439001, + 0.640931575675956, -2.79835885098868, + 0.682440368279291, -5.30400645008189, + 0.726077816913567, -8.52809746163143, + 0.762721110017611, -9.03919130533637, + 0.802405340308712, -7.30944473123762, + 0.846498929521047, -5.55578067364513, + 0.885944229033165, -5.87317544184486, + 0.925563296384544, -7.14939949266443, + 0.968258054490832, -6.10169723438909, + 1.00949316274733, -4.10721983893183, + 1.05237037708484, -3.31554531122748, + 1.087, -1.77083891076532 + } // PULS_FLOW + }; + + // MATERIAL + options.materialName = {"MAT1"}; + options.materialType = {"OLUFSEN"}; + options.materialDensity = {1.06}; + options.materialViscosity = {0.04}; + options.materialPRef = {0}; + options.materialExponent = {2.0}; + options.materialParam1 = {1.0e15}; + options.materialParam2 = {-20}; + options.materialParam3 = {1e9}; + + // SOLVER OPTIONS + options.timeStep = 0.001087; + options.stepSize = 50; + options.maxStep = 1000; + options.quadPoints = 2; + options.inletDataTableName = "STEADY_FLOW"; + options.boundaryType = "FLOW"; + options.convergenceTolerance = 1.0e-6; + options.useIV = 1; + options.useStab = 1; + options.outputType = "TEXT"; + options.solverOptionDefined = true; + + return options; +} + +void expectEqOptions(const cvOneDOptions& actual, const cvOneDOptions& expected) { + // Compare modelName + EXPECT_EQ(expected.modelName, actual.modelName); + EXPECT_EQ(expected.modelNameDefined, actual.modelNameDefined); + + // Compare node data + EXPECT_EQ(expected.nodeName, actual.nodeName); + EXPECT_EQ(expected.nodeXcoord, actual.nodeXcoord); + EXPECT_EQ(expected.nodeYcoord, actual.nodeYcoord); + EXPECT_EQ(expected.nodeZcoord, actual.nodeZcoord); + + // Compare joint data + EXPECT_EQ(expected.jointName, actual.jointName); + EXPECT_EQ(expected.jointNode, actual.jointNode); + EXPECT_EQ(expected.jointXcoord, actual.jointXcoord); + EXPECT_EQ(expected.jointYcoord, actual.jointYcoord); + EXPECT_EQ(expected.jointZcoord, actual.jointZcoord); + EXPECT_EQ(expected.jointInletName, actual.jointInletName); + EXPECT_EQ(expected.jointOutletName, actual.jointOutletName); + + // Compare joint inlet and outlet lists + EXPECT_EQ(expected.jointInletListNames, actual.jointInletListNames); + EXPECT_EQ(expected.jointInletListNumber, actual.jointInletListNumber); + EXPECT_EQ(expected.jointInletList, actual.jointInletList); + EXPECT_EQ(expected.jointOutletListNames, actual.jointOutletListNames); + EXPECT_EQ(expected.jointOutletListNumber, actual.jointOutletListNumber); + EXPECT_EQ(expected.jointOutletList, actual.jointOutletList); + + // Compare material data + EXPECT_EQ(expected.materialName, actual.materialName); + EXPECT_EQ(expected.materialType, actual.materialType); + EXPECT_EQ(expected.materialDensity, actual.materialDensity); + EXPECT_EQ(expected.materialViscosity, actual.materialViscosity); + EXPECT_EQ(expected.materialPRef, actual.materialPRef); + EXPECT_EQ(expected.materialExponent, actual.materialExponent); + EXPECT_EQ(expected.materialParam1, actual.materialParam1); + EXPECT_EQ(expected.materialParam2, actual.materialParam2); + EXPECT_EQ(expected.materialParam3, actual.materialParam3); + + // Compare data table information + EXPECT_EQ(expected.dataTableName, actual.dataTableName); + EXPECT_EQ(expected.dataTableType, actual.dataTableType); + EXPECT_EQ(expected.dataTableVals, actual.dataTableVals); + + // Compare segment data + EXPECT_EQ(expected.segmentName, actual.segmentName); + EXPECT_EQ(expected.segmentID, actual.segmentID); + EXPECT_EQ(expected.segmentLength, actual.segmentLength); + EXPECT_EQ(expected.segmentTotEls, actual.segmentTotEls); + EXPECT_EQ(expected.segmentInNode, actual.segmentInNode); + EXPECT_EQ(expected.segmentOutNode, actual.segmentOutNode); + EXPECT_EQ(expected.segmentInInletArea, actual.segmentInInletArea); + EXPECT_EQ(expected.segmentInOutletArea, actual.segmentInOutletArea); + EXPECT_EQ(expected.segmentInFlow, actual.segmentInFlow); + EXPECT_EQ(expected.segmentMatName, actual.segmentMatName); + EXPECT_EQ(expected.segmentLossType, actual.segmentLossType); + EXPECT_EQ(expected.segmentBranchAngle, actual.segmentBranchAngle); + EXPECT_EQ(expected.segmentUpstreamSegment, actual.segmentUpstreamSegment); + EXPECT_EQ(expected.segmentBranchSegment, actual.segmentBranchSegment); + EXPECT_EQ(expected.segmentBoundType, actual.segmentBoundType); + EXPECT_EQ(expected.segmentDataTableName, actual.segmentDataTableName); + + // Compare solver options + EXPECT_EQ(expected.timeStep, actual.timeStep); + EXPECT_EQ(expected.stepSize, actual.stepSize); + EXPECT_EQ(expected.maxStep, actual.maxStep); + EXPECT_EQ(expected.quadPoints, actual.quadPoints); + EXPECT_EQ(expected.inletDataTableName, actual.inletDataTableName); + EXPECT_EQ(expected.boundaryType, actual.boundaryType); + EXPECT_EQ(expected.convergenceTolerance, actual.convergenceTolerance); + EXPECT_EQ(expected.useIV, actual.useIV); + EXPECT_EQ(expected.useStab, actual.useStab); + EXPECT_EQ(expected.solverOptionDefined, actual.solverOptionDefined); + // For now, we're not going to verify the outputType. Why not? Because, currently + // the legacy serializer does not record the outputType. Instead, it stores it + // in the global settings. + // + // A more consistent behavior would be to store it within the options like the + // other settings and update clients to use it. Once that's done, we can test it here + // in a consistent way. +} + +struct LegacySerializerTestParams { + std::string filePath; + cvOneDOptions expOptions; +}; + +class LegacySerializerTest : public ::testing::TestWithParam {}; + +INSTANTIATE_TEST_SUITE_P( + TestReadOptionsLegacyFormat, + LegacySerializerTest, + ::testing::Values( + LegacySerializerTestParams{"TestFiles/BifurcationR.in", bifurcationOptions()}, + LegacySerializerTestParams{"TestFiles/SimpleArtery.in", simpleArteryOptions()} + ) +); + +TEST_P(LegacySerializerTest, ParseSimpleFile) { + // Test parameters + const auto& params = GetParam(); + + // Execute and verify + cvOneDOptions actOptions; + cvOneD::readOptionsLegacyFormat(params.filePath, &actOptions); + expectEqOptions(actOptions, params.expOptions); } From 8a2d431ec69971ca2e69c8fc1a80d536dc2a5919 Mon Sep 17 00:00:00 2001 From: arsLibera Date: Sat, 8 Feb 2025 14:26:29 -0500 Subject: [PATCH 4/4] upgrade .yml to upload-artifact@v4 --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b88752b..d77a6bf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,7 +31,7 @@ jobs: cpack cp oneD*.pkg ../svOneDSolver-macOS.pkg - name: Upload artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: macOS Installer path: svOneDSolver-macOS.pkg @@ -67,7 +67,7 @@ jobs: cpack cp oneD*.deb ../svOneDSolver-Ubuntu.deb - name: Upload artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Ubuntu Installer path: svOneDSolver-Ubuntu.deb @@ -104,7 +104,7 @@ jobs: # C:\cygwin64\bin\bash.exe -c "./build_windows_github.bat" # cp Release/sim*.msi ../svOneDSolver-Windows.msi # - name: Upload artifact -# uses: actions/upload-artifact@v3 +# uses: actions/upload-artifact@v4 # with: # name: Windows Installer # path: svOneDSolver-Windows.msi