From aa5e6db726c034204d45f9367e370d99bb0490f4 Mon Sep 17 00:00:00 2001 From: Giulio Ungaretti Date: Tue, 26 Jul 2016 19:26:49 +0200 Subject: [PATCH] docs: add Meta instrument. --- docs/user/meta.py | 108 +++++++++++++++++++++++++++++++++++++++++ docs/user/tutorial.rst | 101 +++++++++++++++++++------------------- 2 files changed, 159 insertions(+), 50 deletions(-) create mode 100644 docs/user/meta.py diff --git a/docs/user/meta.py b/docs/user/meta.py new file mode 100644 index 00000000000..fbe9d93652b --- /dev/null +++ b/docs/user/meta.py @@ -0,0 +1,108 @@ +" MWE of meta instrument (with debugging enabled) " +import concurrent.futures as futures +import logging +import multiprocessing as mp +import time + +from functools import partial + +from qcodes import Instrument + + +# agreesive logging +logging.basicConfig(level=logging.DEBUG, format='%(asctime)s- %(message)s') + +class MyInstrument(Instrument): + + def __init__(self, name, **kwargs): + super().__init__(name, **kwargs) + self.value=0 + self.add_parameter('x', get_cmd=self.getx, set_cmd=self.setx) + + def getx(self): + return self.value + + def setx(self, val): + logging.debug("set {}".format(val)) + self.value = val + # simulate delay 5 seconds + time.sleep(5) + logging.debug("done {}".format(val)) + return + + +class Meta(Instrument): + shared_kwargs = ['instruments'] + + # Instruments will be a list of RemoteInstrument objects, which can be + # given to a server on creation but not later on, so it needs to be + # listed in shared_kwargs + + def __init__(self, name, instruments=(), **kwargs): + super().__init__(name, **kwargs) + self._instrument_list = instruments + self.no_instruments = len(instruments) + for gate in range(len(self._instrument_list)): + self.add_parameter('c%d' % gate, + get_cmd=partial(self._get, gate=gate), + set_cmd=partial(self._set, gate=gate)) + + self.add_parameter("setBoth", set_cmd=partial(self._set_both)) + self.add_parameter("setBothAsync", set_cmd=partial(self._set_async)) + + def _set_both(self, value): + for i in self._instrument_list: + i.set('x', value) + + def _set_async(self, value): + with futures.ThreadPoolExecutor(max_workers=self.no_instruments+10) as executor: + jobs = [] + for i in self._instrument_list: + job = executor.submit(partial(i.set, 'x'), value) + jobs.append(job) + + futures.wait(jobs) + + def _get(self, gate): + value =self._instrument_list[gate].get('x') + logging.debug('Meta get gate %s' % (value)) + return value + + def _set(self, value, gate): + logging.debug('Meta set gate %s @ value %s' % (gate, value)) + i = self._instrument_list[gate] + i.set('x', value) + + + +if __name__ == '__main__': + + mp.set_start_method('spawn') + + base1 = MyInstrument(name='zero', server_name="foo") + base2 = MyInstrument(name='one', server_name="bar") + + meta_server_name = "meta_server" + meta = Meta(name='meta', server_name=meta_server_name, + instruments=[base1, base2]) + + + print("--- set meta --- ") + meta.c1.set(25) + print(meta.c1.get()) + print(base1.x.get()) + + print("--- set base --- ") + base1.x.set(1) + print(meta.c1.get()) + print(base1.x.get()) + + + print("--- both --- ") + meta.setBoth(0) + print(base2.x.get()) + print(base1.x.get()) + + print("--- no block --- ") + meta.setBothAsync(10) + logging.debug(base1.x.get()) diff --git a/docs/user/tutorial.rst b/docs/user/tutorial.rst index e3eb6063436..bb994431e21 100644 --- a/docs/user/tutorial.rst +++ b/docs/user/tutorial.rst @@ -11,7 +11,7 @@ In this tutorial we'll walk through ***** Writing a Driver ---------------- -Write a simple driver example +Write a simple driver example with commented code - add parameter - add validator @@ -35,12 +35,15 @@ Explain the mock mock .. todo:: missing -Composite Instruments ---------------------- -The concept of a composite instrument is that of having +.. __metainstrument : + +Meta Instruments +--------------------- +The concept of a meta instrument is that of having two separate Instrument, real or virtual, whose actions can -the be controlled from the composite instrument. -In the following example we will create two dummy instruments and a composite instruments. +the be controlled from the meta instrument. +In the following example we will create two dummy instruments and a meta instruments. +>>>>>>> docs: add Meta instrument. All the instruments will live on a InstrumentServer. @@ -64,43 +67,46 @@ First we create an instrument: def setx(self, val): self.x=val -Then we create the composite instrument, this will hold any of the base +Then we create the meta instrument, this will hold any of the base instruments. -Since we want the composite instrument to be able to talk to the base instrumetns +Since we want the meta instrument to be able to talk to the base instruments we need to include a list of them as shared_kwargs. -.. note:: Every InstrumentServer needs to have identical shared_kwargs among all the instruments loaded there. That's because these args get loaded into the server when it's created, then passed on to each instrument that's loaded there during its construction on the server side. +.. note:: Every InstrumentServer needs to have identical shared_kwargs among all the instruments loaded there. That's because these args get loaded into the server when it's created, then passed on to each instrument that's loaded there during its construction on the server side. .. code:: python - class base1base2(Instrument): + class Meta(Instrument): shared_kwargs = ['instruments'] # Instruments will be a list of RemoteInstrument objects, which can be # given to a server on creation but not later on, so it needs to be # listed in shared_kwargs + def __init__(self, name, instruments=(), **kwargs): super().__init__(name, **kwargs) self._instrument_list = instruments - + self.no_instruments = len(instruments) for gate in range(len(self._instrument_list)): self.add_parameter('c%d' % gate, get_cmd=partial(self._get, gate=gate), set_cmd=partial(self._set, gate=gate)) - self.add_parameter("setBoth", set_cmd= partial(self._set_both)) + self.add_parameter("setBoth", set_cmd=partial(self._set_both)) + self.add_parameter("setBothAsync", set_cmd=partial(self._set_async)) def _set_both(self, value): for i in self._instrument_list: i.set('x', value) def _get(self, gate): - logging.debug('base1base2._get: %s' % (gate,)) - return self._instrument_list[gate].get('x') + value =self._instrument_list[gate].get('x') + logging.debug('Meta get gate %s' % (value)) + return value def _set(self, value, gate): - logging.debug('base1base2._set: gate %s, value %s' % (gate, value)) + logging.debug('Meta set gate %s @ value %s' % (gate, value)) i = self._instrument_list[gate] i.set('x', value) @@ -116,52 +122,54 @@ Let's put these babies on servers: That means that base1 and base2 don't know about eachoter. .. code:: python - composite_server_name - composite = base1base2(name='composite', server_name=composite_server_name, + + meta_server_name = "meta_server" + meta = Meta(name='meta', server_name=meta_server_name, instruments=[base1, base2]) -.. notes:: Composite instruments go on a different server from the - low-level instruments it references, because reaons. +.. notes:: Meta instruments go on a different server from the + low-level instruments it references, because reasons. -And now one case use the composite as expected: +And now one case use the meta as expected: .. code:: python - print("--- set composite --- ") - composite.c1.set(25) - print(composite.c1.get()) + print("--- set meta --- ") + meta.c1.set(25) + print(meta.c1.get()) >>> 25 print(base1.x.get()) >>> 25 print("--- set base --- ") base1.x.set(1) - print(composite.c1.get()) + print(meta.c1.get()) >>> 1 print(base1.x.get()) >>> 1 - - composite.setBoth(0) + meta.setBoth(0) print(base1.x.get()) >>> 0 print(base0.x.get()) >>> 0 -Async Composite -~~~~~~~~~~~~~~~ -Say you want to set two instruments at the same time. + +Async Meta +========== + +Say you want to set two instruments at the same time. You can use the following: -.. note:: the curernt architecture is so b0rken, you MUST one server per base instrument +.. note:: the curernt architecture is so that you MUST one server per base instrument -The base instrument classe stays the same, composite gets a new method f.ex: +The base instrument class stays the same, meta gets a new method f.ex: .. code:: python - class base1base2(Instrument): + class Meta(Instrument): shared_kwargs = ['instruments'] # Instruments will be a list of RemoteInstrument objects, which can be @@ -175,8 +183,8 @@ The base instrument classe stays the same, composite gets a new method f.ex: self.add_parameter('c%d' % gate, get_cmd=partial(self._get, gate=gate), set_cmd=partial(self._set, gate=gate)) - self.add_parameter("setBoth", set_cmd= partial(self._set_both)) - self.add_parameter("setBothAsync", set_cmd= partial(self._set_async)) + self.add_parameter("setBoth", set_cmd=partial(self._set_both)) + self.add_parameter("setBothAsync", set_cmd=partial(self._set_async)) def _set_both(self, value): for i in self._instrument_list: @@ -188,33 +196,26 @@ The base instrument classe stays the same, composite gets a new method f.ex: for i in self._instrument_list: job = executor.submit(partial(i.set, 'x'), value) jobs.append(job) - futures.wait(jobs) + futures.wait(jobs) def _get(self, gate): - logging.debug('base1base2._get: %s' % (gate,)) - return self._instrument_list[gate].get('x') + value =self._instrument_list[gate].get('x') + logging.debug('Meta get gate %s' % (value)) + return value def _set(self, value, gate): - logging.debug('base1base2._set: gate %s, value %s' % (gate, value)) + logging.debug('Meta set gate %s @ value %s' % (gate, value)) i = self._instrument_list[gate] i.set('x', value) - # note the different server names - # that's required - base0 = MyInstrument(name='zero', server_name="foo") - base1 = MyInstrument(name='one', server_name="bar") - composite_server_name = "composite_server" - composite = base1base2(name='composite', server_name=composite_server_name, - instruments=[base0, base1]) +This way: + >>> meta.setBothAsync(0) -This way: - >>> composite.setBothAsync(0) - -will set both instrument at the same time, say it takes 10 seconds per set, +will set both instrument at the same time, say it takes 10 seconds per set, then setting two things will take 10 seconds, not 20 seconds. - +For a complete working example see :download:`this example script <./meta.py>`. Avanced -------