Skip to content

Commit

Permalink
add single twist and frontend many updates
Browse files Browse the repository at this point in the history
  • Loading branch information
ryichando committed Jan 5, 2025
1 parent ff143d3 commit 544539c
Show file tree
Hide file tree
Showing 28 changed files with 1,345 additions and 399 deletions.
10 changes: 10 additions & 0 deletions .github/workflows/cancel_all.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash

gh run list --json status,workflowName,databaseId --jq '.[] | select(.status=="in_progress")' |
while read -r run; do
id=$(echo "$run" | jq -r '.databaseId')
workflow_name=$(echo "$run" | jq -r '.workflowName')

echo "Canceling running workflow '$workflow_name' (#$id)"
gh run cancel $id
done
68 changes: 68 additions & 0 deletions .github/workflows/example_twist.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# File: example_twist.yml
# Author: Ryoichi Ando ([email protected])
# License: Apache v2.0

name: twist.ipynb

on:
workflow_dispatch:
inputs:
runner:
type: string
required: true
description: 'Runner Name'

env:
VAST_API_KEY: ${{ secrets.VAST_API_KEY }}
EXAMPLE_NAME: twist
HELPER_PATH: .github/workflows/vast/helper.sh

jobs:
run:
runs-on: ${{ github.event.inputs.runner }}
steps:

- name: check out repo
uses: actions/checkout@v3

- name: print scene
run: |
echo "Scene: $EXAMPLE_NAME" >> $GITHUB_STEP_SUMMARY
- name: prepare
timeout-minutes: 30
run: bash $HELPER_PATH create $VAST_API_KEY

- name: 1st run
run: bash $HELPER_PATH run ${EXAMPLE_NAME}.py

- name: 2nd run
run: bash $HELPER_PATH run ${EXAMPLE_NAME}.py

- name: 3rd run
run: bash $HELPER_PATH run ${EXAMPLE_NAME}.py

- name: 4th run
run: bash $HELPER_PATH run ${EXAMPLE_NAME}.py

- name: 5th run
run: bash $HELPER_PATH run ${EXAMPLE_NAME}.py

- name: 6th run
run: bash $HELPER_PATH run ${EXAMPLE_NAME}.py

- name: 7th run
run: bash $HELPER_PATH run ${EXAMPLE_NAME}.py

- name: 8th run
run: bash $HELPER_PATH run ${EXAMPLE_NAME}.py

- name: 9th run
run: bash $HELPER_PATH run ${EXAMPLE_NAME}.py

- name: 10th run
run: bash $HELPER_PATH run ${EXAMPLE_NAME}.py

- name: shutdown
if: always()
run: bash $HELPER_PATH delete
22 changes: 22 additions & 0 deletions .github/workflows/run_all.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash

if [ -z "$1" ]; then
echo "Error: Missing string parameter."
echo "Usage: $0 <runner>"
exit 1
fi

runner="$1"

WORKFLOW_PATTERN="example_*"
WORKFLOW_FILES=$(ls $WORKFLOW_PATTERN*.yml 2>/dev/null)

if [ -z "$WORKFLOW_FILES" ]; then
echo "No workflow files found."
exit 1
fi

for WORKFLOW_FILE in $WORKFLOW_FILES; do
echo "Triggering GitHub Action workflow: $WORKFLOW_FILE with runner=$runner"
gh workflow run "$WORKFLOW_FILE" -f runner="$runner"
done
72 changes: 72 additions & 0 deletions .github/workflows/vast/cuda-tester.cu
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#include <cuda_runtime.h>
#include <stdio.h>

__global__ void vectorAdd(const float *A, const float *B, float *C, int n) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx < n) {
C[idx] = A[idx] + B[idx];
}
}

void checkCudaError(cudaError_t err, const char *msg) {
if (err != cudaSuccess) {
fprintf(stderr, "CUDA Error: %s: %s\n", msg, cudaGetErrorString(err));
exit(EXIT_FAILURE);
}
}

int main() {
const int N = 1024; // Vector size
size_t size = N * sizeof(float);

float *h_A = (float *)malloc(size);
float *h_B = (float *)malloc(size);
float *h_C = (float *)malloc(size);

for (int i = 0; i < N; i++) {
h_A[i] = i;
h_B[i] = i * 2;
}

float *d_A, *d_B, *d_C;
checkCudaError(cudaMalloc(&d_A, size), "cudaMalloc A");
checkCudaError(cudaMalloc(&d_B, size), "cudaMalloc B");
checkCudaError(cudaMalloc(&d_C, size), "cudaMalloc C");

checkCudaError(cudaMemcpy(d_A, h_A, size, cudaMemcpyHostToDevice),
"cudaMemcpy A");
checkCudaError(cudaMemcpy(d_B, h_B, size, cudaMemcpyHostToDevice),
"cudaMemcpy B");

int threadsPerBlock = 256;
int blocksPerGrid = (N + threadsPerBlock - 1) / threadsPerBlock;
vectorAdd<<<blocksPerGrid, threadsPerBlock>>>(d_A, d_B, d_C, N);

checkCudaError(cudaGetLastError(), "Kernel launch");
checkCudaError(cudaDeviceSynchronize(), "Kernel synchronization");

checkCudaError(cudaMemcpy(h_C, d_C, size, cudaMemcpyDeviceToHost),
"cudaMemcpy result");

bool success = true;
for (int i = 0; i < N; i++) {
if (h_C[i] != h_A[i] + h_B[i]) {
printf("Verification failed at index %d: %f != %f + %f\n", i,
h_C[i], h_A[i], h_B[i]);
success = false;
break;
}
}
checkCudaError(cudaFree(d_A), "cudaFree A");
checkCudaError(cudaFree(d_B), "cudaFree B");
checkCudaError(cudaFree(d_C), "cudaFree C");
free(h_A);
free(h_B);
free(h_C);

if (success) {
printf("CUDA test passed successfully!\n");
}

return 0;
}
28 changes: 26 additions & 2 deletions .github/workflows/vast/provision.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
# Author: Ryoichi Ando ([email protected])
# License: Apache v2.0

