Skip to content

Commit

Permalink
Merge branch 'master' into development
Browse files Browse the repository at this point in the history
  • Loading branch information
pgleeson committed Mar 22, 2024
2 parents c2fefba + 7e6823e commit b62bc52
Show file tree
Hide file tree
Showing 10 changed files with 406 additions and 24 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@
/WormSim/sundials-2.3.0/config.log
/WormSim/sundials-2.3.0/config.status
/WormSim/sundials-2.3.0/src/*/.libs
/WormSim/Model/objects.csv
.DS_Store
.ipynb_checkpoints
__pycache__
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

[![C/C++ CI](https://github.com/OpenSourceBrain/CelegansNeuromechanicalGaitModulation/actions/workflows/build.yml/badge.svg)](https://github.com/OpenSourceBrain/CelegansNeuromechanicalGaitModulation/actions/workflows/build.yml)

![](http://www.opensourcebrain.org/attachments/download/33/Selection_095.png)
<img width="400" alt="MatlabView" src="images/MatlabView.png"/>&nbsp;&nbsp;&nbsp;<img width="400" alt="PythonView" src="images/Replay.gif"/>

A model of C. elegans locomotion described in Boyle, Berri and Cohen, [Gait modulation in C. elegans: an integrated neuromechanical model](http://www.frontiersin.org/Computational_Neuroscience/10.3389/fncom.2012.00010/abstract), Front. Comput. Neurosci., 2012.

A model of <i>C. elegans</i> locomotion described in Boyle, Berri and Cohen, [Gait modulation in C. elegans: an integrated neuromechanical model](http://www.frontiersin.org/Computational_Neuroscience/10.3389/fncom.2012.00010/abstract), Front. Comput. Neurosci., 2012.

For information on installing and running this code, see [here](https://github.com/OpenSourceBrain/CelegansNeuromechanicalGaitModulation/tree/master/WormSim).


2 changes: 1 addition & 1 deletion WormSim/MatlabSupport/WormView.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
%during previous runs.
usingObjects = exist('../Model/objects.csv','file');
if usingObjects
Objects = importdata('../Model/objects.csv');
Objects = importdata('../Model/objects.csv', ',');
Sz = size(Objects);
Nobj = Sz(1);
end
Expand Down
133 changes: 133 additions & 0 deletions WormSim/Model/Player.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Based on example at https://stackoverflow.com/questions/46325447/animated-interactive-plot-using-matplotlib

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import mpl_toolkits.axes_grid1
import matplotlib.widgets


class Player(FuncAnimation):
def __init__(
self,
fig,
func,
frames=None,
init_func=None,
fargs=None,
save_count=None,
mini=0,
maxi=100,
pos=(0.125, 0.92),
**kwargs
):
self.i = 0
self.min = mini
self.max = maxi
self.runs = True
self.forwards = True
self.fig = fig
self.func = func
self.setup(pos)
FuncAnimation.__init__(
self,
self.fig,
self.update,
frames=self.play(),
init_func=init_func,
fargs=fargs,
save_count=save_count,
**kwargs
)

def play(self):
while self.runs:
self.i = self.i + self.forwards - (not self.forwards)
if self.i > self.min and self.i < self.max:
yield self.i
else:
self.stop()
yield self.i

def start(self):
self.runs = True
self.event_source.start()

def stop(self, event=None):
self.runs = False
self.event_source.stop()

def forward(self, event=None):
self.forwards = True
self.start()

def backward(self, event=None):
self.forwards = False
self.start()

def oneforward(self, event=None):
self.forwards = True
self.onestep()

def onebackward(self, event=None):
self.forwards = False
self.onestep()

def onestep(self):
if self.i > self.min and self.i < self.max:
self.i = self.i + self.forwards - (not self.forwards)
elif self.i == self.min and self.forwards:
self.i += 1
elif self.i == self.max and not self.forwards:
self.i -= 1
self.func(self.i)
self.slider.set_val(self.i)
self.fig.canvas.draw_idle()

def setup(self, pos):
playerax = self.fig.add_axes([pos[0], pos[1], 0.64, 0.04])
divider = mpl_toolkits.axes_grid1.make_axes_locatable(playerax)
bax = divider.append_axes("right", size="80%", pad=0.05)
sax = divider.append_axes("right", size="80%", pad=0.05)
fax = divider.append_axes("right", size="80%", pad=0.05)
ofax = divider.append_axes("right", size="100%", pad=0.05)
sliderax = divider.append_axes("right", size="500%", pad=0.07)
self.button_oneback = matplotlib.widgets.Button(playerax, label="$\u29CF$")
self.button_back = matplotlib.widgets.Button(bax, label="$\u25C0$")
self.button_stop = matplotlib.widgets.Button(sax, label="$\u25A0$")
self.button_forward = matplotlib.widgets.Button(fax, label="$\u25B6$")
self.button_oneforward = matplotlib.widgets.Button(ofax, label="$\u29D0$")
self.button_oneback.on_clicked(self.onebackward)
self.button_back.on_clicked(self.backward)
self.button_stop.on_clicked(self.stop)
self.button_forward.on_clicked(self.forward)
self.button_oneforward.on_clicked(self.oneforward)
self.slider = matplotlib.widgets.Slider(
sliderax, "", self.min, self.max, valinit=self.i
)
self.slider.on_changed(self.set_pos)

def set_pos(self, i):
self.i = int(self.slider.val)
self.func(self.i)

def update(self, i):
self.slider.set_val(i)


if __name__ == "__main__":
### using this class is as easy as using FuncAnimation:

fig, ax = plt.subplots()
x = np.linspace(0, 6 * np.pi, num=100)
y = np.sin(x)

ax.plot(x, y)
(point,) = ax.plot([], [], marker="o", color="crimson", ms=15)

def update(i):
point.set_data(x[i], y[i])

ani = Player(fig, update, maxi=len(y) - 1)

plt.show()
93 changes: 93 additions & 0 deletions WormSim/Model/Test.ipynb

Large diffs are not rendered by default.

122 changes: 122 additions & 0 deletions WormSim/Model/WormView.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
from matplotlib import pyplot as plt

from numpy import genfromtxt
import numpy as np
import math
import os

data = genfromtxt("simdata.csv", delimiter=",").T

print("Loaded data: %s" % (str(data.shape)))

t = data[0]

x_offset = 1
y_offset = 2
d_offset = 3

"""
for i in [(j*3)+y_offset for j in range(49)]:
plt.plot(t,my_data[i],label=i)
plt.legend()"""

fig, ax = plt.subplots()
plt.get_current_fig_manager().set_window_title("2D WormSim replay")
ax.set_aspect("equal")

usingObjects = os.path.isfile("objects.csv")
if usingObjects:
Objects = genfromtxt("objects.csv", delimiter=",")
for o in Objects:
x = o[0]
y = o[1]
r = o[2]
# print("Circle at (%s, %s), radius %s"%(x,y,r))
circle1 = plt.Circle((x, y), r, color="b")
plt.gca().add_patch(circle1)
else:
print("No objects found")

num_t = 30
timesteps = len(t)

# Using same variable names as WormView.m
Sz = len(data)
Nt = len(data[0])
Nbar = int((Sz - 1) / 3)
NSEG = int(Nbar - 1)
D = 80e-6

R = [
D / 2.0 * abs(math.sin(math.acos(((i) - NSEG / 2.0) / (NSEG / 2.0 + 0.2))))
for i in range(Nbar)
]

CoM = np.zeros([Nt, Nbar, 3])
CoMplot = np.zeros([Nt, 2])
Dorsal = np.zeros([Nbar, 2])
Ventral = np.zeros([Nbar, 2])

print(f"Sz: {Sz}, Nt: {Nt}, Nbar: {Nbar}, NSEG: {NSEG}")
# Dt = data(2,1) - data(1,1);

ventral_plot = None
midline_plot = None
dorsal_plot = None

from Player import Player


def update(ti):
global dorsal_plot, ventral_plot, midline_plot
f = ti / timesteps

color = "#%02x%02x00" % (int(0xFF * (f)), int(0xFF * (1 - f) * 0.8))
print("Time step: %s, fract: %f, color: %s" % (ti, f, color))
ds = []
xs = []
ys = []

for i in [(j * 3) + d_offset for j in range(Nbar)]:
ds.append(data[i][ti])

for i in [(j * 3) + x_offset for j in range(Nbar)]:
xs.append(data[i][ti])

for i in [(j * 3) + y_offset for j in range(Nbar)]:
ys.append(data[i][ti])

for j in range(Nbar):
dX = R[j] * math.cos(ds[j])
dY = R[j] * math.sin(ds[j])

Dorsal[j, 0] = xs[j] + dX
Dorsal[j, 1] = ys[j] + dY
Ventral[j, 0] = xs[j] - dX
Ventral[j, 1] = ys[j] - dY

if dorsal_plot == None:
(dorsal_plot,) = ax.plot(Dorsal[:, 0], Dorsal[:, 1], color="grey", linewidth=1)
else:
dorsal_plot.set_data(Dorsal[:, 0], Dorsal[:, 1])

if ventral_plot == None:
(ventral_plot,) = ax.plot(
Ventral[:, 0], Ventral[:, 1], color="grey", linewidth=1
)
else:
ventral_plot.set_data(Ventral[:, 0], Ventral[:, 1])

if midline_plot == None:
(midline_plot,) = ax.plot(
xs, ys, color="g", label="t=%sms" % t[ti], linewidth=0.5
)
else:
midline_plot.set_data(xs, ys)


ax.plot() # Causes an autoscale update.

ani = Player(fig, update, maxi=timesteps - 1)

plt.show()
69 changes: 48 additions & 21 deletions WormSim/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## WormSim c elegans simulation engine from Boyle, Berri and Cohen 2012
## WormSim C. elegans simulation engine from Boyle, Berri and Cohen 2012

See Boyle, Berri and Cohen, [Gait modulation in C. elegans: an integrated neuromechanical model](http://www.frontiersin.org/Computational_Neuroscience/10.3389/fncom.2012.00010/abstract), Front. Comput. Neurosci., 2012.

Expand All @@ -10,22 +10,30 @@ Installation guide:

1) Clone this project:

git clone https://github.com/OpenSourceBrain/CelegansNeuromechanicalGaitModulation.git
```
git clone https://github.com/OpenSourceBrain/CelegansNeuromechanicalGaitModulation.git
```

2) Go to "WormSim" directory.

cd WormSim
```
cd WormSim
```

3) Set idaInstallDir:

idaInstallDir=`pwd`/Sundials
```
idaInstallDir=`pwd`/Sundials
```

4) Go to sundials-2.3.0 directory, configure & make:

cd sundials-2.3.0
./configure CC=g++ --prefix=$idaInstallDir --disable-mpi --disable-fcmix
make
make install
```
cd sundials-2.3.0
./configure CC=g++ --prefix=$idaInstallDir --disable-mpi --disable-fcmix
make
make install
```

5) Look carefully at the resulting output and check for any error messages (there shouldn't be any...). Fix and repeat step 4 if necessary.

Expand All @@ -37,26 +45,45 @@ Installation guide:

2) To compile the program, make sure you're in the "Model" directory.

3) Enter the command "make".
3) Enter the command:

```
make
```

4) Check the resulting output for errors (some warnings are unfortunately inevitable, but they shouldn't be a problem), fix and re-run "make" if necessary.

5) Enter the command "./program" and wait for it to complete.
5) Enter the following command and wait for it to complete.

```
./program
```

6) `program` will generate a file called `simdata.csv`, and possibly also `objects.csv` (if objects are being used).

7) Open the Matlab [viewer program](https://github.com/OpenSourceBrain/CelegansNeuromechanicalGaitModulation/blob/master/WormSim/MatlabSupport/WormView.m), found in `WormSim/MatlabSupport/`.

```
matlab WormView.m
```

6) "program" will generate a file called "simdata.csv", and possibly also "objects.csv" (if objects are being used).
If you don't have access to Matlab, try [Octave](https://www.gnu.org/software/octave/it), or should be possible to reproduce this viewer in another language by examining the code and translating it as appropriate.

7) Open the appropriate [viewer program](https://github.com/OpenSourceBrain/CelegansNeuromechanicalGaitModulation/blob/master/WormSim/MatlabSupport/WormView.m), found in "WormSim/MatlabSupport/",
in Matlab. If you don't have access to Matlab, try [Octave](https://www.gnu.org/software/octave/it), or should be possible to reproduce this viewer
in another language by examining the code and translating it as appropriate.
<img width="500" alt="MatlabView" src="../images/MatlabView.png"/>

8) Run the viewer to visualize the model behaviour.
8) Try also the [new Python viewer](https://github.com/OpenSourceBrain/CelegansNeuromechanicalGaitModulation/blob/master/WormSim/Model/WormView.py):

9) Summary:
```
python WormView.py
```

cd ../Model
make
./program
cd ../MatlabSupport
matlab WormView.m
<img width="500" alt="PythonView" src="../images/Replay.gif"/>

**Note:** to get the objects shown in the visualisations above, change the following values in [worm.cc](https://github.com/OpenSourceBrain/CelegansNeuromechanicalGaitModulation/blob/master/WormSim/Model/worm.cc), then remake the main program, and rerun:

```
#define DURATION 20 //duration of simulation (in seconds)
#define MEDIUM 1.0 //change in the range 0.0 (water) to 1.0 (agar)
#define OBJECTS 20 //set number of objects (>= 0)
#define LAYOUT 2 //change between 0 (square) 1 (hex) 2 (random)
```
Binary file added images/MatlabView.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/PythonView.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/Replay.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit b62bc52

Please sign in to comment.