MRSimulator I/O¶
We offer a range of serialization options based on a JSON structure demonstrated below.
Dictionary Representation of Class Instances¶
All MRSimulator class instances can be serialized into a JSON format. Calling the
json()
method on an instance will return a Python dictionary representing the instance
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 class instances 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 instance 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 instances directly from classes.
Saving and Loading Spin Systems from a File¶
A list of spin systems in a Simulator instance 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 instance
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 instances.
We encourage the convention of using .mrsys extension for this JSON file.
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 instance 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 instance. If multiple
methods are present, e.g., at different spinning speeds, they will also be serialized. We
encourage the convention of using .mrmtd extension for this JSON file.
Just like spin systems, methods can also be loaded from a file. Here we load the DAS
method into a new Simulator instance 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 Instance¶
The entire Simulator instance 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 instance
sim.save("sample.mrsim")
Now the file sample.mrsim
holds the JSON representation of sim
, a Simulator instance.
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 instances¶
The Simulator instance and a list of Signal Processor instances
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],
)
By default, all attribute values are serialized with units. You can serialize attributes
without units, assuming the default unit of the attribute, by passing
with_units=False
to the method. Recall that all class instances in MRSimulator
have the attribute property_units
which provides the default units for all class
properties. 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 instance, a list of
Signal Processor instances, 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.