Skip to content

Commit aa5e6db

Browse files
docs: add Meta instrument.
1 parent e76f95b commit aa5e6db

2 files changed

Lines changed: 159 additions & 50 deletions

File tree

docs/user/meta.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
" MWE of meta instrument (with debugging enabled) "
2+
import concurrent.futures as futures
3+
import logging
4+
import multiprocessing as mp
5+
import time
6+
7+
from functools import partial
8+
9+
from qcodes import Instrument
10+
11+
12+
# agreesive logging
13+
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s- %(message)s')
14+
15+
class MyInstrument(Instrument):
16+
17+
def __init__(self, name, **kwargs):
18+
super().__init__(name, **kwargs)
19+
self.value=0
20+
self.add_parameter('x', get_cmd=self.getx, set_cmd=self.setx)
21+
22+
def getx(self):
23+
return self.value
24+
25+
def setx(self, val):
26+
logging.debug("set {}".format(val))
27+
self.value = val
28+
# simulate delay 5 seconds
29+
time.sleep(5)
30+
logging.debug("done {}".format(val))
31+
return
32+
33+
34+
class Meta(Instrument):
35+
shared_kwargs = ['instruments']
36+
37+
# Instruments will be a list of RemoteInstrument objects, which can be
38+
# given to a server on creation but not later on, so it needs to be
39+
# listed in shared_kwargs
40+
41+
def __init__(self, name, instruments=(), **kwargs):
42+
super().__init__(name, **kwargs)
43+
self._instrument_list = instruments
44+
self.no_instruments = len(instruments)
45+
for gate in range(len(self._instrument_list)):
46+
self.add_parameter('c%d' % gate,
47+
get_cmd=partial(self._get, gate=gate),
48+
set_cmd=partial(self._set, gate=gate))
49+
50+
self.add_parameter("setBoth", set_cmd=partial(self._set_both))
51+
self.add_parameter("setBothAsync", set_cmd=partial(self._set_async))
52+
53+
def _set_both(self, value):
54+
for i in self._instrument_list:
55+
i.set('x', value)
56+
57+
def _set_async(self, value):
58+
with futures.ThreadPoolExecutor(max_workers=self.no_instruments+10) as executor:
59+
jobs = []
60+
for i in self._instrument_list:
61+
job = executor.submit(partial(i.set, 'x'), value)
62+
jobs.append(job)
63+
64+
futures.wait(jobs)
65+
66+
def _get(self, gate):
67+
value =self._instrument_list[gate].get('x')
68+
logging.debug('Meta get gate %s' % (value))
69+
return value
70+
71+
def _set(self, value, gate):
72+
logging.debug('Meta set gate %s @ value %s' % (gate, value))
73+
i = self._instrument_list[gate]
74+
i.set('x', value)
75+
76+
77+
78+
if __name__ == '__main__':
79+
80+
mp.set_start_method('spawn')
81+
82+
base1 = MyInstrument(name='zero', server_name="foo")
83+
base2 = MyInstrument(name='one', server_name="bar")
84+
85+
meta_server_name = "meta_server"
86+
meta = Meta(name='meta', server_name=meta_server_name,
87+
instruments=[base1, base2])
88+
89+
90+
print("--- set meta --- ")
91+
meta.c1.set(25)
92+
print(meta.c1.get())
93+
print(base1.x.get())
94+
95+
print("--- set base --- ")
96+
base1.x.set(1)
97+
print(meta.c1.get())
98+
print(base1.x.get())
99+
100+
101+
print("--- both --- ")
102+
meta.setBoth(0)
103+
print(base2.x.get())
104+
print(base1.x.get())
105+
106+
print("--- no block --- ")
107+
meta.setBothAsync(10)
108+
logging.debug(base1.x.get())

docs/user/tutorial.rst

Lines changed: 51 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ In this tutorial we'll walk through *****
1111
Writing a Driver
1212
----------------
1313

14-
Write a simple driver example
14+
Write a simple driver example
1515
with commented code
1616
- add parameter
1717
- add validator
@@ -35,12 +35,15 @@ Explain the mock mock
3535
.. todo:: missing
3636

3737

38-
Composite Instruments
39-
---------------------
40-
The concept of a composite instrument is that of having
38+
.. __metainstrument :
39+
40+
Meta Instruments
41+
---------------------
42+
The concept of a meta instrument is that of having
4143
two separate Instrument, real or virtual, whose actions can
42-
the be controlled from the composite instrument.
43-
In the following example we will create two dummy instruments and a composite instruments.
44+
the be controlled from the meta instrument.
45+
In the following example we will create two dummy instruments and a meta instruments.
46+
>>>>>>> docs: add Meta instrument.
4447
All the instruments will live on a InstrumentServer.
4548

4649

@@ -64,43 +67,46 @@ First we create an instrument:
6467
def setx(self, val):
6568
self.x=val
6669
67-
Then we create the composite instrument, this will hold any of the base
70+
Then we create the meta instrument, this will hold any of the base
6871
instruments.
69-
Since we want the composite instrument to be able to talk to the base instrumetns
72+
Since we want the meta instrument to be able to talk to the base instruments
7073
we need to include a list of them as shared_kwargs.
7174

7275

73-
.. 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.
76+
.. 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.
7477

