diff --git a/.github/workflows/codechecks.yml b/.github/workflows/codechecks.yml index 50b90de9d..f5b8ee7af 100644 --- a/.github/workflows/codechecks.yml +++ b/.github/workflows/codechecks.yml @@ -5,7 +5,7 @@ jobs: clang-format: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install dependencies run: | sudo apt install clang-format diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index f8bb537ca..468303e0e 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -7,7 +7,7 @@ jobs: documentation: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Make build directory run: mkdir docs/build - name: Build doxygen documentation diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2598e078b..a764539f4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,18 +10,18 @@ jobs: os: [ubuntu-20.04, ubuntu-22.04, macos-11, macos-12] fail-fast: false steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install ubuntu dependencies - if: ${{ matrix.os == 'ubuntu-*' }} - run: sudo apt update && sudo apt install build-essential cmake + if: startsWith(matrix.os, 'ubuntu') + run: sudo apt update && sudo apt install build-essential cmake lcov - name: Install svZeroDPlus run: | - conda create -n zerod python=3.9 + conda create -n zerod python=3.11.4 conda run -n zerod pip install -e ".[dev]" - name: Test the build run: | cd tests - conda run -n zerod pytest + conda run -n zerod pytest -v --durations=0 - name: Build using CMake run: | mkdir Release @@ -38,4 +38,20 @@ jobs: cd test_01 ./svZeroD_interface_test01 ../../../../Release ../../test_01/svzerod_3Dcoupling.json cd ../test_02 - ./svZeroD_interface_test02 ../../../../Release ../../test_02/svzerod_tuned.json \ No newline at end of file + ./svZeroD_interface_test02 ../../../../Release ../../test_02/svzerod_tuned.json + - name: Generate code coverage + if: startsWith(matrix.os, 'ubuntu-22.04') + run: | + cd Release + cmake -DENABLE_COVERAGE=ON .. + cmake --build . + cd ../tests + conda run -n zerod pytest -v --durations=0 --coverage + cd ../Release + make coverage + - name: Save coverage report + if: startsWith(matrix.os, 'ubuntu-22.04') + uses: actions/upload-artifact@v3 + with: + name: coverage_report + path: Release/coverage \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 33a393952..a4641c2a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,28 @@ set(CMAKE_CXX_STANDARD_REQUIRED True) project(svZeroDSolver) include(FetchContent) +# ----------------------------------------------------------------------------- +# Enable code coverage +# ----------------------------------------------------------------------------- +set(ENABLE_COVERAGE OFF CACHE BOOL "Enable code coverage") +# coverage +if(ENABLE_COVERAGE) + # set compiler flags + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -coverage -g") + + # find required tools + find_program(LCOV lcov REQUIRED) + find_program(GENHTML genhtml REQUIRED) + + # add coverage target + add_custom_target(coverage + # gather data + COMMAND ${LCOV} --directory . --capture --output-file coverage.info --ignore-errors gcov --exclude '/usr/include/*' --exclude '/usr/lib/*' --exclude '*/_deps/*' + # generate report + COMMAND ${GENHTML} --demangle-cpp -o coverage coverage.info + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +endif() + # ----------------------------------------------------------------------------- # Fetch Eigen # ----------------------------------------------------------------------------- diff --git a/src/helpers/endswith.hpp b/src/helpers/endswith.hpp deleted file mode 100644 index 7abe83228..000000000 --- a/src/helpers/endswith.hpp +++ /dev/null @@ -1,57 +0,0 @@ -// 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. -/** - * @file endswith.hpp - * @brief HELPERS::endswith source file - */ -#ifndef SVZERODSOLVER_HELPERS_ENDSWITH_HPP_ -#define SVZERODSOLVER_HELPERS_ENDSWITH_HPP_ - -#include - -namespace HELPERS { -/** - * @brief Check if a string ends with the letters of another string - * - * @param str The string to check for the specified suffix - * @param suffix The suffix the string should be checked for - * @return true if strings ends with the suffix, otherwise false - */ -bool endswith(const std::string &str, const std::string &suffix) { - if (str.length() >= suffix.length()) { - return (0 == str.compare(str.length() - suffix.length(), suffix.length(), - suffix)); - } else { - return false; - } -} -} // namespace HELPERS - -#endif // SVZERODSOLVER_HELPERS_ENDSWITH_HPP_ \ No newline at end of file diff --git a/src/helpers/startswith.hpp b/src/helpers/startswith.hpp deleted file mode 100644 index 77fe03987..000000000 --- a/src/helpers/startswith.hpp +++ /dev/null @@ -1,53 +0,0 @@ -// 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. -/** - * @file startswith.hpp - * @brief HELPERS::startswith source file - */ -#ifndef SVZERODSOLVER_HELPERS_STARTSWITH_HPP_ -#define SVZERODSOLVER_HELPERS_STARTSWITH_HPP_ - -#include - -namespace HELPERS { -/** - * @brief Check if a string starts with the letters of another string - * - * @param str The string to check for the specified prefix - * @param prefix The prefix the string should be checked for - * @return true if strings starts with the prefix, otherwise false - */ -bool startswith(const std::string &str, const std::string &prefix) { - return str.size() >= prefix.size() && - str.compare(0, prefix.size(), prefix) == 0; -} -} // namespace HELPERS - -#endif // SVZERODSOLVER_HELPERS_STARTSWITH_HPP_ \ No newline at end of file diff --git a/src/interface/interface.h b/src/interface/interface.h index 05de7cd51..1ddd5c2a4 100644 --- a/src/interface/interface.h +++ b/src/interface/interface.h @@ -3,7 +3,6 @@ #include "algebra/sparsesystem.hpp" #include "algebra/state.hpp" #include "helpers/debug.hpp" -#include "helpers/endswith.hpp" #include "io/configreader.hpp" #include "io/csvwriter.hpp" #include "model/model.hpp" diff --git a/src/io/configreader.hpp b/src/io/configreader.hpp index 1b00ae76e..261259735 100644 --- a/src/io/configreader.hpp +++ b/src/io/configreader.hpp @@ -40,7 +40,6 @@ #include #include "../helpers/debug.hpp" -#include "../helpers/startswith.hpp" #include "../model/model.hpp" namespace IO { diff --git a/src/io/csvwriter.hpp b/src/io/csvwriter.hpp index 244be40ff..c46b79ae7 100644 --- a/src/io/csvwriter.hpp +++ b/src/io/csvwriter.hpp @@ -39,7 +39,6 @@ #include #include "../algebra/state.hpp" -#include "../helpers/startswith.hpp" #include "../model/model.hpp" namespace IO { diff --git a/src/optimize/calibrate.hpp b/src/optimize/calibrate.hpp index 0a5913f2b..d76415969 100644 --- a/src/optimize/calibrate.hpp +++ b/src/optimize/calibrate.hpp @@ -40,7 +40,6 @@ #include #include "helpers/debug.hpp" -#include "helpers/endswith.hpp" #include "levenbergmarquardtoptimizer.hpp" #include "model/model.hpp" diff --git a/src/solve/solver.hpp b/src/solve/solver.hpp index f1d16424a..505ce99e9 100644 --- a/src/solve/solver.hpp +++ b/src/solve/solver.hpp @@ -35,7 +35,6 @@ #include "algebra/integrator.hpp" #include "algebra/state.hpp" #include "helpers/debug.hpp" -#include "helpers/endswith.hpp" #include "io/configreader.hpp" #include "io/csvwriter.hpp" #include "model/model.hpp" diff --git a/tests/cases/pulsatileFlow_R_RCR_derivative.json b/tests/cases/pulsatileFlow_R_RCR_derivative.json new file mode 100644 index 000000000..7baab438c --- /dev/null +++ b/tests/cases/pulsatileFlow_R_RCR_derivative.json @@ -0,0 +1,264 @@ +{ + "description": { + "description of test case" : "pulsatile flow -> R -> RCR", + "analytical results" : [ "Notes:", + "Let t0 = start of cardiac cycle", + "Notice that the inflow waveform has a period of 1 second", + "Boundary conditions:", + "inlet:", + "flow rate: Q(t) = 2.5*SIN(2*PI()*t) + 2.2", + "outlet:", + "RCR + distal pressure: Rp = 1000, Rd = 1000, Pd = 0", + "Solutions:", + "inlet flow (at time = t0) = Q(t = 0) = 2.2", + "outlet flow (at time = t0) = Q(t = 0) = 2.2", + "outlet pressure (at time = t0) = Q(t = 0) * (Rp + Rd) + Pd = 2.2 * (1000 + 1000) + 0 = 4400", + "inlet pressure (at time = t0) = outlet pressure + Q(t = 0) * R_poiseuille = 4400 + 2.2 * 100 = 4620" + ] + }, + "boundary_conditions": [ + { + "bc_name": "INFLOW", + "bc_type": "FLOW", + "bc_values": { + "Q": [ + 2.2, + 2.35697629882328, + 2.51333308391076, + 2.66845328646431, + 2.82172471791214, + 2.97254248593737, + 3.1203113817117, + 3.26444822891268, + 3.40438418525429, + 3.53956698744749, + 3.66946313073118, + 3.79355997437172, + 3.91136776482172, + 4.02242156855353, + 4.12628310693947, + 4.22254248593737, + 4.31081981375504, + 4.39076670010966, + 4.46206763116505, + 4.52444121472063, + 4.57764129073788, + 4.62145790282158, + 4.65571812682172, + 4.6802867532862, + 4.69506682107068, + 4.7, + 4.69506682107068, + 4.6802867532862, + 4.65571812682172, + 4.62145790282158, + 4.57764129073788, + 4.52444121472063, + 4.46206763116505, + 4.39076670010966, + 4.31081981375504, + 4.22254248593737, + 4.12628310693947, + 4.02242156855353, + 3.91136776482172, + 3.79355997437172, + 3.66946313073118, + 3.53956698744749, + 3.40438418525429, + 3.26444822891268, + 3.1203113817117, + 2.97254248593737, + 2.82172471791214, + 2.66845328646431, + 2.51333308391076, + 2.35697629882328, + 2.2, + 2.04302370117672, + 1.88666691608924, + 1.73154671353569, + 1.57827528208786, + 1.42745751406263, + 1.2796886182883, + 1.13555177108732, + 0.995615814745713, + 0.860433012552509, + 0.730536869268818, + 0.606440025628276, + 0.488632235178278, + 0.377578431446472, + 0.273716893060527, + 0.177457514062632, + 0.089180186244962, + 0.009233299890341, + -0.06206763116505, + -0.124441214720628, + -0.177641290737883, + -0.221457902821577, + -0.255718126821721, + -0.280286753286194, + -0.295066821070679, + -0.3, + -0.295066821070679, + -0.280286753286195, + -0.255718126821721, + -0.221457902821578, + -0.177641290737884, + -0.124441214720628, + -0.062067631165049, + 0.009233299890342, + 0.089180186244962, + 0.177457514062631, + 0.273716893060526, + 0.377578431446471, + 0.488632235178278, + 0.606440025628276, + 0.730536869268817, + 0.860433012552509, + 0.995615814745712, + 1.13555177108732, + 1.27968861828831, + 1.42745751406263, + 1.57827528208786, + 1.73154671353569, + 1.88666691608924, + 2.04302370117672, + 2.2 + ], + "t": [ + 0.0, + 0.01, + 0.02, + 0.03, + 0.04, + 0.05, + 0.06, + 0.07, + 0.08, + 0.09, + 0.1, + 0.11, + 0.12, + 0.13, + 0.14, + 0.15, + 0.16, + 0.17, + 0.18, + 0.19, + 0.2, + 0.21, + 0.22, + 0.23, + 0.24, + 0.25, + 0.26, + 0.27, + 0.28, + 0.29, + 0.3, + 0.31, + 0.32, + 0.33, + 0.34, + 0.35, + 0.36, + 0.37, + 0.38, + 0.39, + 0.4, + 0.41, + 0.42, + 0.43, + 0.44, + 0.45, + 0.46, + 0.47, + 0.48, + 0.49, + 0.5, + 0.51, + 0.52, + 0.53, + 0.54, + 0.55, + 0.56, + 0.57, + 0.58, + 0.59, + 0.6, + 0.61, + 0.62, + 0.63, + 0.64, + 0.65, + 0.66, + 0.67, + 0.68, + 0.69, + 0.7, + 0.71, + 0.72, + 0.73, + 0.74, + 0.75, + 0.76, + 0.77, + 0.78, + 0.79, + 0.8, + 0.81, + 0.82, + 0.83, + 0.84, + 0.85, + 0.86, + 0.87, + 0.88, + 0.89, + 0.9, + 0.91, + 0.92, + 0.93, + 0.94, + 0.95, + 0.96, + 0.97, + 0.98, + 0.99, + 1.0 + ] + } + }, + { + "bc_name": "OUT", + "bc_type": "RCR", + "bc_values": { + "C": 0.0001, + "Pd": 0.0, + "Rd": 1000.0, + "Rp": 1000.0 + } + } + ], + "junctions": [], + "simulation_parameters": { + "number_of_cardiac_cycles": 10, + "number_of_time_pts_per_cardiac_cycle": 201, + "output_derivative": true + }, + "vessels": [ + { + "boundary_conditions": { + "inlet": "INFLOW", + "outlet": "OUT" + }, + "vessel_id": 0, + "vessel_length": 10.0, + "vessel_name": "branch0_seg0", + "zero_d_element_type": "BloodVessel", + "zero_d_element_values": { + "R_poiseuille": 100.0 + } + } + ] +} \ No newline at end of file diff --git a/tests/cases/pulsatileFlow_R_RCR_derivative_variable.json b/tests/cases/pulsatileFlow_R_RCR_derivative_variable.json new file mode 100644 index 000000000..1b6abee7d --- /dev/null +++ b/tests/cases/pulsatileFlow_R_RCR_derivative_variable.json @@ -0,0 +1,265 @@ +{ + "description": { + "description of test case" : "pulsatile flow -> R -> RCR", + "analytical results" : [ "Notes:", + "Let t0 = start of cardiac cycle", + "Notice that the inflow waveform has a period of 1 second", + "Boundary conditions:", + "inlet:", + "flow rate: Q(t) = 2.5*SIN(2*PI()*t) + 2.2", + "outlet:", + "RCR + distal pressure: Rp = 1000, Rd = 1000, Pd = 0", + "Solutions:", + "inlet flow (at time = t0) = Q(t = 0) = 2.2", + "outlet flow (at time = t0) = Q(t = 0) = 2.2", + "outlet pressure (at time = t0) = Q(t = 0) * (Rp + Rd) + Pd = 2.2 * (1000 + 1000) + 0 = 4400", + "inlet pressure (at time = t0) = outlet pressure + Q(t = 0) * R_poiseuille = 4400 + 2.2 * 100 = 4620" + ] + }, + "boundary_conditions": [ + { + "bc_name": "INFLOW", + "bc_type": "FLOW", + "bc_values": { + "Q": [ + 2.2, + 2.35697629882328, + 2.51333308391076, + 2.66845328646431, + 2.82172471791214, + 2.97254248593737, + 3.1203113817117, + 3.26444822891268, + 3.40438418525429, + 3.53956698744749, + 3.66946313073118, + 3.79355997437172, + 3.91136776482172, + 4.02242156855353, + 4.12628310693947, + 4.22254248593737, + 4.31081981375504, + 4.39076670010966, + 4.46206763116505, + 4.52444121472063, + 4.57764129073788, + 4.62145790282158, + 4.65571812682172, + 4.6802867532862, + 4.69506682107068, + 4.7, + 4.69506682107068, + 4.6802867532862, + 4.65571812682172, + 4.62145790282158, + 4.57764129073788, + 4.52444121472063, + 4.46206763116505, + 4.39076670010966, + 4.31081981375504, + 4.22254248593737, + 4.12628310693947, + 4.02242156855353, + 3.91136776482172, + 3.79355997437172, + 3.66946313073118, + 3.53956698744749, + 3.40438418525429, + 3.26444822891268, + 3.1203113817117, + 2.97254248593737, + 2.82172471791214, + 2.66845328646431, + 2.51333308391076, + 2.35697629882328, + 2.2, + 2.04302370117672, + 1.88666691608924, + 1.73154671353569, + 1.57827528208786, + 1.42745751406263, + 1.2796886182883, + 1.13555177108732, + 0.995615814745713, + 0.860433012552509, + 0.730536869268818, + 0.606440025628276, + 0.488632235178278, + 0.377578431446472, + 0.273716893060527, + 0.177457514062632, + 0.089180186244962, + 0.009233299890341, + -0.06206763116505, + -0.124441214720628, + -0.177641290737883, + -0.221457902821577, + -0.255718126821721, + -0.280286753286194, + -0.295066821070679, + -0.3, + -0.295066821070679, + -0.280286753286195, + -0.255718126821721, + -0.221457902821578, + -0.177641290737884, + -0.124441214720628, + -0.062067631165049, + 0.009233299890342, + 0.089180186244962, + 0.177457514062631, + 0.273716893060526, + 0.377578431446471, + 0.488632235178278, + 0.606440025628276, + 0.730536869268817, + 0.860433012552509, + 0.995615814745712, + 1.13555177108732, + 1.27968861828831, + 1.42745751406263, + 1.57827528208786, + 1.73154671353569, + 1.88666691608924, + 2.04302370117672, + 2.2 + ], + "t": [ + 0.0, + 0.01, + 0.02, + 0.03, + 0.04, + 0.05, + 0.06, + 0.07, + 0.08, + 0.09, + 0.1, + 0.11, + 0.12, + 0.13, + 0.14, + 0.15, + 0.16, + 0.17, + 0.18, + 0.19, + 0.2, + 0.21, + 0.22, + 0.23, + 0.24, + 0.25, + 0.26, + 0.27, + 0.28, + 0.29, + 0.3, + 0.31, + 0.32, + 0.33, + 0.34, + 0.35, + 0.36, + 0.37, + 0.38, + 0.39, + 0.4, + 0.41, + 0.42, + 0.43, + 0.44, + 0.45, + 0.46, + 0.47, + 0.48, + 0.49, + 0.5, + 0.51, + 0.52, + 0.53, + 0.54, + 0.55, + 0.56, + 0.57, + 0.58, + 0.59, + 0.6, + 0.61, + 0.62, + 0.63, + 0.64, + 0.65, + 0.66, + 0.67, + 0.68, + 0.69, + 0.7, + 0.71, + 0.72, + 0.73, + 0.74, + 0.75, + 0.76, + 0.77, + 0.78, + 0.79, + 0.8, + 0.81, + 0.82, + 0.83, + 0.84, + 0.85, + 0.86, + 0.87, + 0.88, + 0.89, + 0.9, + 0.91, + 0.92, + 0.93, + 0.94, + 0.95, + 0.96, + 0.97, + 0.98, + 0.99, + 1.0 + ] + } + }, + { + "bc_name": "OUT", + "bc_type": "RCR", + "bc_values": { + "C": 0.0001, + "Pd": 0.0, + "Rd": 1000.0, + "Rp": 1000.0 + } + } + ], + "junctions": [], + "simulation_parameters": { + "number_of_cardiac_cycles": 10, + "number_of_time_pts_per_cardiac_cycle": 201, + "output_derivative": true, + "output_variable_based": true + }, + "vessels": [ + { + "boundary_conditions": { + "inlet": "INFLOW", + "outlet": "OUT" + }, + "vessel_id": 0, + "vessel_length": 10.0, + "vessel_name": "branch0_seg0", + "zero_d_element_type": "BloodVessel", + "zero_d_element_values": { + "R_poiseuille": 100.0 + } + } + ] +} diff --git a/tests/cases/pulsatileFlow_R_RCR_mean.json b/tests/cases/pulsatileFlow_R_RCR_mean.json new file mode 100644 index 000000000..4608036e4 --- /dev/null +++ b/tests/cases/pulsatileFlow_R_RCR_mean.json @@ -0,0 +1,265 @@ +{ + "description": { + "description of test case" : "pulsatile flow -> R -> RCR", + "analytical results" : [ "Notes:", + "Let t0 = start of cardiac cycle", + "Notice that the inflow waveform has a period of 1 second", + "Boundary conditions:", + "inlet:", + "flow rate: Q(t) = 2.5*SIN(2*PI()*t) + 2.2", + "outlet:", + "RCR + distal pressure: Rp = 1000, Rd = 1000, Pd = 0", + "Solutions:", + "inlet flow (at time = t0) = Q(t = 0) = 2.2", + "outlet flow (at time = t0) = Q(t = 0) = 2.2", + "outlet pressure (at time = t0) = Q(t = 0) * (Rp + Rd) + Pd = 2.2 * (1000 + 1000) + 0 = 4400", + "inlet pressure (at time = t0) = outlet pressure + Q(t = 0) * R_poiseuille = 4400 + 2.2 * 100 = 4620" + ] + }, + "boundary_conditions": [ + { + "bc_name": "INFLOW", + "bc_type": "FLOW", + "bc_values": { + "Q": [ + 2.2, + 2.35697629882328, + 2.51333308391076, + 2.66845328646431, + 2.82172471791214, + 2.97254248593737, + 3.1203113817117, + 3.26444822891268, + 3.40438418525429, + 3.53956698744749, + 3.66946313073118, + 3.79355997437172, + 3.91136776482172, + 4.02242156855353, + 4.12628310693947, + 4.22254248593737, + 4.31081981375504, + 4.39076670010966, + 4.46206763116505, + 4.52444121472063, + 4.57764129073788, + 4.62145790282158, + 4.65571812682172, + 4.6802867532862, + 4.69506682107068, + 4.7, + 4.69506682107068, + 4.6802867532862, + 4.65571812682172, + 4.62145790282158, + 4.57764129073788, + 4.52444121472063, + 4.46206763116505, + 4.39076670010966, + 4.31081981375504, + 4.22254248593737, + 4.12628310693947, + 4.02242156855353, + 3.91136776482172, + 3.79355997437172, + 3.66946313073118, + 3.53956698744749, + 3.40438418525429, + 3.26444822891268, + 3.1203113817117, + 2.97254248593737, + 2.82172471791214, + 2.66845328646431, + 2.51333308391076, + 2.35697629882328, + 2.2, + 2.04302370117672, + 1.88666691608924, + 1.73154671353569, + 1.57827528208786, + 1.42745751406263, + 1.2796886182883, + 1.13555177108732, + 0.995615814745713, + 0.860433012552509, + 0.730536869268818, + 0.606440025628276, + 0.488632235178278, + 0.377578431446472, + 0.273716893060527, + 0.177457514062632, + 0.089180186244962, + 0.009233299890341, + -0.06206763116505, + -0.124441214720628, + -0.177641290737883, + -0.221457902821577, + -0.255718126821721, + -0.280286753286194, + -0.295066821070679, + -0.3, + -0.295066821070679, + -0.280286753286195, + -0.255718126821721, + -0.221457902821578, + -0.177641290737884, + -0.124441214720628, + -0.062067631165049, + 0.009233299890342, + 0.089180186244962, + 0.177457514062631, + 0.273716893060526, + 0.377578431446471, + 0.488632235178278, + 0.606440025628276, + 0.730536869268817, + 0.860433012552509, + 0.995615814745712, + 1.13555177108732, + 1.27968861828831, + 1.42745751406263, + 1.57827528208786, + 1.73154671353569, + 1.88666691608924, + 2.04302370117672, + 2.2 + ], + "t": [ + 0.0, + 0.01, + 0.02, + 0.03, + 0.04, + 0.05, + 0.06, + 0.07, + 0.08, + 0.09, + 0.1, + 0.11, + 0.12, + 0.13, + 0.14, + 0.15, + 0.16, + 0.17, + 0.18, + 0.19, + 0.2, + 0.21, + 0.22, + 0.23, + 0.24, + 0.25, + 0.26, + 0.27, + 0.28, + 0.29, + 0.3, + 0.31, + 0.32, + 0.33, + 0.34, + 0.35, + 0.36, + 0.37, + 0.38, + 0.39, + 0.4, + 0.41, + 0.42, + 0.43, + 0.44, + 0.45, + 0.46, + 0.47, + 0.48, + 0.49, + 0.5, + 0.51, + 0.52, + 0.53, + 0.54, + 0.55, + 0.56, + 0.57, + 0.58, + 0.59, + 0.6, + 0.61, + 0.62, + 0.63, + 0.64, + 0.65, + 0.66, + 0.67, + 0.68, + 0.69, + 0.7, + 0.71, + 0.72, + 0.73, + 0.74, + 0.75, + 0.76, + 0.77, + 0.78, + 0.79, + 0.8, + 0.81, + 0.82, + 0.83, + 0.84, + 0.85, + 0.86, + 0.87, + 0.88, + 0.89, + 0.9, + 0.91, + 0.92, + 0.93, + 0.94, + 0.95, + 0.96, + 0.97, + 0.98, + 0.99, + 1.0 + ] + } + }, + { + "bc_name": "OUT", + "bc_type": "RCR", + "bc_values": { + "C": 0.0001, + "Pd": 0.0, + "Rd": 1000.0, + "Rp": 1000.0 + } + } + ], + "junctions": [], + "simulation_parameters": { + "number_of_cardiac_cycles": 10, + "number_of_time_pts_per_cardiac_cycle": 201, + "output_all_cycles": true, + "output_mean_only": true + }, + "vessels": [ + { + "boundary_conditions": { + "inlet": "INFLOW", + "outlet": "OUT" + }, + "vessel_id": 0, + "vessel_length": 10.0, + "vessel_name": "branch0_seg0", + "zero_d_element_type": "BloodVessel", + "zero_d_element_values": { + "R_poiseuille": 100.0 + } + } + ] +} \ No newline at end of file diff --git a/tests/cases/pulsatileFlow_R_RCR_mean_derivative.json b/tests/cases/pulsatileFlow_R_RCR_mean_derivative.json new file mode 100644 index 000000000..9c380df7b --- /dev/null +++ b/tests/cases/pulsatileFlow_R_RCR_mean_derivative.json @@ -0,0 +1,266 @@ +{ + "description": { + "description of test case" : "pulsatile flow -> R -> RCR", + "analytical results" : [ "Notes:", + "Let t0 = start of cardiac cycle", + "Notice that the inflow waveform has a period of 1 second", + "Boundary conditions:", + "inlet:", + "flow rate: Q(t) = 2.5*SIN(2*PI()*t) + 2.2", + "outlet:", + "RCR + distal pressure: Rp = 1000, Rd = 1000, Pd = 0", + "Solutions:", + "inlet flow (at time = t0) = Q(t = 0) = 2.2", + "outlet flow (at time = t0) = Q(t = 0) = 2.2", + "outlet pressure (at time = t0) = Q(t = 0) * (Rp + Rd) + Pd = 2.2 * (1000 + 1000) + 0 = 4400", + "inlet pressure (at time = t0) = outlet pressure + Q(t = 0) * R_poiseuille = 4400 + 2.2 * 100 = 4620" + ] + }, + "boundary_conditions": [ + { + "bc_name": "INFLOW", + "bc_type": "FLOW", + "bc_values": { + "Q": [ + 2.2, + 2.35697629882328, + 2.51333308391076, + 2.66845328646431, + 2.82172471791214, + 2.97254248593737, + 3.1203113817117, + 3.26444822891268, + 3.40438418525429, + 3.53956698744749, + 3.66946313073118, + 3.79355997437172, + 3.91136776482172, + 4.02242156855353, + 4.12628310693947, + 4.22254248593737, + 4.31081981375504, + 4.39076670010966, + 4.46206763116505, + 4.52444121472063, + 4.57764129073788, + 4.62145790282158, + 4.65571812682172, + 4.6802867532862, + 4.69506682107068, + 4.7, + 4.69506682107068, + 4.6802867532862, + 4.65571812682172, + 4.62145790282158, + 4.57764129073788, + 4.52444121472063, + 4.46206763116505, + 4.39076670010966, + 4.31081981375504, + 4.22254248593737, + 4.12628310693947, + 4.02242156855353, + 3.91136776482172, + 3.79355997437172, + 3.66946313073118, + 3.53956698744749, + 3.40438418525429, + 3.26444822891268, + 3.1203113817117, + 2.97254248593737, + 2.82172471791214, + 2.66845328646431, + 2.51333308391076, + 2.35697629882328, + 2.2, + 2.04302370117672, + 1.88666691608924, + 1.73154671353569, + 1.57827528208786, + 1.42745751406263, + 1.2796886182883, + 1.13555177108732, + 0.995615814745713, + 0.860433012552509, + 0.730536869268818, + 0.606440025628276, + 0.488632235178278, + 0.377578431446472, + 0.273716893060527, + 0.177457514062632, + 0.089180186244962, + 0.009233299890341, + -0.06206763116505, + -0.124441214720628, + -0.177641290737883, + -0.221457902821577, + -0.255718126821721, + -0.280286753286194, + -0.295066821070679, + -0.3, + -0.295066821070679, + -0.280286753286195, + -0.255718126821721, + -0.221457902821578, + -0.177641290737884, + -0.124441214720628, + -0.062067631165049, + 0.009233299890342, + 0.089180186244962, + 0.177457514062631, + 0.273716893060526, + 0.377578431446471, + 0.488632235178278, + 0.606440025628276, + 0.730536869268817, + 0.860433012552509, + 0.995615814745712, + 1.13555177108732, + 1.27968861828831, + 1.42745751406263, + 1.57827528208786, + 1.73154671353569, + 1.88666691608924, + 2.04302370117672, + 2.2 + ], + "t": [ + 0.0, + 0.01, + 0.02, + 0.03, + 0.04, + 0.05, + 0.06, + 0.07, + 0.08, + 0.09, + 0.1, + 0.11, + 0.12, + 0.13, + 0.14, + 0.15, + 0.16, + 0.17, + 0.18, + 0.19, + 0.2, + 0.21, + 0.22, + 0.23, + 0.24, + 0.25, + 0.26, + 0.27, + 0.28, + 0.29, + 0.3, + 0.31, + 0.32, + 0.33, + 0.34, + 0.35, + 0.36, + 0.37, + 0.38, + 0.39, + 0.4, + 0.41, + 0.42, + 0.43, + 0.44, + 0.45, + 0.46, + 0.47, + 0.48, + 0.49, + 0.5, + 0.51, + 0.52, + 0.53, + 0.54, + 0.55, + 0.56, + 0.57, + 0.58, + 0.59, + 0.6, + 0.61, + 0.62, + 0.63, + 0.64, + 0.65, + 0.66, + 0.67, + 0.68, + 0.69, + 0.7, + 0.71, + 0.72, + 0.73, + 0.74, + 0.75, + 0.76, + 0.77, + 0.78, + 0.79, + 0.8, + 0.81, + 0.82, + 0.83, + 0.84, + 0.85, + 0.86, + 0.87, + 0.88, + 0.89, + 0.9, + 0.91, + 0.92, + 0.93, + 0.94, + 0.95, + 0.96, + 0.97, + 0.98, + 0.99, + 1.0 + ] + } + }, + { + "bc_name": "OUT", + "bc_type": "RCR", + "bc_values": { + "C": 0.0001, + "Pd": 0.0, + "Rd": 1000.0, + "Rp": 1000.0 + } + } + ], + "junctions": [], + "simulation_parameters": { + "number_of_cardiac_cycles": 10, + "number_of_time_pts_per_cardiac_cycle": 201, + "output_all_cycles": true, + "output_mean_only": true, + "output_derivative": true + }, + "vessels": [ + { + "boundary_conditions": { + "inlet": "INFLOW", + "outlet": "OUT" + }, + "vessel_id": 0, + "vessel_length": 10.0, + "vessel_name": "branch0_seg0", + "zero_d_element_type": "BloodVessel", + "zero_d_element_values": { + "R_poiseuille": 100.0 + } + } + ] +} \ No newline at end of file diff --git a/tests/cases/pulsatileFlow_R_RCR_mean_derivative_variable.json b/tests/cases/pulsatileFlow_R_RCR_mean_derivative_variable.json new file mode 100644 index 000000000..3dfa9d095 --- /dev/null +++ b/tests/cases/pulsatileFlow_R_RCR_mean_derivative_variable.json @@ -0,0 +1,267 @@ +{ + "description": { + "description of test case" : "pulsatile flow -> R -> RCR", + "analytical results" : [ "Notes:", + "Let t0 = start of cardiac cycle", + "Notice that the inflow waveform has a period of 1 second", + "Boundary conditions:", + "inlet:", + "flow rate: Q(t) = 2.5*SIN(2*PI()*t) + 2.2", + "outlet:", + "RCR + distal pressure: Rp = 1000, Rd = 1000, Pd = 0", + "Solutions:", + "inlet flow (at time = t0) = Q(t = 0) = 2.2", + "outlet flow (at time = t0) = Q(t = 0) = 2.2", + "outlet pressure (at time = t0) = Q(t = 0) * (Rp + Rd) + Pd = 2.2 * (1000 + 1000) + 0 = 4400", + "inlet pressure (at time = t0) = outlet pressure + Q(t = 0) * R_poiseuille = 4400 + 2.2 * 100 = 4620" + ] + }, + "boundary_conditions": [ + { + "bc_name": "INFLOW", + "bc_type": "FLOW", + "bc_values": { + "Q": [ + 2.2, + 2.35697629882328, + 2.51333308391076, + 2.66845328646431, + 2.82172471791214, + 2.97254248593737, + 3.1203113817117, + 3.26444822891268, + 3.40438418525429, + 3.53956698744749, + 3.66946313073118, + 3.79355997437172, + 3.91136776482172, + 4.02242156855353, + 4.12628310693947, + 4.22254248593737, + 4.31081981375504, + 4.39076670010966, + 4.46206763116505, + 4.52444121472063, + 4.57764129073788, + 4.62145790282158, + 4.65571812682172, + 4.6802867532862, + 4.69506682107068, + 4.7, + 4.69506682107068, + 4.6802867532862, + 4.65571812682172, + 4.62145790282158, + 4.57764129073788, + 4.52444121472063, + 4.46206763116505, + 4.39076670010966, + 4.31081981375504, + 4.22254248593737, + 4.12628310693947, + 4.02242156855353, + 3.91136776482172, + 3.79355997437172, + 3.66946313073118, + 3.53956698744749, + 3.40438418525429, + 3.26444822891268, + 3.1203113817117, + 2.97254248593737, + 2.82172471791214, + 2.66845328646431, + 2.51333308391076, + 2.35697629882328, + 2.2, + 2.04302370117672, + 1.88666691608924, + 1.73154671353569, + 1.57827528208786, + 1.42745751406263, + 1.2796886182883, + 1.13555177108732, + 0.995615814745713, + 0.860433012552509, + 0.730536869268818, + 0.606440025628276, + 0.488632235178278, + 0.377578431446472, + 0.273716893060527, + 0.177457514062632, + 0.089180186244962, + 0.009233299890341, + -0.06206763116505, + -0.124441214720628, + -0.177641290737883, + -0.221457902821577, + -0.255718126821721, + -0.280286753286194, + -0.295066821070679, + -0.3, + -0.295066821070679, + -0.280286753286195, + -0.255718126821721, + -0.221457902821578, + -0.177641290737884, + -0.124441214720628, + -0.062067631165049, + 0.009233299890342, + 0.089180186244962, + 0.177457514062631, + 0.273716893060526, + 0.377578431446471, + 0.488632235178278, + 0.606440025628276, + 0.730536869268817, + 0.860433012552509, + 0.995615814745712, + 1.13555177108732, + 1.27968861828831, + 1.42745751406263, + 1.57827528208786, + 1.73154671353569, + 1.88666691608924, + 2.04302370117672, + 2.2 + ], + "t": [ + 0.0, + 0.01, + 0.02, + 0.03, + 0.04, + 0.05, + 0.06, + 0.07, + 0.08, + 0.09, + 0.1, + 0.11, + 0.12, + 0.13, + 0.14, + 0.15, + 0.16, + 0.17, + 0.18, + 0.19, + 0.2, + 0.21, + 0.22, + 0.23, + 0.24, + 0.25, + 0.26, + 0.27, + 0.28, + 0.29, + 0.3, + 0.31, + 0.32, + 0.33, + 0.34, + 0.35, + 0.36, + 0.37, + 0.38, + 0.39, + 0.4, + 0.41, + 0.42, + 0.43, + 0.44, + 0.45, + 0.46, + 0.47, + 0.48, + 0.49, + 0.5, + 0.51, + 0.52, + 0.53, + 0.54, + 0.55, + 0.56, + 0.57, + 0.58, + 0.59, + 0.6, + 0.61, + 0.62, + 0.63, + 0.64, + 0.65, + 0.66, + 0.67, + 0.68, + 0.69, + 0.7, + 0.71, + 0.72, + 0.73, + 0.74, + 0.75, + 0.76, + 0.77, + 0.78, + 0.79, + 0.8, + 0.81, + 0.82, + 0.83, + 0.84, + 0.85, + 0.86, + 0.87, + 0.88, + 0.89, + 0.9, + 0.91, + 0.92, + 0.93, + 0.94, + 0.95, + 0.96, + 0.97, + 0.98, + 0.99, + 1.0 + ] + } + }, + { + "bc_name": "OUT", + "bc_type": "RCR", + "bc_values": { + "C": 0.0001, + "Pd": 0.0, + "Rd": 1000.0, + "Rp": 1000.0 + } + } + ], + "junctions": [], + "simulation_parameters": { + "number_of_cardiac_cycles": 10, + "number_of_time_pts_per_cardiac_cycle": 201, + "output_all_cycles": true, + "output_mean_only": true, + "output_derivative": true, + "output_variable_based": true + }, + "vessels": [ + { + "boundary_conditions": { + "inlet": "INFLOW", + "outlet": "OUT" + }, + "vessel_id": 0, + "vessel_length": 10.0, + "vessel_name": "branch0_seg0", + "zero_d_element_type": "BloodVessel", + "zero_d_element_values": { + "R_poiseuille": 100.0 + } + } + ] +} diff --git a/tests/cases/pulsatileFlow_R_RCR_mean_variable.json b/tests/cases/pulsatileFlow_R_RCR_mean_variable.json new file mode 100644 index 000000000..1abbdb381 --- /dev/null +++ b/tests/cases/pulsatileFlow_R_RCR_mean_variable.json @@ -0,0 +1,266 @@ +{ + "description": { + "description of test case" : "pulsatile flow -> R -> RCR", + "analytical results" : [ "Notes:", + "Let t0 = start of cardiac cycle", + "Notice that the inflow waveform has a period of 1 second", + "Boundary conditions:", + "inlet:", + "flow rate: Q(t) = 2.5*SIN(2*PI()*t) + 2.2", + "outlet:", + "RCR + distal pressure: Rp = 1000, Rd = 1000, Pd = 0", + "Solutions:", + "inlet flow (at time = t0) = Q(t = 0) = 2.2", + "outlet flow (at time = t0) = Q(t = 0) = 2.2", + "outlet pressure (at time = t0) = Q(t = 0) * (Rp + Rd) + Pd = 2.2 * (1000 + 1000) + 0 = 4400", + "inlet pressure (at time = t0) = outlet pressure + Q(t = 0) * R_poiseuille = 4400 + 2.2 * 100 = 4620" + ] + }, + "boundary_conditions": [ + { + "bc_name": "INFLOW", + "bc_type": "FLOW", + "bc_values": { + "Q": [ + 2.2, + 2.35697629882328, + 2.51333308391076, + 2.66845328646431, + 2.82172471791214, + 2.97254248593737, + 3.1203113817117, + 3.26444822891268, + 3.40438418525429, + 3.53956698744749, + 3.66946313073118, + 3.79355997437172, + 3.91136776482172, + 4.02242156855353, + 4.12628310693947, + 4.22254248593737, + 4.31081981375504, + 4.39076670010966, + 4.46206763116505, + 4.52444121472063, + 4.57764129073788, + 4.62145790282158, + 4.65571812682172, + 4.6802867532862, + 4.69506682107068, + 4.7, + 4.69506682107068, + 4.6802867532862, + 4.65571812682172, + 4.62145790282158, + 4.57764129073788, + 4.52444121472063, + 4.46206763116505, + 4.39076670010966, + 4.31081981375504, + 4.22254248593737, + 4.12628310693947, + 4.02242156855353, + 3.91136776482172, + 3.79355997437172, + 3.66946313073118, + 3.53956698744749, + 3.40438418525429, + 3.26444822891268, + 3.1203113817117, + 2.97254248593737, + 2.82172471791214, + 2.66845328646431, + 2.51333308391076, + 2.35697629882328, + 2.2, + 2.04302370117672, + 1.88666691608924, + 1.73154671353569, + 1.57827528208786, + 1.42745751406263, + 1.2796886182883, + 1.13555177108732, + 0.995615814745713, + 0.860433012552509, + 0.730536869268818, + 0.606440025628276, + 0.488632235178278, + 0.377578431446472, + 0.273716893060527, + 0.177457514062632, + 0.089180186244962, + 0.009233299890341, + -0.06206763116505, + -0.124441214720628, + -0.177641290737883, + -0.221457902821577, + -0.255718126821721, + -0.280286753286194, + -0.295066821070679, + -0.3, + -0.295066821070679, + -0.280286753286195, + -0.255718126821721, + -0.221457902821578, + -0.177641290737884, + -0.124441214720628, + -0.062067631165049, + 0.009233299890342, + 0.089180186244962, + 0.177457514062631, + 0.273716893060526, + 0.377578431446471, + 0.488632235178278, + 0.606440025628276, + 0.730536869268817, + 0.860433012552509, + 0.995615814745712, + 1.13555177108732, + 1.27968861828831, + 1.42745751406263, + 1.57827528208786, + 1.73154671353569, + 1.88666691608924, + 2.04302370117672, + 2.2 + ], + "t": [ + 0.0, + 0.01, + 0.02, + 0.03, + 0.04, + 0.05, + 0.06, + 0.07, + 0.08, + 0.09, + 0.1, + 0.11, + 0.12, + 0.13, + 0.14, + 0.15, + 0.16, + 0.17, + 0.18, + 0.19, + 0.2, + 0.21, + 0.22, + 0.23, + 0.24, + 0.25, + 0.26, + 0.27, + 0.28, + 0.29, + 0.3, + 0.31, + 0.32, + 0.33, + 0.34, + 0.35, + 0.36, + 0.37, + 0.38, + 0.39, + 0.4, + 0.41, + 0.42, + 0.43, + 0.44, + 0.45, + 0.46, + 0.47, + 0.48, + 0.49, + 0.5, + 0.51, + 0.52, + 0.53, + 0.54, + 0.55, + 0.56, + 0.57, + 0.58, + 0.59, + 0.6, + 0.61, + 0.62, + 0.63, + 0.64, + 0.65, + 0.66, + 0.67, + 0.68, + 0.69, + 0.7, + 0.71, + 0.72, + 0.73, + 0.74, + 0.75, + 0.76, + 0.77, + 0.78, + 0.79, + 0.8, + 0.81, + 0.82, + 0.83, + 0.84, + 0.85, + 0.86, + 0.87, + 0.88, + 0.89, + 0.9, + 0.91, + 0.92, + 0.93, + 0.94, + 0.95, + 0.96, + 0.97, + 0.98, + 0.99, + 1.0 + ] + } + }, + { + "bc_name": "OUT", + "bc_type": "RCR", + "bc_values": { + "C": 0.0001, + "Pd": 0.0, + "Rd": 1000.0, + "Rp": 1000.0 + } + } + ], + "junctions": [], + "simulation_parameters": { + "number_of_cardiac_cycles": 10, + "number_of_time_pts_per_cardiac_cycle": 201, + "output_all_cycles": true, + "output_mean_only": true, + "output_variable_based": true + }, + "vessels": [ + { + "boundary_conditions": { + "inlet": "INFLOW", + "outlet": "OUT" + }, + "vessel_id": 0, + "vessel_length": 10.0, + "vessel_name": "branch0_seg0", + "zero_d_element_type": "BloodVessel", + "zero_d_element_values": { + "R_poiseuille": 100.0 + } + } + ] +} \ No newline at end of file diff --git a/tests/cases/pulsatileFlow_R_RCR_variable.json b/tests/cases/pulsatileFlow_R_RCR_variable.json new file mode 100644 index 000000000..9dd118f36 --- /dev/null +++ b/tests/cases/pulsatileFlow_R_RCR_variable.json @@ -0,0 +1,264 @@ +{ + "description": { + "description of test case" : "pulsatile flow -> R -> RCR", + "analytical results" : [ "Notes:", + "Let t0 = start of cardiac cycle", + "Notice that the inflow waveform has a period of 1 second", + "Boundary conditions:", + "inlet:", + "flow rate: Q(t) = 2.5*SIN(2*PI()*t) + 2.2", + "outlet:", + "RCR + distal pressure: Rp = 1000, Rd = 1000, Pd = 0", + "Solutions:", + "inlet flow (at time = t0) = Q(t = 0) = 2.2", + "outlet flow (at time = t0) = Q(t = 0) = 2.2", + "outlet pressure (at time = t0) = Q(t = 0) * (Rp + Rd) + Pd = 2.2 * (1000 + 1000) + 0 = 4400", + "inlet pressure (at time = t0) = outlet pressure + Q(t = 0) * R_poiseuille = 4400 + 2.2 * 100 = 4620" + ] + }, + "boundary_conditions": [ + { + "bc_name": "INFLOW", + "bc_type": "FLOW", + "bc_values": { + "Q": [ + 2.2, + 2.35697629882328, + 2.51333308391076, + 2.66845328646431, + 2.82172471791214, + 2.97254248593737, + 3.1203113817117, + 3.26444822891268, + 3.40438418525429, + 3.53956698744749, + 3.66946313073118, + 3.79355997437172, + 3.91136776482172, + 4.02242156855353, + 4.12628310693947, + 4.22254248593737, + 4.31081981375504, + 4.39076670010966, + 4.46206763116505, + 4.52444121472063, + 4.57764129073788, + 4.62145790282158, + 4.65571812682172, + 4.6802867532862, + 4.69506682107068, + 4.7, + 4.69506682107068, + 4.6802867532862, + 4.65571812682172, + 4.62145790282158, + 4.57764129073788, + 4.52444121472063, + 4.46206763116505, + 4.39076670010966, + 4.31081981375504, + 4.22254248593737, + 4.12628310693947, + 4.02242156855353, + 3.91136776482172, + 3.79355997437172, + 3.66946313073118, + 3.53956698744749, + 3.40438418525429, + 3.26444822891268, + 3.1203113817117, + 2.97254248593737, + 2.82172471791214, + 2.66845328646431, + 2.51333308391076, + 2.35697629882328, + 2.2, + 2.04302370117672, + 1.88666691608924, + 1.73154671353569, + 1.57827528208786, + 1.42745751406263, + 1.2796886182883, + 1.13555177108732, + 0.995615814745713, + 0.860433012552509, + 0.730536869268818, + 0.606440025628276, + 0.488632235178278, + 0.377578431446472, + 0.273716893060527, + 0.177457514062632, + 0.089180186244962, + 0.009233299890341, + -0.06206763116505, + -0.124441214720628, + -0.177641290737883, + -0.221457902821577, + -0.255718126821721, + -0.280286753286194, + -0.295066821070679, + -0.3, + -0.295066821070679, + -0.280286753286195, + -0.255718126821721, + -0.221457902821578, + -0.177641290737884, + -0.124441214720628, + -0.062067631165049, + 0.009233299890342, + 0.089180186244962, + 0.177457514062631, + 0.273716893060526, + 0.377578431446471, + 0.488632235178278, + 0.606440025628276, + 0.730536869268817, + 0.860433012552509, + 0.995615814745712, + 1.13555177108732, + 1.27968861828831, + 1.42745751406263, + 1.57827528208786, + 1.73154671353569, + 1.88666691608924, + 2.04302370117672, + 2.2 + ], + "t": [ + 0.0, + 0.01, + 0.02, + 0.03, + 0.04, + 0.05, + 0.06, + 0.07, + 0.08, + 0.09, + 0.1, + 0.11, + 0.12, + 0.13, + 0.14, + 0.15, + 0.16, + 0.17, + 0.18, + 0.19, + 0.2, + 0.21, + 0.22, + 0.23, + 0.24, + 0.25, + 0.26, + 0.27, + 0.28, + 0.29, + 0.3, + 0.31, + 0.32, + 0.33, + 0.34, + 0.35, + 0.36, + 0.37, + 0.38, + 0.39, + 0.4, + 0.41, + 0.42, + 0.43, + 0.44, + 0.45, + 0.46, + 0.47, + 0.48, + 0.49, + 0.5, + 0.51, + 0.52, + 0.53, + 0.54, + 0.55, + 0.56, + 0.57, + 0.58, + 0.59, + 0.6, + 0.61, + 0.62, + 0.63, + 0.64, + 0.65, + 0.66, + 0.67, + 0.68, + 0.69, + 0.7, + 0.71, + 0.72, + 0.73, + 0.74, + 0.75, + 0.76, + 0.77, + 0.78, + 0.79, + 0.8, + 0.81, + 0.82, + 0.83, + 0.84, + 0.85, + 0.86, + 0.87, + 0.88, + 0.89, + 0.9, + 0.91, + 0.92, + 0.93, + 0.94, + 0.95, + 0.96, + 0.97, + 0.98, + 0.99, + 1.0 + ] + } + }, + { + "bc_name": "OUT", + "bc_type": "RCR", + "bc_values": { + "C": 0.0001, + "Pd": 0.0, + "Rd": 1000.0, + "Rp": 1000.0 + } + } + ], + "junctions": [], + "simulation_parameters": { + "number_of_cardiac_cycles": 10, + "number_of_time_pts_per_cardiac_cycle": 201, + "output_all_cycles": true + }, + "vessels": [ + { + "boundary_conditions": { + "inlet": "INFLOW", + "outlet": "OUT" + }, + "vessel_id": 0, + "vessel_length": 10.0, + "vessel_name": "branch0_seg0", + "zero_d_element_type": "BloodVessel", + "zero_d_element_values": { + "R_poiseuille": 100.0 + } + } + ] +} \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 000000000..8d7c76372 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,18 @@ +from tempfile import TemporaryDirectory + +import numpy as np +import pytest + + +def pytest_addoption(parser): + """Pass optional argument to test coverage (slower)""" + parser.addoption( + "--coverage", + action="store_true", + default="run tests to generate coverage report", + ) + + +def pytest_configure(config): + """Workaround because store_true doesn't work""" + pytest.coverage = config.option.coverage == True diff --git a/tests/test_calibrator.py b/tests/test_calibrator.py new file mode 100644 index 000000000..180cf225f --- /dev/null +++ b/tests/test_calibrator.py @@ -0,0 +1,60 @@ +import json +import os +import pytest + +import numpy as np + +from .utils import execute_svzerodplus, RTOL_PRES + +this_file_dir = os.path.abspath(os.path.dirname(__file__)) + + +def test_steady_flow_calibration(): + testfile = os.path.join(this_file_dir, "cases", "steadyFlow_calibration.json") + + result, _ = execute_svzerodplus(testfile, "calibrator") + + calibrated_parameters = result["vessels"][0]["zero_d_element_values"] + + assert np.isclose( + np.mean(calibrated_parameters["R_poiseuille"]), 100, rtol=RTOL_PRES + ) + assert np.isclose(np.mean(calibrated_parameters["C"]), 0.0001, rtol=RTOL_PRES) + assert np.isclose(np.mean(calibrated_parameters["L"]), 1.0, rtol=RTOL_PRES) + assert np.isclose( + np.mean(calibrated_parameters["stenosis_coefficient"]), 0.0, rtol=RTOL_PRES + ) + + +@pytest.mark.parametrize("model_id", ["0080_0001", "0104_0001", "0140_2001"]) +def test_calibration_vmr(model_id): + """Test actual models from the vascular model repository.""" + with open( + os.path.join( + this_file_dir, "cases", "vmr", "input", f"{model_id}_calibrate_from_0d.json" + ) + ) as ff: + reference = json.load(ff) + + test = os.path.join( + this_file_dir, "cases", "vmr", "input", f"{model_id}_calibrate_from_0d.json" + ) + + result, _ = execute_svzerodplus(test, "calibrator") + + for i, vessel in enumerate(reference["vessels"]): + for key, value in vessel["zero_d_element_values"].items(): + np.isclose( + result["vessels"][i]["zero_d_element_values"][key], + value, + rtol=RTOL_PRES, + ) + + for i, junction in enumerate(reference["junctions"]): + if "junction_values" in junction: + for key, value in junction["junction_values"].items(): + np.allclose( + result["junctions"][i]["junction_values"][key], + value, + rtol=RTOL_PRES, + ) diff --git a/tests/test_io.py b/tests/test_io.py new file mode 100644 index 000000000..33da18f18 --- /dev/null +++ b/tests/test_io.py @@ -0,0 +1,123 @@ +import numpy as np + +from .utils import run_test_case_by_name, RTOL_FLOW, RTOL_PRES + +# use coarse absolute tolerances for gradient calculation +# we're comparing gradients from gen-alpha in svZeroDPlus with central differences in np.gradient +ATOL = {"f": 0.6, "p": 800.0} +ATOL_MEAN = {"f": 0.01, "p": 20.0} + + +def test_pulsatile_flow_r_rcr_mean(): + # time-dependent results + res_time = run_test_case_by_name("pulsatileFlow_R_RCR") + + # time-averaged results + res_mean = run_test_case_by_name("pulsatileFlow_R_RCR_mean") + + # compare all time-dependent results to their average + tol = {"f": RTOL_FLOW, "p": RTOL_PRES} + for k, res in res_time.items(): + assert np.isclose(np.mean(res[0]), res_mean[k][0][0], rtol=tol[k[0]]) + + +def test_pulsatile_flow_r_rcr_mean_variable(): + # time-dependent results + res_time = run_test_case_by_name( + "pulsatileFlow_R_RCR_variable", output_variable_based=True + ) + + # time-averaged results + res_mean = run_test_case_by_name( + "pulsatileFlow_R_RCR_mean_variable", output_variable_based=True + ) + + # fields and segments to test + fields = ["flow", "pressure"] + segments = {"in": "INFLOW:branch0_seg0", "out": "branch0_seg0:OUT"} + + tol = {"f": RTOL_FLOW, "p": RTOL_PRES} + for f in fields: + for k, v in segments.items(): + rt = np.mean(res_time[f + "_" + k]) + rm = res_mean["y"][res_mean["name"] == f + ":" + v] + assert np.isclose(rt, rm, rtol=tol[f[0]]) + + +def test_pulsatile_flow_r_rcr_derivative(): + results = run_test_case_by_name( + "pulsatileFlow_R_RCR_derivative", output_variable_based=True + ) + + # time step + dt = results["time"][1] - results["time"][0] + + # fields to test + fields = ["pressure_in", "pressure_out", "flow_in", "flow_out"] + for f in fields: + ref = np.gradient(results[f], dt) + res = results["d_" + f] + assert np.all(np.isclose(ref, res, atol=ATOL[f[0]])) + + +def test_pulsatile_flow_r_rcr_derivative_variable(): + results = run_test_case_by_name( + "pulsatileFlow_R_RCR_derivative_variable", output_variable_based=True + ) + + # time step + dt = results["time"][1] - results["time"][0] + + # fields and segments to test + fields = ["flow", "pressure"] + segments = ["INFLOW:branch0_seg0", "branch0_seg0:OUT"] + for f in fields: + for v in segments: + ids = results["name"] == f + ":" + v + ref = np.gradient(results.y[ids], dt) + res = results.ydot[ids] + assert np.all(np.isclose(ref, res, atol=ATOL[f[0]])) + + +def test_pulsatile_flow_r_rcr_mean_derivative(): + # time-dependent results + res_time = run_test_case_by_name("pulsatileFlow_R_RCR", output_variable_based=True) + + # time-averaged results + res_mean = run_test_case_by_name( + "pulsatileFlow_R_RCR_mean_derivative", output_variable_based=True + ) + + # time step + dt = res_time["time"][1] - res_time["time"][0] + + # fields to test + fields = ["pressure_in", "pressure_out", "flow_in", "flow_out"] + for f in fields: + ref = np.mean(np.gradient(res_time[f], dt)) + res = res_mean["d_" + f] + assert np.isclose(ref, res, atol=ATOL_MEAN[f[0]]) + + +def test_pulsatile_flow_r_rcr_mean_derivative_variable(): + # time-dependent results + res_time = run_test_case_by_name( + "pulsatileFlow_R_RCR_variable", output_variable_based=True + ) + + # time-averaged results + res_mean = run_test_case_by_name( + "pulsatileFlow_R_RCR_mean_derivative_variable", output_variable_based=True + ) + + # time step + dt = res_time["time"][1] - res_time["time"][0] + + # fields and segments to test + fields = ["flow", "pressure"] + segments = {"in": "INFLOW:branch0_seg0", "out": "branch0_seg0:OUT"} + for f in fields: + for k, v in segments.items(): + rt = np.mean(np.gradient(res_time[f + "_" + k], dt)) + rm = res_mean["ydot"][res_mean["name"] == f + ":" + v] + assert np.isclose(rt, rm, atol=ATOL_MEAN[f[0]]) diff --git a/tests/test_integration.py b/tests/test_solver.py similarity index 78% rename from tests/test_integration.py rename to tests/test_solver.py index 34a2d28f5..7336f857e 100644 --- a/tests/test_integration.py +++ b/tests/test_solver.py @@ -1,78 +1,6 @@ -import json -import os - import numpy as np -import pytest - -import svzerodplus - -this_file_dir = os.path.abspath(os.path.dirname(__file__)) - -RTOL_PRES = 1.0e-7 -RTOL_FLOW = 1.0e-8 - - -def run_test_case_by_name(name, output_variable_based=False, folder="."): - """Run a test case by its case name. - - Args: - name: Name of the test case. - testdir: Directory for performing the simulation. - """ - testfile = os.path.join(this_file_dir, "cases", folder, name + ".json") - with open(testfile) as ff: - config = json.load(ff) - result = svzerodplus.simulate(config) - - if output_variable_based == False: - output = { - "pressure_in": {}, - "pressure_out": {}, - "flow_in": {}, - "flow_out": {}, - } - - last_seg_id = 0 - - for vessel in config["vessels"]: - name = vessel["vessel_name"] - branch_id, seg_id = name.split("_") - branch_id, seg_id = int(branch_id[6:]), int(seg_id[3:]) - vessel_id = vessel["vessel_id"] - - if seg_id == 0: - output["pressure_in"][branch_id] = np.array( - result[result.name == name]["pressure_in"] - ) - output["flow_in"][branch_id] = np.array( - result[result.name == name]["flow_in"] - ) - output["pressure_out"][branch_id] = np.array( - result[result.name == name]["pressure_out"] - ) - output["flow_out"][branch_id] = np.array( - result[result.name == name]["flow_out"] - ) - elif seg_id > last_seg_id: - output["pressure_out"][branch_id] = np.array( - result[result.name == name]["pressure_out"] - ) - output["flow_out"][branch_id] = np.array( - result[result.name == name]["flow_out"] - ) - - last_seg_id = seg_id - - elif output_variable_based == True: - output = result - - return output - - -def get_result(result_array, field, branch, time_step): - """ "Get results at specific field, branch, branch_node and time step.""" - # extract result - return result_array[field][branch][time_step] + +from .utils import run_test_case_by_name, get_result, RTOL_FLOW, RTOL_PRES def test_steady_flow_R_R(): @@ -483,64 +411,3 @@ def test_coupled_block_heart_with_coronaries(): assert np.isclose( np.amin(aortic_pressure[-50:]), 38.80066561075395, rtol=RTOL_PRES ) # min aortic pressure - - -def test_steady_flow_calibration(): - with open( - os.path.join(this_file_dir, "cases", "steadyFlow_calibration.json") - ) as ff: - config = json.load(ff) - - result = svzerodplus.calibrate(config) - - calibrated_parameters = result["vessels"][0]["zero_d_element_values"] - - assert np.isclose( - np.mean(calibrated_parameters["R_poiseuille"]), 100, rtol=RTOL_PRES - ) - assert np.isclose(np.mean(calibrated_parameters["C"]), 0.0001, rtol=RTOL_PRES) - assert np.isclose(np.mean(calibrated_parameters["L"]), 1.0, rtol=RTOL_PRES) - assert np.isclose( - np.mean(calibrated_parameters["stenosis_coefficient"]), 0.0, rtol=RTOL_PRES - ) - - -@pytest.mark.parametrize("model_id", ["0080_0001", "0104_0001", "0140_2001"]) -def test_calibration_vmr(model_id): - """Test actual models from the vascular model repository.""" - with open( - os.path.join( - this_file_dir, "cases", "vmr", "input", f"{model_id}_calibrate_from_0d.json" - ) - ) as ff: - config = json.load(ff) - - with open( - os.path.join( - this_file_dir, - "cases", - "vmr", - "reference", - f"{model_id}_optimal_from_0d.json", - ) - ) as ff: - reference = json.load(ff) - - result = svzerodplus.calibrate(config) - - for i, vessel in enumerate(reference["vessels"]): - for key, value in vessel["zero_d_element_values"].items(): - np.isclose( - result["vessels"][i]["zero_d_element_values"][key], - value, - rtol=RTOL_PRES, - ) - - for i, junction in enumerate(reference["junctions"]): - if "junction_values" in junction: - for key, value in junction["junction_values"].items(): - np.allclose( - result["junctions"][i]["junction_values"][key], - value, - rtol=RTOL_PRES, - ) diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 000000000..a01f97593 --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,96 @@ +import json +import os +import subprocess +from tempfile import TemporaryDirectory + +import numpy as np +import pandas as pd + +# global boolean to perform coverage testing +# (run executables instead of Python interface, much slower) +from pytest import coverage + +import svzerodplus + +this_file_dir = os.path.abspath(os.path.dirname(__file__)) + +RTOL_PRES = 1.0e-7 +RTOL_FLOW = 1.0e-8 + + +def execute_svzerodplus(testfile, mode): + """Execute svzerodplus (via Python interface or executable). + + Args: + testfile: Path to the input file. + mode: svZeroDPlus application (solver or calibrator). + """ + assert mode in ["solver", "calibrator"], "unknown mode: " + mode + + # read configuration + with open(testfile) as ff: + config = json.load(ff) + + if coverage: + # run via executable (slow) + with TemporaryDirectory() as tempdir: + out_name = os.path.join(tempdir, "out") + exe = os.path.join(this_file_dir, "..", "Release", "svzerod") + subprocess.run([exe + mode, testfile, out_name]) + if mode == "solver": + result = pd.read_csv(out_name) + elif mode == "calibrator": + with open(out_name) as ff: + result = json.load(ff) + else: + # run via Python binding (fast) + if mode == "solver": + result = svzerodplus.simulate(config) + elif mode == "calibrator": + result = svzerodplus.calibrate(config) + + return result, config + + +def run_test_case_by_name(name, output_variable_based=False, folder="."): + """Run a test case by its case name. + + Args: + name: Name of the test case. + testdir: Directory for performing the simulation. + """ + # file name of test case + testfile = os.path.join(this_file_dir, "cases", name + ".json") + + # run test + result, config = execute_svzerodplus(testfile, "solver") + + if not output_variable_based: + output = { + "pressure_in": {}, + "pressure_out": {}, + "flow_in": {}, + "flow_out": {}, + } + + for vessel in config["vessels"]: + name = vessel["vessel_name"] + branch_id, seg_id = name.split("_") + branch_id, seg_id = int(branch_id[6:]), int(seg_id[3:]) + + for f in ["pressure", "flow"]: + for l in ["in"] * (seg_id == 0) + ["out"]: + n = f + "_" + l + ids = result.name == name + output[n][branch_id] = np.array(result[ids][n]) + + else: + output = result + + return output + + +def get_result(result_array, field, branch, time_step): + """ "Get results at specific field, branch, branch_node and time step.""" + # extract result + return result_array[field][branch][time_step]