Skip to content

Commit

Permalink
remove thrust dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
ryichsecondary committed Dec 16, 2024
1 parent 224b4d0 commit 902000a
Show file tree
Hide file tree
Showing 29 changed files with 811 additions and 162 deletions.
15 changes: 5 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Published in [ACM Transactions on Graphics (TOG)](https://dl.acm.org/doi/abs/10.
- 🐳 A Docker environment (see [below](#-getting-started)).

## 🐍 How To Use
Our frontend is accessible through a browser using our built-in JupyterLab interface (see this video [[Video]](https://drive.google.com/file/d/1MtOEOvm5KEPwvgO-oeMY736he7H2DoNp/view?usp=sharing)).
Our frontend is accessible through a browser using our built-in JupyterLab interface (see this video [[Video]](https://drive.google.com/file/d/1n068Ai_hlfgapf2xkAutOHo3PkLpJXA4/view?usp=sharing)).
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.
Expand All @@ -46,7 +46,7 @@ Please look into the [examples](./examples/) directory for more examples.
from frontend import App

# make an app with the label "drape"
app = App("drape").clear()
app = App("drape", renew=True)

# create a square mesh resolution 128
V, F = app.mesh.square(res=128)
Expand Down Expand Up @@ -105,8 +105,8 @@ session.stream()
# or interactively view the animation sequences
session.animate()

# save the results (run this separately in a new cell)
session.export("five-sheets.ply")
# export all simulated frames (downloadable from the file browser)
session.export_animation(f"export/{session.info.name}")
```
<img src="./asset/image/drape.jpg" alt="drape">

Expand Down Expand Up @@ -174,11 +174,6 @@ docker run -it `
Windows users do not need to install the NVIDIA Container Toolkit.
> [!WARNING]
> We have confirmed that some NVIDIA drivers result in runtime errors 🚫.
For example, driver version `560.94` leads to a simulation failure at startup.
If you encounter such an issue ⚠️, try upgrading to the latest driver 🔄 or, if necessary, downgrading to a previous version ⬇️. I would appreciate it if you could open an issue 📝 to report the driver version that causes runtime errors 🚫, so that others can know which driver should be avoided.
### 🐧 Linux
Linux users will also need to install Docker 🐋 on their system.
Expand Down Expand Up @@ -268,7 +263,7 @@ python3 warmup.py
```
> [!NOTE]
> If you’re suspicious, you can look around ```warmup.py``` before you proceed.
> If you’re suspicious, you can look around ```warmup.py``` before you proceed. Run `less warmup.py`, scroll all the way to the bottom, and type `q` to quit.
Now we're set. Let's kick in the compilation!🏃
Expand Down
1 change: 0 additions & 1 deletion build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use std::env;
use std::process::Command;
use num_cpus;

fn main() {
let out_dir = env::var("OUT_DIR").unwrap();
Expand Down
26 changes: 24 additions & 2 deletions examples/curtain.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"source": [
"from frontend import App\n",
"\n",
"app = App(\"curtain\").clear()\n",
"app = App(\"curtain\", renew=True)\n",
"\n",
"V, F = app.mesh.square(res=64)\n",
"app.asset.add.tri(\"sheet\", V, F)\n",
Expand Down Expand Up @@ -44,10 +44,32 @@
"param.set(\"dt\", 0.01)\n",
"param.set(\"min-newton-steps\", 8)\n",
"\n",
"session = app.session.create(\"dt-001-newton-8\").init(fixed)\n",
"session = app.session.create(\"dt-001-newton-8-curtain\").init(fixed)\n",
"session.start(param).preview();\n",
"session.stream();"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2aa65b45-4226-40cd-8e57-febbb11f6fe6",
"metadata": {},
"outputs": [],
"source": [
"# run this cell after sufficnt frames are simulated\n",
"session.animate()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cb760aaa-cb65-476c-97f2-60891eda4b76",
"metadata": {},
"outputs": [],
"source": [
"# export all simulated frames\n",
"session.export_animation(f\"export/{session.info.name}\")"
]
}
],
"metadata": {
Expand Down
26 changes: 24 additions & 2 deletions examples/drape.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"from frontend import App\n",
"\n",
"# make an app with the label \"drape\"\n",
"app = App(\"drape\").clear()\n",
"app = App(\"drape\", renew=True)\n",
"\n",
"# create a square mesh resolution 128\n",
"V, F = app.mesh.square(res=128)\n",
Expand Down Expand Up @@ -68,14 +68,36 @@
"param.set(\"frames\", 100)\n",
"\n",
"# create a new session and initialize with the compiled scene\n",
"session = app.session.create(\"dt-001\").init(fixed)\n",
"session = app.session.create(\"dt-001-drape\").init(fixed)\n",
"\n",
"# start the simulation and live-preview the results\n",
"session.start(param).preview();\n",
"\n",
"# also show simulation logs in realtime\n",
"session.stream();"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9a1476ea-5daa-4eb2-8c00-b1cb4918e936",
"metadata": {},
"outputs": [],
"source": [
"# run this cell after sufficnt frames are simulated\n",
"session.animate()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3c782236-bb47-4109-af66-abb0b30a905d",
"metadata": {},
"outputs": [],
"source": [
"# export all simulated frames\n",
"session.export_animation(f\"export/{session.info.name}\")"
]
}
],
"metadata": {
Expand Down
8 changes: 5 additions & 3 deletions examples/frontend/_app_.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@
class App:
def __init__(self, name: str, renew: bool = False, cache_dir: str = ""):
self.extra = Extra()
self._name = name
self._root = os.path.expanduser(os.path.join("~", ".local", "ppf-cts", name))
self._path = os.path.join(self._root, "app.pickle")
proj_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
if not cache_dir:
if cache_dir:
self.cache_dir = cache_dir
else:
self.cache_dir = os.path.expanduser(os.path.join("~", ".cache", "ppf-cts"))
if not os.path.exists(self.cache_dir):
os.makedirs(self.cache_dir)
Expand All @@ -40,8 +43,7 @@ def clear(self) -> "App":
self.asset.clear()
self.scene.clear()
self.session.clear()
self.save()
return self
return App(self._name, True, self.cache_dir)

def darkmode(self) -> "App":
self.plot.darkmode(True)
Expand Down
2 changes: 2 additions & 0 deletions examples/frontend/_scene_.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,8 @@ def export(
o3d_static.vertex_colors = o3d.utility.Vector3dVector(self._static_color)
o3d_mesh += o3d_static

if not os.path.exists(os.path.dirname(path)):
os.makedirs(os.path.dirname(path))
o3d.io.write_triangle_mesh(path, o3d_mesh)
return self

Expand Down
55 changes: 36 additions & 19 deletions examples/frontend/_session_.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,21 +195,28 @@ def param(self) -> Param:
return Param(self._proj_root)


class SessionInfo:
def __init__(self, name: str, path: str):
self.name = name
self.path = path


class Session:
def __init__(self, app_root: str, proj_root: str, name: str, save_func):
self._app_root = app_root
self._proj_root = proj_root
self._fixed = None
self._path = os.path.expanduser(os.path.join(app_root, "session", name))
path = os.path.expanduser(os.path.join(app_root, "session", name))
self.info = SessionInfo(name, path)
self._save_func = save_func
self._update_preview_interval = 1.0 / 60.0
self._update_terminal_interval = 1.0 / 30.0
self._update_table_interval = 0.25
self.delete()

def delete(self):
if os.path.exists(self._path):
shutil.rmtree(self._path)
if os.path.exists(self.info.path):
shutil.rmtree(self.info.path)

def _check_ready(self):
if self._fixed is None:
Expand All @@ -223,39 +230,39 @@ def init(self, scene: FixedScene) -> "Session":

self._fixed = scene

if os.path.exists(self._path):
shutil.rmtree(self._path)
if os.path.exists(self.info.path):
shutil.rmtree(self.info.path)
else:
os.makedirs(self._path)
os.makedirs(self.info.path)

if self._fixed is not None:
self._fixed.export_fixed(self._path, True)
self._fixed.export_fixed(self.info.path, True)
else:
raise ValueError("Scene and param must be initialized")

self._save_func()
return self

def output_path(self) -> str:
return os.path.join(self._path, "output")
return os.path.join(self.info.path, "output")

def export_shell_command(
self,
param: Param,
) -> str:
param.export(self._path)
param.export(self.info.path)
program_path = os.path.join(
self._proj_root, "target", "release", "ppf-contact-solver"
)
if os.path.exists(program_path):
command = " ".join(
[
program_path,
f"--path {self._path}",
f"--path {self.info.path}",
f"--output {self.output_path()}",
]
)
path = os.path.join(self._path, "command.sh")
path = os.path.join(self.info.path, "command.sh")
with open(path, "w") as f:
f.write(command)
os.chmod(path, 0o755)
Expand All @@ -273,8 +280,8 @@ def start(self, param: Param, force: bool = True, blocking=False) -> "Session":
display(self._terminate_button("Terminate Now"))
return self
cmd_path = self.export_shell_command(param)
err_path = os.path.join(self._path, "error.log")
log_path = os.path.join(self._path, "stdout.log")
err_path = os.path.join(self.info.path, "error.log")
log_path = os.path.join(self.info.path, "stdout.log")
if blocking:
subprocess.run(cmd_path, cwd=self._proj_root, shell=True)
return self
Expand All @@ -290,7 +297,9 @@ def start(self, param: Param, force: bool = True, blocking=False) -> "Session":
if process.poll() is not None:
raise ValueError("Solver failed to start")
else:
init_path = os.path.join(self._path, "output", "data", "initialize.out")
init_path = os.path.join(
self.info.path, "output", "data", "initialize.out"
)
time.sleep(1)
while is_running():
if os.path.exists(init_path):
Expand All @@ -301,7 +310,7 @@ def start(self, param: Param, force: bool = True, blocking=False) -> "Session":
raise ValueError("Solver failed to start")

def get_number(self, name: str):
path = os.path.join(self._path, "output", "data", f"{name}.out")
path = os.path.join(self.info.path, "output", "data", f"{name}.out")
if os.path.exists(path):
with open(path, "r") as f:
lines = f.readlines()
Expand All @@ -313,7 +322,7 @@ def get_number(self, name: str):
return None

def _get_vertex_frame_count(self) -> int:
path = os.path.join(self._path, "output")
path = os.path.join(self.info.path, "output")
max_frame = 0
if os.path.exists(path):
files = os.listdir(path)
Expand All @@ -324,7 +333,7 @@ def _get_vertex_frame_count(self) -> int:
return max_frame

def _get_latest_frame(self) -> int:
path = os.path.join(self._path, "output")
path = os.path.join(self.info.path, "output")
if os.path.exists(path):
files = os.listdir(path)
frames = []
Expand All @@ -340,7 +349,7 @@ def _get_vertex(self, n: Optional[int] = None) -> Optional[tuple[np.ndarray, int
if self._fixed is None:
raise ValueError("Scene must be initialized")
else:
path = os.path.join(self._path, "output")
path = os.path.join(self.info.path, "output")
if os.path.exists(path):
if n is None:
files = os.listdir(path)
Expand Down Expand Up @@ -541,14 +550,22 @@ def export(
self._fixed.export(vert, path, include_static)
return self

def export_animation(self, path: str, ext="ply", include_static: bool = True):
if os.path.exists(path):
shutil.rmtree(path)
else:
os.makedirs(path)
for i in tqdm(range(self._get_latest_frame()), desc="export", ncols=70):
self.export(os.path.join(path, f"frame_{i}.{ext}"), i, include_static)

def stream(self, n_lines=40) -> "Session":
log_widget = widgets.HTML()
display(log_widget)
button = widgets.Button(description="Stop Live Stream")
display(widgets.HBox((button, self._terminate_button())))

stop = False
log_path = os.path.join(self._path, "output", "cudasim_log.txt")
log_path = os.path.join(self.info.path, "output", "cudasim_log.txt")
if os.path.exists(log_path):

def live_stream(self):
Expand Down
26 changes: 24 additions & 2 deletions examples/hang.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"source": [
"from frontend import App\n",
"\n",
"app = App(\"hang\").clear()\n",
"app = App(\"hang\", renew=True)\n",
"\n",
"V, F = app.mesh.square(res=128)\n",
"app.asset.add.tri(\"sheet\", V, F)\n",
Expand All @@ -35,10 +35,32 @@
"param.set(\"dt\", 0.01).set(\"frames\",200)\n",
"param.dyn(\"gravity\").time(1).hold().time(1.1).change(9.8).time(2.0).change(-9.8)\n",
"\n",
"session = app.session.create(\"hang-two-pins\").init(fixed)\n",
"session = app.session.create(\"two-pins-hang\").init(fixed)\n",
"session.start(param).preview();\n",
"session.stream();"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "45fa755d-8e22-41e3-8a91-0e9dcbd8ee14",
"metadata": {},
"outputs": [],
"source": [
"# run this cell after sufficnt frames are simulated\n",
"session.animate()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "64ab11bd-a5d6-4651-b816-3d7fa2134ec7",
"metadata": {},
"outputs": [],
"source": [
"# export all simulated frames\n",
"session.export_animation(f\"export/{session.info.name}\")"
]
}
],
"metadata": {
Expand Down
Loading

0 comments on commit 902000a

Please sign in to comment.