Skip to content

Commit 00a072e

Browse files
authored
Merge pull request #2 from openworm/development
Sibernetic & dockerfile updates
2 parents b578b66 + 0b8d693 commit 00a072e

File tree

9 files changed

+385
-202
lines changed

9 files changed

+385
-202
lines changed

Dockerfile

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
FROM python:3.12-slim
2+
3+
WORKDIR /app
4+
5+
RUN apt-get update && apt-get install -y \
6+
build-essential \
7+
curl \
8+
software-properties-common \
9+
git \
10+
libxrender1 procps libgl1-mesa-glx xvfb \
11+
&& rm -rf /var/lib/apt/lists/*
12+
13+
14+
COPY requirements.txt ./
15+
RUN pip3 install -r requirements.txt
16+
17+
18+
COPY *.py *.stl *.obj ./
19+
COPY Sibernetic/* ./Sibernetic/
20+
COPY NeuroML2/* ./NeuroML2/
21+
COPY NeuroML2/cells/* ./NeuroML2/cells/
22+
23+
EXPOSE 8501
24+
25+
HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health
26+
27+
ENTRYPOINT ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0"]

SiberneticReplay.py

Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
"""
2+
A PyVista based viewer/player for saved Sibernetic simulations
3+
4+
Loads in the generated position_buffer.txt file
5+
6+
"""
7+
8+
import pyvista as pv
9+
import sys
10+
import os
11+
import time
12+
import json
13+
import numpy as np
14+
import matplotlib.pyplot as plt
15+
16+
17+
last_meshes = {}
18+
19+
replay_speed = 0.05 # seconds between frames
20+
replaying = False
21+
22+
all_points = []
23+
all_point_types = []
24+
25+
26+
plotter = None
27+
offset3d_ = (0, 0, 0)
28+
slider = None
29+
30+
show_boundary = False
31+
32+
33+
def get_color_info_for_type(type_):
34+
"""
35+
Get color, info string and point size for a given point type
36+
returns: color, info, size
37+
"""
38+
39+
if type_ == 1.1:
40+
return "#8BEBFC", "liquid 1", 5
41+
elif type_ == 1.2:
42+
return "#3ACFF0", "liquid 2", 5
43+
elif type_ == 2.1:
44+
return "yellow", "elastic 1", 5
45+
elif type_ == 2.2:
46+
return "#FF0000", "elastic 2", 5
47+
elif type_ > 2 and type_ < 3:
48+
return "#00cc00", "elastic variable", 5
49+
elif type_ == 3:
50+
return "grey", "boundary 0", 3
51+
elif type_ == 3.1:
52+
return "black", "boundary 1", 7
53+
else:
54+
return "orange", "unknown", 5
55+
56+
57+
def add_sibernetic_model(
58+
pl,
59+
position_file="Sibernetic/position_buffer.txt",
60+
report_file=None,
61+
swap_y_z=False,
62+
offset3d=(0, 0, 0),
63+
include_boundary=False,
64+
):
65+
global \
66+
all_points, \
67+
all_point_types, \
68+
last_meshes, \
69+
plotter, \
70+
offset3d_, \
71+
slider, \
72+
show_boundary
73+
74+
offset3d_ = offset3d
75+
plotter = pl
76+
show_boundary = include_boundary
77+
78+
points = {}
79+
types = []
80+
81+
line_count = 0
82+
pcount = 0
83+
time_count = 0
84+
logStep = None
85+
86+
report_data = None
87+
count_point_types = {}
88+
89+
if report_file is not None:
90+
sim_dir = os.path.dirname(os.path.abspath(report_file))
91+
report_data = json.load(open(report_file, "r"))
92+
print(report_data)
93+
position_file = os.path.join(sim_dir, "position_buffer.txt")
94+
95+
if "worm" in report_data["configuration"]:
96+
muscle_activation_file = os.path.join(
97+
sim_dir, "muscles_activity_buffer.txt"
98+
)
99+
print("Loading muscle activation file from: %s" % muscle_activation_file)
100+
musc_dat = np.loadtxt(muscle_activation_file, delimiter="\t").T
101+
print(musc_dat)
102+
print(musc_dat.shape)
103+
# plt.imshow(musc_dat, interpolation="none", aspect="auto", cmap="YlOrRd")
104+
105+
f, ax = plt.subplots(tight_layout=True)
106+
ax.imshow(musc_dat, interpolation="none", aspect="auto", cmap="YlOrRd")
107+
# ax.set_ylim([-1, 1])
108+
ax.set_xlabel("Time (s)")
109+
_ = ax.set_ylabel("Muscle")
110+
111+
h_chart = pv.ChartMPL(f, size=(0.35, 0.35), loc=(0.02, 0.06))
112+
h_chart.title = None
113+
h_chart.border_color = "white"
114+
h_chart.show_title = False
115+
h_chart.background_color = (1.0, 1.0, 1.0, 0.4)
116+
pl.add_chart(
117+
h_chart,
118+
)
119+
120+
first_pass_complete = False
121+
122+
for line in open(position_file):
123+
ws = line.split()
124+
# print(ws)
125+
if line_count == 6:
126+
numOfElasticP = int(ws[0])
127+
if line_count == 7:
128+
numOfLiquidP = int(ws[0])
129+
if line_count == 8:
130+
numOfBoundaryP = int(ws[0])
131+
if line_count == 9:
132+
timeStep = float(ws[0]) # noqa: F841
133+
if line_count == 10:
134+
logStep = int(ws[0])
135+
136+
if len(ws) == 4:
137+
type_ = float(ws[3])
138+
if type_ not in points:
139+
points[type_] = []
140+
141+
if not first_pass_complete:
142+
if type_ not in count_point_types:
143+
count_point_types[type_] = 0
144+
count_point_types[type_] += 1
145+
146+
if swap_y_z:
147+
points[type_].append([float(ws[1]), 1 * float(ws[0]), float(ws[2])])
148+
else:
149+
points[type_].append([float(ws[0]), float(ws[1]), float(ws[2])])
150+
151+
types.append(type_)
152+
153+
if logStep is not None:
154+
pcount += 1
155+
156+
if pcount == numOfBoundaryP + numOfElasticP + numOfLiquidP:
157+
first_pass_complete = True
158+
print(
159+
"End of one batch of %i total points (%i types), at line %i, time: %i"
160+
% (pcount, len(points), line_count, time_count)
161+
)
162+
all_points.append(points)
163+
all_point_types.append(types)
164+
165+
points = {}
166+
types = []
167+
pcount = 0
168+
numOfBoundaryP = 0
169+
170+
time_count += 1
171+
172+
line_count += 1
173+
174+
print(
175+
"Loaded positions with %i elastic, %i liquid and %i boundary points (%i total), over %i lines"
176+
% (
177+
numOfElasticP,
178+
numOfLiquidP,
179+
numOfBoundaryP,
180+
numOfElasticP + numOfLiquidP + numOfBoundaryP,
181+
line_count,
182+
)
183+
)
184+
185+
print("Num of time points found: %i" % len(all_points))
186+
print("Count of point types found: %s" % dict(sorted(count_point_types.items())))
187+
188+
create_mesh(0)
189+
190+
max_time = len(all_points) - 1
191+
192+
slider = pl.add_slider_widget(
193+
create_mesh, rng=[0, max_time], value=0, title="Time point", style="modern"
194+
)
195+
196+
pl.add_checkbox_button_widget(play_animation, value=False)
197+
198+
199+
def play_animation(play):
200+
global plotter, last_meshes, all_points, all_point_types, replaying, slider
201+
print("Playing animation: %s" % play)
202+
203+
if not play:
204+
replaying = False
205+
print("Animation stopped.")
206+
return
207+
else:
208+
replaying = True
209+
print("Animation started.")
210+
211+
if last_meshes is None:
212+
print("No meshes to animate. Please load a model first.")
213+
return
214+
215+
for i in range(len(all_points)):
216+
if not replaying:
217+
break
218+
curr_time = slider.GetSliderRepresentation().GetValue()
219+
220+
print(
221+
" --- Animating step %i (curr_time: %s) of %i, %s"
222+
% (i, curr_time, len(all_points), play)
223+
)
224+
next_time = curr_time + 1
225+
slider.GetSliderRepresentation().SetValue(next_time)
226+
227+
create_mesh(next_time)
228+
plotter.update()
229+
plotter.render()
230+
time.sleep(replay_speed)
231+
232+
233+
def create_mesh(step):
234+
step_count = step
235+
value = step_count
236+
global all_points, last_meshes, plotter, offset3d_, replaying, show_boundary
237+
238+
index = int(value)
239+
if index >= len(all_points):
240+
print(
241+
"Index %i out of bounds for all_points with length %i"
242+
% (index, len(all_points))
243+
)
244+
replaying = False
245+
return
246+
247+
print(" -- Creating new mesh at time point: %s (%s) " % (index, value))
248+
curr_points_dict = all_points[index]
249+
250+
print(" Plotting %i point types" % (len(curr_points_dict)))
251+
252+
for type_, curr_points in curr_points_dict.items():
253+
color, info, size = get_color_info_for_type(type_)
254+
is_boundary = "boundary" in info
255+
if show_boundary is False and is_boundary:
256+
continue
257+
258+
print(
259+
" - Plotting %i points of type '%s' (%s), color: %s, size: %i"
260+
% (len(curr_points), type_, info, color, size)
261+
)
262+
263+
if len(curr_points) == 0:
264+
continue
265+
if type_ not in last_meshes:
266+
last_meshes[type_] = pv.PolyData(curr_points)
267+
last_meshes[type_].translate(offset3d_, inplace=True)
268+
269+
# last_actor =
270+
plotter.add_mesh(
271+
last_meshes[type_],
272+
render_points_as_spheres=True,
273+
point_size=size,
274+
color=color,
275+
)
276+
else:
277+
if not is_boundary:
278+
last_meshes[type_].points = curr_points
279+
last_meshes[type_].translate(
280+
(offset3d_[0], offset3d_[1], offset3d_[2]), inplace=True
281+
)
282+
else:
283+
print("Boundary points not translated")
284+
285+
plotter.render()
286+
# time.sleep(0.1)
287+
288+
return
289+
290+
291+
if __name__ == "__main__":
292+
plotter = pv.Plotter()
293+
294+
position_file = "buffers/position_buffer.txt" # can be overwritten by arg
295+
report_file = None
296+
297+
if not os.path.isfile(position_file):
298+
position_file = (
299+
"Sibernetic/position_buffer.txt" # example location in Worm3DViewer repo
300+
)
301+
302+
include_boundary = False
303+
304+
if "-b" in sys.argv:
305+
include_boundary = True
306+
else:
307+
print("Run with -b to display boundary box")
308+
309+
if len(sys.argv) > 1 and os.path.isfile(sys.argv[1]):
310+
if "json" in sys.argv[1]:
311+
position_file = None
312+
report_file = sys.argv[1]
313+
else:
314+
position_file = sys.argv[1]
315+
316+
add_sibernetic_model(
317+
plotter,
318+
position_file,
319+
report_file,
320+
swap_y_z=True,
321+
include_boundary=include_boundary,
322+
)
323+
plotter.set_background("white")
324+
plotter.add_axes()
325+
plotter.camera_position = "zx"
326+
plotter.camera.roll = 90
327+
plotter.camera.elevation = 45
328+
print(plotter.camera_position)
329+
330+
if "-nogui" not in sys.argv:
331+
plotter.show()

app.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from neuromlmodel import add_neuroml_model # noqa: F401
2-
from siberneticmodel import add_sibernetic_model # noqa: F401
2+
from SiberneticReplay import add_sibernetic_model # noqa: F401
33
from virtualworm import add_virtualworm_muscles # noqa: F401
44
from virtualworm import add_virtualworm_neurons # noqa: F401
55

generate.sh

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/bin/bash
2+
set -ex
3+
4+
no_cache_flag=""
5+
if [[ ($# -eq 1) && ($1 == '-r') ]]; then
6+
no_cache_flag="--no-cache"
7+
fi
8+
9+
# Set the platform flag if we're on ARM
10+
arch=$(uname -m)
11+
if [[ "$arch" == "arm64" || "$arch" == "aarch64" ]]; then
12+
platform_flag="--platform linux/amd64"
13+
else
14+
platform_flag=""
15+
fi
16+
17+
18+
docker build $platform_flag -t worm3dviewer $no_cache_flag .

0 commit comments

Comments
 (0)