Skip to content

Commit a60cd9a

Browse files
committed
new line search for strain limiting implemented
1 parent 93a4b82 commit a60cd9a

File tree

5 files changed

+140
-48
lines changed

5 files changed

+140
-48
lines changed

README.md

Lines changed: 54 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Published in [ACM Transactions on Graphics (TOG)](https://dl.acm.org/doi/abs/10.
2828
- [⚡️ Requirements](#️-requirements)
2929
- [📝 Change History](#-change-history)
3030
- [🐍 How To Use](#-how-to-use)
31+
- [🔍 Obtaining Logs](#-obtaining-logs)
3132
- [🖼️ Catalogue](#️-catalogue)
3233
- [🚀 GitHub Actions](#-github-actions)
3334
- [💨 Getting Started](#-getting-started)
@@ -64,6 +65,7 @@ Published in [ACM Transactions on Graphics (TOG)](https://dl.acm.org/doi/abs/10.
6465

6566
## 📝 Change History
6667

68+
- (2024.12.27) Line search for strain limiting is improved [[Markdown]](./articles/bug.md#new-strain-limiting-line-search).
6769
- (2024.12.23) Added [[Bug Fixes and Updates]](./articles/bug.md)
6870
- (2024.12.21) Added a [house of cards example](./examples/cards.ipynb) [[Video]](https://drive.google.com/file/d/1PMdDnlyCsjinbvICKph_0UcXUfUvvUmZ/view)
6971
- (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)
@@ -164,13 +166,13 @@ session.export.animation(path)
164166
```
165167
<img src="./asset/image/drape.jpg" alt="drape">
166168

167-
### 🔍 Obtaining Logs
169+
## 🔍 Obtaining Logs
168170

169171
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.
170172

171173
```python
172-
# get list of logs files list[str]
173-
logs = session.get.log()
174+
# get the list of log names as list[str]
175+
logs = session.get.logfiles()
174176
assert 'per_video_frame' in logs
175177
assert 'advance.newton_steps' in logs
176178

@@ -187,23 +189,48 @@ newton_steps = session.get.numbers('advance.newton_steps')
187189
print('avg:', sum([n for _,n in newton_steps])/len(newton_steps))
188190
```
189191

190-
Here are the representative ones.
192+
Below are some representatives.
191193
`vid_time` refers to the video time in seconds and is recorded as `float`.
192-
`msec` refers to the consumed simulation time recorded as `int`.
194+
`ms` refers to the consumed simulation time in milliseconds recorded as `int`.
193195
`vid_frame` is the video frame count recorede as `int`.
194196

195197
| **Log Name** | **Description** | **Format**
196198
|---------------|----------------|------------
197-
| **per_video_frame** | Time per video frame | list[(vid_frame,msec)] |
198-
| **advance.matrix_assembly** | Matrix assembly time | list[(vid_time,msec)] |
199-
| **advance.linsolve** | Linear system solve time | list[(vid_time,msec)] |
200-
| **advance.line_search** | Line search time | list[(vid_time,msec)] |
201-
| **advance** | Time per step | list[(vid_time,msec)] |
199+
| **per_video_frame** | Time per video frame | list[(vid_frame,ms)] |
200+
| **advance.matrix_assembly** | Matrix assembly time | list[(vid_time,ms)] |
201+
| **advance.linsolve** | Linear system solve time | list[(vid_time,ms)] |
202+
| **advance.line_search** | Line search time | list[(vid_time,ms)] |
203+
| **advance** | Time per step | list[(vid_time,ms)] |
202204
| **advance.newton_steps** | Newton iterations per step | list[(vid_time,count)] |
203205
| **advance.num_contact** | Contact count | list[(vid_time,count)] |
204206
| **advance.max_sigma** | Max stretch | list(vid_time,strech) |
205207

206-
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 iteration 🧮. 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 may appear in the record at the same 📊 video time.
208+
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 may appear in the record at the same 📊 video time.
209+
210+
If you would like to retrieve the raw log stream, you can do so by
211+
212+
```python
213+
# Last 8 lines. Omit for everything.
214+
for line in session.get.log(n_lines=8):
215+
print(line)
216+
```
217+
218+
This will output something like:
219+
220+
```
221+
* dt: 1.000e-03
222+
* max_sigma: 1.045e+00
223+
* avg_sigma: 1.030e+00
224+
------ newton step 1 ------
225+
====== contact_matrix_assembly ======
226+
> dry_pass...0 msec
227+
> rebuild...7 msec
228+
> fillin_pass...0 msec
229+
```
230+
231+
If you would like to read `stdout` and `stderr`, you can do so using `session.get.stdout()` and `session.get.stderr()` (if it exists). They return `list[str]`.
232+
233+
All the log files 📂 are available ✅ and can be fetched ⬇️ during the simulation 🧑‍💻.
207234

208235
## 🖼️ Catalogue
209236

@@ -568,22 +595,22 @@ The author also extends thanks to the teams in the IP department for permitting
568595
569596
```
570597
@article{Ando2024CB,
571-
author = {Ando, Ryoichi},
572-
title = {A Cubic Barrier with Elasticity-Inclusive Dynamic Stiffness},
573-
year = {2024},
574-
issue_date = {December 2024},
575-
publisher = {Association for Computing Machinery},
576-
address = {New York, NY, USA},
577-
volume = {43},
578-
number = {6},
579-
issn = {0730-0301},
580-
url = {https://doi.org/10.1145/3687908},
581-
doi = {10.1145/3687908},
582-
journal = {ACM Trans. Graph.},
583-
month = nov,
584-
articleno = {224},
585-
numpages = {13},
586-
keywords = {collision, contact}
598+
author = {Ando, Ryoichi},
599+
title = {A Cubic Barrier with Elasticity-Inclusive Dynamic Stiffness},
600+
year = {2024},
601+
issue_date = {December 2024},
602+
publisher = {Association for Computing Machinery},
603+
address = {New York, NY, USA},
604+
volume = {43},
605+
number = {6},
606+
issn = {0730-0301},
607+
url = {https://doi.org/10.1145/3687908},
608+
doi = {10.1145/3687908},
609+
journal = {ACM Trans. Graph.},
610+
month = nov,
611+
articleno = {224},
612+
numpages = {13},
613+
keywords = {collision, contact}
587614
}
588615
```
589616

articles/bug.md

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,53 @@
11
## Bug Fixes 🐞 and Updates 🔄
22

3+
### New Strain Limiting Line Search
4+
5+
On 2024-Dec-27, we made minor improvements to the line search algorithm for strain limiting.
6+
The new algorithm is laid out below.
7+
Compared to the one presented in the supplementary PDF, this new algorithm searches for `t` (time of impact) with exact numerical precision; no parameters, such as the `eps` tolerance for checking convergence or the maximum loop count, are necessary.
8+
This new approach converges quickly and exits the loop fast.
9+
10+
```c++
11+
/* Apache v2.0 License */
12+
float line_search_strain_limiting(
13+
const Mat3x2f &F0, // Deformation gradient at t = 0.0
14+
const Mat3x2f &F1, // Deformation gradient at t = 1.0
15+
float t, // Maximal time of impact, such as t = 1.0
16+
float max_sigma ) // Maximal sigma, such as 1.01
17+
{
18+
const Mat3x2f dF = F1 - F0;
19+
if (svd3x2(F0 + t * dF).S.max() >= max_sigma) {
20+
float upper_t = t;
21+
float lower_t = 0.0f;
22+
float window = upper_t - lower_t;
23+
while (true) {
24+
t = 0.5f * (upper_t + lower_t);
25+
float diff = svd3x2(F0 + t * dF).S.maxCoeff() - max_sigma;
26+
if (diff < 0.0f) {
27+
lower_t = t;
28+
} else {
29+
upper_t = t;
30+
}
31+
float new_window = upper_t - lower_t;
32+
if (new_window == window) {
33+
break;
34+
} else {
35+
window = new_window;
36+
}
37+
}
38+
t = lower_t;
39+
}
40+
return t;
41+
}
42+
```
43+
344
### BVH Construction
445
546
In the supplementary PDF, we mentioned that the BVH is reconstructed every 10 video frames; however, this is not what is implemented in the public code.
647
748
In this code, the BVH is continuously updated on the CPU side in the background without blocking the simulation. At the beginning of each step, we check if the BVH construction is finished, and if it is, we update the GPU buffer.
849
9-
### Hang Example (2024-Dec-22)
50+
### Hang Example
1051
1152
On 2024-Dec-22, we encountered a situation where the PCG solver in our "hang" example `hang.ipynb` failed to converge, resulting in a simulation failure.
1253

examples/frontend/_session_.py

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -266,14 +266,40 @@ class SessionGet:
266266
def __init__(self, session: "Session"):
267267
self._session = session
268268

269-
def log(self) -> list[str]:
269+
def logfiles(self) -> list[str]:
270270
path = os.path.join(self._session.info.path, "output", "data")
271271
result = []
272272
for file in os.listdir(path):
273273
if file.endswith(".out"):
274274
result.append(file.replace(".out", ""))
275275
return result
276276

277+
def _tail_file(self, path: str, n_lines: Optional[int] = None) -> list[str]:
278+
if os.path.exists(path):
279+
with open(path, "r") as f:
280+
lines = f.readlines()
281+
lines = [line.rstrip("\n") for line in lines]
282+
if n_lines is not None:
283+
return lines[-n_lines:]
284+
else:
285+
return lines
286+
return []
287+
288+
def log(self, n_lines: Optional[int] = None) -> list[str]:
289+
return self._tail_file(
290+
os.path.join(self._session.info.path, "output", "cudasim_log.txt"), n_lines
291+
)
292+
293+
def stdout(self, n_lines: Optional[int] = None) -> list[str]:
294+
return self._tail_file(
295+
os.path.join(self._session.info.path, "stdout.log"), n_lines
296+
)
297+
298+
def stderr(self, n_lines: Optional[int] = None) -> list[str]:
299+
return self._tail_file(
300+
os.path.join(self._session.info.path, "error.log"), n_lines
301+
)
302+
277303
def numbers(self, name: str):
278304
def float_or_int(var):
279305
var = float(var)
@@ -427,13 +453,10 @@ def init(self, scene: FixedScene) -> "Session":
427453

428454
def finished(self) -> bool:
429455
finished_path = os.path.join(self.output.path, "finished.txt")
430-
err_path = os.path.join(self.info.path, "error.log")
431-
if os.path.exists(err_path):
432-
file = open(err_path, "r")
433-
lines = file.readlines()
434-
if len(lines) > 0:
435-
for line in lines:
436-
print(line)
456+
error = self.get.stderr()
457+
if len(error) > 0:
458+
for line in error:
459+
print(line)
437460
return os.path.exists(finished_path)
438461

439462
def start(self, param: Param, force: bool = True, blocking=False) -> "Session":

src/backend.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ impl Backend {
133133
let face = builder::build_bvh(&vertex, Some(&face));
134134
let edge = builder::build_bvh(&vertex, Some(&edge));
135135
let vertex = builder::build_bvh::<1>(&vertex, None);
136-
result_sender.send(BvhSet { face, edge, vertex }).unwrap();
136+
let _ = result_sender.send(BvhSet { face, edge, vertex });
137137
}
138138
});
139139

src/cpp/strainlimiting/strainlimiting.cu

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -109,26 +109,27 @@ float line_search(const DataSet &data, const Kinematic &kinematic,
109109
utility::compute_deformation_grad(x1, inv_rest2x2[i]);
110110
const Mat3x2f dF = F1 - F0;
111111
if (utility::svd3x2(F0 + t * dF).S.maxCoeff() >= max_sigma) {
112-
float tol = param.ccd_reduction *
113-
(max_sigma - utility::svd3x2(F0).S.maxCoeff());
114-
float target = max_sigma - 2.0f * tol;
115112
float upper_t = t;
116113
float lower_t = 0.0f;
117-
for (unsigned k = 0; k < param.binary_search_max_iter; ++k) {
114+
float window = upper_t - lower_t;
115+
while (true) {
118116
t = 0.5f * (upper_t + lower_t);
119117
Svd3x2 svd = utility::svd3x2(F0 + t * dF);
120-
float diff = svd.S.maxCoeff() - target;
121-
if (fabs(diff) < tol) {
122-
break;
123-
} else if (diff < 0.0f) {
118+
float diff = svd.S.maxCoeff() - max_sigma;
119+
if (diff < 0.0f) {
124120
lower_t = t;
125121
} else {
126122
upper_t = t;
127123
}
124+
float new_window = upper_t - lower_t;
125+
if (new_window == window) {
126+
break;
127+
} else {
128+
window = new_window;
129+
}
128130
}
129-
while (utility::svd3x2(F0 + t * dF).S.maxCoeff() >= target) {
130-
t *= param.dt_decrease_factor;
131-
}
131+
t = lower_t;
132+
assert(t > std::numeric_limits<float>::epsilon());
132133
}
133134
}
134135
toi[i] = t;

0 commit comments

Comments
 (0)