ZOZO's Contact Solver ๐ซถ
A contact solver for physics-based simulations involving ๐ shells, ๐ชต solids and ๐ชข rods. All made by ZOZO.
โจ Highlights
๐ช Robust : Contact resolutions are completely penetration-free. No snagging intersections.
: Contact resolutions are completely penetration-free. No snagging intersections. โฒ Scalable : An extreme case includes beyond 150M contacts. Not just one million.
: An extreme case includes beyond 150M contacts. Not just one million. ๐ฒ Cache Efficient : All on the GPU runs in single precision. No double precision.
: All on the GPU runs in single precision. No double precision. ๐ฅผ Inextensible : Cloth never extends beyond very strict upper bounds, such as 1%.
: Cloth never extends beyond very strict upper bounds, such as 1%. ๐ Physically Accurate : Our deformable solver is driven by the Finite Element Method.
: Our deformable solver is driven by the Finite Element Method. โ๏ธ Highly Stressed : We run GitHub Actions to run stress tests 10 times in a row.
: We run GitHub Actions to run stress tests 10 times in a row. ๐ Massively Parallel : Both contact and elasticity solvers are run on the GPU.
: Both contact and elasticity solvers are run on the GPU. ๐ณ Docker Sealed : Everything is designed to work out of the box.
: Everything is designed to work out of the box. ๐ JupyterLab Included : Open your browser and run examples right away (Video).
: Open your browser and run examples right away (Video). ๐ Documented Python APIs : Our Python code is fully docstringed and lintable (Video).
: Our Python code is fully docstringed and lintable (Video). โ๏ธ Cloud-Ready : Our solver can be seamlessly deployed on major cloud platforms.
: Our solver can be seamlessly deployed on major cloud platforms. โจ Stay Clean: You can remove all traces after use.
๐ Table of Contents
๐ Advanced Contents
๐ง ๐ป Setting Up Your Development Environment (Markdown)
๐ Bug Fixes and Updates (Markdown)
๐ Change History
More history records - (2025.1.8) Added a [domino example](./examples/domino.ipynb) [(Video)](https://drive.google.com/file/d/1N9y8eZrjSQhAUhKwiO9w8jW_T18zPnYf/view). - (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) - (2024.12.21) Added a [house of cards example](./examples/cards.ipynb) [(Video)](https://drive.google.com/file/d/1PMdDnlyCsjinbvICKph_0UcXUfUvvUmZ/view) - (2024.12.18) Added a [frictional contact example](./examples/friction.ipynb): armadillo sliding on the slope [(Video)](https://drive.google.com/file/d/12WGdfDTFIwCT0UFGEZzfmQreM6WSSHet/view) - (2024.12.18) Added a [hindsight](./articles/hindsight.md) noting that the tilt angle was not $30^\circ$ , but rather $26.57^\circ$ - (2024.12.16) Removed thrust dependencies to fix runtime errors for the driver version `560.94` [(Issue Link)](#1)
๐ Technical Materials
๐ A Cubic Barrier with Elasticity-Inclusive Dynamic Stiffness
๐ Reference Implementation
The main branch is undergoing frequent updates and will deviate from the paper ๐ง. To retain consistency with the paper, we have created a new branch sigasia-2024 .
๐ ๏ธ Only maintenance updates are planned for this branch.
๐ซ General users should not use this branch as it is not optimized for best performance.
๐ซ All algorithmic changes listed in this (Markdown) are excluded from this branch.
๐ฆ We also provide a pre-compiled Docker image: ghcr.io/st-tech/ppf-contact-solver-compiled-sigasia-2024:latest of this branch.
of this branch. ๐ Template Link for vast.ai
๐ Template Link for RunPods
โก๏ธ Requirements
๐ฅ A modern NVIDIA GPU (CUDA 12.8 or newer)
๐ณ A Docker environment (see below)
๐จ Getting Started
Install a ๐ฎ NVIDIA driver (Link) on your ๐ป host system and follow the ๐ instructions below specific to the ๐ฅ๏ธ operating system to get a ๐ณ Docker running:
๐ง Linux ๐ช Windows Install the Docker engine from here (Link). Also, install the NVIDIA Container Toolkit (Link). Just to make sure that the Container Toolkit is loaded, run sudo service docker restart . Install the Docker Desktop (Link). You may need to log out or reboot after the installation. After logging back in, launch Docker Desktop to ensure that Docker is running.
Next, run the following command to start the ๐ฆ container:
๐ช Windows (PowerShell)
$MY_WEB_PORT = 8080 # Web port number for web interface $IMAGE_NAME = " ghcr.io/st-tech/ppf-contact-solver-compiled:latest " docker run --rm --gpus all -p ${MY_WEB_PORT} :8080 $IMAGE_NAME
๐ง Linux (Bash/Zsh)
MY_WEB_PORT=8080 # Web port number for web interface IMAGE_NAME=ghcr.io/st-tech/ppf-contact-solver-compiled:latest docker run --rm --gpus all -p ${MY_WEB_PORT} :8080 $IMAGE_NAME
โณ Wait for a while until the container becomes a steady state. Next, open your ๐ browser and navigate to http://localhost:8080, where 8080 is the port number specified in the MY_WEB_PORT variable. Keep your terminal window open.
๐ Now you are ready to go! ๐
๐ Shutting Down
To shut down the container, just press Ctrl+C in the terminal. The container will be removed and all traces will be ๐งน cleaned up.
๐ง Advanced Installation
If you wish to build the container from scratch ๐ ๏ธ, please refer to the cleaner installation guide (Markdown) ๐.
๐ How To Use
Our frontend is accessible through ๐ a browser using our built-in JupyterLab ๐ interface. All is set up when you open it for the first time. Results can be interactively viewed through the browser and exported as needed.
This allows you to interact with the simulator on your ๐ป laptop while the actual simulation runs on a remote headless server over ๐ the internet. This means that you don't have to own โ๏ธ NVIDIA hardware, but can rent it at vast.ai or RunPod for less than ๐ต $0.5 per hour. For example, this (Video) was recorded on a vast.ai instance. The experience is ๐ good!
Our Python interface is designed with the following principles in mind:
๐ ๏ธ Dynamic Tri/Tet Creation : Relying on non-integrated third-party tools for triangulation, tetrahedralization, and loading can make it difficult to dynamically adjust resolutions. Our built-in tri/tet creation tools eliminate this limitation.
: Relying on non-integrated third-party tools for triangulation, tetrahedralization, and loading can make it difficult to dynamically adjust resolutions. Our built-in tri/tet creation tools eliminate this limitation. ๐ซ No Mesh Data : Preparing mesh data using external tools can be cumbersome. Our frontend minimizes this effort by allowing meshes to be created on the fly or downloaded when needed.
: Preparing mesh data using external tools can be cumbersome. Our frontend minimizes this effort by allowing meshes to be created on the fly or downloaded when needed. ๐ Method Chaining : We adopt the method chaining style from JavaScript, making the API intuitive and easy to understand.
: We adopt the method chaining style from JavaScript, making the API intuitive and easy to understand. ๐ฆ Single Import for Everything: All frontend features are accessible by simply importing with from frontend import App .
Here's an example of draping five sheets over a sphere with two corners pinned. Please look into the examples directory for more examples.
# import our frontend from frontend import App # make an app app = App . create ( "drape" ) # create a square mesh resolution 128 spanning the xz plane V , F = app . mesh . square ( res = 128 , ex = [ 1 , 0 , 0 ], ey = [ 0 , 0 , 1 ]) # add to the asset and name it "sheet" app . asset . add . tri ( "sheet" , V , F ) # create an icosphere mesh radius 0.5 V , F = app . mesh . icosphere ( r = 0.5 , subdiv_count = 4 ) # add to the asset and name it "sphere" app . asset . add . tri ( "sphere" , V , F ) # create a scene scene = app . scene . create () # define gap between sheets gap = 0.01 for i in range ( 5 ): # add the sheet asset to the scene obj = scene . add ( "sheet" ) # pick two corners corner = obj . grab ([ 1 , 0 , - 1 ]) + obj . grab ([ - 1 , 0 , - 1 ]) # place it with an vertical offset and pin the corners obj . at ( 0 , gap * i , 0 ). pin ( corner ) # set fiber directions required for Baraff-Witkin obj . direction ([ 1 , 0 , 0 ], [ 0 , 0 , 1 ]) # set the strainlimiting of 5% obj . param . set ( "strain-limit" , 0.05 ) # add a sphere mesh at a lower position with jitter and set it static collider scene . add ( "sphere" ). at ( 0 , - 0.5 - gap , 0 ). jitter (). pin () # compile the scene and report stats scene = scene . build (). report () # preview the initial scene scene . preview () # create a new session with the compiled scene session = app . session . create ( scene ) # set session params session . param . set ( "frames" , 100 ). set ( "dt" , 0.01 ) # build this session session = session . build () # start the simulation and live-preview the results (image right) session . start (). preview () # also show streaming logs session . stream () # or interactively view the animation sequences session . animate () # export all simulated frames session . export . animation ()
๐ Python APIs and Parameters
Full API documentation ๐ is available on our GitHub Pages. The major APIs are documented using docstrings โ๏ธ and compiled with Sphinx โ๏ธ. We have also included jupyter-lsp to provide interactive linting assistance ๐ ๏ธ and display docstrings as you type. See this video (Video) for an example. The behaviors can be changed through the settings.
A list of parameters used in param.set(key,value) is documented here: (Global Parameters) (Object Parameters).
Note โ ๏ธ Please note that our Python APIs are subject to breaking changes as this repository undergoes frequent iterations. ๐ง
๐ Obtaining Logs
๐ Logs for the simulation can also be queried through the Python APIs ๐. Here's an example of how to get a list of recorded logs ๐, fetch them ๐ฅ, and compute the average ๐งฎ.
# get a list of log names logs = session . get . log . names () print ( logs ) assert "time-per-frame" in logs assert "newton-steps" in logs # get a list of time per video frame msec_per_video = session . get . log . numbers ( "time-per-frame" ) # compute the average time per video frame print ( "avg per frame:" , sum ([ n for _ , n in msec_per_video ]) / len ( msec_per_video )) # get a list of newton steps newton_steps = session . get . log . numbers ( "newton-steps" ) # compute the average of consumed newton steps print ( "avg newton steps:" , sum ([ n for _ , n in newton_steps ]) / len ( newton_steps )) # Last 8 lines. Omit for everything. print ( "==== log stream ====" ) for line in session . get . log . stdout ( n_lines = 8 ): print ( line )
Below are some representatives. vid_time refers to the video time in seconds and is recorded as float . ms refers to the consumed simulation time in milliseconds recorded as int . vid_frame is the video frame count recorede as int .
Name Description Format time-per-frame Time per video frame list[(vid_frame,ms)] matrix-assembly Matrix assembly time list[(vid_time,ms)] pcg-linsolve Linear system solve time list[(vid_time,ms)] line-search Line search time list[(vid_time,ms)] time-per-step Time per step list[(vid_time,ms)] newton-steps Newton iterations per step list[(vid_time,count)] num-contact Contact count list[(vid_time,count)] max-sigma Max stretch list(vid_time,float)
The full list of log names and their descriptions is documented here: (GitHub Pages).
Note that some entries have multiple records at the same video time โฑ๏ธ. This occurs because the same operation is executed multiple times ๐ within a single step during the inner Newton's iterations ๐งฎ. For example, the linear system solve is performed at each Newton's step, so if multiple Newton's steps are ๐ executed, multiple linear system solve times appear in the record at the same ๐ video time.
If you would like to retrieve the raw log stream, you can do so by
# Last 8 lines. Omit for everything. for line in session . get . log . stdout ( n_lines = 8 ): print ( line )
This will output something like:
* dt: 1.000e-03 * max_sigma: 1.045e+00 * avg_sigma: 1.030e+00 ------ newton step 1 ------ ====== contact_matrix_assembly ====== > dry_pass...0 msec > rebuild...7 msec > fillin_pass...0 msec
If you would like to read stderr , you can do so using session.get.stderr() (if it exists). They return list[str] . All the log files ๐ are available โ
 and can be fetched โฌ๏ธ during the simulation ๐ป.
๐ผ๏ธ Catalogue
๐ฐ Budget Table on AWS
Below is a table summarizing the estimated costs for running our examples on a NVIDIA L4 instance g6.2xlarge at Amazon Web Services US regions ( us-east-1 and us-east-2 ).
๐ฐ Uptime cost is approximately $1 per hour.
โณ Deployment time is approximately 8 minutes ($0.13). Instance loading takes 3 minutes, and Docker pull & load takes 5 minutes.
๐ฎ The NVIDIA L4 delivers 30.3 TFLOPS for FP32, offering approximately 36% of the performance of an RTX 4090.
๐ฅ Video frame rate is 60fps.
Example Cost Time #Frame #Vert #Face #Tet #Seg Max Strain trapped $0.37 22.6m 300 263K 299K 885K N/A N/A twist $0.91 55m 500 203K 406K N/A N/A N/A stack $0.60 36.2m 120 166.7K 327.7K 8.8K N/A 5% trampoline $0.74 44.5m 120 56.8K 62.2K 158.0K N/A 1% needle $0.31 18.4m 120 86K 168.9K 8.8K N/A 5% cards $0.29 17.5m 300 8.7K 13.8K 1.9K N/A 5% domino $0.12 4.3m 250 0.5K 0.8K N/A N/A N/A drape $0.10 3.5m 100 81.9K 161.3K N/A N/A 5% curtain $0.33 19.6m 300 64K 124K N/A N/A 5% friction $0.17 10m 700 1.1K N/A 1K N/A N/A hang $0.12 7.5m 200 16.3K 32.2K N/A N/A 1% belt $0.19 11.4m 200 12.3K 23.3K N/A N/A 5% codim $0.36 21.6m 240 122.7K 90K 474.1K 1.3K N/A fishingknot $0.38 22.5m 830 19.6K 36.9K N/A N/A 5% fitting $0.03 1.54m 240 28.4K 54.9K N/A N/A 10% noodle $0.14 8.45m 240 116.2K N/A N/A 116.2K N/A ribbon $0.23 13.9m 480 34.9K 52.9K 8.8K N/A 5% woven $0.58 34.6m 450 115.6K N/A N/A 115.4K N/A yarn $0.01 0.24m 120 28.5K N/A N/A 28.5K N/A roller $0.03 2.08m 240 21.4K 22.2K 61.0K N/A N/A
๐๏ธ Large Scale Examples
Large scale examples are run on a vast.ai instance with an RTX 4090. At the moment, not all large scale examples are ready yet, but they will be added/updated one by one. The author is actively woriking on it.
Example Commit #Vert #Face #Tet #Seg #Contact #Frame Time/Frame large-twist cbafbd2 3.2M 6.4M N/A N/A 56.7M 2,000 46.4s
๐ GitHub Actions
We implemented GitHub Actions that test all of our examples except for large scale ones, which take from hours to days to finish. We perform explicit intersection checks ๐ at the end of each step, which raises an error โ if an intersection is detected. This ensures that all steps are confirmed to be penetration-free if tests are pass โ
. The runner types are described as follows:
The tested ๐ runner of this action is the Ubuntu NVIDIA GPU-Optimized Image for AI and HPC with an NVIDIA Tesla T4 (16 GB VRAM) with Driver version 570.133.20. This is not a self-hosted runner, meaning that each time the runner launches, all environments are ๐ฑ fresh.
We use the GitHub-hosted runner ๐ฅ๏ธ, but the actual simulation runs on a g6e.2xlarge AWS instance ๐. Since we start with a fresh ๐ฑ instance, the environment is clean ๐งน every time. We take advantage of the ability to deploy on the cloud; this action is performed in parallel, which reduces the total action time.
๐ฆ Action Artifacts
We generate zipped action artifacts ๐ฆ for each run. These artifacts include:
๐ Logs : Detailed logs of the simulation runs.
: Detailed logs of the simulation runs. ๐ Metrics : Performance metrics and statistics.
: Performance metrics and statistics. ๐น Videos: Simulated animations.
Please note that these artifacts will be deleted after a month.
โ๏ธ Ten Consecutive Runs
We know that you can't judge the reliability of contact resolution by simply watching a single success ๐ฅ video example. To ensure greater transparency, we implemented GitHub Actions to run many of our examples via automated GitHub Actions โ๏ธ, not just once, but 10 times in a row ๐. This means that a single failure out of 10 tests is considered a failure of the entire test suite!
Also, we apply small jitters to the position of objects in the scene ๐, so at each run, the scene is slightly different.
๐ก Deploying on Cloud Services
Our contact solver is designed for heavy use in cloud services โ๏ธ, enabling:
๐ฐ Cost-Effective Development : Quickly deploy testing environments ๐ and delete ๐๏ธ them when not in use, saving costs.
: 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 โฐ.
: 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 ๐ฅ๏ธ.
: 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.
: Users and developers can easily share the same hardware, kernel, and driver environment, making it easier to track and fix bugs. ๐ ๏ธ Free Maintenance Cost: No need to maintain hardware for everyday operations or introduce redundancy for malfunctions.
This is made possible with our purely web-based frontends ๐ and scalable capability ๐งฉ. 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 ๐.
Important: For all the services below, don't forget to โ delete the instance after use, or youโll be ๐ธ charged for nothing.
Select our template (Link).
Create an instance and click Open button.
๐ฆ Deploying on RunPod
Follow this link (Link) and deploy an instance using our template.
Click Connect button and open the HTTP Services link.
๐ฆ Deploying on Scaleway
Set zone to fr-par-2
Select type L4-1-24G or GPU-3070-S
or Choose Ubuntu Jammy GPU OS 12
Do not skip the Docker container creation in the installation process; it is required.
This setup costs approximately โฌ0.76 per hour.
CLI instructions are described in (Markdown).
Amazon Machine Image (AMI): Deep Learning Base AMI with Single CUDA (Ubuntu 22.04)
Instance Type: g6.2xlarge (Recommended)
(Recommended) This setup costs around $1 per hour.
Do not skip the Docker container creation in the installation process; it is required.
Select GPUs . We recommend the GPU type NVIDIA L4 because it's affordable and accessible, as it does not require a high quota. You may select T4 instead for testing purposes.
Do not check Enable Virtual Workstation (NVIDIA GRID) .
We recommend the machine type g2-standard-8 .
Choose the OS type Deep Learning VM with CUDA 12.4 M129 and set the disk size to 50GB .
As of late 2024, this configuration costs approximately $0.86 per hour in us-central1 (Iowa) and $1.00 per hour in asia-east1 (Taiwan) .
Port number 8080 is reserved by the OS image. Set $MY_WEB_PORT to 8888 . When connecting via gcloud , use the following format: gcloud compute ssh --zone "xxxx" "instance-name" -- -L 8080:localhost:8888 .
Do not skip the Docker container creation in the installation process; it is required.
CLI instructions are described in (Markdown).
โ๏ธ Citation
@software { ppf-contact-solver-2024 , title = { ZOZO's Contact Solver } , author = { Ryoichi Ando } , note = { https://github.com/st-tech/ppf-contact-solver } , year = 2024 , }
๐ Acknowledgements
The author thanks ZOZO, Inc. for permitting the release of the code and the team members for assisting with the internal paperwork for this project.