.. _file_io: .. py:currentmodule:: tissue_forge I/O Operations --------------- Tissue Forge supports a number of operations associated with importing and exporting simulation and simulation data. At any time during simulation, data can be archived during simulation for later import, execution and analysis, or exported to common 3D model or `JSON `_ file formats for easy sharing of model objects and browsable three-dimensional simulation results among colleagues, research groups and the broader scientific community. In general, I/O operations are defined in the :py:mod:`io` module (``io`` namespace in C++). For detailed information on classes and methods available in the :py:mod:`io` module, refer to the :ref:`Tissue Forge API Reference `. Loading and Saving a Simulation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Tissue Forge supports saving the state of a simulation to file at any time during simulation. Almost all simulation data can be written to file in JSON format using function :meth:`toFile `, :: import tissue_forge as tf from os import path tf.init() # Simulation code here... fp = path.join(path.dirname(path.abspath(__file__)), 'fileexport.json') # Path of file to export tf.io.toFile(fp) # Export to file Data files exported using :meth:`toFile ` can be imported and used to initialize a simulation in approximately the same state as when the exported file was generated. The path to a file containing exported data can be passed directly to the keyword ``load_file`` of :func:`init` when initializing Tissue Forge, :: import tissue_forge as tf from os import path fp = path.join(path.dirname(path.abspath(__file__)), 'fileexport.json') # Path of file to import tf.init(load_file=fp) Initialization from an imported simulation state is not limited to the data defined in the imported file. Rather, Tissue Forge begins by initializing from whatever data is defined in the imported file, and then resumes all other initializations in the typical way. For example, exported data includes select simulator details like the timestep used by the simulator. However, passing an explicit timestep to Tissue Forge during initialization will use the specified timestep, and not the one defined in the imported file, :: import tissue_forge as tf from os import path fp = path.join(path.dirname(path.abspath(__file__)), 'fileexport.json') # Path of file to import tf.init(load_file=fp, dt=0.005) Furthermore, all Tissue Forge functionality concerning creating objects and interactions between them are fully available after importing a simulation state, while (almost) all objects and processes defined in the imported file are also available after initialization, :: # Get an instance of particle type named "ExportedType" from imported data Exported = tf.ParticleType_FindFromName('ExportedType') # Print the number of imported ExportedType particles print('Number of imported particles:', len(Exported.items())) # Create a few more ExportedType particles [Exported() for _ in range(10)] The versatility of Tissue Forge's approach to importing simulation data comes with the tradeoff of that not all simulation data is conserved during import. Certain data used to identify objects like particles and particle types are not necessarily the same between an original simulation state and its state after import. For example, suppose that a certain particle has an ``id`` attribute value of ``10`` at export. After import, the attribute value for ``id`` is not guaranteed to again be ``10``. However, Tissue Forge provides mappings of simulation state data from values in the original state at export to values in the current simulation state after import, :: # Get the id of the particle that had an id of 20 at export id_part_20 = tf.io.mapImportParticleId(20) .. note:: All data import maps are available between initialization and the first simulation step, after which they are purged. Not all features of Tissue Forge are (or even can be) written to file during export. While rendering details of particles, particle types and all bond types are exported, non-critical simulation details like camera view are not exported. More importantly, features that rely on custom functions and callbacks (*e.g.*, :ref:`custom potentials `, :ref:`custom forces ` and :ref:`events `) cannot be exported. Whenever necessary, such features must be created and loaded into Tissue Forge in the same way after import to reproduce the complete simulation state. For a complete list of information exported by Tissue Forge feature, see :ref:`Appendix A `. 3D Model Formats ^^^^^^^^^^^^^^^^^ Tissue Forge makes sharing 3D results simple. At any time during simulation execution, the state of the simulation can be exported to a 3D model format as a mesh, :: fp_3df = path.join(path.dirname(path.abspath(__file__)), 'fileexport.stl') # Path to export stl tf.io.toFile3DF(format="stl", filePath=fp_3df, pRefinements=2) # Export stl mesh Tissue Forge integrates the Open Asset Import Library (`Assimp `_) for working with 3D model formats, and so `all formats supported by Assimp `_ are also supported by Tissue Forge. Tissue Forge can also import mesh data in a 3D file and make it available for constructing a simulation. The :py:mod:`io` module method :meth:`fromFile3DF ` returns a structure of mesh data as imported from a 3D file, :: fp_mesh = path.join(path.dirname(path.abspath(__file__)), 'mesh.obj') # Path of mesh to import io_struct = tf.io.fromFile3DF(fp_mesh) # Import mesh # Print import summary print(io_struct.num_meshes, 'meshes') print(io_struct.num_faces, 'faces') print(io_struct.num_edges, 'edges') print(io_struct.num_nodes, 'nodes') print('Mesh centroid:', io_struct.centroid) The :py:class:`Structure3DF` instance returned by :meth:`fromFile3DF ` contains all vertices, edges, faces and meshes imported from the 3D file, and provides a few useful methods for using the mesh data in a simulation (`e.g.`, building a simulation from a mesh designed in Blender), :: import math # Translate mesh centroid to center of universe io_struct.translateTo(tf.Universe.center) # Rotate 90 degrees about X io_struct.rotate(tf.FMatrix4.rotationX(math.pi/2).rotation()) # Double the size about the centroid io_struct.scale(2.0) For example, particles can readily be constructed at each vertex of a mesh by simply iterating over all vertices of the mesh, :: class VertexType(tf.ParticleTypeSpec): """A type for particles built from mesh data""" pass Vertex = VertexType.get() # Create particles from mesh vertices for v in io_struct.vertices: Vertex(v.position) Serializing Tissue Forge Objects ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Tissue Forge supports serialization of most objects using JSON strings for sharing individual model objects. Any object that can be serialized has the method ``toString``, and its class has the static method ``fromString``. ``toString`` returns a JSON-formatted string of the state of the object, which can be exported for sharing, :: # A Tissue Forge simulation written by Modeler A. import tissue_forge as tf from os import path tf.init() class ParticleTypeA(tf.ParticleTypeSpec): """Awesome Tissue Forge particle""" A = ParticleTypeA.get() # Export the type to share with a friend fp = path.join(path.dirname(path.abspath(__file__)), 'ptypea.json') with open(fp, 'w') as f: f.write(A.toString()) The generated string can later be used by the ``fromString`` method of the class that generated the string to recreate the object, :: # A Tissue Forge simulation written by Modeler B. import tissue_forge as tf from os import path tf.init() # Import a type shared by a friend fp = path.join(path.dirname(path.abspath(__file__)), 'ptypea.json') with open(fp, 'r') as f: A = tf.ParticleType.fromString(f.read()) Tissue Forge provides built-in support in Python for pickling all objects that can be serialized. All objects that support pickling can be seamlessly integrated into multithreading applications, :: from multiprocessing import Pool def energy_diff(bond): """Calculates the difference of the potential and dissociation energies of a bond""" return bond.dissociation_energy - bond.potential_energy # Calculate all bond energy differences in parallel with Pool(8) as p: energy_diffs = p.map(energy_diff, [bh.get() for bh in tf.Universe.bonds]) All objects that can be pickled have the method ``__reduce__`` marked in the documentation of their class in the :doc:`Tissue Forge Python API Reference `. .. note:: Special care must be taken to account for that deserialized Tissue Forge objects are copies of their original object, and that the Tissue Forge engine is not available in separate processes. As such, calls to methods that require the engine in a spawned Python process will fail.