Skip to content

Commit

Permalink
Add Levenberg-Marquardt model calibration (SimVascular#31)
Browse files Browse the repository at this point in the history
* Split json handling from config reader
Fix Debug build
Set debug messages  to be printed in debug build
Fix deprecation warnings

* Add documentation

* Try to fix ubuntu build problems by fixing simdjson version

* try with CMAKE_POSITION_INDEPENDENT_CODE again

* Adress reviewers comments

* Move block count increment out of loop

* Rename time dependent parameter

* Add block method for model class

* Revise parameter class

* Make elements use parameter IDs

* Fix bug and support more elements

* Support blood vessel junction

* Fix gradient of blood vessel and add calibrator

* Add gradient update to blood vessel junction

* Support normal BC names

* Write calibrated parameter values back into config

* Add support for open loop coronary back

* Add support for resistive junction back

* Clean up

* Fix bugs in calibrator and adapt after cleanup

* Reduce blood vessel system size

* Update bloodvessel junction to new blood vessel system

* Some fixes

* Fix bugs

* Fix stenosis coeff gradient

* Add support for remaining blocks back

* Move determination of cardiac cycle period to model

* Clean up reader

* Use nlohmann json

* Add gradient info to documentation

* Fix calibrator

* Improve python binding

* Adapt solver interface

* Add Levenberg Marquardt optimizer

* Full LM algorithm and fixes for capacitance

* Fix bug with calibrating stenosis coefficient

* Fix bug with capacitance in steady model

* Add termination criteria

* Fix pytest

* Move solver to own class

* New python binding with thread safety

* Dockerfile

* Fix profiling

* Allow installation via pip

* Add reading derivative of initial condition

* Get rid of Jsonhandler

* Dockerize profiling

* Adapt svzerodcalibrator

* Use output last cycle only as default

* Clean up

* Fix interface

* Fix tests

* Setup for new documentation

* update main page

* Add svzerodsolver Guide

* Finalize

* Fix clang-format

* Fix bug

* Try to fix documentation

* Another try

* New try

* fix typo

* fix bug using calibration_parameters

* clang format

* add script to generate calibration problems from test cases

* add VMR model 0075_1001

* Blood vessel junction without internal variables

* exit normally if LM doesn't converge and use sci notation

* add vmr 0d models. tried calibration from 3d, doesn't work. giving up

* Fix bug

* clang format

* Address reviewer's comments

* Typo in error msg

* Make specifying the output file optional

* default cardiac cycle period

* Fix bug

* make cmake compile interface by default

* make specifying output file optional

* added get_block_type

* bug fix: update_parameter_value

* update initial condition reading and other debugging

* interface working again

* clang format

* updated parameter descriptions for heart model

* Add calibration tests using vmr geometries

* remove python script for plotting calibration

---------

Co-authored-by: Jakob Richter <[email protected]>
Co-authored-by: Martin R. Pfaller <[email protected]>
Co-authored-by: Karthik Menon <[email protected]>
  • Loading branch information
4 people authored Sep 1, 2023
1 parent e123e9c commit 0ba4490
Show file tree
Hide file tree
Showing 62 changed files with 242,478 additions and 3,106 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
run: mkdir docs/html
- name: Build doxygen documentation
continue-on-error: true
uses: mattnotmitt/[email protected].4
uses: mattnotmitt/[email protected].5
with:
working-directory: '.'
doxyfile-path: 'docs/cpp/Doxyfile'
Expand Down
10 changes: 2 additions & 8 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,14 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-18.04, ubuntu-20.04, ubuntu-22.04, macos-11, macos-12]
os: [ubuntu-20.04, ubuntu-22.04, macos-11, macos-12]
fail-fast: false
steps:
- uses: actions/checkout@v2
- name: Install ubuntu dependencies
if: ${{ matrix.os == 'ubuntu-*' }}
run: sudo apt update && sudo apt install build-essential cmake
- name: Build svZeroDSolver
run: |
mkdir Release
cd Release
cmake -DCMAKE_BUILD_TYPE=Release ..
cmake --build .
- name: Install test dependencies
- name: Install svZeroDPlus
run: |
conda create -n zerod python=3.9
conda run -n zerod pip install pytest pytest-cov pytest-mock
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,5 @@ docs/python/build
Release*/
Debug*/
externals/
*.so
build
45 changes: 29 additions & 16 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
project(svZeroDSolver)
include(FetchContent)

# -----------------------------------------------------------------------------
# Fetch simdjson
# -----------------------------------------------------------------------------
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
FetchContent_Declare(
simdjson
GIT_REPOSITORY https://github.com/simdjson/simdjson.git
GIT_TAG master
GIT_SHALLOW TRUE)
FetchContent_MakeAvailable(simdjson)

