I/O Operations
Mechanica 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 io module (MxIO in C++). For detailed information on classes and
methods available in the io module, refer to the Mechanica API Reference.
Loading and Saving a Simulation
Mechanica 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
the io module command toFile,
import mechanica as mx
from os import path
mx.init()
# Simulation code here...
fp = path.join(path.dirname(path.abspath(__file__)), 'fileexport.json') # Path of file to export
mx.io.toFile(fp) # Export to file
Data files exported using 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 init when initializing
Mechanica,
import mechanica as mx
from os import path
fp = path.join(path.dirname(path.abspath(__file__)), 'fileexport.json') # Path of file to import
mx.init(load_file=fp)
Initialization from an imported simulation state is not limited to the data defined in the imported file. Rather, Mechanica 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 Mechanica during initialization will use the specified timestep, and not the one defined in the imported file,
import mechanica as mx
from os import path
fp = path.join(path.dirname(path.abspath(__file__)), 'fileexport.json') # Path of file to import
mx.init(load_file=fp, dt=0.005)
Furthermore, all Mechanica 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 = mx.MxParticleType_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 Mechanica’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, Mechanica 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 = mx.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 Mechanica 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., custom potentials, custom forces and events) cannot be exported. Whenever necessary, such features must be created and loaded into Mechanica in the same way after import to reproduce the complete simulation state. For a complete list of information exported by Mechanica feature, see Appendix A.
3D Model Formats
Mechanica 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
mx.io.toFile3DF(format="stl", filePath=fp_3df, pRefinements=2) # Export stl mesh
Mechanica integrates the Open Asset Import Library (Assimp) for working with 3D model formats, and so all formats supported by Assimp are also supported by Mechanica.
Mechanica can also import mesh data in a 3D file and make it available for constructing
a simulation. The io method 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 = mx.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 Structure3DF (Mx3DFStructure in C++) instance returned by
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(mx.Universe.center)
# Rotate 90 degress about X
io_struct.rotate(mx.MxMatrix4f.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(mx.ParticleType):
"""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 Mechanica Objects
Mechanica 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 Mechanica simulation written by Modeler A.
import mechanica as mx
from os import path
mx.init()
class ParticleTypeA(mx.ParticleType):
"""Awesome Mechanica 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 Mechanica simulation written by Modeler B.
import mechanica as mx
from os import path
mx.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 = mx.MxParticleType.fromString(f.read())
Mechanica provides built-in support in Python for pickling all objects that can be serialized. All objects that support pickling can be seemlessly 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 mx.Universe.bonds()])
All objects that can be pickled have the method __reduce__ marked in the
documentation of their class in the Mechanica Python API Reference.
Note
Special care must be taken to account for that deserialized Mechanica objects are copies of their original object, and that the Mechanica engine is not available in separate processes. As such, calls to methods that require the engine in a spawned Python process will fail.