7578
.. code:: python
7679
77-
class base1base2(Instrument):
80+
class Meta(Instrument):
7881
shared_kwargs = ['instruments']
7982
8083
# Instruments will be a list of RemoteInstrument objects, which can be
8184
# given to a server on creation but not later on, so it needs to be
8285
# listed in shared_kwargs
86+
8387
def __init__(self, name, instruments=(), **kwargs):
8488
super().__init__(name, **kwargs)
8589
self._instrument_list = instruments
86-
90+
self.no_instruments = len(instruments)
8791
for gate in range(len(self._instrument_list)):
8892
self.add_parameter('c%d' % gate,
8993
get_cmd=partial(self._get, gate=gate),
9094
set_cmd=partial(self._set, gate=gate))
9195
92-
self.add_parameter("setBoth", set_cmd= partial(self._set_both))
96+
self.add_parameter("setBoth", set_cmd=partial(self._set_both))
97+
self.add_parameter("setBothAsync", set_cmd=partial(self._set_async))
9398
9499
def _set_both(self, value):
95100
for i in self._instrument_list:
96101
i.set('x', value)
97102
98103
def _get(self, gate):
99-
logging.debug('base1base2._get: %s' % (gate,))
100-
return self._instrument_list[gate].get('x')
104+
value =self._instrument_list[gate].get('x')
105+
logging.debug('Meta get gate %s' % (value))
106+
return value
101107
102108
def _set(self, value, gate):
103-
logging.debug('base1base2._set: gate %s, value %s' % (gate, value))
109+
logging.debug('Meta set gate %s @ value %s' % (gate, value))
104110
i = self._instrument_list[gate]
105111
i.set('x', value)
106112
@@ -116,52 +122,54 @@ Let's put these babies on servers:
116122
That means that base1 and base2 don't know about eachoter.
117123

118124
.. code:: python
119-
composite_server_name
120-
composite = base1base2(name='composite', server_name=composite_server_name,
125+
126+
meta_server_name = "meta_server"
127+
meta = Meta(name='meta', server_name=meta_server_name,
121128
instruments=[base1, base2])
122129
123-
.. notes:: Composite instruments go on a different server from the
124-
low-level instruments it references, because reaons.
130+
.. notes:: Meta instruments go on a different server from the
131+
low-level instruments it references, because reasons.
125132

126133

127-
And now one case use the composite as expected:
134+
And now one case use the meta as expected:
128135

129136
.. code:: python
130137
131-
print("--- set composite --- ")
132-
composite.c1.set(25)
133-
print(composite.c1.get())
138+
print("--- set meta --- ")
139+
meta.c1.set(25)
140+
print(meta.c1.get())
134141
>>> 25
135142
print(base1.x.get())
136143
>>> 25
137144
138145
print("--- set base --- ")
139146
base1.x.set(1)
140-
print(composite.c1.get())
147+
print(meta.c1.get())
141148
>>> 1
142149
print(base1.x.get())
143150
>>> 1
144151
145-
146-
composite.setBoth(0)
152+
meta.setBoth(0)
147153
print(base1.x.get())
148154
>>> 0
149155
print(base0.x.get())
150156
>>> 0
151157
152158
153-
Async Composite
154-
~~~~~~~~~~~~~~~
155-
Say you want to set two instruments at the same time.
159+
160+
Async Meta
161+
==========
162+
163+
Say you want to set two instruments at the same time.
156164
You can use the following:
157165

158-
.. note:: the curernt architecture is so b0rken, you MUST one server per base instrument
166+
.. note:: the curernt architecture is so that you MUST one server per base instrument
159167

160-
The base instrument classe stays the same, composite gets a new method f.ex:
168+
The base instrument class stays the same, meta gets a new method f.ex:
161169

162170
.. code:: python
163171
164-
class base1base2(Instrument):
172+
class Meta(Instrument):
165173
shared_kwargs = ['instruments']
166174
167175
# 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:
175183
self.add_parameter('c%d' % gate,
176184
get_cmd=partial(self._get, gate=gate),
177185
set_cmd=partial(self._set, gate=gate))
178-
self.add_parameter("setBoth", set_cmd= partial(self._set_both))
179-
self.add_parameter("setBothAsync", set_cmd= partial(self._set_async))
186+
self.add_parameter("setBoth", set_cmd=partial(self._set_both))
187+
self.add_parameter("setBothAsync", set_cmd=partial(self._set_async))
180188
181189
def _set_both(self, value):
182190
for i in self._instrument_list:
@@ -188,33 +196,26 @@ The base instrument classe stays the same, composite gets a new method f.ex:
188196
for i in self._instrument_list:
189197
job = executor.submit(partial(i.set, 'x'), value)
190198
jobs.append(job)
191-
futures.wait(jobs)
199+
futures.wait(jobs)
192200
193201
def _get(self, gate):
194-
logging.debug('base1base2._get: %s' % (gate,))
195-
return self._instrument_list[gate].get('x')
202+
value =self._instrument_list[gate].get('x')
203+
logging.debug('Meta get gate %s' % (value))
204+
return value
196205
197206
def _set(self, value, gate):
198-
logging.debug('base1base2._set: gate %s, value %s' % (gate, value))
207+
logging.debug('Meta set gate %s @ value %s' % (gate, value))
199208
i = self._instrument_list[gate]
200209
i.set('x', value)
201210
202-
# note the different server names
203-
# that's required
204-
base0 = MyInstrument(name='zero', server_name="foo")
205-
base1 = MyInstrument(name='one', server_name="bar")
206211
207-
composite_server_name = "composite_server"
208-
composite = base1base2(name='composite', server_name=composite_server_name,
209-
instruments=[base0, base1])
212+
This way:
213+
>>> meta.setBothAsync(0)
210214

211-
This way:
212-
>>> composite.setBothAsync(0)
213-
214-
will set both instrument at the same time, say it takes 10 seconds per set,
215+
will set both instrument at the same time, say it takes 10 seconds per set,
215216
then setting two things will take 10 seconds, not 20 seconds.
216217

217-
218+
For a complete working example see :download:`this example script <./meta.py>`.
218219

219220
Avanced
220221
-------

0 commit comments

Comments
 (0)