forked from M-Labs/artiq
refactor ddb/pdb/rdb
This commit is contained in:
parent
8b02b58a77
commit
32d141f5ac
|
@ -1,6 +1,5 @@
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
|
|
||||||
from artiq.language.db import AutoDB
|
|
||||||
from artiq.language.units import ms
|
from artiq.language.units import ms
|
||||||
from artiq.coredevice.runtime import LinkInterface
|
from artiq.coredevice.runtime import LinkInterface
|
||||||
|
|
||||||
|
@ -13,7 +12,10 @@ class _RuntimeEnvironment(LinkInterface):
|
||||||
return str(self.llvm_module)
|
return str(self.llvm_module)
|
||||||
|
|
||||||
|
|
||||||
class Comm(AutoDB):
|
class Comm:
|
||||||
|
def __init__(self, dmgr):
|
||||||
|
pass
|
||||||
|
|
||||||
def get_runtime_env(self):
|
def get_runtime_env(self):
|
||||||
return _RuntimeEnvironment()
|
return _RuntimeEnvironment()
|
||||||
|
|
||||||
|
|
|
@ -3,16 +3,15 @@ import serial
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
from artiq.coredevice.comm_generic import CommGeneric
|
from artiq.coredevice.comm_generic import CommGeneric
|
||||||
from artiq.language.db import *
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Comm(CommGeneric, AutoDB):
|
class Comm(CommGeneric):
|
||||||
class DBKeys:
|
def __init__(self, dmgr, serial_dev, baud_rate=115200):
|
||||||
serial_dev = Argument()
|
self.serial_dev = serial_dev
|
||||||
baud_rate = Argument(115200)
|
self.baud_rate = baud_rate
|
||||||
|
|
||||||
def open(self):
|
def open(self):
|
||||||
if hasattr(self, "port"):
|
if hasattr(self, "port"):
|
||||||
|
|
|
@ -2,16 +2,15 @@ import logging
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
from artiq.coredevice.comm_generic import CommGeneric
|
from artiq.coredevice.comm_generic import CommGeneric
|
||||||
from artiq.language.db import *
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Comm(CommGeneric, AutoDB):
|
class Comm(CommGeneric):
|
||||||
class DBKeys:
|
def __init__(self, dmgr, host, port=1381):
|
||||||
host = Argument()
|
self.host = host
|
||||||
port = Argument(1381)
|
self.port = port
|
||||||
|
|
||||||
def open(self):
|
def open(self):
|
||||||
if hasattr(self, "socket"):
|
if hasattr(self, "socket"):
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from artiq.language.core import *
|
from artiq.language.core import *
|
||||||
from artiq.language.db import *
|
|
||||||
from artiq.language.units import ns
|
from artiq.language.units import ns
|
||||||
|
|
||||||
from artiq.transforms.inline import inline
|
from artiq.transforms.inline import inline
|
||||||
|
@ -46,13 +45,12 @@ def _no_debug_unparse(label, node):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Core(AutoDB):
|
class Core:
|
||||||
class DBKeys:
|
def __init__(self, dmgr, ref_period=8*ns, external_clock=False):
|
||||||
comm = Device()
|
self.comm = dmgr.get("comm")
|
||||||
ref_period = Argument(8*ns)
|
self.ref_period = ref_period
|
||||||
external_clock = Argument(False)
|
self.external_clock = external_clock
|
||||||
|
|
||||||
def build(self):
|
|
||||||
self.first_run = True
|
self.first_run = True
|
||||||
self.core = self
|
self.core = self
|
||||||
self.comm.core = self
|
self.comm.core = self
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
from artiq.language.core import *
|
from artiq.language.core import *
|
||||||
from artiq.language.db import *
|
|
||||||
from artiq.language.units import *
|
from artiq.language.units import *
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,14 +22,12 @@ class _BatchContextManager:
|
||||||
self.dds_bus.batch_exit()
|
self.dds_bus.batch_exit()
|
||||||
|
|
||||||
|
|
||||||
class DDSBus(AutoDB):
|
class DDSBus:
|
||||||
"""Core device Direct Digital Synthesis (DDS) bus batching driver.
|
"""Core device Direct Digital Synthesis (DDS) bus batching driver.
|
||||||
|
|
||||||
Manages batching of DDS commands on a DDS shared bus."""
|
Manages batching of DDS commands on a DDS shared bus."""
|
||||||
class DBKeys:
|
def __init__(self, dmgr):
|
||||||
core = Device()
|
self.core = dmgr.get("core")
|
||||||
|
|
||||||
def build(self):
|
|
||||||
self.batch = _BatchContextManager(self)
|
self.batch = _BatchContextManager(self)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
|
@ -46,7 +43,7 @@ class DDSBus(AutoDB):
|
||||||
syscall("dds_batch_exit")
|
syscall("dds_batch_exit")
|
||||||
|
|
||||||
|
|
||||||
class _DDSGeneric(AutoDB):
|
class _DDSGeneric:
|
||||||
"""Core device Direct Digital Synthesis (DDS) driver.
|
"""Core device Direct Digital Synthesis (DDS) driver.
|
||||||
|
|
||||||
Controls one DDS channel managed directly by the core device's runtime.
|
Controls one DDS channel managed directly by the core device's runtime.
|
||||||
|
@ -57,12 +54,10 @@ class _DDSGeneric(AutoDB):
|
||||||
:param sysclk: DDS system frequency.
|
:param sysclk: DDS system frequency.
|
||||||
:param channel: channel number of the DDS device to control.
|
:param channel: channel number of the DDS device to control.
|
||||||
"""
|
"""
|
||||||
class DBKeys:
|
def __init__(self, dmgr, sysclk, channel):
|
||||||
core = Device()
|
self.core = dmgr.get("core")
|
||||||
sysclk = Argument()
|
self.sysclk = sysclk
|
||||||
channel = Argument()
|
self.channel = channel
|
||||||
|
|
||||||
def build(self):
|
|
||||||
self.phase_mode = PHASE_MODE_CONTINUOUS
|
self.phase_mode = PHASE_MODE_CONTINUOUS
|
||||||
|
|
||||||
@portable
|
@portable
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
from artiq.language.core import *
|
from artiq.language.core import *
|
||||||
from artiq.language.db import *
|
|
||||||
|
|
||||||
|
|
||||||
class TTLOut(AutoDB):
|
class TTLOut:
|
||||||
"""RTIO TTL output driver.
|
"""RTIO TTL output driver.
|
||||||
|
|
||||||
This should be used with output-only channels.
|
This should be used with output-only channels.
|
||||||
|
@ -10,12 +9,10 @@ class TTLOut(AutoDB):
|
||||||
:param core: core device
|
:param core: core device
|
||||||
:param channel: channel number
|
:param channel: channel number
|
||||||
"""
|
"""
|
||||||
class DBKeys:
|
def __init__(self, dmgr, channel):
|
||||||
core = Device()
|
self.core = dmgr.get("core")
|
||||||
channel = Argument()
|
self.channel = channel
|
||||||
|
|
||||||
|
|
||||||
def build(self):
|
|
||||||
# in RTIO cycles
|
# in RTIO cycles
|
||||||
self.o_previous_timestamp = int64(0)
|
self.o_previous_timestamp = int64(0)
|
||||||
|
|
||||||
|
@ -58,7 +55,7 @@ class TTLOut(AutoDB):
|
||||||
self.off()
|
self.off()
|
||||||
|
|
||||||
|
|
||||||
class TTLInOut(AutoDB):
|
class TTLInOut:
|
||||||
"""RTIO TTL input/output driver.
|
"""RTIO TTL input/output driver.
|
||||||
|
|
||||||
In output mode, provides functions to set the logic level on the signal.
|
In output mode, provides functions to set the logic level on the signal.
|
||||||
|
@ -76,11 +73,10 @@ class TTLInOut(AutoDB):
|
||||||
:param core: core device
|
:param core: core device
|
||||||
:param channel: channel number
|
:param channel: channel number
|
||||||
"""
|
"""
|
||||||
class DBKeys:
|
def __init__(self, dmgr, channel):
|
||||||
core = Device()
|
self.core = dmgr.get("core")
|
||||||
channel = Argument()
|
self.channel = channel
|
||||||
|
|
||||||
def build(self):
|
|
||||||
# in RTIO cycles
|
# in RTIO cycles
|
||||||
self.o_previous_timestamp = int64(0)
|
self.o_previous_timestamp = int64(0)
|
||||||
self.i_previous_timestamp = int64(0)
|
self.i_previous_timestamp = int64(0)
|
||||||
|
@ -208,7 +204,7 @@ class TTLInOut(AutoDB):
|
||||||
return syscall("ttl_get", self.channel, self.i_previous_timestamp)
|
return syscall("ttl_get", self.channel, self.i_previous_timestamp)
|
||||||
|
|
||||||
|
|
||||||
class TTLClockGen(AutoDB):
|
class TTLClockGen:
|
||||||
"""RTIO TTL clock generator driver.
|
"""RTIO TTL clock generator driver.
|
||||||
|
|
||||||
This should be used with TTL channels that have a clock generator
|
This should be used with TTL channels that have a clock generator
|
||||||
|
@ -217,9 +213,9 @@ class TTLClockGen(AutoDB):
|
||||||
:param core: core device
|
:param core: core device
|
||||||
:param channel: channel number
|
:param channel: channel number
|
||||||
"""
|
"""
|
||||||
class DBKeys:
|
def __init__(self, dmgr, channel):
|
||||||
core = Device()
|
self.core = dmgr.get("core")
|
||||||
channel = Argument()
|
self.channel = channel
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
# in RTIO cycles
|
# in RTIO cycles
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
from artiq.language.core import *
|
from artiq.language.core import *
|
||||||
from artiq.language.db import *
|
|
||||||
from artiq.language.units import *
|
from artiq.language.units import *
|
||||||
|
|
||||||
|
|
||||||
|
@ -154,19 +153,14 @@ class _Frame:
|
||||||
self.pdq.next_segment = -1
|
self.pdq.next_segment = -1
|
||||||
|
|
||||||
|
|
||||||
class CompoundPDQ2(AutoDB):
|
class CompoundPDQ2:
|
||||||
class DBKeys:
|
def __init__(self, dmgr, pdq2_devices, trigger_device, frame_devices):
|
||||||
core = Device()
|
self.core = dmgr.get("core")
|
||||||
pdq2_devices = Argument()
|
self.pdq2s = [dmgr.get(d) for d in self.pdq2_devices]
|
||||||
trigger_device = Argument()
|
self.trigger = dmgr.get(trigger_device)
|
||||||
frame_devices = Argument()
|
self.frame0 = dmgr.get(frame_devices[0])
|
||||||
|
self.frame1 = dmgr.get(frame_devices[1])
|
||||||
def build(self):
|
self.frame2 = dmgr.get(frame_devices[2])
|
||||||
self.pdq2s = [self.dbh.get_device(d) for d in self.pdq2_devices]
|
|
||||||
self.trigger = self.dbh.get_device(self.trigger_device)
|
|
||||||
self.frame0 = self.dbh.get_device(self.frame_devices[0])
|
|
||||||
self.frame1 = self.dbh.get_device(self.frame_devices[1])
|
|
||||||
self.frame2 = self.dbh.get_device(self.frame_devices[2])
|
|
||||||
|
|
||||||
self.frames = []
|
self.frames = []
|
||||||
self.current_frame = -1
|
self.current_frame = -1
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from artiq.language.core import *
|
from artiq.language.core import *
|
||||||
from artiq.language.db import *
|
|
||||||
from artiq.language.units import *
|
from artiq.language.units import *
|
||||||
from artiq.wavesynth.compute_samples import Synthesizer
|
from artiq.wavesynth.compute_samples import Synthesizer
|
||||||
|
|
||||||
|
@ -139,20 +138,16 @@ class _Frame:
|
||||||
self.daqmx.next_segment = -1
|
self.daqmx.next_segment = -1
|
||||||
|
|
||||||
|
|
||||||
class CompoundDAQmx(AutoDB):
|
class CompoundDAQmx:
|
||||||
class DBKeys:
|
def __init__(self, dmgr, daqmx_device, clock_device, channel_count,
|
||||||
core = Device()
|
sample_rate, sample_rate_in_mu=False):
|
||||||
daqmx_device = Argument()
|
self.core = dmgr.get("core")
|
||||||
clock_device = Argument()
|
self.daqmx = dmgr.get(daqmx_device)
|
||||||
channel_count = Argument()
|
self.clock = dmgr.get(clock_device)
|
||||||
sample_rate = Argument()
|
self.channel_count = channel_count
|
||||||
sample_rate_in_mu = Argument(False)
|
if self.sample_rate_in_mu:
|
||||||
|
self.sample_rate = sample_rate
|
||||||
def build(self):
|
else:
|
||||||
self.daqmx = self.dbh.get_device(self.daqmx_device)
|
|
||||||
self.clock = self.dbh.get_device(self.clock_device)
|
|
||||||
|
|
||||||
if not self.sample_rate_in_mu:
|
|
||||||
self.sample_rate = self.clock.frequency_to_ftw(sample_rate)
|
self.sample_rate = self.clock.frequency_to_ftw(sample_rate)
|
||||||
|
|
||||||
self.frame = None
|
self.frame = None
|
||||||
|
|
|
@ -4,7 +4,7 @@ import logging
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
from artiq.protocols.file_db import FlatFileDB
|
from artiq.protocols.file_db import FlatFileDB
|
||||||
from artiq.master.worker_db import DBHub
|
from artiq.master.worker_db import DeviceManager
|
||||||
from artiq.tools import *
|
from artiq.tools import *
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,15 +36,14 @@ def main():
|
||||||
args = get_argparser().parse_args()
|
args = get_argparser().parse_args()
|
||||||
init_logger(args)
|
init_logger(args)
|
||||||
|
|
||||||
ddb = FlatFileDB(args.ddb)
|
dmgr = DeviceManager(FlatFileDB(args.ddb))
|
||||||
pdb = FlatFileDB(args.pdb)
|
pdb = FlatFileDB(args.pdb)
|
||||||
dbh = DBHub(ddb, pdb, rdb=None, read_only=True)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
module = file_import(args.file)
|
module = file_import(args.file)
|
||||||
exp = get_experiment(module, args.experiment)
|
exp = get_experiment(module, args.experiment)
|
||||||
arguments = parse_arguments(args.arguments)
|
arguments = parse_arguments(args.arguments)
|
||||||
exp_inst = exp(dbh, **arguments)
|
exp_inst = exp(dmgr, pdb, **arguments)
|
||||||
|
|
||||||
if (not hasattr(exp.run, "k_function_info")
|
if (not hasattr(exp.run, "k_function_info")
|
||||||
or not exp.run.k_function_info):
|
or not exp.run.k_function_info):
|
||||||
|
@ -56,7 +55,7 @@ def main():
|
||||||
[exp_inst], {},
|
[exp_inst], {},
|
||||||
with_attr_writeback=False)
|
with_attr_writeback=False)
|
||||||
finally:
|
finally:
|
||||||
dbh.close_devices()
|
dmgr.close_devices()
|
||||||
|
|
||||||
if rpc_map:
|
if rpc_map:
|
||||||
raise ValueError("Experiment must not use RPC")
|
raise ValueError("Experiment must not use RPC")
|
||||||
|
|
|
@ -18,9 +18,11 @@ from artiq.gui.parameters import ParametersDock
|
||||||
from artiq.gui.schedule import ScheduleDock
|
from artiq.gui.schedule import ScheduleDock
|
||||||
from artiq.gui.log import LogDock
|
from artiq.gui.log import LogDock
|
||||||
|
|
||||||
|
|
||||||
data_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
|
data_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)),
|
||||||
"..", "gui")
|
"..", "gui")
|
||||||
|
|
||||||
|
|
||||||
def get_argparser():
|
def get_argparser():
|
||||||
parser = argparse.ArgumentParser(description="ARTIQ GUI client")
|
parser = argparse.ArgumentParser(description="ARTIQ GUI client")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
|
|
@ -6,10 +6,10 @@ import atexit
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from artiq.protocols.pc_rpc import Server
|
from artiq.protocols.pc_rpc import Server
|
||||||
from artiq.protocols.sync_struct import Publisher
|
from artiq.protocols.sync_struct import Notifier, Publisher, process_mod
|
||||||
from artiq.protocols.file_db import FlatFileDB
|
from artiq.protocols.file_db import FlatFileDB
|
||||||
from artiq.master.scheduler import Scheduler
|
from artiq.master.scheduler import Scheduler
|
||||||
from artiq.master.results import RTResults, get_last_rid
|
from artiq.master.worker_db import get_last_rid
|
||||||
from artiq.master.repository import Repository
|
from artiq.master.repository import Repository
|
||||||
from artiq.tools import verbosity_args, init_logger
|
from artiq.tools import verbosity_args, init_logger
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ def main():
|
||||||
init_logger(args)
|
init_logger(args)
|
||||||
ddb = FlatFileDB("ddb.pyon")
|
ddb = FlatFileDB("ddb.pyon")
|
||||||
pdb = FlatFileDB("pdb.pyon")
|
pdb = FlatFileDB("pdb.pyon")
|
||||||
rtr = RTResults()
|
rtr = Notifier(dict())
|
||||||
repository = Repository()
|
repository = Repository()
|
||||||
|
|
||||||
if os.name == "nt":
|
if os.name == "nt":
|
||||||
|
@ -47,11 +47,10 @@ def main():
|
||||||
atexit.register(lambda: loop.close())
|
atexit.register(lambda: loop.close())
|
||||||
|
|
||||||
worker_handlers = {
|
worker_handlers = {
|
||||||
"req_device": ddb.request,
|
"get_device": ddb.get,
|
||||||
"req_parameter": pdb.request,
|
"get_parameter": pdb.get,
|
||||||
"set_parameter": pdb.set,
|
"set_parameter": pdb.set,
|
||||||
"init_rt_results": rtr.init,
|
"update_rt_results": lambda mod: process_mod(rtr, mod),
|
||||||
"update_rt_results": rtr.update,
|
|
||||||
}
|
}
|
||||||
scheduler = Scheduler(get_last_rid() + 1, worker_handlers)
|
scheduler = Scheduler(get_last_rid() + 1, worker_handlers)
|
||||||
worker_handlers["scheduler_submit"] = scheduler.submit
|
worker_handlers["scheduler_submit"] = scheduler.submit
|
||||||
|
@ -72,7 +71,7 @@ def main():
|
||||||
"schedule": scheduler.notifier,
|
"schedule": scheduler.notifier,
|
||||||
"devices": ddb.data,
|
"devices": ddb.data,
|
||||||
"parameters": pdb.data,
|
"parameters": pdb.data,
|
||||||
"rt_results": rtr.groups,
|
"rt_results": rtr,
|
||||||
"explist": repository.explist
|
"explist": repository.explist
|
||||||
})
|
})
|
||||||
loop.run_until_complete(server_notify.start(
|
loop.run_until_complete(server_notify.start(
|
||||||
|
|
|
@ -9,20 +9,19 @@ import logging
|
||||||
|
|
||||||
import h5py
|
import h5py
|
||||||
|
|
||||||
from artiq.language.db import *
|
from artiq.language.environment import EnvExperiment
|
||||||
from artiq.language.experiment import Experiment
|
|
||||||
from artiq.protocols.file_db import FlatFileDB
|
from artiq.protocols.file_db import FlatFileDB
|
||||||
from artiq.master.worker_db import DBHub, ResultDB
|
from artiq.master.worker_db import DeviceManager, ResultDB
|
||||||
from artiq.tools import *
|
from artiq.tools import *
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ELFRunner(Experiment, AutoDB):
|
class ELFRunner(EnvExperiment):
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
file = Argument()
|
self.attr_argument("file")
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
with open(self.file, "rb") as f:
|
with open(self.file, "rb") as f:
|
||||||
|
@ -37,12 +36,11 @@ class SimpleParamLogger:
|
||||||
|
|
||||||
|
|
||||||
class DummyScheduler:
|
class DummyScheduler:
|
||||||
def __init__(self, expid):
|
def __init__(self):
|
||||||
self.next_rid = 0
|
self.next_rid = 0
|
||||||
self.next_trid = 0
|
|
||||||
self.pipeline_name = "main"
|
self.pipeline_name = "main"
|
||||||
self.priority = 0
|
self.priority = 0
|
||||||
self.expid = expid
|
self.expid = None
|
||||||
|
|
||||||
def submit(self, pipeline_name, expid, priority, due_date, flush):
|
def submit(self, pipeline_name, expid, priority, due_date, flush):
|
||||||
rid = self.next_rid
|
rid = self.next_rid
|
||||||
|
@ -78,7 +76,7 @@ def get_argparser(with_file=True):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
def _build_experiment(dbh, args):
|
def _build_experiment(dmgr, pdb, rdb, args):
|
||||||
if hasattr(args, "file"):
|
if hasattr(args, "file"):
|
||||||
if args.file.endswith(".elf"):
|
if args.file.endswith(".elf"):
|
||||||
if args.arguments:
|
if args.arguments:
|
||||||
|
@ -86,7 +84,7 @@ def _build_experiment(dbh, args):
|
||||||
if args.experiment:
|
if args.experiment:
|
||||||
raise ValueError("experiment-by-name not supported "
|
raise ValueError("experiment-by-name not supported "
|
||||||
"for ELF kernels")
|
"for ELF kernels")
|
||||||
return ELFRunner(dbh, file=args.file)
|
return ELFRunner(dmgr, pdb, rdb, file=args.file)
|
||||||
else:
|
else:
|
||||||
module = file_import(args.file)
|
module = file_import(args.file)
|
||||||
file = args.file
|
file = args.file
|
||||||
|
@ -100,34 +98,33 @@ def _build_experiment(dbh, args):
|
||||||
"experiment": args.experiment,
|
"experiment": args.experiment,
|
||||||
"arguments": arguments
|
"arguments": arguments
|
||||||
}
|
}
|
||||||
return exp(dbh,
|
dmgr.virtual_devices["scheduler"].expid = expid
|
||||||
scheduler=DummyScheduler(expid),
|
return exp(dmgr, pdb, rdb, **arguments)
|
||||||
**arguments)
|
|
||||||
|
|
||||||
|
|
||||||
def run(with_file=False):
|
def run(with_file=False):
|
||||||
args = get_argparser(with_file).parse_args()
|
args = get_argparser(with_file).parse_args()
|
||||||
init_logger(args)
|
init_logger(args)
|
||||||
|
|
||||||
ddb = FlatFileDB(args.ddb)
|
dmgr = DeviceManager(FlatFileDB(args.ddb),
|
||||||
|
virtual_devices={"scheduler": DummyScheduler()})
|
||||||
pdb = FlatFileDB(args.pdb)
|
pdb = FlatFileDB(args.pdb)
|
||||||
pdb.hooks.append(SimpleParamLogger())
|
pdb.hooks.append(SimpleParamLogger())
|
||||||
rdb = ResultDB(lambda description: None, lambda mod: None)
|
rdb = ResultDB()
|
||||||
dbh = DBHub(ddb, pdb, rdb)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
exp_inst = _build_experiment(dbh, args)
|
exp_inst = _build_experiment(dmgr, pdb, rdb, args)
|
||||||
rdb.build()
|
exp_inst.prepare()
|
||||||
exp_inst.run()
|
exp_inst.run()
|
||||||
exp_inst.analyze()
|
exp_inst.analyze()
|
||||||
finally:
|
finally:
|
||||||
dbh.close_devices()
|
dmgr.close_devices()
|
||||||
|
|
||||||
if args.hdf5 is not None:
|
if args.hdf5 is not None:
|
||||||
with h5py.File(args.hdf5, "w") as f:
|
with h5py.File(args.hdf5, "w") as f:
|
||||||
rdb.write_hdf5(f)
|
rdb.write_hdf5(f)
|
||||||
elif rdb.data.read or rdb.realtime_data.read:
|
elif rdb.rt.read or rdb.nrt:
|
||||||
r = chain(rdb.realtime_data.read.items(), rdb.data.read.items())
|
r = chain(rdb.rt.read.items(), rdb.nrt.items())
|
||||||
for k, v in sorted(r, key=itemgetter(0)):
|
for k, v in sorted(r, key=itemgetter(0)):
|
||||||
print("{}: {}".format(k, v))
|
print("{}: {}".format(k, v))
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
from artiq.language import core, experiment, db, units
|
from artiq.language import core, environment, units
|
||||||
from artiq.language.core import *
|
from artiq.language.core import *
|
||||||
from artiq.language.experiment import *
|
from artiq.language.environment import *
|
||||||
from artiq.language.db import *
|
|
||||||
from artiq.language.units import *
|
from artiq.language.units import *
|
||||||
|
|
||||||
|
|
||||||
__all__ = []
|
__all__ = []
|
||||||
__all__.extend(core.__all__)
|
__all__.extend(core.__all__)
|
||||||
__all__.extend(experiment.__all__)
|
__all__.extend(environment.__all__)
|
||||||
__all__.extend(db.__all__)
|
|
||||||
__all__.extend(units.__all__)
|
__all__.extend(units.__all__)
|
||||||
|
|
|
@ -1,133 +0,0 @@
|
||||||
"""
|
|
||||||
Connection to device, parameter and result database.
|
|
||||||
"""
|
|
||||||
|
|
||||||
__all__ = ["Device", "NoDefault", "Parameter", "Argument", "Result", "AutoDB"]
|
|
||||||
|
|
||||||
|
|
||||||
class _AttributeKind:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Device(_AttributeKind):
|
|
||||||
"""Represents a device for ``AutoDB`` to process."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class NoDefault:
|
|
||||||
"""Represents the absence of a default value for ``Parameter``
|
|
||||||
and ``Argument``.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Parameter(_AttributeKind):
|
|
||||||
"""Represents a parameter (from the database) for ``AutoDB``
|
|
||||||
to process.
|
|
||||||
|
|
||||||
:param default: Default value of the parameter to be used if not found
|
|
||||||
in the database.
|
|
||||||
"""
|
|
||||||
def __init__(self, default=NoDefault):
|
|
||||||
self.default = default
|
|
||||||
|
|
||||||
|
|
||||||
class Argument(_AttributeKind):
|
|
||||||
"""Represents an argument (specifiable at instance creation) for
|
|
||||||
``AutoDB`` to process.
|
|
||||||
|
|
||||||
:param default: Default value of the argument to be used if not specified
|
|
||||||
at instance creation.
|
|
||||||
"""
|
|
||||||
def __init__(self, default=NoDefault):
|
|
||||||
self.default = default
|
|
||||||
|
|
||||||
|
|
||||||
class Result(_AttributeKind):
|
|
||||||
"""Represents a result for ``AutoDB`` to process."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class AutoDB:
|
|
||||||
"""Base class to automate device, parameter and result database access.
|
|
||||||
|
|
||||||
Drivers and experiments should in most cases overload this class to
|
|
||||||
obtain the parameters and devices (including the core device) that they
|
|
||||||
need, report results, and modify parameters.
|
|
||||||
|
|
||||||
:param dbh: database hub to use. If ``None``, all devices and parameters
|
|
||||||
must be supplied as keyword arguments, and reporting results and
|
|
||||||
modifying parameters is not supported.
|
|
||||||
"""
|
|
||||||
class DBKeys:
|
|
||||||
pass
|
|
||||||
|
|
||||||
realtime_results = dict()
|
|
||||||
|
|
||||||
def __init__(self, dbh=None, **kwargs):
|
|
||||||
self.dbh = dbh
|
|
||||||
|
|
||||||
for k, v in kwargs.items():
|
|
||||||
object.__setattr__(self, k, v)
|
|
||||||
|
|
||||||
for k in dir(self.DBKeys):
|
|
||||||
if k not in self.__dict__:
|
|
||||||
ak = getattr(self.DBKeys, k)
|
|
||||||
if isinstance(ak, Argument):
|
|
||||||
if ak.default is NoDefault:
|
|
||||||
raise AttributeError(
|
|
||||||
"No value specified for argument '{}'".format(k))
|
|
||||||
object.__setattr__(self, k, ak.default)
|
|
||||||
elif isinstance(ak, Device):
|
|
||||||
try:
|
|
||||||
dev = self.dbh.get_device(k)
|
|
||||||
except KeyError:
|
|
||||||
raise KeyError("Device '{}' not found".format(k))
|
|
||||||
object.__setattr__(self, k, dev)
|
|
||||||
self.build()
|
|
||||||
if self.dbh is not None and self.realtime_results:
|
|
||||||
self.dbh.add_rt_results(self.realtime_results)
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
ak = getattr(self.DBKeys, name)
|
|
||||||
if isinstance(ak, Parameter):
|
|
||||||
try:
|
|
||||||
if self.dbh is None:
|
|
||||||
raise KeyError
|
|
||||||
return self.dbh.get_parameter(name)
|
|
||||||
except KeyError:
|
|
||||||
if ak.default is not NoDefault:
|
|
||||||
return ak.default
|
|
||||||
else:
|
|
||||||
raise AttributeError("Parameter '{}' not in database"
|
|
||||||
" and without default value"
|
|
||||||
.format(name))
|
|
||||||
elif isinstance(ak, Result):
|
|
||||||
try:
|
|
||||||
return self.dbh.get_result(name)
|
|
||||||
except KeyError:
|
|
||||||
raise AttributeError("Result '{}' not found".format(name))
|
|
||||||
else:
|
|
||||||
raise ValueError
|
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
|
||||||
try:
|
|
||||||
ak = getattr(self.DBKeys, name)
|
|
||||||
except AttributeError:
|
|
||||||
object.__setattr__(self, name, value)
|
|
||||||
else:
|
|
||||||
if isinstance(ak, Parameter):
|
|
||||||
self.dbh.set_parameter(name, value)
|
|
||||||
elif isinstance(ak, Result):
|
|
||||||
self.dbh.set_result(name, value)
|
|
||||||
else:
|
|
||||||
raise ValueError
|
|
||||||
|
|
||||||
def build(self):
|
|
||||||
"""This is called by ``__init__`` after the parameter initialization
|
|
||||||
is done.
|
|
||||||
|
|
||||||
The user may overload this method to complete the object's
|
|
||||||
initialization with all parameters available.
|
|
||||||
"""
|
|
||||||
pass
|
|
|
@ -0,0 +1,182 @@
|
||||||
|
from inspect import isclass
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["NoDefault", "FreeValue", "HasEnvironment",
|
||||||
|
"Experiment", "EnvExperiment", "is_experiment"]
|
||||||
|
|
||||||
|
|
||||||
|
class NoDefault:
|
||||||
|
"""Represents the absence of a default value."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class FreeValue:
|
||||||
|
def __init__(self, default=NoDefault):
|
||||||
|
if default is not NoDefault:
|
||||||
|
self.default_value = default
|
||||||
|
|
||||||
|
def default(self):
|
||||||
|
return self.default_value
|
||||||
|
|
||||||
|
def process(self, x):
|
||||||
|
return x
|
||||||
|
|
||||||
|
def describe(self):
|
||||||
|
d = {"ty": "FreeValue"}
|
||||||
|
if hasattr(self, "default_value"):
|
||||||
|
d["default"] = self.default_value
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
class HasEnvironment:
|
||||||
|
"""Provides methods to manage the environment of an experiment (devices,
|
||||||
|
parameters, results, arguments)."""
|
||||||
|
def __init__(self, dmgr=None, pdb=None, rdb=None, *,
|
||||||
|
param_override=dict(), **kwargs):
|
||||||
|
self.requested_args = dict()
|
||||||
|
|
||||||
|
self.__dmgr = dmgr
|
||||||
|
self.__pdb = pdb
|
||||||
|
self.__rdb = rdb
|
||||||
|
self.__param_override = param_override
|
||||||
|
|
||||||
|
self.__kwargs = kwargs
|
||||||
|
self.__in_build = True
|
||||||
|
self.build()
|
||||||
|
self.__in_build = False
|
||||||
|
for key in self.__kwargs.keys():
|
||||||
|
if key not in self.requested_args:
|
||||||
|
raise TypeError("Got unexpected argument: " + key)
|
||||||
|
del self.__kwargs
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def dbs(self):
|
||||||
|
return self.__dmgr, self.__pdb, self.__rdb
|
||||||
|
|
||||||
|
def get_argument(self, key, processor=None):
|
||||||
|
if not self.__in_build:
|
||||||
|
raise TypeError("get_argument() should only "
|
||||||
|
"be called from build()")
|
||||||
|
if processor is None:
|
||||||
|
processor = FreeValue()
|
||||||
|
self.requested_args[key] = processor
|
||||||
|
try:
|
||||||
|
argval = self.__kwargs[key]
|
||||||
|
except KeyError:
|
||||||
|
return processor.default()
|
||||||
|
return processor.process(argval)
|
||||||
|
|
||||||
|
def attr_argument(self, key, processor=None):
|
||||||
|
setattr(self, key, self.get_argument(key, processor))
|
||||||
|
|
||||||
|
def get_device(self, key):
|
||||||
|
if self.__dmgr is None:
|
||||||
|
raise ValueError("Device manager not present")
|
||||||
|
return self.__dmgr.get(key)
|
||||||
|
|
||||||
|
def attr_device(self, key):
|
||||||
|
setattr(self, key, self.get_device(key))
|
||||||
|
|
||||||
|
def get_parameter(self, key, default=NoDefault):
|
||||||
|
if self.__pdb is None:
|
||||||
|
raise ValueError("Parameter database not present")
|
||||||
|
if key in self.__param_override:
|
||||||
|
return self.__param_override[key]
|
||||||
|
try:
|
||||||
|
return self.__pdb.get(key)
|
||||||
|
except KeyError:
|
||||||
|
if default is not NoDefault:
|
||||||
|
return default
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def attr_parameter(self, key, default=NoDefault):
|
||||||
|
setattr(self, key, self.get_parameter(key, default))
|
||||||
|
|
||||||
|
def set_parameter(self, key, value):
|
||||||
|
if self.__pdb is None:
|
||||||
|
raise ValueError("Parameter database not present")
|
||||||
|
self.__pdb.set(key, value)
|
||||||
|
|
||||||
|
def set_result(self, key, value, realtime=False):
|
||||||
|
if self.__rdb is None:
|
||||||
|
raise ValueError("Result database not present")
|
||||||
|
if realtime:
|
||||||
|
if key in self.__rdb.nrt:
|
||||||
|
raise ValueError("Result is already non-realtime")
|
||||||
|
self.__rdb.rt[key] = value
|
||||||
|
notifier = self.__rdb.rt[key]
|
||||||
|
notifier.kernel_attr_init = False
|
||||||
|
return notifier
|
||||||
|
else:
|
||||||
|
if key in self.__rdb.rt.read:
|
||||||
|
raise ValueError("Result is already realtime")
|
||||||
|
self.__rdb.nrt[key] = value
|
||||||
|
|
||||||
|
def attr_rtresult(self, key, init_value):
|
||||||
|
setattr(self, key, set_result(key, init_value, True))
|
||||||
|
|
||||||
|
def get_result(self, key):
|
||||||
|
if self.__rdb is None:
|
||||||
|
raise ValueError("Result database not present")
|
||||||
|
return self.__rdb.get(key)
|
||||||
|
|
||||||
|
|
||||||
|
class Experiment:
|
||||||
|
"""Base class for experiments.
|
||||||
|
|
||||||
|
Deriving from this class enables automatic experiment discovery in
|
||||||
|
Python modules.
|
||||||
|
"""
|
||||||
|
def prepare(self):
|
||||||
|
"""Entry point for pre-computing data necessary for running the
|
||||||
|
experiment.
|
||||||
|
|
||||||
|
Doing such computations outside of ``run`` enables more efficient
|
||||||
|
scheduling of multiple experiments that need to access the shared
|
||||||
|
hardware during part of their execution.
|
||||||
|
|
||||||
|
This method must not interact with the hardware.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""The main entry point of the experiment.
|
||||||
|
|
||||||
|
This method must be overloaded by the user to implement the main
|
||||||
|
control flow of the experiment.
|
||||||
|
|
||||||
|
This method may interact with the hardware.
|
||||||
|
|
||||||
|
The experiment may call the scheduler's ``pause`` method while in
|
||||||
|
``run``.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def analyze(self):
|
||||||
|
"""Entry point for analyzing the results of the experiment.
|
||||||
|
|
||||||
|
This method may be overloaded by the user to implement the analysis
|
||||||
|
phase of the experiment, for example fitting curves.
|
||||||
|
|
||||||
|
Splitting this phase from ``run`` enables tweaking the analysis
|
||||||
|
algorithm on pre-existing data, and CPU-bound analyses to be run
|
||||||
|
overlapped with the next experiment in a pipelined manner.
|
||||||
|
|
||||||
|
This method must not interact with the hardware.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class EnvExperiment(Experiment, HasEnvironment):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def is_experiment(o):
|
||||||
|
"""Checks if a Python object is an instantiable user experiment."""
|
||||||
|
return (isclass(o)
|
||||||
|
and issubclass(o, Experiment)
|
||||||
|
and o is not Experiment
|
||||||
|
and o is not EnvExperiment)
|
|
@ -1,54 +0,0 @@
|
||||||
from inspect import isclass
|
|
||||||
|
|
||||||
__all__ = ["Experiment", "is_experiment"]
|
|
||||||
|
|
||||||
|
|
||||||
class Experiment:
|
|
||||||
"""Base class for experiments.
|
|
||||||
|
|
||||||
Deriving from this class enables automatic experiment discovery in
|
|
||||||
Python modules.
|
|
||||||
"""
|
|
||||||
def prepare(self):
|
|
||||||
"""Entry point for pre-computing data necessary for running the
|
|
||||||
experiment.
|
|
||||||
|
|
||||||
Doing such computations outside of ``run`` enables more efficient
|
|
||||||
scheduling of multiple experiments that need to access the shared
|
|
||||||
hardware during part of their execution.
|
|
||||||
|
|
||||||
This method must not interact with the hardware.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
"""The main entry point of the experiment.
|
|
||||||
|
|
||||||
This method must be overloaded by the user to implement the main
|
|
||||||
control flow of the experiment.
|
|
||||||
|
|
||||||
This method may interact with the hardware.
|
|
||||||
|
|
||||||
The experiment may call the scheduler's ``pause`` method while in
|
|
||||||
``run``.
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def analyze(self):
|
|
||||||
"""Entry point for analyzing the results of the experiment.
|
|
||||||
|
|
||||||
This method may be overloaded by the user to implement the analysis
|
|
||||||
phase of the experiment, for example fitting curves.
|
|
||||||
|
|
||||||
Splitting this phase from ``run`` enables tweaking the analysis
|
|
||||||
algorithm on pre-existing data, and CPU-bound analyses to be run
|
|
||||||
overlapped with the next experiment in a pipelined manner.
|
|
||||||
|
|
||||||
This method must not interact with the hardware.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def is_experiment(o):
|
|
||||||
"""Checks if a Python object is an instantiable experiment."""
|
|
||||||
return isclass(o) and issubclass(o, Experiment) and o is not Experiment
|
|
|
@ -2,7 +2,7 @@ import os
|
||||||
|
|
||||||
from artiq.protocols.sync_struct import Notifier
|
from artiq.protocols.sync_struct import Notifier
|
||||||
from artiq.tools import file_import
|
from artiq.tools import file_import
|
||||||
from artiq.language.experiment import is_experiment
|
from artiq.language.environment import is_experiment
|
||||||
|
|
||||||
|
|
||||||
def scan_experiments():
|
def scan_experiments():
|
||||||
|
@ -23,8 +23,7 @@ def scan_experiments():
|
||||||
name = name[:-1]
|
name = name[:-1]
|
||||||
entry = {
|
entry = {
|
||||||
"file": os.path.join("repository", f),
|
"file": os.path.join("repository", f),
|
||||||
"experiment": k,
|
"experiment": k
|
||||||
"gui_file": getattr(v, "__artiq_gui_file__", None)
|
|
||||||
}
|
}
|
||||||
r[name] = entry
|
r[name] = entry
|
||||||
return r
|
return r
|
||||||
|
|
|
@ -1,103 +0,0 @@
|
||||||
import os
|
|
||||||
import time
|
|
||||||
import re
|
|
||||||
|
|
||||||
import numpy
|
|
||||||
import h5py
|
|
||||||
|
|
||||||
from artiq.protocols.sync_struct import Notifier, process_mod
|
|
||||||
|
|
||||||
|
|
||||||
def get_hdf5_output(start_time, rid, name):
|
|
||||||
dirname = os.path.join("results",
|
|
||||||
time.strftime("%Y-%m-%d", start_time),
|
|
||||||
time.strftime("%H-%M", start_time))
|
|
||||||
filename = "{:09}-{}.h5".format(rid, name)
|
|
||||||
os.makedirs(dirname, exist_ok=True)
|
|
||||||
return h5py.File(os.path.join(dirname, filename), "w")
|
|
||||||
|
|
||||||
|
|
||||||
def get_last_rid():
|
|
||||||
r = -1
|
|
||||||
try:
|
|
||||||
day_folders = os.listdir("results")
|
|
||||||
except:
|
|
||||||
return r
|
|
||||||
day_folders = filter(lambda x: re.fullmatch('\d\d\d\d-\d\d-\d\d', x),
|
|
||||||
day_folders)
|
|
||||||
for df in day_folders:
|
|
||||||
day_path = os.path.join("results", df)
|
|
||||||
try:
|
|
||||||
minute_folders = os.listdir(day_path)
|
|
||||||
except:
|
|
||||||
continue
|
|
||||||
minute_folders = filter(lambda x: re.fullmatch('\d\d-\d\d', x),
|
|
||||||
minute_folders)
|
|
||||||
for mf in minute_folders:
|
|
||||||
minute_path = os.path.join(day_path, mf)
|
|
||||||
try:
|
|
||||||
h5files = os.listdir(minute_path)
|
|
||||||
except:
|
|
||||||
continue
|
|
||||||
for x in h5files:
|
|
||||||
m = re.fullmatch('(\d\d\d\d\d\d\d\d\d)-.*\.h5', x)
|
|
||||||
rid = int(m.group(1))
|
|
||||||
if rid > r:
|
|
||||||
r = rid
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
_type_to_hdf5 = {
|
|
||||||
int: h5py.h5t.STD_I64BE,
|
|
||||||
float: h5py.h5t.IEEE_F64BE
|
|
||||||
}
|
|
||||||
|
|
||||||
def result_dict_to_hdf5(f, rd):
|
|
||||||
for name, data in rd.items():
|
|
||||||
if isinstance(data, list):
|
|
||||||
el_ty = type(data[0])
|
|
||||||
for d in data:
|
|
||||||
if type(d) != el_ty:
|
|
||||||
raise TypeError("All list elements must have the same"
|
|
||||||
" type for HDF5 output")
|
|
||||||
try:
|
|
||||||
el_ty_h5 = _type_to_hdf5[el_ty]
|
|
||||||
except KeyError:
|
|
||||||
raise TypeError("List element type {} is not supported for"
|
|
||||||
" HDF5 output".format(el_ty))
|
|
||||||
dataset = f.create_dataset(name, (len(data), ), el_ty_h5)
|
|
||||||
dataset[:] = data
|
|
||||||
elif isinstance(data, numpy.ndarray):
|
|
||||||
f.create_dataset(name, data=data)
|
|
||||||
else:
|
|
||||||
ty = type(data)
|
|
||||||
try:
|
|
||||||
ty_h5 = _type_to_hdf5[ty]
|
|
||||||
except KeyError:
|
|
||||||
raise TypeError("Type {} is not supported for HDF5 output"
|
|
||||||
.format(ty))
|
|
||||||
dataset = f.create_dataset(name, (), ty_h5)
|
|
||||||
dataset[()] = data
|
|
||||||
|
|
||||||
|
|
||||||
class RTResults:
|
|
||||||
def __init__(self):
|
|
||||||
self.groups = Notifier(dict())
|
|
||||||
self.current_group = "default"
|
|
||||||
|
|
||||||
def init(self, description):
|
|
||||||
data = dict()
|
|
||||||
for rtr in description.keys():
|
|
||||||
if isinstance(rtr, tuple):
|
|
||||||
for e in rtr:
|
|
||||||
data[e] = []
|
|
||||||
else:
|
|
||||||
data[rtr] = []
|
|
||||||
self.groups[self.current_group] = {
|
|
||||||
"description": description,
|
|
||||||
"data": data
|
|
||||||
}
|
|
||||||
|
|
||||||
def update(self, mod):
|
|
||||||
target = self.groups[self.current_group]["data"]
|
|
||||||
process_mod(target, mod)
|
|
|
@ -1,76 +1,114 @@
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import importlib
|
import importlib
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import re
|
||||||
|
|
||||||
|
import numpy
|
||||||
|
import h5py
|
||||||
|
|
||||||
from artiq.protocols.sync_struct import Notifier
|
from artiq.protocols.sync_struct import Notifier
|
||||||
from artiq.protocols.pc_rpc import Client, BestEffortClient
|
from artiq.protocols.pc_rpc import Client, BestEffortClient
|
||||||
from artiq.master.results import result_dict_to_hdf5
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ResultDB:
|
def get_hdf5_output(start_time, rid, name):
|
||||||
def __init__(self, init_rt_results, update_rt_results):
|
dirname = os.path.join("results",
|
||||||
self.init_rt_results = init_rt_results
|
time.strftime("%Y-%m-%d", start_time),
|
||||||
self.update_rt_results = update_rt_results
|
time.strftime("%H-%M", start_time))
|
||||||
self.rtr_description = dict()
|
filename = "{:09}-{}.h5".format(rid, name)
|
||||||
|
os.makedirs(dirname, exist_ok=True)
|
||||||
|
return h5py.File(os.path.join(dirname, filename), "w")
|
||||||
|
|
||||||
def add_rt_results(self, rtr_description):
|
|
||||||
intr = set(self.rtr_description.keys()).intersection(
|
|
||||||
set(rtr_description.keys()))
|
|
||||||
if intr:
|
|
||||||
raise ValueError("Duplicate realtime results: " + ", ".join(intr))
|
|
||||||
self.rtr_description.update(rtr_description)
|
|
||||||
|
|
||||||
def build(self):
|
def get_last_rid():
|
||||||
realtime_results_set = set()
|
r = -1
|
||||||
for rtr in self.rtr_description.keys():
|
|
||||||
if isinstance(rtr, tuple):
|
|
||||||
for e in rtr:
|
|
||||||
realtime_results_set.add(e)
|
|
||||||
else:
|
|
||||||
realtime_results_set.add(rtr)
|
|
||||||
|
|
||||||
self.realtime_data = Notifier({x: [] for x in realtime_results_set})
|
|
||||||
self.data = Notifier(dict())
|
|
||||||
|
|
||||||
self.init_rt_results(self.rtr_description)
|
|
||||||
self.realtime_data.publish = lambda notifier, data: \
|
|
||||||
self.update_rt_results(data)
|
|
||||||
|
|
||||||
def _request(self, name):
|
|
||||||
try:
|
try:
|
||||||
return self.realtime_data[name]
|
day_folders = os.listdir("results")
|
||||||
except KeyError:
|
except:
|
||||||
|
return r
|
||||||
|
day_folders = filter(lambda x: re.fullmatch('\d\d\d\d-\d\d-\d\d', x),
|
||||||
|
day_folders)
|
||||||
|
for df in day_folders:
|
||||||
|
day_path = os.path.join("results", df)
|
||||||
try:
|
try:
|
||||||
return self.data[name]
|
minute_folders = os.listdir(day_path)
|
||||||
except KeyError:
|
except:
|
||||||
self.data[name] = []
|
continue
|
||||||
return self.data[name]
|
minute_folders = filter(lambda x: re.fullmatch('\d\d-\d\d', x),
|
||||||
|
minute_folders)
|
||||||
def request(self, name):
|
for mf in minute_folders:
|
||||||
r = self._request(name)
|
minute_path = os.path.join(day_path, mf)
|
||||||
r.kernel_attr_init = False
|
try:
|
||||||
|
h5files = os.listdir(minute_path)
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
for x in h5files:
|
||||||
|
m = re.fullmatch('(\d\d\d\d\d\d\d\d\d)-.*\.h5', x)
|
||||||
|
rid = int(m.group(1))
|
||||||
|
if rid > r:
|
||||||
|
r = rid
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def set(self, name, value):
|
|
||||||
if name in self.realtime_data.read:
|
_type_to_hdf5 = {
|
||||||
self.realtime_data[name] = value
|
int: h5py.h5t.STD_I64BE,
|
||||||
|
float: h5py.h5t.IEEE_F64BE
|
||||||
|
}
|
||||||
|
|
||||||
|
def result_dict_to_hdf5(f, rd):
|
||||||
|
for name, data in rd.items():
|
||||||
|
if isinstance(data, list):
|
||||||
|
el_ty = type(data[0])
|
||||||
|
for d in data:
|
||||||
|
if type(d) != el_ty:
|
||||||
|
raise TypeError("All list elements must have the same"
|
||||||
|
" type for HDF5 output")
|
||||||
|
try:
|
||||||
|
el_ty_h5 = _type_to_hdf5[el_ty]
|
||||||
|
except KeyError:
|
||||||
|
raise TypeError("List element type {} is not supported for"
|
||||||
|
" HDF5 output".format(el_ty))
|
||||||
|
dataset = f.create_dataset(name, (len(data), ), el_ty_h5)
|
||||||
|
dataset[:] = data
|
||||||
|
elif isinstance(data, numpy.ndarray):
|
||||||
|
f.create_dataset(name, data=data)
|
||||||
else:
|
else:
|
||||||
self.data[name] = value
|
ty = type(data)
|
||||||
|
try:
|
||||||
|
ty_h5 = _type_to_hdf5[ty]
|
||||||
|
except KeyError:
|
||||||
|
raise TypeError("Type {} is not supported for HDF5 output"
|
||||||
|
.format(ty))
|
||||||
|
dataset = f.create_dataset(name, (), ty_h5)
|
||||||
|
dataset[()] = data
|
||||||
|
|
||||||
|
|
||||||
|
class ResultDB:
|
||||||
|
def __init__(self):
|
||||||
|
self.rt = Notifier(dict())
|
||||||
|
self.nrt = dict()
|
||||||
|
|
||||||
|
def get(self, key):
|
||||||
|
try:
|
||||||
|
return self.nrt[key]
|
||||||
|
except KeyError:
|
||||||
|
return self.rt[key].read
|
||||||
|
|
||||||
def write_hdf5(self, f):
|
def write_hdf5(self, f):
|
||||||
result_dict_to_hdf5(f, self.realtime_data.read)
|
result_dict_to_hdf5(f, self.rt.read)
|
||||||
result_dict_to_hdf5(f, self.data.read)
|
result_dict_to_hdf5(f, self.nrt)
|
||||||
|
|
||||||
|
|
||||||
def create_device(desc, dbh):
|
def create_device(desc, dmgr):
|
||||||
ty = desc["type"]
|
ty = desc["type"]
|
||||||
if ty == "local":
|
if ty == "local":
|
||||||
module = importlib.import_module(desc["module"])
|
module = importlib.import_module(desc["module"])
|
||||||
device_class = getattr(module, desc["class"])
|
device_class = getattr(module, desc["class"])
|
||||||
return device_class(dbh, **desc["arguments"])
|
return device_class(dmgr, **desc["arguments"])
|
||||||
elif ty == "controller":
|
elif ty == "controller":
|
||||||
if desc["best_effort"]:
|
if desc["best_effort"]:
|
||||||
cl = BestEffortClient
|
cl = BestEffortClient
|
||||||
|
@ -81,30 +119,26 @@ def create_device(desc, dbh):
|
||||||
raise ValueError("Unsupported type in device DB: " + ty)
|
raise ValueError("Unsupported type in device DB: " + ty)
|
||||||
|
|
||||||
|
|
||||||
class DBHub:
|
class DeviceManager:
|
||||||
"""Connects device, parameter and result databases to experiment.
|
"""Handles creation and destruction of local device drivers and controller
|
||||||
Handle device driver creation and destruction.
|
RPC clients."""
|
||||||
"""
|
def __init__(self, ddb, virtual_devices=dict()):
|
||||||
def __init__(self, ddb, pdb, rdb, read_only=False):
|
|
||||||
self.ddb = ddb
|
self.ddb = ddb
|
||||||
|
self.virtual_devices = virtual_devices
|
||||||
self.active_devices = OrderedDict()
|
self.active_devices = OrderedDict()
|
||||||
|
|
||||||
self.get_parameter = pdb.request
|
def get(self, name):
|
||||||
|
"""Get the device driver or controller client corresponding to a
|
||||||
if not read_only:
|
device database entry."""
|
||||||
self.set_parameter = pdb.set
|
if name in self.virtual_devices:
|
||||||
self.add_rt_results = rdb.add_rt_results
|
return self.virtual_devices[name]
|
||||||
self.get_result = rdb.request
|
|
||||||
self.set_result = rdb.set
|
|
||||||
|
|
||||||
def get_device(self, name):
|
|
||||||
if name in self.active_devices:
|
if name in self.active_devices:
|
||||||
return self.active_devices[name]
|
return self.active_devices[name]
|
||||||
else:
|
else:
|
||||||
desc = self.ddb.request(name)
|
desc = self.ddb.get(name)
|
||||||
while isinstance(desc, str):
|
while isinstance(desc, str):
|
||||||
# alias
|
# alias
|
||||||
desc = self.ddb.request(desc)
|
desc = self.ddb.get(desc)
|
||||||
dev = create_device(desc, self)
|
dev = create_device(desc, self)
|
||||||
self.active_devices[name] = dev
|
self.active_devices[name] = dev
|
||||||
return dev
|
return dev
|
||||||
|
|
|
@ -3,9 +3,8 @@ import time
|
||||||
|
|
||||||
from artiq.protocols import pyon
|
from artiq.protocols import pyon
|
||||||
from artiq.tools import file_import
|
from artiq.tools import file_import
|
||||||
from artiq.master.worker_db import DBHub, ResultDB
|
from artiq.master.worker_db import DeviceManager, ResultDB, get_hdf5_output
|
||||||
from artiq.master.results import get_hdf5_output
|
from artiq.language.environment import is_experiment
|
||||||
from artiq.language.experiment import is_experiment
|
|
||||||
from artiq.language.core import set_watchdog_factory
|
from artiq.language.core import set_watchdog_factory
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,15 +45,14 @@ def make_parent_action(action, argnames, exception=ParentActionError):
|
||||||
|
|
||||||
|
|
||||||
class ParentDDB:
|
class ParentDDB:
|
||||||
request = make_parent_action("req_device", "name", KeyError)
|
get = make_parent_action("get_device", "name", KeyError)
|
||||||
|
|
||||||
|
|
||||||
class ParentPDB:
|
class ParentPDB:
|
||||||
request = make_parent_action("req_parameter", "name", KeyError)
|
get = make_parent_action("get_parameter", "name", KeyError)
|
||||||
set = make_parent_action("set_parameter", "name value")
|
set = make_parent_action("set_parameter", "name value")
|
||||||
|
|
||||||
|
|
||||||
init_rt_results = make_parent_action("init_rt_results", "description")
|
|
||||||
update_rt_results = make_parent_action("update_rt_results", "mod")
|
update_rt_results = make_parent_action("update_rt_results", "mod")
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,7 +80,7 @@ class Scheduler:
|
||||||
"pipeline_name expid priority due_date flush"))
|
"pipeline_name expid priority due_date flush"))
|
||||||
cancel = staticmethod(make_parent_action("scheduler_cancel", "rid"))
|
cancel = staticmethod(make_parent_action("scheduler_cancel", "rid"))
|
||||||
|
|
||||||
def __init__(self, pipeline_name, expid, priority):
|
def set_run_info(self, pipeline_name, expid, priority):
|
||||||
self.pipeline_name = pipeline_name
|
self.pipeline_name = pipeline_name
|
||||||
self.expid = expid
|
self.expid = expid
|
||||||
self.priority = priority
|
self.priority = priority
|
||||||
|
@ -110,8 +108,10 @@ def main():
|
||||||
exp = None
|
exp = None
|
||||||
exp_inst = None
|
exp_inst = None
|
||||||
|
|
||||||
rdb = ResultDB(init_rt_results, update_rt_results)
|
dmgr = DeviceManager(ParentDDB,
|
||||||
dbh = DBHub(ParentDDB, ParentPDB, rdb)
|
virtual_devices={"scheduler": Scheduler()})
|
||||||
|
rdb = ResultDB()
|
||||||
|
rdb.rt.publish = update_rt_results
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
|
@ -120,16 +120,12 @@ def main():
|
||||||
if action == "build":
|
if action == "build":
|
||||||
start_time = time.localtime()
|
start_time = time.localtime()
|
||||||
rid = obj["rid"]
|
rid = obj["rid"]
|
||||||
pipeline_name = obj["pipeline_name"]
|
|
||||||
expid = obj["expid"]
|
expid = obj["expid"]
|
||||||
priority = obj["priority"]
|
|
||||||
exp = get_exp(expid["file"], expid["experiment"])
|
exp = get_exp(expid["file"], expid["experiment"])
|
||||||
exp_inst = exp(dbh,
|
dmgr.virtual_devices["scheduler"].set_run_info(
|
||||||
scheduler=Scheduler(pipeline_name,
|
obj["pipeline_name"], expid, obj["priority"])
|
||||||
expid,
|
exp_inst = exp(dmgr, ParentPDB, rdb,
|
||||||
priority),
|
|
||||||
**expid["arguments"])
|
**expid["arguments"])
|
||||||
rdb.build()
|
|
||||||
put_object({"action": "completed"})
|
put_object({"action": "completed"})
|
||||||
elif action == "prepare":
|
elif action == "prepare":
|
||||||
exp_inst.prepare()
|
exp_inst.prepare()
|
||||||
|
@ -150,7 +146,7 @@ def main():
|
||||||
elif action == "terminate":
|
elif action == "terminate":
|
||||||
break
|
break
|
||||||
finally:
|
finally:
|
||||||
dbh.close_devices()
|
dmgr.close_devices()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -20,7 +20,7 @@ class FlatFileDB:
|
||||||
def save(self):
|
def save(self):
|
||||||
pyon.store_file(self.filename, self.data.read)
|
pyon.store_file(self.filename, self.data.read)
|
||||||
|
|
||||||
def request(self, name):
|
def get(self, name):
|
||||||
return self.data.read[name]
|
return self.data.read[name]
|
||||||
|
|
||||||
def set(self, name, value):
|
def set(self, name, value):
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
from random import Random
|
from random import Random
|
||||||
|
|
||||||
from artiq.language.core import delay, kernel
|
from artiq.language.core import delay, kernel
|
||||||
from artiq.language.db import *
|
|
||||||
from artiq.language import units
|
from artiq.language import units
|
||||||
from artiq.sim import time
|
from artiq.sim import time
|
||||||
|
|
||||||
|
|
||||||
class Core(AutoDB):
|
class Core:
|
||||||
def build(self):
|
def __init__(self, dmgr):
|
||||||
self.ref_period = 1
|
self.ref_period = 1
|
||||||
self._level = 0
|
self._level = 0
|
||||||
|
|
||||||
|
@ -20,12 +19,11 @@ class Core(AutoDB):
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
class Input(AutoDB):
|
class Input:
|
||||||
class DBKeys:
|
def __init__(self, dmgr, name):
|
||||||
core = Device()
|
self.core = dmgr.get("core")
|
||||||
name = Argument()
|
self.name = name
|
||||||
|
|
||||||
def build(self):
|
|
||||||
self.prng = Random()
|
self.prng = Random()
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
|
@ -42,10 +40,10 @@ class Input(AutoDB):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
class WaveOutput(AutoDB):
|
class WaveOutput:
|
||||||
class DBKeys:
|
def __init__(self, dmgr, name):
|
||||||
core = Device()
|
self.core = dmgr.get("core")
|
||||||
name = Argument()
|
self.name = name
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def pulse(self, frequency, duration):
|
def pulse(self, frequency, duration):
|
||||||
|
@ -53,10 +51,10 @@ class WaveOutput(AutoDB):
|
||||||
delay(duration)
|
delay(duration)
|
||||||
|
|
||||||
|
|
||||||
class VoltageOutput(AutoDB):
|
class VoltageOutput:
|
||||||
class DBKeys:
|
def __init__(self, dmgr, name):
|
||||||
core = Device()
|
self.core = dmgr.get("core")
|
||||||
name = Argument()
|
self.name = name
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set(self, value):
|
def set(self, value):
|
||||||
|
|
|
@ -6,11 +6,10 @@ from artiq.coredevice.runtime_exceptions import RTIOUnderflow
|
||||||
from artiq.coredevice import runtime_exceptions
|
from artiq.coredevice import runtime_exceptions
|
||||||
|
|
||||||
|
|
||||||
class RTT(Experiment, AutoDB):
|
class RTT(EnvExperiment):
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
ttl_inout = Device()
|
self.attr_device("ttl_inout")
|
||||||
rtt = Result()
|
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -24,15 +23,15 @@ class RTT(Experiment, AutoDB):
|
||||||
delay(1*us)
|
delay(1*us)
|
||||||
t0 = now_mu()
|
t0 = now_mu()
|
||||||
self.ttl_inout.pulse(1*us)
|
self.ttl_inout.pulse(1*us)
|
||||||
self.rtt = mu_to_seconds(self.ttl_inout.timestamp() - t0)
|
self.set_result("rtt",
|
||||||
|
mu_to_seconds(self.ttl_inout.timestamp() - t0))
|
||||||
|
|
||||||
|
|
||||||
class Loopback(Experiment, AutoDB):
|
class Loopback(EnvExperiment):
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
loop_in = Device()
|
self.attr_device("loop_in")
|
||||||
loop_out = Device()
|
self.attr_device("loop_out")
|
||||||
rtt = Result()
|
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -44,15 +43,15 @@ class Loopback(Experiment, AutoDB):
|
||||||
delay(1*us)
|
delay(1*us)
|
||||||
t0 = now_mu()
|
t0 = now_mu()
|
||||||
self.loop_out.pulse(1*us)
|
self.loop_out.pulse(1*us)
|
||||||
self.rtt = mu_to_seconds(self.loop_in.timestamp() - t0)
|
self.set_result("rtt",
|
||||||
|
mu_to_seconds(self.loop_in.timestamp() - t0))
|
||||||
|
|
||||||
|
|
||||||
class ClockGeneratorLoopback(Experiment, AutoDB):
|
class ClockGeneratorLoopback(EnvExperiment):
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
loop_clock_in = Device()
|
self.attr_device("loop_clock_in")
|
||||||
loop_clock_out = Device()
|
self.attr_device("loop_clock_out")
|
||||||
count = Result()
|
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -64,14 +63,14 @@ class ClockGeneratorLoopback(Experiment, AutoDB):
|
||||||
with sequential:
|
with sequential:
|
||||||
delay(200*ns)
|
delay(200*ns)
|
||||||
self.loop_clock_out.set(1*MHz)
|
self.loop_clock_out.set(1*MHz)
|
||||||
self.count = self.loop_clock_in.count()
|
self.set_result("count",
|
||||||
|
self.loop_clock_in.count())
|
||||||
|
|
||||||
|
|
||||||
class PulseRate(Experiment, AutoDB):
|
class PulseRate(EnvExperiment):
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
loop_out = Device()
|
self.attr_device("loop_out")
|
||||||
pulse_rate = Result()
|
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -85,13 +84,14 @@ class PulseRate(Experiment, AutoDB):
|
||||||
dt += 1
|
dt += 1
|
||||||
self.core.break_realtime()
|
self.core.break_realtime()
|
||||||
else:
|
else:
|
||||||
self.pulse_rate = mu_to_seconds(2*dt)
|
self.set_result("pulse_rate",
|
||||||
|
mu_to_seconds(2*dt))
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
class Watchdog(Experiment, AutoDB):
|
class Watchdog(EnvExperiment):
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -100,14 +100,11 @@ class Watchdog(Experiment, AutoDB):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class LoopbackCount(Experiment, AutoDB):
|
class LoopbackCount(EnvExperiment):
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
ttl_inout = Device()
|
self.attr_device("ttl_inout")
|
||||||
npulses = Argument()
|
self.attr_argument("npulses")
|
||||||
|
|
||||||
def report(self, n):
|
|
||||||
self.result = n
|
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -119,13 +116,14 @@ class LoopbackCount(Experiment, AutoDB):
|
||||||
for i in range(self.npulses):
|
for i in range(self.npulses):
|
||||||
delay(25*ns)
|
delay(25*ns)
|
||||||
self.ttl_inout.pulse(25*ns)
|
self.ttl_inout.pulse(25*ns)
|
||||||
self.report(self.ttl_inout.count())
|
self.set_result("count",
|
||||||
|
self.ttl_inout.count())
|
||||||
|
|
||||||
|
|
||||||
class Underflow(Experiment, AutoDB):
|
class Underflow(EnvExperiment):
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
ttl_out = Device()
|
self.attr_device("ttl_out")
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -134,10 +132,10 @@ class Underflow(Experiment, AutoDB):
|
||||||
self.ttl_out.pulse(25*ns)
|
self.ttl_out.pulse(25*ns)
|
||||||
|
|
||||||
|
|
||||||
class SequenceError(Experiment, AutoDB):
|
class SequenceError(EnvExperiment):
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
ttl_out = Device()
|
self.attr_device("ttl_out")
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -147,21 +145,18 @@ class SequenceError(Experiment, AutoDB):
|
||||||
self.ttl_out.pulse(25*us)
|
self.ttl_out.pulse(25*us)
|
||||||
|
|
||||||
|
|
||||||
class TimeKeepsRunning(Experiment, AutoDB):
|
class TimeKeepsRunning(EnvExperiment):
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
time_at_start = Result()
|
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def run(self):
|
def run(self):
|
||||||
self.time_at_start = now_mu()
|
self.set_result("time_at_start", now_mu())
|
||||||
|
|
||||||
|
|
||||||
class Handover(Experiment, AutoDB):
|
class Handover(EnvExperiment):
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
t1 = Result()
|
|
||||||
t2 = Result()
|
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def get_now(self):
|
def get_now(self):
|
||||||
|
@ -169,34 +164,34 @@ class Handover(Experiment, AutoDB):
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.get_now()
|
self.get_now()
|
||||||
self.t1 = self.time_at_start
|
self.set_result("t1", self.time_at_start)
|
||||||
self.get_now()
|
self.get_now()
|
||||||
self.t2 = self.time_at_start
|
self.set_result("t2", self.time_at_start)
|
||||||
|
|
||||||
|
|
||||||
class CoredeviceTest(ExperimentCase):
|
class CoredeviceTest(ExperimentCase):
|
||||||
def test_rtt(self):
|
def test_rtt(self):
|
||||||
self.execute(RTT)
|
self.execute(RTT)
|
||||||
rtt = self.dbh.get_result("rtt").read
|
rtt = self.rdb.get("rtt")
|
||||||
print(rtt)
|
print(rtt)
|
||||||
self.assertGreater(rtt, 0*ns)
|
self.assertGreater(rtt, 0*ns)
|
||||||
self.assertLess(rtt, 100*ns)
|
self.assertLess(rtt, 100*ns)
|
||||||
|
|
||||||
def test_loopback(self):
|
def test_loopback(self):
|
||||||
self.execute(Loopback)
|
self.execute(Loopback)
|
||||||
rtt = self.dbh.get_result("rtt").read
|
rtt = self.rdb.get("rtt")
|
||||||
print(rtt)
|
print(rtt)
|
||||||
self.assertGreater(rtt, 0*ns)
|
self.assertGreater(rtt, 0*ns)
|
||||||
self.assertLess(rtt, 50*ns)
|
self.assertLess(rtt, 50*ns)
|
||||||
|
|
||||||
def test_clock_generator_loopback(self):
|
def test_clock_generator_loopback(self):
|
||||||
self.execute(ClockGeneratorLoopback)
|
self.execute(ClockGeneratorLoopback)
|
||||||
count = self.dbh.get_result("count").read
|
count = self.rdb.get("count")
|
||||||
self.assertEqual(count, 10)
|
self.assertEqual(count, 10)
|
||||||
|
|
||||||
def test_pulse_rate(self):
|
def test_pulse_rate(self):
|
||||||
self.execute(PulseRate)
|
self.execute(PulseRate)
|
||||||
rate = self.dbh.get_result("pulse_rate").read
|
rate = self.rdb.get("pulse_rate")
|
||||||
print(rate)
|
print(rate)
|
||||||
self.assertGreater(rate, 100*ns)
|
self.assertGreater(rate, 100*ns)
|
||||||
self.assertLess(rate, 2500*ns)
|
self.assertLess(rate, 2500*ns)
|
||||||
|
@ -204,7 +199,8 @@ class CoredeviceTest(ExperimentCase):
|
||||||
def test_loopback_count(self):
|
def test_loopback_count(self):
|
||||||
npulses = 2
|
npulses = 2
|
||||||
r = self.execute(LoopbackCount, npulses=npulses)
|
r = self.execute(LoopbackCount, npulses=npulses)
|
||||||
self.assertEqual(r.result, npulses)
|
count = self.rdb.get("count")
|
||||||
|
self.assertEqual(count, npulses)
|
||||||
|
|
||||||
def test_underflow(self):
|
def test_underflow(self):
|
||||||
with self.assertRaises(runtime_exceptions.RTIOUnderflow):
|
with self.assertRaises(runtime_exceptions.RTIOUnderflow):
|
||||||
|
@ -221,26 +217,23 @@ class CoredeviceTest(ExperimentCase):
|
||||||
|
|
||||||
def test_time_keeps_running(self):
|
def test_time_keeps_running(self):
|
||||||
self.execute(TimeKeepsRunning)
|
self.execute(TimeKeepsRunning)
|
||||||
t1 = self.dbh.get_result("time_at_start").read
|
t1 = self.rdb.get("time_at_start")
|
||||||
self.execute(TimeKeepsRunning)
|
self.execute(TimeKeepsRunning)
|
||||||
t2 = self.dbh.get_result("time_at_start").read
|
t2 = self.rdb.get("time_at_start")
|
||||||
dead_time = mu_to_seconds(t2 - t1, self.dbh.get_device("core"))
|
dead_time = mu_to_seconds(t2 - t1, self.dmgr.get("core"))
|
||||||
print(dead_time)
|
print(dead_time)
|
||||||
self.assertGreater(dead_time, 1*ms)
|
self.assertGreater(dead_time, 1*ms)
|
||||||
self.assertLess(dead_time, 300*ms)
|
self.assertLess(dead_time, 300*ms)
|
||||||
|
|
||||||
def test_handover(self):
|
def test_handover(self):
|
||||||
self.execute(Handover)
|
self.execute(Handover)
|
||||||
self.assertEqual(self.dbh.get_result("t1").read,
|
self.assertEqual(self.rdb.get("t1"), self.rdb.get("t2"))
|
||||||
self.dbh.get_result("t2").read)
|
|
||||||
|
|
||||||
|
|
||||||
class RPCTiming(Experiment, AutoDB):
|
class RPCTiming(EnvExperiment):
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
repeats = Argument(100)
|
self.attr_argument("repeats", FreeValue(100))
|
||||||
rpc_time_mean = Result()
|
|
||||||
rpc_time_stddev = Result()
|
|
||||||
|
|
||||||
def nop(self, x):
|
def nop(self, x):
|
||||||
pass
|
pass
|
||||||
|
@ -257,14 +250,14 @@ class RPCTiming(Experiment, AutoDB):
|
||||||
def run(self):
|
def run(self):
|
||||||
self.bench()
|
self.bench()
|
||||||
mean = sum(self.ts)/self.repeats
|
mean = sum(self.ts)/self.repeats
|
||||||
self.rpc_time_stddev = sqrt(
|
self.set_result("rpc_time_stddev", sqrt(
|
||||||
sum([(t - mean)**2 for t in self.ts])/self.repeats)*s
|
sum([(t - mean)**2 for t in self.ts])/self.repeats))
|
||||||
self.rpc_time_mean = mean*s
|
self.set_result("rpc_time_mean", mean)
|
||||||
|
|
||||||
|
|
||||||
class RPCTest(ExperimentCase):
|
class RPCTest(ExperimentCase):
|
||||||
def test_rpc_timing(self):
|
def test_rpc_timing(self):
|
||||||
self.execute(RPCTiming)
|
self.execute(RPCTiming)
|
||||||
self.assertGreater(self.dbh.get_result("rpc_time_mean").read, 100*ns)
|
self.assertGreater(self.rdb.get("rpc_time_mean"), 100*ns)
|
||||||
self.assertLess(self.dbh.get_result("rpc_time_mean").read, 15*ms)
|
self.assertLess(self.rdb.get("rpc_time_mean"), 15*ms)
|
||||||
self.assertLess(self.dbh.get_result("rpc_time_stddev").read, 1*ms)
|
self.assertLess(self.rdb.get("rpc_time_stddev"), 1*ms)
|
||||||
|
|
|
@ -6,18 +6,19 @@ from artiq.sim import devices as sim_devices
|
||||||
from artiq.test.hardware_testbench import ExperimentCase
|
from artiq.test.hardware_testbench import ExperimentCase
|
||||||
|
|
||||||
|
|
||||||
def _run_on_host(k_class, **parameters):
|
def _run_on_host(k_class, **arguments):
|
||||||
coredev = sim_devices.Core()
|
dmgr = dict()
|
||||||
k_inst = k_class(core=coredev, **parameters)
|
dmgr["core"] = sim_devices.Core(dmgr)
|
||||||
|
k_inst = k_class(dmgr, **arguments)
|
||||||
k_inst.run()
|
k_inst.run()
|
||||||
return k_inst
|
return k_inst
|
||||||
|
|
||||||
|
|
||||||
class _Primes(Experiment, AutoDB):
|
class _Primes(EnvExperiment):
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
output_list = Argument()
|
self.attr_argument("output_list")
|
||||||
maximum = Argument()
|
self.attr_argument("maximum")
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -33,11 +34,10 @@ class _Primes(Experiment, AutoDB):
|
||||||
self.output_list.append(x)
|
self.output_list.append(x)
|
||||||
|
|
||||||
|
|
||||||
class _Misc(Experiment, AutoDB):
|
class _Misc(EnvExperiment):
|
||||||
class DBKeys:
|
|
||||||
core = Device()
|
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
|
self.attr_device("core")
|
||||||
|
|
||||||
self.input = 84
|
self.input = 84
|
||||||
self.al = [1, 2, 3, 4, 5]
|
self.al = [1, 2, 3, 4, 5]
|
||||||
self.list_copy_in = [2*Hz, 10*MHz]
|
self.list_copy_in = [2*Hz, 10*MHz]
|
||||||
|
@ -52,11 +52,11 @@ class _Misc(Experiment, AutoDB):
|
||||||
self.list_copy_out = self.list_copy_in
|
self.list_copy_out = self.list_copy_in
|
||||||
|
|
||||||
|
|
||||||
class _PulseLogger(Experiment, AutoDB):
|
class _PulseLogger(EnvExperiment):
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
output_list = Argument()
|
self.attr_argument("output_list")
|
||||||
name = Argument()
|
self.attr_argument("name")
|
||||||
|
|
||||||
def _append(self, t, l, f):
|
def _append(self, t, l, f):
|
||||||
if not hasattr(self, "first_timestamp"):
|
if not hasattr(self, "first_timestamp"):
|
||||||
|
@ -79,14 +79,13 @@ class _PulseLogger(Experiment, AutoDB):
|
||||||
self.off(now_mu())
|
self.off(now_mu())
|
||||||
|
|
||||||
|
|
||||||
class _Pulses(Experiment, AutoDB):
|
class _Pulses(EnvExperiment):
|
||||||
class DBKeys:
|
|
||||||
core = Device()
|
|
||||||
output_list = Argument()
|
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
|
self.attr_device("core")
|
||||||
|
self.attr_argument("output_list")
|
||||||
|
|
||||||
for name in "a", "b", "c", "d":
|
for name in "a", "b", "c", "d":
|
||||||
pl = _PulseLogger(core=self.core,
|
pl = _PulseLogger(*self.dbs(),
|
||||||
output_list=self.output_list,
|
output_list=self.output_list,
|
||||||
name=name)
|
name=name)
|
||||||
setattr(self, name, pl)
|
setattr(self, name, pl)
|
||||||
|
@ -107,10 +106,10 @@ class _MyException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class _Exceptions(Experiment, AutoDB):
|
class _Exceptions(EnvExperiment):
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
trace = Argument()
|
self.attr_argument("trace")
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -151,12 +150,11 @@ class _Exceptions(Experiment, AutoDB):
|
||||||
self.trace.append(104)
|
self.trace.append(104)
|
||||||
|
|
||||||
|
|
||||||
class _RPCExceptions(Experiment, AutoDB):
|
class _RPCExceptions(EnvExperiment):
|
||||||
class DBKeys:
|
|
||||||
core = Device()
|
|
||||||
catch = Argument(False)
|
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
|
self.attr_device("core")
|
||||||
|
self.attr_argument("catch", FreeValue(False))
|
||||||
|
|
||||||
self.success = False
|
self.success = False
|
||||||
|
|
||||||
def exception_raiser(self):
|
def exception_raiser(self):
|
||||||
|
|
|
@ -5,9 +5,8 @@ import logging
|
||||||
|
|
||||||
from artiq.language import *
|
from artiq.language import *
|
||||||
from artiq.protocols.file_db import FlatFileDB
|
from artiq.protocols.file_db import FlatFileDB
|
||||||
from artiq.master.worker_db import DBHub, ResultDB
|
from artiq.master.worker_db import DeviceManager, ResultDB
|
||||||
from artiq.frontend.artiq_run import (
|
from artiq.frontend.artiq_run import DummyScheduler
|
||||||
DummyScheduler, DummyWatchdog, SimpleParamLogger)
|
|
||||||
|
|
||||||
|
|
||||||
artiq_root = os.getenv("ARTIQ_ROOT")
|
artiq_root = os.getenv("ARTIQ_ROOT")
|
||||||
|
@ -33,9 +32,10 @@ def get_from_ddb(*path, default="skip"):
|
||||||
class ExperimentCase(unittest.TestCase):
|
class ExperimentCase(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.ddb = FlatFileDB(os.path.join(artiq_root, "ddb.pyon"))
|
self.ddb = FlatFileDB(os.path.join(artiq_root, "ddb.pyon"))
|
||||||
|
self.dmgr = DeviceManager(self.ddb,
|
||||||
|
virtual_devices={"scheduler": DummyScheduler()})
|
||||||
self.pdb = FlatFileDB(os.path.join(artiq_root, "pdb.pyon"))
|
self.pdb = FlatFileDB(os.path.join(artiq_root, "pdb.pyon"))
|
||||||
self.rdb = ResultDB(lambda description: None, lambda mod: None)
|
self.rdb = ResultDB()
|
||||||
self.dbh = DBHub(self.ddb, self.pdb, self.rdb)
|
|
||||||
|
|
||||||
def execute(self, cls, **kwargs):
|
def execute(self, cls, **kwargs):
|
||||||
expid = {
|
expid = {
|
||||||
|
@ -43,16 +43,16 @@ class ExperimentCase(unittest.TestCase):
|
||||||
"experiment": cls.__name__,
|
"experiment": cls.__name__,
|
||||||
"arguments": kwargs
|
"arguments": kwargs
|
||||||
}
|
}
|
||||||
sched = DummyScheduler(expid)
|
self.dmgr.virtual_devices["scheduler"].expid = expid
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
exp = cls(self.dbh, scheduler=sched, **kwargs)
|
exp = cls(self.dmgr, self.pdb, self.rdb, **kwargs)
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
# skip if ddb does not match requirements
|
# skip if ddb does not match requirements
|
||||||
raise unittest.SkipTest(*e.args)
|
raise unittest.SkipTest(*e.args)
|
||||||
self.rdb.build()
|
exp.prepare()
|
||||||
exp.run()
|
exp.run()
|
||||||
exp.analyze()
|
exp.analyze()
|
||||||
return exp
|
return exp
|
||||||
finally:
|
finally:
|
||||||
self.dbh.close_devices()
|
self.dmgr.close_devices()
|
||||||
|
|
|
@ -7,12 +7,18 @@ from artiq import *
|
||||||
from artiq.master.scheduler import Scheduler
|
from artiq.master.scheduler import Scheduler
|
||||||
|
|
||||||
|
|
||||||
class EmptyExperiment(Experiment, AutoDB):
|
class EmptyExperiment(EnvExperiment):
|
||||||
|
def build(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class BackgroundExperiment(Experiment, AutoDB):
|
class BackgroundExperiment(EnvExperiment):
|
||||||
|
def build(self):
|
||||||
|
self.attr_device("scheduler")
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
while True:
|
while True:
|
||||||
self.scheduler.pause()
|
self.scheduler.pause()
|
||||||
|
|
|
@ -36,7 +36,9 @@ def run():
|
||||||
|
|
||||||
class OptimizeCase(unittest.TestCase):
|
class OptimizeCase(unittest.TestCase):
|
||||||
def test_optimize(self):
|
def test_optimize(self):
|
||||||
coredev = core.Core(comm=comm_dummy.Comm(), ref_period=1*ns)
|
dmgr = dict()
|
||||||
|
dmgr["comm"] = comm_dummy.Comm(dmgr)
|
||||||
|
coredev = core.Core(dmgr, ref_period=1*ns)
|
||||||
func_def = ast.parse(optimize_in).body[0]
|
func_def = ast.parse(optimize_in).body[0]
|
||||||
coredev.transform_stack(func_def, dict(), dict())
|
coredev.transform_stack(func_def, dict(), dict())
|
||||||
self.assertEqual(unparse(func_def), optimize_out)
|
self.assertEqual(unparse(func_def), optimize_out)
|
||||||
|
|
|
@ -7,20 +7,26 @@ from artiq import *
|
||||||
from artiq.master.worker import *
|
from artiq.master.worker import *
|
||||||
|
|
||||||
|
|
||||||
class WatchdogNoTimeout(Experiment, AutoDB):
|
class WatchdogNoTimeout(EnvExperiment):
|
||||||
|
def build(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
with watchdog(0.5*s):
|
with watchdog(0.5*s):
|
||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
|
|
||||||
|
|
||||||
class WatchdogTimeout(Experiment, AutoDB):
|
class WatchdogTimeout(EnvExperiment):
|
||||||
|
def build(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
with watchdog(0.1*s):
|
with watchdog(0.1*s):
|
||||||
sleep(100.0)
|
sleep(100.0)
|
||||||
|
|
||||||
|
|
||||||
class WatchdogTimeoutInBuild(Experiment, AutoDB):
|
class WatchdogTimeoutInBuild(EnvExperiment):
|
||||||
def build(self):
|
def build(self):
|
||||||
with watchdog(0.1*s):
|
with watchdog(0.1*s):
|
||||||
sleep(100.0)
|
sleep(100.0)
|
||||||
|
|
|
@ -7,7 +7,7 @@ import asyncio
|
||||||
import time
|
import time
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
from artiq.language.experiment import is_experiment
|
from artiq.language.environment import is_experiment
|
||||||
from artiq.protocols import pyon
|
from artiq.protocols import pyon
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -118,7 +118,7 @@ pygments_style = 'sphinx'
|
||||||
|
|
||||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
# a list of builtin themes.
|
# a list of builtin themes.
|
||||||
html_theme = 'classic'
|
html_theme = 'default'
|
||||||
|
|
||||||
# Theme options are theme-specific and customize the look and feel of a theme
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
# further. For a list of options available for each theme, see the
|
# further. For a list of options available for each theme, see the
|
||||||
|
|
|
@ -9,10 +9,10 @@ The most commonly used features from those modules can be imported with ``from a
|
||||||
.. automodule:: artiq.language.core
|
.. automodule:: artiq.language.core
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
:mod:`artiq.language.db` module
|
:mod:`artiq.language.environment` module
|
||||||
-------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
.. automodule:: artiq.language.db
|
.. automodule:: artiq.language.environment
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
:mod:`artiq.language.units` module
|
:mod:`artiq.language.units` module
|
||||||
|
|
|
@ -103,7 +103,7 @@ Run it as before, while the controller is running. You should see the message ap
|
||||||
$ ./hello_controller.py
|
$ ./hello_controller.py
|
||||||
message: Hello World!
|
message: Hello World!
|
||||||
|
|
||||||
When using the driver in an experiment, for simple cases the ``Client`` instance can be returned by the :class:`artiq.language.db.AutoDB` mechanism and used normally as a device.
|
When using the driver in an experiment, the ``Client`` instance can be returned by the environment mechanism (via the ``get_device`` and ``attr_device`` methods of :class:`artiq.language.environment.HasEnvironment`) and used normally as a device.
|
||||||
|
|
||||||
:warning: RPC servers operate on copies of objects provided by the client, and modifications to mutable types are not written back. For example, if the client passes a list as a parameter of an RPC method, and that method ``append()s`` an element to the list, the element is not appended to the client's list.
|
:warning: RPC servers operate on copies of objects provided by the client, and modifications to mutable types are not written back. For example, if the client passes a list as a parameter of an RPC method, and that method ``append()s`` an element to the list, the element is not appended to the client's list.
|
||||||
|
|
||||||
|
|
|
@ -7,30 +7,19 @@ How do I ...
|
||||||
prevent my first RTIO command from causing an underflow?
|
prevent my first RTIO command from causing an underflow?
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
|
|
||||||
The RTIO timestamp counter starts counting at zero at the beginning of the first kernel run on the core device. The first RTIO event is programmed with a small timestamp above zero. If the kernel needs more time than this timestamp to produce the event, an underflow will occur. You can prevent it by calling ``break_realtime`` just before programming the first event.
|
The first RTIO event is programmed with a small timestamp above the value of the timecounter at the start of the experiment. If the kernel needs more time than this timestamp to produce the event, an underflow will occur. You can prevent it by calling ``break_realtime`` just before programming the first event, or by adding a sufficient delay.
|
||||||
|
|
||||||
override the `sysclk` frequency of just one DDS?
|
|
||||||
------------------------------------------------
|
|
||||||
|
|
||||||
Override the parameter using an argument in the DDB.
|
|
||||||
|
|
||||||
organize parameters in folders?
|
organize parameters in folders?
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
Use GUI auto-completion and filtering.
|
Folders are not supported yet, use GUI filtering for now. Names need to be unique.
|
||||||
Names need to be unique.
|
|
||||||
|
|
||||||
enforce functional dependencies between parameters?
|
enforce functional dependencies between parameters?
|
||||||
---------------------------------------------------
|
---------------------------------------------------
|
||||||
|
|
||||||
If you want to override a parameter ``b`` in the PDB to be ``b = 2*a``,
|
If you want to override a parameter ``b`` in the PDB to be ``b = 2*a``,
|
||||||
use wrapper experiments, overriding parameters by passing them to the
|
use wrapper experiments, overriding parameters by passing them to the
|
||||||
experiment's constructor.
|
experiment's constructor (``param_override`` argument).
|
||||||
|
|
||||||
get rid of ``DBKeys``?
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
``DBKeys`` references keys in PDB, DDB and RDB.
|
|
||||||
|
|
||||||
write a generator feeding a kernel feeding an analyze function?
|
write a generator feeding a kernel feeding an analyze function?
|
||||||
---------------------------------------------------------------
|
---------------------------------------------------------------
|
||||||
|
|
|
@ -11,23 +11,23 @@ As a very first step, we will turn on a LED on the core device. Create a file ``
|
||||||
from artiq import *
|
from artiq import *
|
||||||
|
|
||||||
|
|
||||||
class LED(Experiment, AutoDB):
|
class LED(EnvExperiment):
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
led = Device()
|
self.attr_device("led")
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def run(self):
|
def run(self):
|
||||||
self.led.on()
|
self.led.on()
|
||||||
|
|
||||||
|
|
||||||
The central part of our code is our ``LED`` class, that derives from :class:`artiq.language.db.AutoDB`. ``AutoDB`` is part of the mechanism that attaches device drivers and retrieves parameters according to a database. Our ``DBKeys`` class lists the devices (and parameters) that ``LED`` needs in order to operate, and the names of the attributes (e.g. ``led``) are used to search the database. ``AutoDB`` replaces them with the actual device drivers (and parameter values). Finally, the ``@kernel`` decorator tells the system that the ``run`` method must be executed on the core device (instead of the host).
|
The central part of our code is our ``LED`` class, that derives from :class:`artiq.language.environment.EnvExperiment`. Among other features, ``EnvExperiment`` calls our ``build`` method and provides the ``attr_device`` method that interfaces to the device database to create the appropriate device drivers and make those drivers accessible as ``self.core`` and ``self.led``. The ``@kernel`` decorator tells the system that the ``run`` method must be executed on the core device (instead of the host). The decorator uses ``self.core`` internally, which is why we request the core device using ``attr_device`` like any other.
|
||||||
|
|
||||||
Copy the files ``ddb.pyon`` and ``pdb.pyon`` (containing the device and parameter databases) from the ``examples`` folder of ARTIQ into the same directory as ``led.py`` (alternatively, you can use the ``-d`` and ``-p`` options of ``artiq_run.py``). You can open the database files using a text editor - their contents are in a human-readable format.
|
Copy the files ``ddb.pyon`` and ``pdb.pyon`` (containing the device and parameter databases) from the ``examples`` folder of ARTIQ into the same directory as ``led.py`` (alternatively, you can use the ``-d`` and ``-p`` options of ``artiq_run.py``). You can open the database files using a text editor - their contents are in a human-readable format.
|
||||||
|
|
||||||
Run your code using ``artiq_run.py``, which is part of the ARTIQ front-end tools: ::
|
Run your code using ``artiq_run``, which is part of the ARTIQ front-end tools: ::
|
||||||
|
|
||||||
$ artiq_run.py led.py
|
$ artiq_run led.py
|
||||||
|
|
||||||
The LED of the device should turn on. Congratulations! You have a basic ARTIQ system up and running.
|
The LED of the device should turn on. Congratulations! You have a basic ARTIQ system up and running.
|
||||||
|
|
||||||
|
@ -41,10 +41,10 @@ Modify the code as follows: ::
|
||||||
def input_led_state():
|
def input_led_state():
|
||||||
return int(input("Enter desired LED state: "))
|
return int(input("Enter desired LED state: "))
|
||||||
|
|
||||||
class LED(Experiment, AutoDB):
|
class LED(EnvExperiment):
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
led = Device()
|
self.attr_device("led")
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -58,9 +58,9 @@ Modify the code as follows: ::
|
||||||
|
|
||||||
You can then turn the LED off and on by entering 0 or 1 at the prompt that appears: ::
|
You can then turn the LED off and on by entering 0 or 1 at the prompt that appears: ::
|
||||||
|
|
||||||
$ artiq_run.py led.py
|
$ artiq_run led.py
|
||||||
Enter desired LED state: 1
|
Enter desired LED state: 1
|
||||||
$ artiq_run.py led.py
|
$ artiq_run led.py
|
||||||
Enter desired LED state: 0
|
Enter desired LED state: 0
|
||||||
|
|
||||||
What happens is the ARTIQ compiler notices that the ``input_led_state`` function does not have a ``@kernel`` decorator and thus must be executed on the host. When the core device calls it, it sends a request to the host to execute it. The host displays the prompt, collects user input, and sends the result back to the core device, which sets the LED state accordingly.
|
What happens is the ARTIQ compiler notices that the ``input_led_state`` function does not have a ``@kernel`` decorator and thus must be executed on the host. When the core device calls it, it sends a request to the host to execute it. The host displays the prompt, collects user input, and sends the result back to the core device, which sets the LED state accordingly.
|
||||||
|
@ -90,10 +90,10 @@ Create a new file ``rtio.py`` containing the following: ::
|
||||||
|
|
||||||
from artiq import *
|
from artiq import *
|
||||||
|
|
||||||
class Tutorial(Experiment, AutoDB):
|
class Tutorial(EnvExperiment):
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
ttl0 = Device()
|
self.attr_device("ttl0")
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -113,10 +113,10 @@ Try reducing the period of the generated waveform until the CPU cannot keep up w
|
||||||
def print_underflow():
|
def print_underflow():
|
||||||
print("RTIO underflow occured")
|
print("RTIO underflow occured")
|
||||||
|
|
||||||
class Tutorial(Experiment, AutoDB):
|
class Tutorial(EnvExperiment):
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
ttl0 = Device()
|
self.attr_device("ttl0")
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
from artiq import *
|
from artiq import *
|
||||||
|
|
||||||
|
|
||||||
class DDSTest(Experiment, AutoDB):
|
class DDSTest(EnvExperiment):
|
||||||
"""DDS test"""
|
"""DDS test"""
|
||||||
|
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
dds_bus = Device()
|
self.attr_device("dds_bus")
|
||||||
dds0 = Device()
|
self.attr_device("dds0")
|
||||||
dds1 = Device()
|
self.attr_device("dds1")
|
||||||
dds2 = Device()
|
self.attr_device("dds2")
|
||||||
ttl0 = Device()
|
self.attr_device("ttl0")
|
||||||
ttl1 = Device()
|
self.attr_device("ttl1")
|
||||||
ttl2 = Device()
|
self.attr_device("ttl2")
|
||||||
led = Device()
|
self.attr_device("led")
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
|
@ -23,25 +23,21 @@ def model_numpy(xdata, F0):
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
class FloppingF(Experiment, AutoDB):
|
class FloppingF(EnvExperiment):
|
||||||
"""Flopping F simulation"""
|
"""Flopping F simulation"""
|
||||||
|
|
||||||
class DBKeys:
|
def build(self):
|
||||||
npoints = Argument(100)
|
self.attr_argument("npoints", FreeValue(100))
|
||||||
min_freq = Argument(1000)
|
self.attr_argument("min_freq", FreeValue(1000))
|
||||||
max_freq = Argument(2000)
|
self.attr_argument("max_freq", FreeValue(2000))
|
||||||
|
|
||||||
F0 = Argument(1500)
|
self.attr_argument("F0", FreeValue(1500))
|
||||||
noise_amplitude = Argument(0.1)
|
self.attr_argument("noise_amplitude", FreeValue(0.1))
|
||||||
|
|
||||||
frequency = Result()
|
self.frequency = self.set_result("flopping_f_frequency", [], True)
|
||||||
brightness = Result()
|
self.brightness = self.set_result("flopping_f_brightness", [], True)
|
||||||
|
|
||||||
flopping_freq = Parameter()
|
self.attr_device("scheduler")
|
||||||
|
|
||||||
realtime_results = {
|
|
||||||
("frequency", "brightness"): "xy"
|
|
||||||
}
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
for i in range(self.npoints):
|
for i in range(self.npoints):
|
||||||
|
@ -56,7 +52,7 @@ class FloppingF(Experiment, AutoDB):
|
||||||
def analyze(self):
|
def analyze(self):
|
||||||
popt, pcov = curve_fit(model_numpy,
|
popt, pcov = curve_fit(model_numpy,
|
||||||
self.frequency.read, self.brightness.read,
|
self.frequency.read, self.brightness.read,
|
||||||
p0=[self.flopping_freq])
|
p0=[self.get_parameter("flopping_freq")])
|
||||||
perr = np.sqrt(np.diag(pcov))
|
perr = np.sqrt(np.diag(pcov))
|
||||||
if perr < 0.1:
|
if perr < 0.1:
|
||||||
self.flopping_freq = float(popt)
|
self.set_parameter("flopping_freq", float(popt))
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
from artiq import *
|
from artiq import *
|
||||||
|
|
||||||
|
|
||||||
class Handover(Experiment, AutoDB):
|
class Handover(EnvExperiment):
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
led = Device()
|
self.attr_device("led")
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def blink_once(self):
|
def blink_once(self):
|
||||||
|
|
|
@ -3,11 +3,11 @@ import sys
|
||||||
from artiq import *
|
from artiq import *
|
||||||
|
|
||||||
|
|
||||||
class Mandelbrot(Experiment, AutoDB):
|
class Mandelbrot(EnvExperiment):
|
||||||
"""Mandelbrot set demo"""
|
"""Mandelbrot set demo"""
|
||||||
|
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
|
|
||||||
def col(self, i):
|
def col(self, i):
|
||||||
sys.stdout.write(" .,-:;i+hHM$*#@ "[i])
|
sys.stdout.write(" .,-:;i+hHM$*#@ "[i])
|
||||||
|
|
|
@ -1,29 +1,24 @@
|
||||||
from artiq import *
|
from artiq import *
|
||||||
|
|
||||||
|
|
||||||
class PhotonHistogram(Experiment, AutoDB):
|
class PhotonHistogram(EnvExperiment):
|
||||||
"""Photon histogram"""
|
"""Photon histogram"""
|
||||||
|
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
dds_bus = Device()
|
self.attr_device("dds_bus")
|
||||||
bd_dds = Device()
|
self.attr_device("bd_dds")
|
||||||
bd_sw = Device()
|
self.attr_device("bd_sw")
|
||||||
bdd_dds = Device()
|
self.attr_device("bdd_dds")
|
||||||
bdd_sw = Device()
|
self.attr_device("bdd_sw")
|
||||||
pmt = Device()
|
self.attr_device("pmt")
|
||||||
|
|
||||||
nbins = Argument(100)
|
self.attr_argument("nbins", FreeValue(100))
|
||||||
repeats = Argument(100)
|
self.attr_argument("repeats", FreeValue(100))
|
||||||
|
|
||||||
cool_f = Parameter(230*MHz)
|
self.attr_parameter("cool_f", 230*MHz)
|
||||||
detect_f = Parameter(220*MHz)
|
self.attr_parameter("detect_f", 220*MHz)
|
||||||
detect_t = Parameter(100*us)
|
self.attr_parameter("detect_t", 100*us)
|
||||||
|
|
||||||
ion_present = Parameter(True)
|
|
||||||
|
|
||||||
hist = Result()
|
|
||||||
total = Result()
|
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def program_cooling(self):
|
def program_cooling(self):
|
||||||
|
@ -65,9 +60,8 @@ class PhotonHistogram(Experiment, AutoDB):
|
||||||
hist[n] += 1
|
hist[n] += 1
|
||||||
total += n
|
total += n
|
||||||
|
|
||||||
self.hist = hist
|
self.set_result("cooling_photon_histogram", hist)
|
||||||
self.total = total
|
self.set_parameter("ion_present", total > 5*self.repeats)
|
||||||
self.ion_present = total > 5*self.repeats
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -10,23 +10,22 @@ transport_data = dict(
|
||||||
# 4 devices, 3 board each, 3 dacs each
|
# 4 devices, 3 board each, 3 dacs each
|
||||||
)
|
)
|
||||||
|
|
||||||
class Transport(Experiment, AutoDB):
|
class Transport(EnvExperiment):
|
||||||
"""Transport"""
|
"""Transport"""
|
||||||
|
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
bd = Device()
|
self.attr_device("bd")
|
||||||
bdd = Device()
|
self.attr_device("bdd")
|
||||||
pmt = Device()
|
self.attr_device("pmt")
|
||||||
electrodes = Device()
|
self.attr_device("electrodes")
|
||||||
|
|
||||||
wait_at_stop = Parameter(100*us)
|
self.attr_argument("wait_at_stop", FreeValue(100*us))
|
||||||
speed = Parameter(1.5)
|
self.attr_argument("speed", FreeValue(1.5))
|
||||||
|
self.attr_argument("repeats", FreeValue(100))
|
||||||
|
self.attr_argument("nbins", FreeValue(100))
|
||||||
|
|
||||||
repeats = Argument(100)
|
def calc_waveforms(self, stop):
|
||||||
nbins = Argument(100)
|
|
||||||
|
|
||||||
def prepare(self, stop):
|
|
||||||
t = transport_data["t"][:stop]*self.speed
|
t = transport_data["t"][:stop]*self.speed
|
||||||
u = transport_data["u"][:stop]
|
u = transport_data["u"][:stop]
|
||||||
|
|
||||||
|
@ -89,9 +88,9 @@ class Transport(Experiment, AutoDB):
|
||||||
def scan(self, stops):
|
def scan(self, stops):
|
||||||
for s in stops:
|
for s in stops:
|
||||||
self.histogram = []
|
self.histogram = []
|
||||||
# non-kernel, calculate waveforms, build frames
|
# non-kernel, build frames
|
||||||
# could also be rpc'ed from repeat()
|
# could also be rpc'ed from repeat()
|
||||||
self.prepare(s)
|
self.calc_waveforms(s)
|
||||||
# kernel part
|
# kernel part
|
||||||
self.repeat()
|
self.repeat()
|
||||||
# live update 2d plot with current self.histogram
|
# live update 2d plot with current self.histogram
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
from artiq import *
|
from artiq import *
|
||||||
|
|
||||||
|
|
||||||
class AluminumSpectroscopy(Experiment, AutoDB):
|
class AluminumSpectroscopy(EnvExperiment):
|
||||||
"""Aluminum spectroscopy (simulation)"""
|
"""Aluminum spectroscopy (simulation)"""
|
||||||
|
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
mains_sync = Device()
|
self.attr_device("mains_sync")
|
||||||
laser_cooling = Device()
|
self.attr_device("laser_cooling")
|
||||||
spectroscopy = Device()
|
self.attr_device("spectroscopy")
|
||||||
spectroscopy_b = Device()
|
self.attr_device("spectroscopy_b")
|
||||||
state_detection = Device()
|
self.attr_device("state_detection")
|
||||||
pmt = Device()
|
self.attr_device("pmt")
|
||||||
spectroscopy_freq = Parameter(432*MHz)
|
self.attr_parameter("spectroscopy_freq", 432*MHz)
|
||||||
photon_limit_low = Argument(10)
|
self.attr_argument("photon_limit_low", FreeValue(10))
|
||||||
photon_limit_high = Argument(15)
|
self.attr_argument("photon_limit_high", FreeValue(15))
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
from artiq import *
|
from artiq import *
|
||||||
|
|
||||||
|
|
||||||
class SimpleSimulation(Experiment, AutoDB):
|
class SimpleSimulation(EnvExperiment):
|
||||||
"""Simple simulation"""
|
"""Simple simulation"""
|
||||||
|
|
||||||
class DBKeys:
|
def build(self):
|
||||||
core = Device()
|
self.attr_device("core")
|
||||||
a = Device()
|
for wo in "abcd":
|
||||||
b = Device()
|
self.attr_device(wo)
|
||||||
c = Device()
|
|
||||||
d = Device()
|
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -23,16 +21,13 @@ class SimpleSimulation(Experiment, AutoDB):
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
from artiq.sim import devices as sd
|
from artiq.sim import devices
|
||||||
|
|
||||||
core = sd.Core()
|
dmgr = dict()
|
||||||
exp = SimpleSimulation(
|
dmgr["core"] = devices.Core(dmgr)
|
||||||
core=core,
|
for wo in "abcd":
|
||||||
a=sd.WaveOutput(core=core, name="a"),
|
dmgr[wo] = devices.WaveOutput(dmgr, wo)
|
||||||
b=sd.WaveOutput(core=core, name="b"),
|
exp = SimpleSimulation(dmgr)
|
||||||
c=sd.WaveOutput(core=core, name="c"),
|
|
||||||
d=sd.WaveOutput(core=core, name="d"),
|
|
||||||
)
|
|
||||||
exp.run()
|
exp.run()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
Loading…
Reference in New Issue