# -----------------------------------------------------------------------------
# Fetch Eigen
# -----------------------------------------------------------------------------
Expand Down Expand Up @@ -44,17 +33,41 @@ if(NOT pybind11_POPULATED)
add_subdirectory(${pybind11_SOURCE_DIR} ${pybind11_BINARY_DIR})
endif()

# -----------------------------------------------------------------------------
# Fetch nlohmann/json
# -----------------------------------------------------------------------------
FetchContent_Declare(
json
URL https://github.com/nlohmann/json/releases/download/v3.11.2/json.tar.xz
)
FetchContent_MakeAvailable(json)

# -----------------------------------------------------------------------------
# Fetch pybind11_json
# -----------------------------------------------------------------------------
FetchContent_Declare(
pybind11_json
GIT_REPOSITORY https://github.com/pybind/pybind11_json.git
GIT_TAG master
)
FetchContent_MakeAvailable(pybind11_json)

# -----------------------------------------------------------------------------
# Set executable and python library
# -----------------------------------------------------------------------------
add_executable(svzerodsolver src/main.cpp)
pybind11_add_module(libsvzerodsolver src/python.cpp)
include_directories(src)
add_executable(svzerodsolver applications/svzerodsolver.cpp)
add_executable(svzerodcalibrator applications/svzerodcalibrator.cpp)
pybind11_add_module(svzerodplus EXCLUDE_FROM_ALL applications/svzerodplus.cpp)
add_subdirectory("src/interface")

# -----------------------------------------------------------------------------
# Link libraries
# -----------------------------------------------------------------------------
target_link_libraries(svzerodsolver PRIVATE Eigen3::Eigen)
target_link_libraries(svzerodsolver PRIVATE simdjson)
target_link_libraries(libsvzerodsolver PRIVATE Eigen3::Eigen)
target_link_libraries(libsvzerodsolver PRIVATE simdjson)
target_link_libraries(svzerodsolver PRIVATE nlohmann_json::nlohmann_json)
target_link_libraries(svzerodcalibrator PRIVATE Eigen3::Eigen)
target_link_libraries(svzerodcalibrator PRIVATE nlohmann_json::nlohmann_json)
target_link_libraries(svzerodplus PRIVATE Eigen3::Eigen)
target_link_libraries(svzerodplus PRIVATE nlohmann_json::nlohmann_json)
target_link_libraries(svzerodplus PRIVATE pybind11_json)
64 changes: 64 additions & 0 deletions applications/svzerodcalibrator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// 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 svzerodcalibrator.cpp
* @brief Main routine for svZeroDCalibrator
*/
#include "optimize/calibrate.hpp"

// Setting scalar type to double
typedef double T;

int main(int argc, char* argv[]) {
DEBUG_MSG("Starting svZeroDCalibrator");

// Get input and output file name
if (argc != 3) {
std::cout
<< "Usage: svzerodcalibrator path/to/config.json path/to/output.json"
<< std::endl;
exit(1);
}

// Get input and output file names
std::string input_file = argv[1];
std::string output_file = argv[2];

// Parse the configuration
DEBUG_MSG("Parse configuration");
std::ifstream ifs(input_file);
const auto& config = nlohmann::json::parse(ifs);

auto output_config = OPT::calibrate<T>(config);

// Write optimized simulation config
std::ofstream o(output_file);
o << std::setw(4) << output_config << std::endl;
}
104 changes: 104 additions & 0 deletions applications/svzerodplus.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// 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 svzerodplus.cpp
* @brief Python interface for svZeroDSolver
*/
#include <pybind11/eigen.h>
#include <pybind11/operators.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>

#include "optimize/calibrate.hpp"
#include "pybind11_json/pybind11_json.hpp"
#include "solve/solver.hpp"

namespace py = pybind11;

PYBIND11_MODULE(svzerodplus, m) {
using Solver = SOLVE::Solver<double>;
m.doc() = "svZeroDSolver";
py::class_<Solver>(m, "Solver")
.def(py::init([](py::dict& config) {
const nlohmann::json& config_json = config;
return Solver(config_json);
}))
.def(py::init([](std::string config_file) {
std::ifstream ifs(config_file);
const auto& config_json = nlohmann::json::parse(ifs);
return Solver(config_json);
}))
.def("copy", [](Solver& solver) { return Solver(solver); })
.def("run", &Solver::run)
.def("get_single_result", &Solver::get_single_result)
.def("get_single_result_avg", &Solver::get_single_result_avg)
.def("update_block_params", &Solver::update_block_params);

m.def("simulate", [](py::dict& config) {
const nlohmann::json& config_json = config;
auto solver = Solver(config_json);
solver.run();
return solver.get_full_result();
});
m.def("calibrate", [](py::dict& config) {
const nlohmann::json& config_json = config;
return OPT::calibrate<double>(config);
});
m.def("run_simulation_cli", []() {
py::module_ sys = py::module_::import("sys");
auto argv = sys.attr("argv").cast<std::vector<std::string>>();
if (argv.size() != 3) {
std::cout
<< "Usage: svzerodsolver path/to/config.json path/to/output.json"
<< std::endl;
exit(1);
}
std::ifstream ifs(argv[1]);
const auto& config = nlohmann::json::parse(ifs);
auto solver = SOLVE::Solver<double>(config);
solver.run();
solver.write_result_to_csv(argv[2]);
});
m.def("run_calibration_cli", []() {
py::module_ sys = py::module_::import("sys");
auto argv = sys.attr("argv").cast<std::vector<std::string>>();
if (argv.size() != 3) {
std::cout
<< "Usage: svzerodcalibrator path/to/config.json path/to/output.json"
<< std::endl;
exit(1);
}
std::ifstream ifs(argv[1]);
const auto& config = nlohmann::json::parse(ifs);
auto output_config = OPT::calibrate<double>(config);
std::ofstream o(argv[2]);
o << std::setw(4) << output_config << std::endl;
});
}
43 changes: 26 additions & 17 deletions src/main.cpp → applications/svzerodsolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/**
* @file main.cpp
* @file svzerodsolver.cpp
* @brief Main routine of svZeroDSolver
*/
#include "main.hpp"
#include "solve/solver.hpp"

/**
*
Expand All @@ -50,28 +50,37 @@
* @param argv Command line arguments
* @return Return code
*/
int main(int argc, char *argv[]) {
int main(int argc, char* argv[]) {
DEBUG_MSG("Starting svZeroDSolver");

// Get input and output file name
if (argc != 3) {
std::cout << "Usage: svzerodsolver path/to/config.json path/to/output.csv"
<< std::endl;
exit(1);
if (argc < 2 && argc > 3) {
std::runtime_error(
"Usage: svzerodsolver path/to/config.json "
"(optional:path/to/output.csv)");
}
std::string input_file = argv[1];
std::string output_file = argv[2];

std::ifstream input_file_stream(input_file);
std::stringstream buffer;
buffer << input_file_stream.rdbuf();
std::string config = buffer.str();
std::string output_file;
if (argc == 3) {
output_file = argv[2];
} else {
// If output file is not provided, default is <path to .json>+"output.csv"
std::size_t end_of_path = input_file.rfind("/");
if (end_of_path == std::string::npos) {
end_of_path = input_file.rfind("\\"); // For Windows paths (?)
if (end_of_path == std::string::npos) {
std::runtime_error("Could not find path to .json file.");
}
}
output_file = input_file.substr(0, end_of_path) + "output.csv";
}

auto output = run(config);
std::ifstream ifs(input_file);
const auto& config = nlohmann::json::parse(ifs);

std::ofstream ofs(output_file);
ofs << output;
ofs.close();
auto solver = SOLVE::Solver<double>(config);
solver.run();
solver.write_result_to_csv(output_file);

return 0;
}
32 changes: 32 additions & 0 deletions container/profiling/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
FROM ubuntu:22.04

# Install dependencies
RUN apt-get -q update && \
DEBIAN_FRONTEND=noninteractive \
TZ=Europe/Berlin \
apt-get -q -y --no-install-recommends install \
git \
gcc \
g++ \
make \
cmake \
python3 \
python3-dev \
ca-certificates \
google-perftools \
libgoogle-perftools-dev \
graphviz \
ghostscript \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

COPY . /opt/svzerodplus

RUN mkdir -p /opt/svzerodplus-build/relwithdebinfo && \
cd /opt/svzerodplus-build/relwithdebinfo && \
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo /opt/svzerodplus && \
cmake --build . --target svzerodsolver

WORKDIR /opt/data
RUN chmod +x /opt/svzerodplus/container/profiling/entrypoint.sh
ENTRYPOINT ["/opt/svzerodplus/container/profiling/entrypoint.sh"]
5 changes: 5 additions & 0 deletions container/profiling/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#! /bin/bash

export LIBPROFILER=$(find /usr/lib -name "libprofiler.so")
LD_PRELOAD=$LIBPROFILER CPUPROFILE=/opt/main.prof CPUPROFILE_FREQUENCY=100000 /opt/svzerodplus-build/relwithdebinfo/svzerodsolver $@ /opt/output.csv
google-pprof --pdf /opt/svzerodplus-build/relwithdebinfo/svzerodsolver /opt/main.prof > profiling_report.pdf
Loading

0 comments on commit 0ba4490

Please sign in to comment.