mrsimulator I/O¶
We offer a range of serialization options based on a JSON structure demonstrated below.
Dictionary Representation of Objects¶
All mrsimulator objects can be serialized into a JSON format. Calling the
json()
method on an object will return a Python dictionary representing the object
in JSON format.
Below we call the json()
method of the Site class.
from mrsimulator import Site, SpinSystem
from mrsimulator.spin_system.tensors import SymmetricTensor
Si29_site = Site(
isotope="29Si",
isotropic_chemical_shift=-89.0,
shielding_symmetric=SymmetricTensor(
zeta=59.8,
eta=0.62,
),
)
py_dict = Si29_site.json()
print(py_dict)
# {
# 'isotope': '29Si',
# 'isotropic_chemical_shift': '-89.0 ppm',
# 'shielding_symmetric': {'zeta': '59.8 ppm', 'eta': 0.62}
# }
All values are serialized with units when applicable, but you may call json(units=False)
if you wish to serialize values without units.
Similarly, all mrsimulator objects can be loaded from a dictionary representation. Here we
construct the same site as a dictionary and call parse_dict_with_units()
to create a Site object from a dictionary.
site_dict = {
"isotope": "29Si",
"isotropic_chemical_shift": "-89.0 ppm",
"shielding_symmetric": {
"zeta": "59.8 ppm",
"eta": 0.62,
},
}
Si29_site_from_dict = Site().parse_dict_with_units(site_dict)
print(Si29_site_from_dict == Si29_site)
# True
We see that both these sites are equivalent. Values in dictionaries can be given as a number and a unit in a string. However, passing values with units increases overhead and will throw errors if the units cannot be converted into the expected units for a field. For this reason, we recommend instantiating objects directly from classes.
Saving and Loading Spin Systems from a File¶
A list of spin systems in a Simulator object can be serialized to a file. Here we create
a simulator with three distinct \(^{29}\text{Si}\) spin systems and serialize these spin
systems to a file by calling export_spin_systems()
.
from mrsimulator import Site, SpinSystem, Simulator
from mrsimulator.spin_system.tensors import SymmetricTensor
# Create the spin systems
Si29_1 = SpinSystem(
sites=[
Site(
isotope="29Si",
isotropic_chemical_shift=-89.0,
shielding_symmetric=SymmetricTensor(zeta=59.8, eta=0.62),
)
]
)
Si29_2 = SpinSystem(
sites=[
Site(
isotope="29Si",
isotropic_chemical_shift=-89.5,
shielding_symmetric=SymmetricTensor(zeta=52.1, eta=0.68),
)
]
)
Si29_3 = SpinSystem(
sites=[
Site(
isotope="29Si",
isotropic_chemical_shift=-87.8,
shielding_symmetric=SymmetricTensor(zeta=69.4, eta=0.60),
)
]
)
# Create the Simulator object
sim = Simulator(spin_systems=[Si29_1, Si29_2, Si29_3])
# Save spin systems to file
sim.export_spin_systems("example.mrsys")
Now the file example.mrsys
holds a JSON representation of the spin system objects. The
extension of the file is irrelevant; however, we strongly encourage using .mrsys
to
adhere to the convention.
Just as spin systems can be saved to a file, spin systems can be loaded from a file. Loading spin
systems is useful when working with a large number of spin systems over multiple Python scripts. Here
we load the spin system file, example.mrsys
, into a new simulator using the method
load_spin_systems()
.
new_sim = Simulator()
new_sim.load_spin_systems("example.mrsys")
print(len(new_sim.spin_systems))
# 3
Saving and Loading Methods from a File¶
A list of methods in a Simulator object can be serialized to a file. Here we create a
custom DAS method and serialize it to a file using the method
export_methods()
.
from mrsimulator import Simulator
from mrsimulator.method import Method
from mrsimulator.method import SpectralDimension, SpectralEvent
# Create DAS method
das = Method(
name="DAS of 17O",
channels=["17O"],
magnetic_flux_density=11.744,
spectral_dimensions=[
SpectralDimension(
count=512,
spectral_width=10000,
reference_offset=-1220.9,
origin_offset=67793215,
label="Isotropic dimension",
events=[
SpectralEvent(
fraction=0.5,
rotor_angle=37.38 * 3.14159 / 180,
transition_queries=[{"ch1": {"P": [-1], "D": [0]}}],
),
SpectralEvent(
fraction=0.5,
rotor_angle=79.19 * 3.14159 / 180,
transition_queries=[{"ch1": {"P": [-1], "D": [0]}}],
),
],
),
# The last spectral dimension block is the direct-dimension
SpectralDimension(
count=256,
spectral_width=11001,
reference_offset=-1228,
origin_offset=67793215,
label="MAS dimension",
events=[
SpectralEvent(
rotor_angle=54.735 * 3.14159 / 180,
transition_queries=[{"ch1": {"P": [-1], "D": [0]}}],
)
],
),
],
)
# Create simulator with das method
sim = Simulator(methods=[das])
# Save methods to file
sim.export_methods("example.mrmtd")
Now the file example.mrmtd
holds a JSON representation of the method object. If multiple
methods are present, e.g., at different spinning speeds, they will also be serialized. The file’s extension
is not essential; however, we strongly encourage using .mrmtd
to adhere to the convention.
Just like spin systems, methods can also be loaded from a file. Here we load the DAS
method into a new simulator object by calling the method
load_methods()
.
new_sim = Simulator()
new_sim.load_methods("example.mrmtd")
print(new_sim.methods[0].name)
# DAS of 17O
Loading complex methods from a file, like the DAS example above, can reduce complex code. Methods representing actual experiments can be saved to a file to later be loaded into a script as needed.
Serializing a Simulator Object¶
The entire Simulator object may be serialized to a JSON-compliant file using the
save()
Python method.
By default, the attribute values are serialized as physical quantities represented as a
string with a value and a unit.
sim = Simulator()
# ... Setup Simulator object
sim.save("sample.mrsim")
Now the file sample.mrsim
holds the JSON representation of sim
, a Simulator object.
To load a simulator from a file, call the class method load()
.
By default, the load method parses the file for units.
new_sim = Simulator.load("sample.mrsim")
Serialize simulation from a Method to a CSDM Compliant File¶
The simulated spectrum may be exported to a CSDM-compliant JSON file using the following code:
sim_coesite.methods[0].simulation.save("coesite_simulation.csdf")
For more information on the CSDM format, see the csdmpy documentation.
Serialize Simulator and SignalProcessor object¶
The Simulator object and a list of Signal Processor objects
can both be serialized within the same file by calling the save()
method.
from mrsimulator import save
from mrsimulator import Simulator
from mrsimulator import signal_processor as sp
sim = Simulator()
processor1 = sp.SignalProcessor()
processor2 = sp.SignalProcessor()
save(
filename="example.mrsim",
simulator=sim,
signal_processors=[processor1, processor2],
)
All attribute values are serialized with units by default, but you may serialize without
units by passing with_units=False
to the method.
Additionally, a metadata dictionary can be passed using the application
keyword.
To load a simulator and signal processors from a file, call the load()
method. This method will return an ordered list of a Simulator object, a list of
Signal Processor objects, and a metadata dictionary
from mrsimulator import load
sim, processors, application = load("example.mrsim")
Note
The serialization structure has been updated in mrsimulator v0.7. Any .mrsim files from v0.6 and earlier will not work. See Changelog for breaking changes.