Data Sample

An instance of the DataSample class stores all data related to exactly one scene and simulation and represents one folder on the drive. It has a unique ID, which also corresponds to the folder name it will have on disk (sample 0 is stored in folder 000000, sample 15 in folder 000015 and so on).

The DataSample stores logs, config data, statistics and errors that occured while processing this particular sample.

Besides these, the main contents of the Data Sample is a list of SceneObjects. Each SceneObject represents a physical object in the scene, such as a liver, the abdominal wall, or a tool - but also boundary conditions (ligament, fixed-boundary, …). To keep track of these and pass them around between different parts of the Pipeline, each DataSample conatins the list of scene objects for this specific scene, accessible via get_scene_objects(). In this way, one PipelineBlock could add a new liver mesh to the DataSample and another PipelineBlock could simulate its deformation while a third PipelineBlock could render 2D endoscopic images of it.

Scene Objects

A scene object is always an instance of a (subclass of) SceneObject. The Scene Object holds all the file names related to this object. These are stored in the form of FileList s, where each FileList usually represents the different frames of a single mesh. For example, there may be a file list liver_surface which holds the initial state of the liver surface liver_surface_f0.stl as well as a deformed state of the same surface for frame 1 called liver_surface_f1. Another FileList liver_volume on the same SceneObject may hold the various states of the liver’s volume mesh, i.e. liver_volume_f0.vtp and liver_volume_f1.vtp.

The default Scene Object classes are given in the objects module.

Important: When you create your pipeline, DataSamples and SceneObjects should NEVER be created directly! This is because every DataSample instance will need to construct its scene with its own parameters. For example, DataSample 10 may construct 2 ligaments with high stiffness and DataSample 30 may instead create 4 with a low stiffness. This is why instead of instancing the scene objects yourself, you need to let the pipeline do this for you. We solve this by using a factory for each scene object type and the the SceneObjectGeneratorBlock to which you can add these factories via add_factory(). When the pipeline runs, each DataSample that is constructed will be passed to the SceneObjectGeneratorBlock and the factory then has the chance to instanciate its specific SceneObject with fixed or random parameters.

If you start programming your own types of scene objects, you should:

  1. Subclass SceneObject for your own scene object.

  2. Subclass SceneObjectFactory for your own scene object.

  3. Add a SceneObjectGeneratorBlock to your pipeline if you haven’t already, call the add_factory() method and pass an instance of your Factory class.

  4. The above will make sure that the pipeline will create the scene object for every DataSample. Now when the PipelineBlock’s are run on this sample, they must handle your new scene object type correctly. For example, you might want to adapt the RandomSceneBlock and the SimulationBlock to correctly handle your scene objects as well.

SceneObjects may be optional, i.e. some SceneObjects may not exist for some scenes (for example a grasper tool could only be added to half the scenes, randomly). This can be configured via the optional existence_likelyhood (default: 1) parameter in add_factory(). Note that this also means that when processing SceneObjects in your PipelineBlocks you should always first ensure that they actually exist for the current DataSample.

Note: SceneObject can become a bit confusing at times, we recommend to closely look at the default SceneObjects that already exist and how they are used in various example pipelines. We have gone through many iterations of the API, and this seems to be the cleanest solution we have found, clearly separating data into meshes and making it accessible to all PipelineBlocks.