Skip to content

Add 3D-1D coupling interface APIs - #131

Open
taeoukkim wants to merge 3 commits into
SimVascular:masterfrom
taeoukkim:issue-129-3d-1d-coupling
Open

Add 3D-1D coupling interface APIs#131
taeoukkim wants to merge 3 commits into
SimVascular:masterfrom
taeoukkim:issue-129-3d-1d-coupling

Conversation

@taeoukkim

Copy link
Copy Markdown

Current situation

resolves #129

Release Notes

Update 1D solver for 3D-1D coupling.
Functions are stored in "interface.cpp" file.
Main functions are: initialize_1d, set_external_step_size_1d, return_1d_solution, update_1d_solution, run_1d_simulation_step_1d, and extract_coupled_dof.
Now, when you compile the code, it will create a shared library for coupling at ".../svOneDSolver/build/lib/libsvoned_interface.dylib"
The API functions can be accessed through this shared library.

Documentation

I will update document.

Testing

3D-1D testing done

Code of Conduct & Contributing Guidelines

@taeoukkim
taeoukkim requested a review from Copilot July 14, 2026 21:56
@taeoukkim
taeoukkim requested review from ktbolt and mrp089 and removed request for Copilot July 14, 2026 22:00
@ktbolt

ktbolt commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

@taeoukkim This PR does not build on MacOS.

svOneDSolver/Code/Source/cvOneDOptions.h:128:10: error: no template named 'optional' in namespace 'std'
    std::optional<int> vtkOutputType = std::nullopt; 
    ~~~~~^

This is a CMake issue I think; I'll have a look.

@ktbolt

ktbolt commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

@taeoukkim The compiler used to build svOneDSolver/Code/Source/interface/interface.cpp was set to c++17.

I added

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)

after the PROJECT CMake command and now everything compiles ok.

@ktbolt ktbolt left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@taeoukkim You've done a great job here adding an interface to an old and confusing code !

A couple of important things to clean up

  1. Only use print statements for debugging; they should not be active for release

  2. Use a consistent error handing; really everything should use cvException I'm guessing; writing to cerr is not very useful should throw I think

svOneDSolver writes out lots of .dat files and prints a simulation history to stdout I think. How is this managed for the 3D interface ?

Comment thread Code/Source/interface/CMakeLists.txt Outdated
Comment thread Code/Source/interface/interface.cpp
Comment thread Code/Source/interface/interface.cpp
Comment thread Code/Source/interface/interface.cpp Outdated
Comment thread Code/Source/interface/interface.cpp
Comment thread Code/Source/interface/interface.cpp
Comment thread Code/Source/interface/interface.cpp Outdated
Comment thread Code/Source/interface/interface.cpp Outdated
Comment thread Code/Source/interface/interface.cpp
Comment thread Code/Source/interface/interface.cpp
@taeoukkim

Copy link
Copy Markdown
Author

@taeoukkim The compiler used to build svOneDSolver/Code/Source/interface/interface.cpp was set to c++17.

I added

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)

after the PROJECT CMake command and now everything compiles ok.

Thank you for identifying the issue and suggesting the fix!! I added the C++20 settings to the top-level CMakeLists.txt and confirmed that the interface library builds successfully on my Mac.

@taeoukkim

Copy link
Copy Markdown
Author

@ktbolt Thank you very much for the thorough review and helpful suggestions. I have addressed all of the comments. I also clarified that accepted coupled solutions are written to VTK and text files at the requested output interval. Both the standalone OneDSolver and svoned_interface build successfully on macOS, and I confirmed that the standalone solver still runs correctly.

@ktbolt ktbolt left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@taeoukkim Fine work here, nice clean up !

Please add any necessary comments to the non-API functions in interface.cpp.

I want to confirm that if the 1D solver fails then the failure is reported by the 3D solver.

#else
#define SVONED_DEBUG_LOG(message) \
do { } while (false)
#endif

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@taeoukkim Why the do statements ?

if(argc == 2) {
string inputFile{removeQuotesIfPresent(argv[1])};
return readLegacyOptions(inputFile);
if (errorCode == CV_ERROR) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@taeoukkim It is not good to cast away a const qualifier; better to change the signature of CreateNode.

CreateNode always returns CV_OK.

for (int jointIndex = 0; jointIndex < totalJoints; ++jointIndex) {
const std::string& inletName = opts.jointInletName[jointIndex];
const std::string& outletName = opts.jointOutletName[jointIndex];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@taeoukkim You can use const auto& inletName = opts.jointInletName[jointIndex];


const int totalInlets = opts.jointInletListNumber[inletID];
const int totalOutlets = opts.jointOutletListNumber[outletID];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@taeoukkim All of this use of const just really adds noise in my opinion but maybe this is your style; no problem with that.

You can also use auto errMsg = ERROR: Cannot Find JOINTOUTLET for key " + outletName;

for (int i = 0; i < totalInlets; ++i) {
inletSegments.push_back(opts.jointInletList[inletID][i]);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@taeoukkim You can replace all this (111-115) with

 std::vector<int> inletSegments(opts.jointInletList[inletID].begin(), opts.jointInletList[inletID].end());

cvOneDGlobal::outputType = OutputTypeScope::OUTPUT_BOTH;
} else {
throw cvException("ERROR: Invalid OUTPUT Type.\n");
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@taeoukkim Include in the exception message what the invalid type was.


if (cvOneDGlobal::vtkOutputType > 1) {
throw cvException("ERROR: Invalid OUTPUT VTK Type.\n");
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@taeoukkim What does it mean to have cvOneDGlobal::vtkOutputType > 1 ?

}

int getDataTableIDFromStringKey(std::string key) {
std::size_t count = 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@taeoukkim Use const std::string& key

upper_string(cvOneDGlobal::gDataTables[count]->getName())) {
return static_cast<int>(count);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@taeoukkim Maybe cleaner to use a for loop or iterators; while loops are typically not used for a known range.

  auto ukey = upper_string(key);
  
  for (auto& entry : cvOneDGlobal::gDataTables) {
    if (ukey == upper_string(entry->getName())) {
      return count;
    }
    count += 1;
  } 

if (str.size() >= 2 && str.front() == '"' && str.back() == '"') {
return str.substr(1, str.size() - 2);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@taeoukkim It would be perhaps cleaner to use

  auto mstr = str;
  mstr.erase(std::remove(mstr.begin(), mstr.end(), '"'), mstr.end());
  return mstr;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3D-1D coupling

2 participants