.. _data_sample: Data Sample ******************* An instance of the :class:`~core.datasample.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 :class:`~core.datasample.DataSample` conatins the list of scene objects for this specific scene, accessible via :meth:`~core.datasample.DataSample.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) :class:`~core.objects.sceneobject.SceneObject`. The Scene Object holds all the file names related to this object. These are stored in the form of :class:`~core.objects.file_list.FileList` s, where each FileList usually represents the different frames of a single mesh. For example, there may be a file list :code:`liver_surface` which holds the initial state of the liver surface :code:`liver_surface_f0.stl` as well as a deformed state of the same surface for frame 1 called :code:`liver_surface_f1`. Another FileList :code:`liver_volume` on the same SceneObject may hold the various states of the liver's volume mesh, i.e. :code:`liver_volume_f0.vtp` and :code:`liver_volume_f1.vtp`. The default Scene Object classes are given in the :mod:`~core.objects` module. Important: When you create your pipeline, DataSamples and SceneObjects should NEVER be created directly! This is because every :class:`~core.datasample.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 :class:`~blocks.scene_objects.scene_object_generator_block.SceneObjectGeneratorBlock` to which you can add these factories via :meth:`~blocks.scene_objects.scene_object_generator_block.SceneObjectGeneratorBlock.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: #. Subclass :class:`~core.objects.sceneobject.SceneObject` for your own scene object. #. Subclass :class:`~core.objects.sceneobject.SceneObjectFactory` for your own scene object. #. Add a :class:`~blocks.scene_objects.scene_object_generator_block.SceneObjectGeneratorBlock` to your pipeline if you haven't already, call the :meth:`~blocks.scene_objects.scene_object_generator_block.SceneObjectGeneratorBlock.add_factory()` method and pass an instance of your Factory class. #. The above will make sure that the pipeline will create the scene object for every DataSample. Now when the :class:`~core.pipeline_block.PipelineBlock`'s are run on this sample, they must handle your new scene object type correctly. For example, you might want to adapt the :class:`~blocks.scene_generation.random_scene_block.RandomSceneBlock` and the :class:`~blocks.simulation.simulation_block.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 :code:`existence_likelyhood` (default: 1) parameter in :meth:`~blocks.scene_objects.scene_object_generator_block.SceneObjectGeneratorBlock.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.