# CUDA tester file path
CUDA_TESTER_PATH=$(pwd)/.github/workflows/vast/cuda-tester.cu

# check if the CUDA tester file exists
if [ ! -f "$CUDA_TESTER_PATH" ]; then
echo "Error: CUDA tester file not found: $CUDA_TESTER_PATH"
exit 1
fi

# set working directory
WORKDIR=/tmp/vast-ci

Expand Down Expand Up @@ -186,6 +195,7 @@ while true; do
# Loop until SSH connection is successful
retry_count=0
ssh_ready=false
cuda_ready=false
while true; do
sleep $RETRY_INTERVAL
echo "trying to establish SSH connection..."
Expand All @@ -205,6 +215,20 @@ while true; do
fi
done
if [ "$ssh_ready" = true ]; then
break
scp_command="scp -i $WORKDIR/id_ed25519 -o StrictHostKeyChecking=no -o ConnectTimeout=5 -P $port $CUDA_TESTER_PATH root@${hostname}:/tmp/"
echo "==== copy cuda-tester.cu ======"
echo $scp_command
eval $scp_command
echo "==== compile cuda ======"
eval $ssh_command "nvcc /tmp/cuda-tester.cu -o /tmp/cuda-tester"
echo "==== run cuda ======"
eval $ssh_command "/tmp/cuda-tester"
if [ $? -eq 0 ]; then
cuda_ready=true
break
else
echo "CUDA test failed"
$WORKDIR/delete-instance.sh
fi
fi
done
done
23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ Published in [ACM Transactions on Graphics (TOG)](https://dl.acm.org/doi/abs/10.

## 📝 Change History

- (2025.1.5) Added a [single twist example](./examples/twist.ipynb) [[Video]](https://drive.google.com/file/d/1LDFKS-iBvl2uDdPVKaazQL25tYGEEyXr/view).
- (2024.12.31) Added full documentation for Python APIs, parameters, and log files [[GitHub Pages]](https://st-tech.github.io/ppf-contact-solver).
- (2024.12.27) Line search for strain limiting is improved [[Markdown]](./articles/bug.md#new-strain-limiting-line-search)
- (2024.12.23) Added [[Bug Fixes and Updates]](./articles/bug.md)
Expand Down Expand Up @@ -154,8 +155,8 @@ fixed.preview()
param = app.session.param()
param.set("dt", 0.01)

# create a new session with a name
session = app.session.create("dt-001").init(fixed)
# create a new session with the built scene
session = app.session.create(fixed)

# start the simulation and live-preview the results (image right)
session.start(param).preview()
Expand All @@ -166,9 +167,9 @@ session.stream()
# or interactively view the animation sequences
session.animate()

# export all simulated frames (downloadable from the file browser)
# export all simulated frames and zip them
path = f"export/{scene.info.name}/{session.info.name}"
session.export.animation(path)
session.export.animation(path).zip()
```
<img src="./asset/image/drape.jpg" alt="drape">

Expand All @@ -182,7 +183,7 @@ The behaviors can be changed through the settings.

## 🔍 Obtaining Logs

Logs for the simulation can also be queried through the Python APIs. Here's an example of how to get the list of recorded logs, fetch them, and compute the average.
📊 Logs for the simulation can also be queried through the Python APIs 🐍. Here's an example of how to get the list of recorded logs 📝, fetch them 📥, and compute the average 🧮.

```python
# get a list of log names
Expand Down Expand Up @@ -256,7 +257,7 @@ All the log files 📂 are available ✅ and can be fetched ⬇️ during the si
|![](./asset/image/catalogue/woven.mp4.gif)|![](./asset/image/catalogue/stack.mp4.gif)|![](./asset/image/catalogue/trampoline.mp4.gif)|![](./asset/image/catalogue/needle.mp4.gif)|
|[cards](./examples/cards.ipynb) [[Video]](https://drive.google.com/file/d/1PMdDnlyCsjinbvICKph_0UcXUfUvvUmZ/view)|codim|[hang](./examples/hang.ipynb) [[Video]](https://drive.google.com/file/d/1gIjwaRrEifH0FQnZ8HO8Q9-f9FF5ZivG/view)|[trapped](./examples/trapped.ipynb)|
|![](./asset/image/catalogue/cards.mp4.gif)|![](./asset/image/catalogue/codim.mp4.gif)|![](./asset/image/catalogue/hang.mp4.gif)|![](./asset/image/catalogue/trapped.mp4.gif)|
|domino|noodle|[drape](./examples/drape.ipynb) [[Video]](https://drive.google.com/file/d/1PGL3tbA451VhHOViSJJNNdQvmUpg7bQd/view)|quintuple|
|domino|noodle|[drape](./examples/drape.ipynb) [[Video]](https://drive.google.com/file/d/1PGL3tbA451VhHOViSJJNNdQvmUpg7bQd/view)|[twist](./examples/twist.ipynb) [[Video]](https://drive.google.com/file/d/1LDFKS-iBvl2uDdPVKaazQL25tYGEEyXr/view)|
|![](./asset/image/catalogue/domino.mp4.gif)|![](./asset/image/catalogue/noodle.mp4.gif)|![](./asset/image/catalogue/drape.mp4.gif)|![](./asset/image/catalogue/quintupletwist.mp4.gif)|
|ribbon|[curtain](./examples/curtain.ipynb) [[Video]](https://drive.google.com/file/d/1c9W3YAFAS5r9m9i7sZHsFu8h98C8yy1T/view)|fishingknot|[friction](./examples/friction.ipynb) [[Video]](https://drive.google.com/file/d/12WGdfDTFIwCT0UFGEZzfmQreM6WSSHet/view)|
|![](./asset/image/catalogue/ribbon.mp4.gif)|![](./asset/image/catalogue/curtain.mp4.gif)|![](./asset/image/catalogue/fishingknot.mp4.gif)|![](./asset/image/catalogue/friction-armadillo.mp4.gif)|
Expand Down Expand Up @@ -461,13 +462,13 @@ docker rm $MY_CONTAINER_NAME
Our contact solver is designed for heavy use in cloud services ☁️, enabling us to:
- Quickly deploy testing environments 🚀 and delete them when not in use, saving costs 💰.
- Scale as needed based on demand 📈. For example, you can create multiple instances to perform numerous tasks before a specific deadline ⏰.
- Design a fully automated pipeline 🔄 for trial-and-error iterations without human involvement 🤖.
- Allow anyone with an internet connection 🌍 to try our solver, even on a smartphone 📱 or tablet 🖥️.
- **💰 Cost-Effective Development**: Quickly deploy testing environments 🚀 and delete 🗑️ them when not in use, saving costs.
- **📈 Flexible Scalability**: Scale as needed based on demand 📈. For example, you can launch multiple instances before a specific deadline ⏰.
- **🌍 High Accessibility**: Allow anyone with an internet connection 🌍 to try our solver, even on a smartphone 📱 or tablet 🖥️.
- **🐛 Easier Bug Tracking**: Users and developers can easily share the same hardware, kernel, and driver environment, making it easier to track and fix bugs.
This is all made possible with our purely web-based frontends 🌐 and scalable capability 🧩.
Our solver also runs on the NVIDIA L4 🖱️, a data-center-targeted GPU 🖥️ that offers reasonable pricing 💲, delivering both practical performance 💪 and scalability 📊 without investing in expensive hardware 💻.
Our main target is the NVIDIA L4 🖱️, a data-center-targeted GPU 🖥️ that offers reasonable pricing 💲, delivering both practical performance 💪 and scalability 📊 without investing in expensive hardware 💻.
Below, we describe how to deploy our solver on major cloud services ☁️. These instructions are up to date as of late 2024 📅 and are subject to change 🔄.
Expand Down
16 changes: 10 additions & 6 deletions examples/cards.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,14 @@
"metadata": {},
"outputs": [],
"source": [
"param.set(\"friction\", 0.5)\n",
"param.set(\"area-young-mod\", 30000)\n",
"param.set(\"bend\", 1e6).set(\"dt\", 0.01).set(\"min-newton-steps\", 32)\n",
"session = app.session.create(\"8-stairs\").init(fixed)\n",
"(\n",
" param.set(\"friction\", 0.5)\n",
" .set(\"area-young-mod\", 30000)\n",
" .set(\"bend\", 1e6)\n",
" .set(\"dt\", 0.01)\n",
" .set(\"min-newton-steps\", 32)\n",
")\n",
"session = app.session.create(fixed)\n",
"session.start(param).preview()\n",
"session.stream()"
]
Expand Down Expand Up @@ -100,8 +104,8 @@
"metadata": {},
"outputs": [],
"source": [
"# export all simulated frames\n",
"session.export.animation(f\"export/{scene.info.name}/{session.info.name}\")"
"# export all simulated frames and make a zip file\n",
"session.export.animation(f\"export/{scene.info.name}/{session.info.name}\").zip()"
]
}
],
Expand Down
Loading

0 comments on commit 544539c

Please sign in to comment.