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_query=[{"ch1": {"P": [-1], "D": [0]}}],
),
SpectralEvent(
fraction=0.5,
rotor_angle=79.19 * 3.14159 / 180,
transition_query=[{"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_query=[{"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. Mrsimulator should automatically
take care of this update when loading files from v0.6 and below. However, you can use the
update_old_file_struct()
method
to convert older files to the new format.