def test_rho0(self): muexp = MuonExperiment(['e', 'mu']) rho0 = muexp.get_starting_state() self.assertTrue( np.all( np.isclose(rho0.matrix, [[0.25, 0.25, 0, 0], [0.25, 0.25, 0, 0], [0, 0, 0.25, 0.25], [0, 0, 0.25, 0.25]]))) gmu = constants.MU_GAMMA ge = constants.ELEC_GAMMA T = 100 muexp.set_magnetic_field(2.0e-6 * cnst.k * T / (ge * cnst.h)) muexp.set_temperature(T) muexp.set_muon_polarization('z') rho0 = muexp.get_starting_state() Z = np.exp([-1, 1]) Z /= np.sum(Z) self.assertTrue( np.all(np.isclose(np.diag(rho0.matrix), [Z[0], 0, Z[1], 0])))
def test_dissipation(self): # Simple system g = 1.0 muexp = MuonExperiment(['mu']) muexp.set_dissipation_coupling(0, g) times = np.linspace(0, 10) results = muexp.run_experiment(times) evol = results['e'] solx = np.real(0.5 * np.exp(-np.pi * g * times)) self.assertTrue(np.all(np.isclose(evol[:, 0], solx))) # Check for temperature equilibrium T = 0.1 muexp.set_magnetic_field(-1.0) muexp.set_temperature(T) beta = 1.0 / (cnst.k * T) Z = np.exp(-cnst.h * constants.MU_GAMMA * 1e6 * beta) # Just let it evolve, see the result at long times Sz = muexp.spin_system.operator({0: 'z'}) rhoinf = muexp.run_experiment([20], Sz)['e'] self.assertAlmostEqual(rhoinf[0, 0], 0.5 * (1 - Z) / (1 + Z))
def test_slices(self): gmu = constants.MU_GAMMA muexp = MuonExperiment(['mu']) muexp.spin_system.add_zeeman_term(0, 1.0 / gmu) muexp.set_powder_average(100) times = np.linspace(0, 1.0) cos = np.cos(2 * np.pi * times) signal = muexp.run_experiment(times)['e'][:, 0] self.assertTrue( np.all( np.isclose(signal, 0.5 * (2.0 / 3.0 + 1.0 / 3.0 * cos), atol=1e-3))) # Now slice signal_0 = muexp.run_experiment(times, orient_slice=slice(0, 1))['e'][:, 0] self.assertTrue( np.all( np.isclose(signal_0, 0.5 * cos * muexp.weights[0], atol=1e-3)))
def test_run(self): # Empty system muexp = MuonExperiment(['e', 'mu']) times = np.linspace(0, 10) results = muexp.run_experiment(times) self.assertTrue(np.all(results['e'] == 0.5)) # Simple system muexp.spin_system.add_linear_term(1, [0, 0, 1.0]) results = muexp.run_experiment(times, acquire='ei') tau = constants.MU_TAU self.assertTrue( np.all( np.isclose(results['e'][:, 0], 0.5 * np.cos(2 * np.pi * times)))) self.assertAlmostEqual(results['i'][0, 0], 0.5 / (1.0 + 4 * np.pi**2 * tau**2))
def test_create(self): muexp = MuonExperiment(['e', 'mu']) self.assertEqual(muexp.spin_system.elec_indices, {0}) self.assertEqual(muexp.spin_system.muon_index, 1) gmu = constants.MU_GAMMA ge = constants.ELEC_GAMMA self.assertTrue( np.all(muexp._Hz.matrix == np.diag([ 0.5 * (ge + gmu), 0.5 * (ge - gmu), 0.5 * (-ge + gmu), 0.5 * (-ge - gmu) ]))) self.assertTrue(isinstance(muexp._Hz, Hamiltonian))
def test_powder(self): muexp = MuonExperiment() N = 20 muexp.set_powder_average(N) self.assertTrue(muexp.orientations.shape[0] == muexp.weights.shape[0]) self.assertTrue(muexp.weights.shape[0] >= N) # Are these correct? Basic test muexp.set_powder_average(1000) o = muexp.orientations w = muexp.weights f = 3 * np.cos(o[:, 0])**2 - 1 self.assertAlmostEqual(np.sum(f * w), 0.0, 5)
import cProfile import numpy as np from muspinsim.experiment import MuonExperiment exp = MuonExperiment(['e', 'mu', 'H']) exp.spin_system.add_hyperfine_term(1, np.diag([1,1,4])*100) exp.spin_system.add_hyperfine_term(2, np.diag([1,1,4])*100/3) exp.set_powder_average(100) exp.set_magnetic_field(5.0) exp.set_dissipation_coupling(0, 1.0) exp.set_muon_polarization('z') times = np.linspace(0, 1, 1000) op = exp.spin_system.operator({1: 'z'}) cProfile.run('exp.run_experiment(times, [op], "i")', sort='cumulative')
def __init__(self, params, logfile=None): self._log = logfile if logfile: logging.basicConfig(filename=logfile, level=logging.INFO, format='[%(levelname)s] [%(threadName)s] [%(asctime)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S') # Create self.experiment = MuonExperiment(params.spins) self.log('Hamiltonian created with spins:') self.log(', '.join(map(str, params.spins))) self.log('Adding Hamiltonian terms:') for i, B in params.zeeman.items(): self.experiment.spin_system.add_zeeman_term(i, B) self.log('\tAdded zeeman term to spin {0}'.format(i+1)) for (i, j), A in params.hyperfine.items(): self.experiment.spin_system.add_hyperfine_term(i, np.array(A), j) self.log('\tAdded hyperfine term to spin {0}'.format(i+1)) for (i, j), r in params.dipolar.items(): self.experiment.spin_system.add_dipolar_term(i, j, r) self.log('\tAdded dipolar term to spins {0}, {1}'.format(i+1, j+1)) for i, EFG in params.quadrupolar.items(): self.experiment.spin_system.add_quadrupolar_term(i, EFG) self.log('\tAdded quadrupolar term to spin {0}'.format(i+1)) for i, d in params.dissipation.items(): self.experiment.spin_system.set_dissipation(i, d) self.log('\tSet dissipation parameter for spin ' '{0} to {1} MHz'.format(i+1, d)) self.log('') # Ranges trange = _make_range(params.time) self.log('Using time range: ' '{0} to {1} us in {2} steps'.format(*trange)) self.time_axis = np.linspace(*trange) brange = _make_range(params.field) self.log('Using field range: ' '{0} to {1} T in {2} steps'.format(*brange)) self.field_axis = np.linspace(*brange) # Powder averaging if params.powder is None: self.experiment.set_single_crystal(0, 0) else: scheme = params.powder[0] N = params.powder[1] self.experiment.set_powder_average(N, scheme) self.log('Using powder averaging scheme ' '{0}'.format(scheme.upper())) self.log( '{0} orientations generated'.format( len(self.experiment.weights))) if params.polarization == 'longitudinal': self.muon_axis = 'z' elif params.polarization == 'transverse': self.muon_axis = 'x' else: raise RuntimeError( 'Invalid polarization {0}'.format(params.polarization)) self.experiment.set_muon_polarization(self.muon_axis) self.log('Muon beam polarized along axis {0}'.format(self.muon_axis)) # Temperature self.temperature = params.temperature self.experiment.set_temperature(self.temperature) self.log('Using temperature of {0} K'.format(self.temperature)) # What to save ssys = self.experiment.spin_system self.observable = ssys.operator({ssys.muon_index: self.muon_axis}) self.save = [p[0] for p in params.save] self.log('*'*20 + '\n')
class MuonExperimentalSetup(object): @mpi.execute_on_root def __init__(self, params, logfile=None): self._log = logfile if logfile: logging.basicConfig(filename=logfile, level=logging.INFO, format='[%(levelname)s] [%(threadName)s] [%(asctime)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S') # Create self.experiment = MuonExperiment(params.spins) self.log('Hamiltonian created with spins:') self.log(', '.join(map(str, params.spins))) self.log('Adding Hamiltonian terms:') for i, B in params.zeeman.items(): self.experiment.spin_system.add_zeeman_term(i, B) self.log('\tAdded zeeman term to spin {0}'.format(i+1)) for (i, j), A in params.hyperfine.items(): self.experiment.spin_system.add_hyperfine_term(i, np.array(A), j) self.log('\tAdded hyperfine term to spin {0}'.format(i+1)) for (i, j), r in params.dipolar.items(): self.experiment.spin_system.add_dipolar_term(i, j, r) self.log('\tAdded dipolar term to spins {0}, {1}'.format(i+1, j+1)) for i, EFG in params.quadrupolar.items(): self.experiment.spin_system.add_quadrupolar_term(i, EFG) self.log('\tAdded quadrupolar term to spin {0}'.format(i+1)) for i, d in params.dissipation.items(): self.experiment.spin_system.set_dissipation(i, d) self.log('\tSet dissipation parameter for spin ' '{0} to {1} MHz'.format(i+1, d)) self.log('') # Ranges trange = _make_range(params.time) self.log('Using time range: ' '{0} to {1} us in {2} steps'.format(*trange)) self.time_axis = np.linspace(*trange) brange = _make_range(params.field) self.log('Using field range: ' '{0} to {1} T in {2} steps'.format(*brange)) self.field_axis = np.linspace(*brange) # Powder averaging if params.powder is None: self.experiment.set_single_crystal(0, 0) else: scheme = params.powder[0] N = params.powder[1] self.experiment.set_powder_average(N, scheme) self.log('Using powder averaging scheme ' '{0}'.format(scheme.upper())) self.log( '{0} orientations generated'.format( len(self.experiment.weights))) if params.polarization == 'longitudinal': self.muon_axis = 'z' elif params.polarization == 'transverse': self.muon_axis = 'x' else: raise RuntimeError( 'Invalid polarization {0}'.format(params.polarization)) self.experiment.set_muon_polarization(self.muon_axis) self.log('Muon beam polarized along axis {0}'.format(self.muon_axis)) # Temperature self.temperature = params.temperature self.experiment.set_temperature(self.temperature) self.log('Using temperature of {0} K'.format(self.temperature)) # What to save ssys = self.experiment.spin_system self.observable = ssys.operator({ssys.muon_index: self.muon_axis}) self.save = [p[0] for p in params.save] self.log('*'*20 + '\n') @mpi.execute_on_root def log(self, message): if self._log: logging.info(message) def broadcast(self): # Broadcast the contents of this object to other MPI threads mpi.broadcast_object(self, only=[ 'experiment', 'field_axis', 'time_axis', 'muon_axis', 'temperature', 'observable', 'save' ]) def run(self): self.broadcast() exp = self.experiment ssys = self.experiment.spin_system if ssys.is_dissipative: self.log('Spin system is dissipative; using Lindbladian') # Now slicing the values N_f = len(self.field_axis) # Fields N_o = len(exp.weights) # Orientations results = { 'e': None, 'i': None } if 'e' in self.save: results['e'] = np.zeros((N_f, len(self.time_axis))) if 'i' in self.save: results['i'] = np.zeros(N_f) # Split the tasks tasks = mpi.split_2D(range(N_f), range(N_o)) field_scan, orient_slice = tasks[mpi.rank] self.log(f"MPI: {mpi.rank} will run fields: {field_scan}") if len(tasks) > 1: tsizes = [len(t[0])*len(t[1]) for t in tasks] self.log('Splitting jobs over {0} cores'.format(mpi.size)) self.log('Job sizes:\n\t' + ' '.join(map(str, tsizes))) # Loop over fields for i in field_scan: B = self.field_axis[i] if i%10 == 0: # Reduces logfile spam self.log('Performing calculations for B = {0} T'.format(B)) exp.set_magnetic_field(B) field_results = exp.run_experiment(self.time_axis, operators=[self.observable], acquire=self.save, orient_slice=orient_slice) if 'e' in self.save: results['e'][i] = field_results['e'][:, 0] if 'i' in self.save: results['i'][i] = field_results['i'][0] # Reduce results for k, v in results.items(): if v is None: continue results[k] = mpi.sum_data(v) self.log('*'*20) return results