You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

228 lines
9.7 KiB

import sys
import os
import pathlib
import argparse
import subprocess
import json
from datetime import datetime
import pandas
def get_time_stamp():
return datetime.now().strftime("%Y-%b-%d-%H-%M-%S")
def find_rb_exe():
for build_dir in (pathlib.Path("."), pathlib.Path(__file__).resolve().parents[2] / "build"):
for sub_dir in "", "release", "debug":
rb_exe = build_dir / sub_dir / "FixingCollisions_ngui"
if rb_exe.is_file():
return rb_exe.resolve()
return None
def get_remote_storage():
for remote_name in "google-drive", "nyu-gdrive", None:
if remote_name is None:
print("Unable to find remote storage using rclone! "
"Videos will not be uploaded")
return None
r = subprocess.run(["rclone", "about", f"{remote_name}:"],
capture_output=True, text=True)
print(r.stdout)
print(r.stderr)
if (r.stderr.strip() == ""):
break
return f"{remote_name}:rigid-ipc/ipc-comparison/"
def create_parser():
parser = argparse.ArgumentParser(
description="Run a comparison between IPC and our method.")
parser.add_argument(
"--ipc-exe", "--ipc", "--ipc-bin", metavar=f"path/to/IPC_bin",
type=pathlib.Path, default=None, help="path to IPC executable")
parser.add_argument(
"--rigid-exe", "--rb-exe", metavar=f"path/to/FixingCollisions_ngui",
type=pathlib.Path, default=find_rb_exe(),
help="path to rigid simulation executable")
parser.add_argument(
"-i", "--input", metavar="path/to/input", type=pathlib.Path,
dest="input", default=None, help="path to input json(s)", nargs="+")
parser.add_argument(
"-o", "--output", metavar="path/to/output.csv", type=pathlib.Path,
dest="output", default=pathlib.Path("ipc-vs-rigid.csv"),
help="path to output CSV")
parser.add_argument(
"--no-ipc", action="store_true", default=False,
help="do not run the IPC simulation")
parser.add_argument(
"--no-rigid", action="store_true", default=False,
help="do not run the rigid simulation")
parser.add_argument(
"--no-video", action="store_true", default=False,
help="do not render a video of the sim")
parser.add_argument(
"--loglevel", default=3, type=int, choices=range(7),
help="set log level 0=trace, 1=debug, 2=info, 3=warn, 4=error, 5=critical, 6=off")
parser.add_argument(
"--rigid-args", default="", help=f"arguments to FixingCollisions_ngui")
parser.add_argument(
"--with-viewer", action="store_true", default=False,
help="run simulation through the viewer")
return parser
def parse_arguments():
parser = create_parser()
args = parser.parse_args()
if not args.no_ipc and args.ipc_exe is None:
parser.exit(1, f"IPC executable is required!\n")
if not args.no_rigid and args.rigid_exe is None:
parser.exit(1, f"Rigid simulation executable is required!\n")
if args.input is None:
args.input = [pathlib.Path(__file__).resolve().parent / "scripts"]
input_scripts = []
for input_file in args.input:
if input_file.is_file() and input_file.suffix == ".txt":
input_scripts.append(input_file.resolve())
elif input_file.is_dir():
for script_file in input_file.glob('**/*.txt'):
input_scripts.append(script_file.resolve())
args.input = input_scripts
return args
def append_stem(p, stem_suffix):
# return p.with_stem(p.stem + stem_suffix)
return p.parent / (p.stem + stem_suffix + p.suffix)
def main():
args = parse_arguments()
remote_storage = get_remote_storage()
scripts_dir = pathlib.Path(__file__).resolve().parent / "scripts"
fixtures_dir = pathlib.Path(__file__).resolve().parents[2] / "fixtures"
render_exe = args.rigid_exe.parent / "tools" / "render_simulation"
df = pandas.DataFrame(columns=[
"Scene", "IPC Video", "Rigid Video", "IPC Runtime", "Rigid Runtime",
"IPC Iterations", "Rigid Iterations",
"IPC Linear Solve Time", "IPC CCD Time",
"Rigid Linear Solve Time", "Rigid CCD Time"])
combined_rigid_profile = pandas.DataFrame()
combined_rigid_profile_filename = append_stem(
args.output, "-rigid-profile")
for script in args.input:
rel = script.relative_to(scripts_dir)
output = "output" / rel.parent / rel.stem
df_row = {"Scene": str(rel.parent / rel.stem)}
#######################################################################
# Run the IPC sim
if not args.no_ipc:
print(f"Running {script} in IPC")
subprocess.run([args.ipc_exe, "10" if args.with_viewer else "100",
script.resolve(), "-o", output / "ipc",
"--logLevel", str(args.loglevel)])
# Render the IPC sim
if not args.no_video:
print("Rendering IPC simulation")
video_name = f"{script.stem}-{get_time_stamp()}-ipc.mp4"
subprocess.run([str(render_exe), output / "ipc",
"-o", output / video_name,
"--loglevel", str(args.loglevel),
"--fps", "100"])
if remote_storage is not None:
remote_path = (f"{remote_storage}{rel.parent}")
subprocess.run(
["rclone", "copy", output / video_name, remote_path])
df_row["IPC Video"] = subprocess.run(
["rclone", "link", f"{remote_path}/{video_name}"],
capture_output=True, text=True).stdout.strip()
print(f"Uploaded video to {df_row['IPC Video']}")
# Get running time from info.txt
with open(output / "ipc" / "info.txt") as info:
lines = info.readlines()
df_row["IPC Runtime"] = float(lines[5].strip().split()[0])
print("IPC finished (total_runtime={:g}s)".format(
df_row["IPC Runtime"]))
df_row["IPC Iterations"] = int(lines[1].strip().split()[1])
df_row["IPC Linear Solve Time"] = sum([
float(lines[9].strip().split()[0]) for i in (9, 10, 11)])
lin_solve_time = sum([
float(lines[9].strip().split()[0]) for i in (9, 10, 11)])
df_row["IPC Linear Solve Time"] = (
f"{lin_solve_time / df_row['IPC Runtime'] * 100:g}%")
ccd_time = float(lines[20].strip().split()[0])
df_row["IPC CCD Time"] = (
f"{ccd_time / df_row['IPC Runtime'] * 100:g}%")
#######################################################################
# Run the corresponding rigid body sim
if not args.no_rigid:
fixture = fixtures_dir / rel.with_suffix(".json")
print(f"Running {fixture}")
subprocess.run([str(args.rigid_exe), str(fixture),
str(output), "--log", str(args.loglevel), "--nthreads", "16"]
+ args.rigid_args.split())
# Render the RB sim
if not args.no_video:
print("Rendering rigid simulation")
video_name = f"{script.stem}-{get_time_stamp()}-rigid.mp4"
subprocess.run([str(render_exe), output / "sim.json",
"-o", output / video_name,
"--loglevel", str(args.loglevel),
"--fps", "100"])
if remote_storage is not None:
remote_path = (f"{remote_storage}{rel.parent}")
subprocess.run(
["rclone", "copy", output / video_name, remote_path])
df_row["Rigid Video"] = subprocess.run(
["rclone", "link", f"{remote_path}/{video_name}"],
capture_output=True, text=True).stdout.strip()
print(f"Uploaded video to {df_row['Rigid Video']}")
with open(output / "sim.json") as sim:
sim_dict = json.load(sim)
df_row["Rigid Runtime"] = sum(
sim_dict["stats"]["step_timings"])
df_row["Rigid Iterations"] = sum(
sim_dict["stats"]["solver_iterations"])
log_dirs = list(filter(lambda p: p.is_dir(), output.glob("log*")))
if log_dirs:
profiler_dir = max(log_dirs, key=os.path.getmtime)
profiler_df = pandas.read_csv(
profiler_dir / "summary.csv", header=1, index_col=0,
skipinitialspace=True)
df_row["Rigid Linear Solve Time"] = (
profiler_df.percentage_time.get(
"NewtonSolver::compute_direction:linear_solve", 0))
df_row["Rigid CCD Time"] = profiler_df.percentage_time.get(
"DistanceBarrierConstraint::compute_earliest_toi", 0)
rigid_profile = pandas.DataFrame(
index=profiler_df.index.values)
rigid_profile[df_row["Scene"]] = profiler_df["percentage_time"]
combined_rigid_profile = pandas.concat(
[combined_rigid_profile, rigid_profile], axis=1)
combined_rigid_profile.to_csv(combined_rigid_profile_filename)
else:
print("Profiling not enabled")
#######################################################################
df.loc[df_row["Scene"]] = df_row
df.to_csv(args.output, index=False)
print(f"Results written to {args.output}")
if __name__ == "__main__":
main()