def test_BlochDecaySpectrum(): # test-1 m1 = BlochDecaySpectrum() dimension_dictionary_ = { "count": 1024, "spectral_width": "25000.0 Hz", "reference_offset": "0.0 Hz", } should_be = { "name": "BlochDecaySpectrum", "channels": ["1H"], "magnetic_flux_density": "9.4 T", "rotor_frequency": "0.0 Hz", "rotor_angle": "0.955316618 rad", "spectral_dimensions": [dimension_dictionary_], } dict_ = m1.json() assert Method.parse_dict_with_units(dict_) == m1 dict_.pop("description") assert dict_ == should_be # test-2 m2_dict = { "channels": ["29Si"], "magnetic_flux_density": "11.7 T", "rotor_angle": "90 deg", "spectral_dimensions": [{}], } m2 = BlochDecaySpectrum.parse_dict_with_units(m2_dict) angle = 90 * np.pi / 180 dimension_dictionary_ = { "count": 1024, "spectral_width": "25000.0 Hz", "reference_offset": "0.0 Hz", } should_be = { "name": "BlochDecaySpectrum", "channels": ["29Si"], "magnetic_flux_density": "11.7 T", "rotor_frequency": "0.0 Hz", "rotor_angle": f"{angle} rad", "spectral_dimensions": [dimension_dictionary_], } dict_ = m2.json() assert Method.parse_dict_with_units(dict_) == m2 dict_.pop("description") assert dict_ == should_be
def test_ST1_VAS_general(): """Inner satellite-transition variable-angle spinning method""" mth = ST1_VAS( channels=["87Rb"], magnetic_flux_density=9.4, # in T spectral_dimensions=[ { "count": 1024, "spectral_width": 5e4, # in Hz "reference_offset": 0, # in Hz }, { "count": 1024, "spectral_width": 5e4, # in Hz "reference_offset": 0, # in Hz }, ], ) assert mth.name == "ST1_VAS" des = ( "Simulate a 1.5 -> 0.5 and -0.5 -> -1.5 satellite-transition variable-angle " "spinning spectrum.") assert mth.description == des assert mth.spectral_dimensions[0].events[ 0].transition_query == TransitionQuery(P={"channel-1": [[-1]]}, D={"channel-1": [[2], [-2]]}) assert mth.spectral_dimensions[1].events[ 0].transition_query == TransitionQuery(P={"channel-1": [[-1]]}, D={"channel-1": [[0]]}) assert Method.parse_dict_with_units(mth.json()) == mth
def test_SSB_general(): """Inner satellite-transition variable-angle spinning method""" mth = SSB2D( channels=["87Rb"], magnetic_flux_density=9.4, # in T rotor_frequency=1200, spectral_dimensions=[ { "count": 1024, "spectral_width": 5e4, # in Hz "reference_offset": 0, # in Hz }, { "count": 1024, "spectral_width": 5e4, # in Hz "reference_offset": 0, # in Hz }, ], ) assert mth.name == "SSB2D" assert mth.description == "Simulate a 2D sideband separation method." # test transition query tq = TransitionQuery(P={"channel-1": [[-1]]}, D={"channel-1": [[0]]}) assert mth.spectral_dimensions[0].events[0].transition_query == tq assert mth.spectral_dimensions[1].events[0].transition_query == tq # test rotor_frequency assert mth.spectral_dimensions[0].events[0].rotor_frequency == 1200 assert mth.spectral_dimensions[1].events[0].rotor_frequency == 1e9 # check serialization assert Method.parse_dict_with_units(mth.json()) == mth
def test_04(): """SAS method declaration""" mth = Method2D( channels=["87Rb"], magnetic_flux_density=9.4, # in T rotor_angle=70.12 * np.pi / 180, spectral_dimensions=[ { "count": 512, "spectral_width": 5e4, # in Hz "events": [ { "transition_query": { "P": [-1], "D": [0] } }, ], }, MAS_DIM.copy(), ], ) assert mth.json() == TESTDATA["SAS"] assert Method.parse_dict_with_units(mth.json()) == mth assert Method2D.parse_dict_with_units(mth.json()) == mth
def test_05(): """Satellite to central correlation method declaration""" sp0 = dict( count=512, spectral_width=5e6, events=[{ "rotor_angle": 0 * np.pi / 180, "transition_query": [{ "P": [-1], "D": [2] }, { "P": [-1], "D": [-2] }], }], ) mth = Method2D( channels=["87Rb"], magnetic_flux_density=9.4, # in T spectral_dimensions=[sp0, MAS_DIM.copy()], ) assert mth.json() == TESTDATA["STMAS"] assert Method.parse_dict_with_units(TESTDATA["STMAS"]) == mth assert Method2D.parse_dict_with_units(TESTDATA["STMAS"]) == mth
def test_methods(): das = Method2D( channels=["87Rb"], magnetic_flux_density=4.2, # in T rotor_angle=54.735 * 3.14159 / 180, # in rads spectral_dimensions=[ { "count": 256, "spectral_width": 2e4, # in Hz "reference_offset": -5e3, # in Hz "label": "70.12 dimension", "events": [ { "fraction": 0.5, "rotor_angle": 70.12 * 3.14159 / 180, # in rads "transition_query": [{ "ch1": { "P": [-1], "D": [0] } }], }, { "fraction": 0.5, "rotor_angle": 30.12 * 3.14159 / 180, # in rads "transition_query": [{ "ch1": { "P": [-1], "D": [0] } }], }, ], }, # The last spectral dimension block is the direct-dimension { "count": 256, "spectral_width": 3e4, # in Hz "reference_offset": -7e3, # in Hz "label": "MAS dimension", "events": [{ "transition_query": [{ "ch1": { "P": [-1], "D": [0] } }] }], }, ], ) assert das.affine_matrix is None assert das.json() == TESTDATA["DAS"] assert Method.parse_dict_with_units(das.json()) == das assert Method2D.parse_dict_with_units(das.json()) == das
def test_3Q_VAS_general(): """3Q-VAS method test""" mth = ThreeQ_VAS(channels=["87Rb"], spectral_dimensions=[{}, {}]) assert mth.name == "ThreeQ_VAS" assert mth.description == "Simulate a 3Q variable-angle spinning spectrum." assert mth.spectral_dimensions[0].events[ 0].transition_query == TransitionQuery(P={"channel-1": [[-3]]}, D={"channel-1": [[0]]}) assert mth.spectral_dimensions[1].events[ 0].transition_query == TransitionQuery(P={"channel-1": [[-1]]}, D={"channel-1": [[0]]}) assert Method.parse_dict_with_units(mth.json()) == mth
def load_methods(self, filename: str): """Load a list of methods from the given JSON serialized file. Args: str filename: A local or remote address to a JSON serialized file. Example ------- >>> sim.load_methods(filename) # doctest:+SKIP """ contents = import_json(filename) self.methods = [Method.parse_dict_with_units(obj) for obj in contents]
def test_05(): """Satellite to central correlation method declaration""" mth = Method2D( channels=["87Rb"], magnetic_flux_density=9.4, # in T spectral_dimensions=[ { "count": 512, "spectral_width": 5e6, # in Hz "reference_offset": 0, # in Hz "events": [ { "rotor_angle": 0 * np.pi / 180, "transition_query": { "P": [-1], "D": [2, -2] }, }, ], }, { "count": 128, "spectral_width": 5e4, # in Hz "reference_offset": 0, # in Hz "events": [ { "rotor_angle": 54.735 * np.pi / 180, "transition_query": { "P": [-1], "D": [0] }, }, ], }, ], ) assert TESTDATA["STMAS"] == mth.json() assert Method.parse_dict_with_units(TESTDATA["STMAS"]) == mth
def test_method_2D(): error = "The first element of the affine matrix cannot be zero." with pytest.raises(ValueError, match=f".*{error}.*"): Method2D(spectral_dimensions=[{}, {}], affine_matrix=[0, 1, 2, 3]) # parse dict with units test dict_1d = { "channels": ["87Rb"], "magnetic_flux_density": "7 T", # in T "rotor_angle": "54.735 deg", "spectral_dimensions": [ { "spectral_width": "10 kHz", **sample_test_output }, { "spectral_width": "10 kHz", **sample_test_output }, ], } method1a = Method2D.parse_dict_with_units(dict_1d) assert Method.parse_dict_with_units(method1a.json()) == method1a method1b = Method2D( channels=["87Rb"], magnetic_flux_density=7, # in T rotor_angle=54.735 * np.pi / 180, spectral_dimensions=[ { "spectral_width": 1e4, **sample_test_output }, { "spectral_width": 1e4, **sample_test_output }, ], ) assert method1a == method1b
def test_03(): """generic method declaration""" mth = Method2D( channels=["87Rb"], magnetic_flux_density=9.4, # in T spectral_dimensions=[ { "count": 1024, "spectral_width": 5e4 }, { "count": 1024, "spectral_width": 5e4 }, ], ) assert mth.json() == TESTDATA["generic"] assert Method.parse_dict_with_units(mth.json()) == mth
def test_3QMAS(): """3Q MAS correlation method declaration""" mth = ThreeQ_VAS( channels=["87Rb"], magnetic_flux_density=9.4, # in T spectral_dimensions=[ { "count": 512, "spectral_width": 5e6, # in Hz "reference_offset": 0, # in Hz }, { "count": 128, "spectral_width": 5e4, # in Hz "reference_offset": 0, # in Hz }, ], ) assert np.allclose(mth.affine_matrix, [0.5625, 0.4375, 0, 1]) assert Method.parse_dict_with_units(mth.json()) == mth
def test_method_1D(): error = "Expecting a 1x1 affine matrix." with pytest.raises(ValueError, match=f".*{error}.*"): Method1D(spectral_dimensions=[{}], affine_matrix=[1, 2, 3, 4]) error = "Method requires exactly 1 spectral dimensions, given 2." with pytest.raises(ValueError, match=f".*{error}.*"): Method1D(spectral_dimensions=[{}, {}]) # parse dict with units test dict_1d = { "channels": ["87Rb"], "magnetic_flux_density": "7 T", # in T "rotor_angle": "54.735 deg", "rotor_frequency": "1e9 Hz", "spectral_dimensions": [{ "spectral_width": "10 kHz", "reference_offset": "-4 kHz", **sample_test_output, }], } method1a = Method1D.parse_dict_with_units(dict_1d) assert Method.parse_dict_with_units(method1a.json()) == method1a method1b = Method1D( channels=["87Rb"], magnetic_flux_density=7, # in T rotor_angle=54.735 * np.pi / 180, rotor_frequency=1e9, spectral_dimensions=[{ "spectral_width": 1e4, "reference_offset": -4e3, **sample_test_output, }], ) assert method1a == method1b
def test_03(): """generic method declaration""" mth = Method( name="generic", channels=["87Rb"], magnetic_flux_density=9.4, # in T rotor_frequency=1000000000000.0, spectral_dimensions=[ { "count": 1024, "spectral_width": 5e4, "events": [sq_tq] }, { "count": 1024, "spectral_width": 5e4, "events": [sq_tq] }, ], ) assert mth.json() == TESTDATA["generic"] assert Method.parse_dict_with_units(mth.json()) == mth
def test_method(): # test-1 single dimension method # parse dict with units method_dictionary = { "name": "test-1-d", "description": "Test-1", "channels": ["29Si"], "magnetic_flux_density": "9.6 T", "rotor_frequency": "1 kHz", "rotor_angle": "54.735 deg", "spectral_dimensions": [dimension_dictionary], } the_method = Method.parse_dict_with_units(method_dictionary) basic_method_tests(the_method) # test-2 two dimensional two events method # parse dict with units dimension_dictionary["events"].append(event_dictionary) method_dictionary = { "name": "test-1-d", "description": "Test-1", "channels": ["29Si"], "magnetic_flux_density": "9.6 T", "rotor_frequency": "1 kHz", "rotor_angle": "54.735 deg", "spectral_dimensions": [dimension_dictionary, dimension_dictionary], } the_method = Method.parse_dict_with_units(method_dictionary) # test experiment assignment assert the_method.experiment is None with pytest.raises(ValidationError, match="Unable to read the data."): the_method.experiment = "test" data = np.random.rand(100).reshape(10, 10) csdm_data = cp.as_csdm(data) csdm_data.x[0] *= cp.ScalarQuantity("Hz") csdm_data.x[1] *= cp.ScalarQuantity("Hz") the_method.experiment = csdm_data the_method.simulation = csdm_data assert isinstance(the_method.experiment, cp.CSDM) assert isinstance(the_method.simulation, cp.CSDM) csdm_dict = csdm_data.to_dict() the_method.experiment = csdm_dict assert isinstance(the_method.experiment, cp.CSDM) assert the_method.experiment == csdm_data the_method.simulation = csdm_dict assert isinstance(the_method.simulation, cp.CSDM) assert the_method.simulation == csdm_data # json() event_dictionary_ = { "fraction": 0.5, "transition_query": [{ "ch1": { "P": [-1] } }] } dimension_dictionary_ = { "count": 1024, "spectral_width": "100.0 Hz", "events": [event_dictionary_, event_dictionary_], } method_dictionary_ = { "name": "test-1-d", "description": "Test-1", "channels": ["29Si"], "magnetic_flux_density": "9.6 T", "rotor_frequency": "1000.0 Hz", "rotor_angle": "0.9553059660790962 rad", "spectral_dimensions": [dimension_dictionary_, dimension_dictionary_], "simulation": csdm_data.to_dict(), "experiment": csdm_data.to_dict(), } serialize = the_method.json() serialize["simulation"]["csdm"].pop("timestamp") assert serialize == method_dictionary_ # json(units=False) event_dictionary_ = { "fraction": 0.5, "transition_query": [{ "ch1": { "P": [-1] } }] } dimension_dictionary_ = { "count": 1024, "spectral_width": 100.0, "events": [event_dictionary_, event_dictionary_], } method_dictionary_ = { "name": "test-1-d", "description": "Test-1", "channels": ["29Si"], "magnetic_flux_density": 9.6, "rotor_frequency": 1000.0, "rotor_angle": 0.9553059660790962, "spectral_dimensions": [dimension_dictionary_, dimension_dictionary_], "simulation": csdm_data.to_dict(), "experiment": csdm_data.to_dict(), } serialize = the_method.json(units=False) serialize["simulation"]["csdm"].pop("timestamp") assert serialize == method_dictionary_
def test_method_1D(): error = "Expecting a 1x1 affine matrix." with pytest.raises(ValueError, match=f".*{error}.*"): Method1D(spectral_dimensions=[{}], affine_matrix=[1, 2, 3, 4]) error = r"The method allows 1 spectral dimension\(s\), 2 given." with pytest.raises(ValueError, match=f".*{error}.*"): Method1D(spectral_dimensions=[{}, {}]) # parse dict with units test dict_1d = { "channels": ["87Rb"], "magnetic_flux_density": "7 T", # in T "rotor_angle": "54.735 deg", "rotor_frequency": "1e9 Hz", "spectral_dimensions": [{ "count": 1024, "spectral_width": "10 kHz", # in Hz "reference_offset": "-4 kHz", # in Hz "events": [ { "fraction": 27 / 17, "freq_contrib": ["Quad2_0"] }, { "fraction": 1, "freq_contrib": ["Quad2_4"] }, ], }], } method1a = Method1D.parse_dict_with_units(dict_1d) assert Method.parse_dict_with_units(method1a.json()) == method1a method1b = Method1D( channels=["87Rb"], magnetic_flux_density=7, # in T rotor_angle=54.735 * np.pi / 180, rotor_frequency=1e9, spectral_dimensions=[{ "count": 1024, "spectral_width": 1e4, # in Hz "reference_offset": -4e3, # in Hz "events": [ { "fraction": 27 / 17, "freq_contrib": ["Quad2_0"] }, { "fraction": 1, "freq_contrib": ["Quad2_4"] }, ], }], ) assert method1a == method1b
def constructor(self, spectral_dimensions=[{}], **kwargs): # spectral_dimensions_root = deepcopy(spectral_dimensions) # kwargs_root = deepcopy(kwargs) parse = False if "parse" in kwargs: parse = kwargs["parse"] kwargs.pop("parse") local_template = deepcopy(template) spectral_dimensions = parse_spectral_dimensions(spectral_dimensions) prep = prepare_method_structure(local_template, **kwargs) global_events = local_template["global_event_attributes"] ge = set(global_events) kw = set(kwargs) common = kw.intersection(ge) if common != set(): info = "`, `".join(list(common)) e = f"`{info}` attribute cannot be modified for {prep['name']} method." raise AttributeError(e) dim = [] n_sp = len(spectral_dimensions) n_tem = len(local_template["spectral_dimensions"]) if n_tem < n_sp: raise ValueError( f"The method allows {n_tem} spectral dimension(s), {n_sp} given." ) for i, s in enumerate(local_template["spectral_dimensions"]): events = [] _fill_missing_events_in_template(spectral_dimensions[i], s) for j, e in enumerate(s["events"]): kw = deepcopy(kwargs) ge = deepcopy(global_events) # Remove `property_units` from default events instance. if "property_units" in e: e.pop("property_units") if "events" in spectral_dimensions[i]: kw.update(spectral_dimensions[i]["events"][j]) e.update(kw) # prioritize the keyword arguments over the global arguments. ge.update(kw) e.update(ge) params = _fix_strings_in_events(e) if parse else Event(**e) events.append(params) if "events" in spectral_dimensions[i]: spectral_dimensions[i].pop("events") params = {**spectral_dimensions[i], "events": events} params = params if parse else SpectralDimension(**params) dim.append(params) method = {**prep, "spectral_dimensions": dim} method = Method.parse_dict_with_units(method) if parse else Method(**method) return method
def parse_dict_with_units(cls, py_dict: dict): """Parse the physical quantity from a dictionary representation of the Simulator object, where the physical quantity is expressed as a string with a number and a unit. Args: dict py_dict: A required python dict object. Returns: A :ref:`simulator_api` object. Example ------- >>> sim_py_dict = { ... 'config': { ... 'decompose_spectrum': 'none', ... 'integration_density': 70, ... 'integration_volume': 'octant', ... 'number_of_sidebands': 64 ... }, ... 'spin_systems': [ ... { ... 'abundance': '100 %', ... 'sites': [{ ... 'isotope': '13C', ... 'isotropic_chemical_shift': '20.0 ppm', ... 'shielding_symmetric': {'eta': 0.5, 'zeta': '10.0 ppm'} ... }] ... }, ... { ... 'abundance': '100 %', ... 'sites': [{ ... 'isotope': '1H', ... 'isotropic_chemical_shift': '-4.0 ppm', ... 'shielding_symmetric': {'eta': 0.1, 'zeta': '2.1 ppm'} ... }] ... }, ... { ... 'abundance': '100 %', ... 'sites': [{ ... 'isotope': '27Al', ... 'isotropic_chemical_shift': '120.0 ppm', ... 'shielding_symmetric': {'eta': 0.1, 'zeta': '2.1 ppm'} ... }] ... } ... ] ... } >>> sim = Simulator.parse_dict_with_units(sim_py_dict) >>> len(sim.spin_systems) 3 """ py_copy_dict = deepcopy(py_dict) if "spin_systems" in py_copy_dict: spin_sys = py_copy_dict["spin_systems"] spin_sys = [SpinSystem.parse_dict_with_units(obj) for obj in spin_sys] py_copy_dict["spin_systems"] = spin_sys if "methods" in py_copy_dict: methods = py_copy_dict["methods"] methods = [Method.parse_dict_with_units(obj) for obj in methods] py_copy_dict["methods"] = methods return Simulator(**py_copy_dict)
def test_method(): # test-1 single dimension method # parse dict with units event_dictionary = { "fraction": 0.5, "freq_contrib": freq_default, "magnetic_flux_density": "9.6 T", "rotor_frequency": "1 kHz", "rotor_angle": "54.735 deg", } dimension_dictionary = { "count": 1024, "spectral_width": "100 Hz", "reference_offset": "0 GHz", "events": [event_dictionary], } method_dictionary = { "name": "test-1-d", "description": "Test-1", "channels": ["29Si"], "spectral_dimensions": [dimension_dictionary], } the_method = Method.parse_dict_with_units(method_dictionary) basic_method_tests(the_method) # test-2 two dimensional two events method # parse dict with units event_dictionary = { "fraction": 0.5, "freq_contrib": freq_default, "magnetic_flux_density": "9.6 T", "rotor_frequency": "1 kHz", "rotor_angle": "54.735 deg", } dimension_dictionary = { "count": 1024, "spectral_width": "100 Hz", "reference_offset": "0 GHz", "events": [event_dictionary, event_dictionary], } method_dictionary = { "name": "test-1-d", "description": "Test-1", "channels": ["29Si"], "spectral_dimensions": [dimension_dictionary, dimension_dictionary], } the_method = Method.parse_dict_with_units(method_dictionary) # test experiment assignment assert the_method.experiment is None with pytest.raises(ValidationError, match="Unable to read the data."): the_method.experiment = "test" data = np.random.rand(100).reshape(10, 10) csdm_data = cp.as_csdm(data) csdm_data.dimensions[0] *= cp.ScalarQuantity("Hz") csdm_data.dimensions[1] *= cp.ScalarQuantity("Hz") the_method.experiment = csdm_data assert isinstance(the_method.experiment, cp.CSDM) csdm_dict = csdm_data.to_dict() the_method.experiment = csdm_dict assert isinstance(the_method.experiment, cp.CSDM) assert the_method.experiment == csdm_data # to_dict_with_unit() event_dictionary_ = { "fraction": 0.5, "transition_query": { "P": { "channel-1": [[-1.0]] } }, } dimension_dictionary_ = { "count": 1024, "spectral_width": "100.0 Hz", "reference_offset": "0.0 Hz", "events": [event_dictionary_, event_dictionary_], } method_dictionary_ = { "name": "test-1-d", "description": "Test-1", "channels": ["29Si"], "magnetic_flux_density": "9.6 T", "rotor_frequency": "1000.0 Hz", "rotor_angle": "0.9553059660790962 rad", "spectral_dimensions": [dimension_dictionary_, dimension_dictionary_], "experiment": csdm_data.to_dict(), } assert the_method.json() == method_dictionary_ # reduced_dict() event_dictionary_ = { "fraction": 0.5, "freq_contrib": freq_default, "magnetic_flux_density": 9.6, "rotor_frequency": 1000.0, "rotor_angle": 0.9553059660790962, "transition_query": { "P": { "channel-1": [[-1.0]] } }, } dimension_dictionary_ = { "count": 1024, "spectral_width": 100.0, "reference_offset": 0.0, "events": [event_dictionary_, event_dictionary_], } method_dictionary_ = { "name": "test-1-d", "description": "Test-1", "channels": ["29Si"], "spectral_dimensions": [dimension_dictionary_, dimension_dictionary_], "experiment": csdm_data.to_dict(), } assert the_method.reduced_dict() == method_dictionary_ # update_spectral_dimension_attributes_from_experiment the_method.update_spectral_dimension_attributes_from_experiment() for i in range(2): assert the_method.spectral_dimensions[i].count == 10 assert the_method.spectral_dimensions[i].spectral_width == 10 assert the_method.spectral_dimensions[i].reference_offset == 0 assert the_method.spectral_dimensions[i].origin_offset == 0
def test_SSB_affine(): mth = SSB2D(channels=["13C"], rotor_frequency=1200) np.allclose(mth.affine_matrix, [1, -1, 0, 0]) assert Method.parse_dict_with_units(mth.json()) == mth