From c3c83f86b632fe0f83861d449106ae31cce460f9 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 17 May 2014 14:08:50 +0200 Subject: [PATCH] Basic simulation of Al spectroscopy --- artiq/sim.py | 104 ++++++++++++++++++++++++++++++++++++ artiq/units.py | 52 ++++++++++++++++++ examples/al_spectroscopy.py | 29 ++++++++++ 3 files changed, 185 insertions(+) create mode 100644 artiq/sim.py create mode 100644 artiq/units.py create mode 100644 examples/al_spectroscopy.py diff --git a/artiq/sim.py b/artiq/sim.py new file mode 100644 index 000000000..d6cdb99ef --- /dev/null +++ b/artiq/sim.py @@ -0,0 +1,104 @@ +from random import Random +from operator import itemgetter + +from artiq import units + +class SequentialTimeContext: + def __init__(self, current_time): + self.current_time = current_time + self.block_duration = 0 + + def take_time(self, amount): + self.current_time += amount + self.block_duration += amount + +class ParallelTimeContext: + def __init__(self, current_time): + self.current_time = current_time + self.block_duration = 0 + + def take_time(self, amount): + if amount > self.block_duration: + self.block_duration = amount + +class TimeManager: + def __init__(self): + self.stack = [SequentialTimeContext(0)] + self.timeline = [] + + def enter_sequential(self): + new_context = SequentialTimeContext(self.stack[-1].current_time) + self.stack.append(new_context) + + def enter_parallel(self): + new_context = ParallelTimeContext(self.stack[-1].current_time) + self.stack.append(new_context) + + def exit(self): + old_context = self.stack.pop() + self.take_time(old_context.block_duration) + + def take_time(self, amount): + self.stack[-1].take_time(amount) + + def event(self, description): + self.timeline.append((self.stack[-1].current_time, description)) + + def format_timeline(self): + r = "" + prev_time = 0 + for time, description in sorted(self.timeline, key=itemgetter(0)): + t = units.Quantity(time, units.base_s_unit) + dt = units.Quantity(time-prev_time, units.base_s_unit) + r += "@{:10} (+{:10}) ".format(str(t), str(dt)) + for item in description: + r += "{:16}".format(str(item)) + r += "\n" + prev_time = time + return r + +# global namespace for interpreted kernels + +time_manager = TimeManager() +prng = Random(42) + +class _Sequential: + def __enter__(self): + time_manager.enter_sequential() + + def __exit__(self, type, value, traceback): + time_manager.exit() +sequential = _Sequential() + +class _Parallel: + def __enter__(self): + time_manager.enter_parallel() + + def __exit__(self, type, value, traceback): + time_manager.exit() +parallel = _Parallel() + +def delay(duration): + units.check_unit(duration, units.base_s_unit) + time_manager.take_time(duration.amount) + +def wait_edge(input): + duration = prng.randrange(17)*units.ms + time_manager.event(("wait_edge", input, duration)) + time_manager.take_time(duration.amount) + +def pulse(output, frequency, duration): + units.check_unit(frequency, units.base_Hz_unit) + units.check_unit(duration, units.base_s_unit) + time_manager.event(("pulse", output, frequency, duration)) + time_manager.take_time(duration.amount) + +def count_gate(input, duration): + result = prng.randrange(100) + units.check_unit(duration, units.base_s_unit) + time_manager.event(("count_gate", input, duration, result)) + time_manager.take_time(duration.amount) + return result + +def set_dac_voltage(output): + time_manager.event(("set_dac_voltage", output)) diff --git a/artiq/units.py b/artiq/units.py new file mode 100644 index 000000000..89291207a --- /dev/null +++ b/artiq/units.py @@ -0,0 +1,52 @@ +from collections import namedtuple + +_prefixes_str = "pnum_kM" + +Unit = namedtuple("Unit", "base_prefix name") + +class DimensionError(Exception): + pass + +class Quantity: + def __init__(self, amount, unit): + self.amount = int(amount) + self.unit = unit + + def __repr__(self): + r_amount = self.amount + r_prefix = self.unit.base_prefix + if r_amount: + while not r_amount % 1000 and r_prefix < len(_prefixes_str): + r_amount //= 1000 + r_prefix += 1 + return str(r_amount) + " " + _prefixes_str[r_prefix] + self.unit.name + + def __add__(self, other): + if self.unit != other.unit: + raise DimensionError + return Quantity(self.amount + other.amount, self.unit) + __radd__ = __add__ + + def __rmul__(self, other): + if isinstance(other, Quantity): + return NotImplemented + return Quantity(self.amount*other, self.unit) + +def check_unit(value, unit): + if not isinstance(value, Quantity) or value.unit != unit: + raise DimensionError + +def _register_unit(base_prefix, name, prefixes): + base_prefix_exp = _prefixes_str.index(base_prefix) + unit = Unit(base_prefix_exp, name) + globals()["base_"+name+"_unit"] = unit + for prefix in prefixes: + prefix_exp = _prefixes_str.index(prefix) + exp_d = prefix_exp - base_prefix_exp + assert(exp_d >= 0) + quantity = Quantity(1000**exp_d, unit) + full_name = prefix + name if prefix != "_" else name + globals()[full_name] = quantity + +_register_unit("p", "s", "pnum_") +_register_unit("_", "Hz", "_kM") diff --git a/examples/al_spectroscopy.py b/examples/al_spectroscopy.py new file mode 100644 index 000000000..fa1421e80 --- /dev/null +++ b/examples/al_spectroscopy.py @@ -0,0 +1,29 @@ +from artiq.sim import * +from artiq.units import * + +def al_clock_probe(spectroscopy_freq, A1, A2): + state_0_count = 0 + for count in range(100): + wait_edge("mains_sync") + pulse("laser_cooling", 100*MHz, 100*us) + delay(5*us) + with parallel: + pulse("spectroscopy", spectroscopy_freq, 100*us) + with sequential: + delay(50*us) + set_dac_voltage("spectroscopy_b") + delay(5*us) + while True: + delay(5*us) + with parallel: + pulse("state_detection", 100*MHz, 10*us) + photon_count = count_gate("pmt", 10*us) + if photon_count < A1 or photon_count > A2: + break + if photon_count < A1: + state_0_count += 1 + return state_0_count + +if __name__ == "__main__": + al_clock_probe(30*MHz, 3, 30) + print(time_manager.format_timeline())