The package anypytools
has utilities and tools to work with the AnyBody Modeling System (AMS). This will allow us to do the follwing directly from python:
AnyPyTools is a python library for working with the AnyBody Modeling System. An easy way to get started is to download the Anaconda python distribution:
Download and install the Anaconda python distribution
After installation open the Anaconda command prompt and type:
cmd conda install -c public anypytools
This should install the newest version of AnyPyTools. Try launching the inteactive version of this tutorial (in the Anaconda start menu) and start working with
anypytools
.
Let us see how to run a single model. In this tutorial we will use an example model Knee.any
, which must be placed in the current working directory.
The first step is to import the appropriate class for working with the console application and create an instance of the class AnyPyProcess
.
In [4]:
from anypytools.abcutils import AnyPyProcess
app = AnyPyProcess()
Now we define an AnyScript macro which we want to run on the model. The macro is executed by starting the start_macro
method of the AnyPyProcess
class
In [5]:
macrolist = [['load "Knee.any"',
'operation Main.MyStudy.Kinematics',
'run',
'exit']]
app.start_macro(macrolist);
In [8]:
macrolist = [['load "Knee.any"',
'operation Main.MyStudy.Kinematics',
'run',
'exit'],
['load "Knee.any"',
'operation Main.MyStudy.InverseDynamics',
'run',
'exit']]
app.start_macro(macrolist);
Notice, that AnyPyProcess will run the anyscript macros in parallel. Modern computers have multiple cores, but a single AnyBody instance can only utilize a single core, leaving us with a great potential for speeding things up through penalization.
To test this, let us create 10 macros in a for-loop.
In [12]:
macrolist = []
for i in range(15):
macrolist.append(['load "Knee.any"',
'operation Main.MyStudy.InverseDynamics',
'run',
'exit'] )
AnyPyProcess has a parameter 'num_processes' that controls the number parallel processes. Let us try a small example to see the difference in speed:
In [10]:
# First sequentially
app = AnyPyProcess(num_processes = 1)
app.start_macro(macrolist);
In [13]:
#Then with parallization
app = AnyPyProcess(num_processes = 4)
app.start_macro(macrolist);
In [7]:
macrolist = [['load "Knee.any"',
'operation Main.MyStudy.InverseDynamics',
'run',
'classoperation Main.MyStudy.Output.MaxMuscleActivity "Dump"',
'exit']]
result = app.start_macro(macrolist)
max_muscle_act = result[0]['Main.MyStudy.Output.MaxMuscleActivity']
In order to make the plot we import the matplotlib library, and enable inline figurs
In [10]:
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
plt.plot(max_muscle_act);
In [12]:
macrolist = []
patella_ligament_lengths = np.linspace(0.01, 0.1, 9)
for value in patella_ligament_lengths:
macrolist.append( ['load "Knee.any"',
'classoperation Main.MyModel.PatellaLigament.DriverPos "Set Value"\
--value={%f}'%(value),
'operation Main.MyStudy.InverseDynamics',
'run',
'classoperation Main.MyStudy.Output.MaxMuscleActivity "Dump"',
'exit'] )
output = app.start_macro(macrolist)
We can now analyze the resulting maximum muscle activity by plotting the data in the output
variable:
In [14]:
for i, result in enumerate( output ):
maxact = result['Main.MyStudy.Output.MaxMuscleActivity']
plt.plot(maxact, label='%d cm'%(100*patella_ligament_lengths[i]) )
plt.title('Effect of changing patella ligament length')
plt.xlabel('time steps')
plt.ylabel('max muscle activity')
plt.legend(bbox_to_anchor=(1.05, 1), loc=2);
It can sometimes be convenient to save the entire model with all its data, although this can be several hundred megabytes. It is useful if we do not know which variables we are interested in before running the model. It can also be useful if we want to load the data in the GUI application and replay the result.
The macro language allows us to save the output of a study to the HDF5 file format. To do this we add a "Save data" classoperation to the macro and then re-run the model:
In [16]:
macrolist = [[
'load "Knee.any"',
'operation Main.MyStudy.Kinematics',
'run',
'classoperation Main.MyStudy.Output "Save data" --type="Deep" --file="output.anydata.h5"',
'exit']]
app.start_macro(macrolist);
The data stored in the file output.anydata.h5 can be re-loaded in the AnyBody GUI application. To do this; load the model, then right click the Main.MyStudy.Output
folder and select "Load data".
Moreover, since we set type="Deep"
in the macro we can also load the data into external programs, e.g. Matlab or python. In python this is done using the h5py
module
In [19]:
import h5py
h5file = h5py.File('output.anydata.h5')
data = np.array( h5file['/Output/MomentArm'] )
h5file.close()
print data
The data structure of the HDF5 files can unfortunately be very confusing. AnyBody does not save duplicate copies of the same data. If there are multiple references to the same folder, only one will be present in the HDF5 file.
In our model Knee.any
we have a reference to the Knee
joint folder just before the Model
folder in the study section. Thus, all variables inside the Knee
folder cannot be accessed with the path '/Output/Model/Knee/...', but only through the path of the reference '/Output/kneeref/...'.
We can see the problem in the following code snippet:
In [14]:
with h5py.File('output.anydata.h5') as f:
print '/Output/Model/Knee/Pos' in f
print '/Output/kneeref/Pos' in f
This makes it difficult to find the correct path in large models with many references. AnyPyTools contains a wrapper for the h5py module, which automatically locates the right data, no matter what path is used. Using this module, we can easily locate the data.
In [2]:
import anypytools.h5py_wrapper as h5py2
with h5py2.File('output.anydata.h5') as f:
print '/Output/Model/Knee/Pos' in f
print '/Output/kneeref/Pos' in f
The h5py wrapper will also let us use the AnyScript variable names directly so we don't have to replace every . (dot) with a / (slash), and remove the stuff before the Output folder.
In [25]:
with h5py2.File('output.anydata.h5') as f:
kneeangle = np.array(f['Main.MyStudy.Output.Model.Knee.Pos'] )
momentarm = np.array(f['Output.MomentArm'] )
plt.plot(np.degrees(kneeangle),100*momentarm)
plt.xlabel('Knee flexion (deg)')
plt.ylabel('Moment arm (cm)');
The approach to batch processing depends on the structure of the anybody model. The most commonly used strategy is a single anyscript main file which is loaded with different define statements to the trials, subjects or other parameters. This is the approach which is outlined on the anyscript.org wiki page, the first chapter of the tutorial explains how to handle this.
Another strategy is to have a separate main file for each trial. These indivudual main files hold all trial specific data and includes a base model which is common to all trials and subjects. This is makes it much easier to work with mocap models which have many subject or hundres of trials.
This example model illustrates the concept with the Arm2D demo from AnyBody. The main files are located in subfolders which load the base model "Demo.Arm2D.any" with different parameters.
In [26]:
%cd "BatchProcessExample"
!dir /B
In [28]:
from anypytools.abcutils import AnyPyProcess
app = AnyPyProcess(num_processes = 3)
macrolist = [['load "main.any"',
'operation Main.Study.InverseDynamics',
'run',
'exit']]
app.start_macro(macrolist, search_subdirs= "[model[1-9].*main.any" );
In [ ]:
In [106]:
# This just formats the Notebook so it looks a bit nicer
from IPython.core.display import HTML
def css_styling():
styles = open("custom.css", "r").read()
return HTML(styles)
css_styling()
Out[106]:
In [ ]: