Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
29a6a60
Add pulsatile inflow boundary condition
Feb 5, 2026
6fff1c8
Increase simulation time to 960 steps and add standalone fluid runner
Feb 5, 2026
770e71a
add pulsatile flow example
Feb 6, 2026
eea78ce
Change proj directory
Feb 6, 2026
0a83f3f
Make Docker container persistent and reusable
Feb 6, 2026
12e1d83
Make setup script standalone by embedding Python dependencies
Feb 6, 2026
721db68
Add curl-based one-line installer
Feb 6, 2026
5951d8e
Add one-line curl installer to README
Feb 6, 2026
ff32d7b
add those vtu files for pulsatile case
Feb 6, 2026
b746f1b
add pulsatile_flow (rigid wall, no fsg) case
Eleven7825 Feb 12, 2026
6a211c6
Add pulsatile flow and mean average functions
Eleven7825 Feb 19, 2026
c985d80
Remove pulsatile_flow folder and restore svfsi.py for clean master merge
Eleven7825 Feb 19, 2026
5967999
change svfsi.py to support pulsatile flow
Eleven7825 Feb 19, 2026
f9fa3b2
Merge branch 'pulsatile-flow-clean'
Eleven7825 Feb 19, 2026
f6f8496
Remove svprojects flow file, 0017 flow file, and run_fluid_only script
Eleven7825 Feb 19, 2026
b7cc419
Rename svMultiPhysics mount directory to svfsi
Eleven7825 Feb 19, 2026
e4d1bb6
Fix TTY detection for docker exec when piped from curl
Eleven7825 Feb 19, 2026
ad8c0e4
Add HPC/Singularity installation support
Feb 25, 2026
a349f60
Update the prestress script to include the vmax support
Eleven7825 Mar 6, 2026
e68e200
update post.py to support plotting at loads other than the last one
Eleven7825 Mar 6, 2026
af5c285
Update svfsi.py to support the pulsatile amplitude simuli
Eleven7825 Mar 6, 2026
b8e3592
input file updated for pulsatile case
Eleven7825 Mar 6, 2026
71f6ec5
adding input json file to archive and in_geo to result
Mar 7, 2026
1e3e997
Add script to copy latest partitioned folder from bouchet with select…
Eleven7825 Mar 17, 2026
34ac928
Merge branch 'pulsatile-flow'
Eleven7825 Mar 17, 2026
552b6ca
Merge branch 'master' of github.com:Eleven7825/svFSGe
Eleven7825 Mar 17, 2026
344eb1b
Add back the last 400 fluid bin and vtu file in the pulsatile folder
Eleven7825 Mar 19, 2026
7c671a3
remove the average/reduction default number 96 now it has to be read …
Mar 19, 2026
bb44899
Revert "remove the average/reduction default number 96 now it has to …
Mar 19, 2026
32e9109
Remove default average cycle 96 and set the pulsatile input to be fsg…
Mar 19, 2026
c5b55be
remove 96 from n_reduction_steps as default
Mar 27, 2026
a56fa61
Add debug flag to suppress pulsatile VTU averaging verbose output
Eleven7825 Apr 11, 2026
ca438f1
Add IQN-ILS debug mode, cc coefficient plot, and steep profile case
Eleven7825 Apr 13, 2026
03246d0
Fix IQN-ILS debug: save t/n per call, fix transition lines, suppress …
Eleven7825 Apr 13, 2026
badc52c
post.py: add active column boundary line to cc heatmap
Eleven7825 Apr 13, 2026
ff61f05
post.py: remove label from active column boundary line
Eleven7825 Apr 13, 2026
6060a02
Add iqn_ils_reset flag to clear history vectors between load steps
Eleven7825 Apr 13, 2026
d6f0c3c
Add IQN-ILS debug mode, reset flag, and cc_coefficients plot in post.py
Eleven7825 Apr 13, 2026
acce78a
Fix heatmap x-axis alignment, integer ticks, and legend cleanup in pl…
Eleven7825 Apr 13, 2026
5406fc5
Switch heatmap in plot_cc to blue-red diverging colormap with 95th pe…
Eleven7825 Apr 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 53 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,22 @@
[arXiv:2404.14187](https://arxiv.org/abs/2404.14187)

## Quickstart
1. Install docker and pull the svMultiphyiscs image from

### One-Line Install (Recommended)

Install FSGe on a new machine with a single command:

```bash
curl -fsSL https://raw.githubusercontent.com/Eleven7825/svFSGe/master/scripts/install.sh | bash
```

This will clone the repository, set up the Docker environment, and drop you into an interactive shell ready to run simulations.

**Prerequisites**: Docker and git must be installed and Docker must be running.

### Manual Setup

1. Install docker and pull the svMultiphyiscs image from
```bash
docker pull simvascular/solver:latest
```
Expand Down Expand Up @@ -54,19 +69,52 @@ python3 ./fsg.py in_sim/partitioned_full.json

## Quick Setup Script

For convenience, use the provided setup script to automate Docker environment setup:
If you've already cloned the repository, use the setup script to create a persistent Docker environment:

```bash
# From the svFSGe directory
./scripts/setup_docker.sh
```

This script will:
- Create necessary directories
- Start Docker container with proper mounts
- Create a persistent Docker container named `fsg-dev`
- Start the container with proper volume mounts
- Clone and build svFSIplus
- Install Python dependencies
- Provide an interactive shell ready to run simulations
- Provide an interactive shell ready to run simulations

The container persists after you exit. Run the script again to reconnect, or use:
```bash
docker exec -it fsg-dev /bin/bash
```

## HPC / Singularity Installation

For HPC environments where Docker is not available, use Singularity/Apptainer:

```bash
git clone https://github.com/Eleven7825/svFSGe.git ~/svFSGe
cd ~/svFSGe
./scripts/setup_singularity.sh
```

**Run test simulation:**
```bash
./scripts/run_simulation.sh in_sim/partitioned_test.json
```

**Or manually with Singularity:**
```bash
singularity exec \
--bind ~/svfsi:/svfsi \
--bind ~/svFSGe:/svFSGe \
~/svFSGe/singularity_images/simvascular-solver.sif \
python3 /svFSGe/fsg.py /svFSGe/in_sim/partitioned_test.json
```

**Note**: Adjust the number of cores in your configuration files to match your HPC allocation.

**Prerequisites**: Singularity/Apptainer and git. See [README_SINGULARITY.md](README_SINGULARITY.md) for complete documentation.

## Continuous Integration

Expand Down
129 changes: 129 additions & 0 deletions copy_latest_partitioned.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#!/bin/bash

# Script to copy latest partitioned_* folder with selective VTU/BIN file handling
# Configuration
SOURCE_BASE="${SOURCE_BASE:-bouchet:~/svFSGe}" # Remote source on bouchet (sc3544)
DEST_DIR="${DEST_DIR:-.}" # Current directory by default
KEEP_LAST_N_PULSATILE=400 # Keep last N pulsatile VTU files (ignore .bin files)
KEEP_LAST_N_STEADY=10 # Keep last N steady VTU files (ignore .bin files)

set -e # Exit on error

# Optional argument: folder name to copy (defaults to latest)
FOLDER_ARG="${1:-}"

# SSH ControlMaster settings - authenticate once, reuse connection
SSH_HOST="${SOURCE_BASE%%:*}"
CONTROL_PATH="/tmp/ssh_control_${SSH_HOST}_%h_%p_%r"
SSH_OPTS="-o ControlMaster=auto -o ControlPath=$CONTROL_PATH -o ControlPersist=600"

echo "=== Partitioned Folder Copy Script ==="
echo "Source: $SOURCE_BASE"
echo "Destination: $DEST_DIR"
echo ""

# Establish master connection (one-time authentication)
if [[ $SOURCE_BASE == *:* ]]; then
echo "Establishing SSH connection (you'll authenticate once here)..."
ssh $SSH_OPTS -t "$SSH_HOST" "echo 'Connected'" > /dev/null 2>&1
echo ""
fi

# Find the target partitioned_* directory (use argument if provided, else latest)
if [ -n "$FOLDER_ARG" ]; then
FOLDER_NAME="$FOLDER_ARG"
if [[ $SOURCE_BASE == *:* ]]; then
REMOTE_HOST="${SOURCE_BASE%%:*}"
LATEST_DIR="$REMOTE_HOST:~/svFSGe/$FOLDER_NAME"
else
LATEST_DIR="$SOURCE_BASE/$FOLDER_NAME"
fi
elif [[ $SOURCE_BASE == *:* ]]; then
# Remote source - get just the folder name
REMOTE_HOST="${SOURCE_BASE%%:*}"
FOLDER_NAME=$(ssh $SSH_OPTS "$REMOTE_HOST" "ls -td ~/svFSGe/partitioned_* 2>/dev/null | head -1 | xargs basename" 2>/dev/null | tr -d '\r')
LATEST_DIR="$REMOTE_HOST:~/svFSGe/$FOLDER_NAME"
else
# Local source
LATEST_DIR=$(ls -td "$SOURCE_BASE"/partitioned_* 2>/dev/null | head -1)
FOLDER_NAME=$(basename "$LATEST_DIR")
fi

if [ -z "$FOLDER_NAME" ]; then
echo "Error: No partitioned_* directory found in $SOURCE_BASE"
exit 1
fi
echo "Found latest folder: $FOLDER_NAME"

# Create destination directory
mkdir -p "$DEST_DIR/$FOLDER_NAME"

# Step 1: Copy everything EXCEPT .vtu and .bin files
echo "Step 1: Copying folder structure and other files (excluding VTU/BIN)..."
if [[ $SOURCE_BASE == *:* ]]; then
rsync -av -e "ssh $SSH_OPTS" \
--exclude='pulsatile/*.vtu' \
--exclude='pulsatile/*.bin' \
--exclude='steady/*.vtu' \
--exclude='steady/*.bin' \
--exclude='gr_restart/*.vtu' \
"$LATEST_DIR/" "$DEST_DIR/$FOLDER_NAME/"
else
rsync -av \
--exclude='pulsatile/*.vtu' \
--exclude='pulsatile/*.bin' \
--exclude='steady/*.vtu' \
--exclude='steady/*.bin' \
--exclude='gr_restart/*.vtu' \
"$LATEST_DIR/" "$DEST_DIR/$FOLDER_NAME/"
fi

# Step 2: Copy only last N VTU and BIN files
echo "Step 2: Copying VTU and BIN files (last N timesteps)..."
for dir in pulsatile steady; do
# Determine how many files to keep for this directory
if [ "$dir" = "pulsatile" ]; then
KEEP_LAST_N=$KEEP_LAST_N_PULSATILE
else
KEEP_LAST_N=$KEEP_LAST_N_STEADY
fi

echo " Processing $dir (keeping last $KEEP_LAST_N timesteps)..."
mkdir -p "$DEST_DIR/$FOLDER_NAME/$dir"

if [[ $SOURCE_BASE == *:* ]]; then
# Remote: get base names of last N VTU files, then copy both VTU and BIN
REMOTE_HOST="${SOURCE_BASE%%:*}"
REMOTE_DIR="~/svFSGe/$FOLDER_NAME/$dir"

BASENAMES=$(ssh $SSH_OPTS "$REMOTE_HOST" "ls -1v $REMOTE_DIR/*_*.vtu 2>/dev/null | sort -V | tail -$KEEP_LAST_N | xargs -I{} basename {} .vtu" | tr -d '\r')

echo "$BASENAMES" | while read base; do
if [ -n "$base" ]; then
for ext in vtu bin; do
rsync -av -e "ssh $SSH_OPTS" "$REMOTE_HOST:$REMOTE_DIR/$base.$ext" "$DEST_DIR/$FOLDER_NAME/$dir/" 2>/dev/null || true
done
fi
done
else
# Local: copy last N VTU and BIN files
BASENAMES=$(ls -1v "$LATEST_DIR/$dir"/*_*.vtu 2>/dev/null | sort -V | tail -$KEEP_LAST_N | xargs -I{} basename {} .vtu)

echo "$BASENAMES" | while read base; do
if [ -n "$base" ]; then
for ext in vtu bin; do
[ -f "$LATEST_DIR/$dir/$base.$ext" ] && cp "$LATEST_DIR/$dir/$base.$ext" "$DEST_DIR/$FOLDER_NAME/$dir/"
done
fi
done
fi
done

echo ""
echo "=== Copy completed successfully! ==="
echo "Folder location: $DEST_DIR/$FOLDER_NAME"

# Clean up SSH master connection
if [[ $SOURCE_BASE == *:* ]]; then
ssh $SSH_OPTS -O exit "$SSH_HOST" 2>/dev/null || true
fi
38 changes: 38 additions & 0 deletions fsg.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,10 @@ def plot_convergence(self):
plt.close(fig)

def archive(self):
# save debug QR data if enabled
if self.p["coup"].get("iqn_ils_debug", False):
np.save(os.path.join(self.p["f_out"], "debug_qr.npy"), self.debug_qr)

# save stored results
self.p["error"] = self.err

Expand Down Expand Up @@ -283,6 +287,19 @@ def coup_step_iqn_ils(self, i, t, n, times):
if ((t == 0) or (t == 1 and n < 5)):# or n == 0:
self.coup_relax("solid", "disp", i, t, n)
else:
# reset history vectors at start of each new load step if requested
if n == 0 and self.p["coup"].get("iqn_ils_reset", False):
self.mat_V = []
self.mat_W = []
# also clear dk and res so difference vectors don't span load steps
self.dk["disp"] = [self.dk["disp"][-1]]
self.res = [self.res[-1]]

# fall back to relaxation if no history vectors available yet
if not self.mat_V:
self.coup_relax("solid", "disp", i, t, n)
return

# maximum number of time steps used in IQN-ILS
nq = self.p["coup"]["iqn_ils_q"]

Expand All @@ -294,12 +311,33 @@ def coup_step_iqn_ils(self, i, t, n, times):
tmp_V = np.array(self.mat_V).T
tmp_W = np.array(self.mat_W).T
eps = self.p["coup"]["iqn_ils_eps"]

# debug: save V and W before filtering
if self.p["coup"].get("iqn_ils_debug", False):
self.debug_qr["V_before"] += [tmp_V.copy()]
self.debug_qr["W_before"] += [tmp_W.copy()]
self.debug_qr["ncols_before"] += [tmp_V.shape[1]]

qq, rr, tmp_V, tmp_W = QRfiltering_mod(tmp_V, tmp_W, eps)

# debug: save Q, R and V, W after filtering
if self.p["coup"].get("iqn_ils_debug", False):
self.debug_qr["Q"] += [qq.copy()]
self.debug_qr["R"] += [rr.copy()]
self.debug_qr["V_after"] += [tmp_V.copy()]
self.debug_qr["W_after"] += [tmp_W.copy()]
self.debug_qr["ncols_after"] += [tmp_V.shape[1]]

# solve for coefficients
ss = np.dot(np.transpose(qq), -self.res[-1])
cc = np.linalg.solve(rr, ss)

# debug: save coefficients and load step/sub-iter indices
if self.p["coup"].get("iqn_ils_debug", False):
self.debug_qr["cc"] += [cc.copy()]
self.debug_qr["t"] += [t]
self.debug_qr["n"] += [n]

# update
vec_new = dtk + np.dot(tmp_W, cc)
self.curr.add(("solid", "disp", "int"), vec_new.reshape((-1, 3)))
Expand Down
3 changes: 2 additions & 1 deletion in_sim/partitioned_full.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"method": "iqn_ils",
"omega0": 0.1,
"iqn_ils_q": 20,
"iqn_ils_eps": 1.0e-1
"iqn_ils_eps": 1.0e-1,
"iqn_ils_reset": false
},
"nmax": 10,
"fmax": 1.0,
Expand Down
85 changes: 85 additions & 0 deletions in_sim/partitioned_full_debug.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{
"fsi": true,
"debug": false,
"tortuosity": false,
"mesh": "fsg_full_coarse.json",
"n_procs": {
"fluid": 4,
"mesh": 1,
"solid": 1
},
"fluid": {
"mu": 4e-06,
"rho": 1.06e-6,
"p0": 13.9868,
"q0": 1000.0,
"q0_rate": 200.0
},
"n_max": {
"fluid": 10,
"mesh": 1,
"solid": 1
},
"coup": {
"nmax": 200,
"nmin": 1,
"tol": 1.0e-3,
"method": "iqn_ils",
"omega0": 0.1,
"iqn_ils_q": 20,
"iqn_ils_eps": 1.0e-1,
"iqn_ils_debug": true,
"iqn_ils_reset": false
},
"nmax": 10,
"fmax": 1.0,
"predict_file": false,
"exe": {
"fluid": "/svfsi/svFSI-build/bin/svFSI",
"mesh": "/svfsi/svFSI-build/bin/svFSI",
"solid": "/svfsi/svFSI-build/bin/svFSI"
},
"inp": {
"fluid": "steady_full.xml",
"mesh": "mesh_full.xml",
"solid": "gr_full_restart.xml"
},
"interfaces": {
"bc_flow": "steady_flow.dat",
"bc_pressure": "steady_pressure.dat",
"disp": "interface_displacement.dat",
"inflow_vector": "inflow_vector.dat",
"geo_fluid": "fluid.vtu",
"geo_solid": "solid.vtu",
"geo_mesh": "mesh.vtu",
"load_pressure": "interface_pressure.vtp",
"load_wss": "interface_wss.vtp"
},
"out": {
"fluid": "steady",
"mesh": "mesh",
"solid": "gr_restart"
},
"name": "partitioned",
"paths_linux_ubuntu": {
"in_geo": "/svFSGe/in_geo",
"in_petsc": "/svFSGe/in_petsc",
"in_svfsi": "/svFSGe/in_svfsi_plus",
"exe": "/",
"root": "."
},
"paths_linux_centos": {
"in_geo": "/home/users/pfaller/work/repos/FSG/in_geo",
"in_petsc": "/home/users/pfaller/work/repos/FSG/in_petsc",
"in_svfsi": "/home/users/pfaller/work/repos/FSG/in_svfsi_plus",
"exe": "/home/users/pfaller/work/repos",
"root": "."
},
"paths_darwin": {
"in_geo": "/Users/pfaller/work/repos/FSG/in_geo",
"in_petsc": "/Users/pfaller/work/repos/FSG/in_petsc",
"in_svfsi": "/Users/pfaller/work/repos/FSG/in_svfsi_plus",
"exe": "/Users/pfaller/work/repos",
"root": "."
}
}
Loading
Loading