This page was generated from docs/examples/driver_examples/Qcodes example with Oxford Mercury iPS.ipynb. Interactive online version: Binder badge.

QCoDeS Example with Mercury iPS

Initial instantiation/connection

[1]:
from time import sleep

from qcodes.instrument_drivers.oxford import OxfordMercuryiPS
[2]:
# Note that the MercuryiPS_VISA is a VISA instrument using
# a socket connection. The VISA resource name therefore
# contains the port number and the word 'SOCKET'
mips = OxfordMercuryiPS('mips', 'TCPIP0::192.168.15.106::7020::SOCKET')
Connected to: OXFORD INSTRUMENTS MERCURY IPS (serial:170150002, firmware:2.6.04.000) in 0.15s

Basic driver idea

The driver mainly deals with field values in Tesla. The driver is aware of the field values in three coordinate systems, cartesian, spherical, and cylindrical. The driver thus exposes the field coordinates x, y, z, phi, theta, rho, and r. Each coordinate comes in two versions: target and measured.

The idea is that the magnetic field is always changed in two steps; first a target is set, then the magnet is asked to ramp to said target.

Safe regions

In addition to the safety limits baked in to the physical instrument, the driver can accept a safety limit function provided by the user. The function checks - upon receiving a new field target - whether the target is inside an allowed region.

The limit function must take input arguments Bx, By, Bz (in Tesla) and return a boolean that tells us whether that field value is safe.

[3]:
# example: the safe region is a sphere
import numpy as np


def spherical_limit(x, y, z):
    """
    Safe region is a sphere of radius 1 T
    """
    return np.sqrt(x**2 + y**2 + z**2) <= 1


# assign the limit function (this can also be done at init)
mips.set_new_field_limits(spherical_limit)

Two different ramps

The driver can perfom the ramp in two different ways: simultaneous ramping or safe ramping.

When simultaneously ramping, all three field components are ramped at the same time. This method is non-blocking, and it is thus possible to query the field while it is ramping. The method does, however, not guarantee that the field stays inside the allowed region during the ramp. If the different axes have different ramp speeds, this is a real risk.

When safely ramping, all field components that are ramped towards the origin are ramped before those who are ramped away from the origin. The ramp is thus sequential and blocking, but if the safe region is convex (and contains the origin), you are guaranteed the the field never exceeds the safe region.

Parameter overview

[4]:
mips.print_readable_snapshot(update=True)
mips:
        parameter     value
--------------------------------------------------------------------------------
IDN            :        {'model': 'MERCURY IPS', 'vendor': 'OXFORD INSTRUMENTS', 'se...
phi_measured   :        -0 (T)
phi_target     :        0 (degres)
r_measured     :        0.1 (T)
r_target       :        0.1 (T)
rho_measured   :        0.1 (T)
rho_target     :        0.1 (T)
theta_measured :        90 (T)
theta_target   :        90 (degrees)
timeout        :        5 (s)
x_measured     :        0.1 (T)
x_target       :        0.1 (T)
y_measured     :        -0 (T)
y_target       :        0 (T)
z_measured     :        -0 (T)
z_target       :        -0 (T)
mips_GRPX:
        parameter         value
--------------------------------------------------------------------------------
ATOB               :    59.451 (A/T)
current            :    5.945 (A)
current_persistent :    5.945 (A)
current_ramp_rate  :    0.59451 (A/s)
current_target     :    0 (A)
field              :    0.1 (T)
field_persistent   :    0.1 (T)
field_ramp_rate    :    0.01 (T/s)
field_target       :    0 (T)
ramp_status        :    TO SET
voltage            :    0.0023 (V)
mips_GRPY:
        parameter         value
--------------------------------------------------------------------------------
ATOB               :    59.301 (A/T)
current            :    -0.0005 (A)
current_persistent :    -0.0005 (A)
current_ramp_rate  :    0.59301 (A/s)
current_target     :    0 (A)
field              :    -0 (T)
field_persistent   :    -0 (T)
field_ramp_rate    :    0.01 (T/s)
field_target       :    0 (T)
ramp_status        :    HOLD
voltage            :    -0.0002 (V)
mips_GRPZ:
        parameter         value
--------------------------------------------------------------------------------
ATOB               :    18.168 (A/T)
current            :    0.0002 (A)
current_persistent :    0.0002 (A)
current_ramp_rate  :    0.18168 (A/s)
current_target     :    0 (A)
field              :    0 (T)
field_persistent   :    0 (T)
field_ramp_rate    :    0.01 (T/s)
field_target       :    0 (T)
ramp_status        :    HOLD
voltage            :    0.0003 (V)

Ramp examples

First example: invalid targets

[5]:
mips.x_target(1)  # so far, so good
try:
    mips.y_target(0.5)  # this takes us out of the unit sphere
except ValueError:
    print("Can not set that")
Can not set that
[6]:
# reset and try in a different coordinate system
mips.x_target(0)
try:
    mips.r_target(1.1)
except ValueError:
    print("Can not set that")
Can not set that

Second example: simul ramps to the origin

First we ramp the field to Bx = 1, By = 0, Bz = 0, then rotate out to thea=46, phi=30, then finally ramp it down to zero while measuring r, theta, and phi.

STEP A

[7]:
mips.GRPX.field_ramp_rate(0.01)
mips.GRPY.field_ramp_rate(0.01)
mips.GRPZ.field_ramp_rate(0.01)

mips.x_target(0.1)
mips.y_target(0)
mips.z_target(0)

mips.ramp(mode='simul')

# since simul mode is non-blocking,
# we can read out during the ramp
while mips.is_ramping():
    print(f'Ramping X to {mips.x_target()} T, now at {mips.x_measured()} T')
    sleep(1)
sleep(1)
print(f'Done ramping, now at {mips.x_measured()} T')
Ramping X to 0.1 T, now at 0.1 T
Ramping X to 0.1 T, now at 0.1 T
Done ramping, now at 0.1 T

STEP B

Note that since the magnet itself has no notion of any other coordinate system than cartesian coordinates, it does NOT follow a path where r is constant. The user must MANUALLY ensure to break up a ramp where r is meant to be constant into sufficiently many small steps.

[8]:
mips.theta_target(45)
mips.phi_target(30)
mips.r_target(0.1)

mips.ramp(mode='simul')

while mips.is_ramping():
    print(f"Ramping... r: {mips.r_measured():.6f} T, "
          f"theta: {mips.theta_measured():.2f}, "
          f"phi: {mips.phi_measured():.2f}")
    sleep(1)
print(f"Done... r: {mips.r_measured():.6f} T, "
      f"theta: {mips.theta_measured():.2f}, "
      f"phi: {mips.phi_measured():.2f}")
Ramping... r: 0.100000 T, theta: 90.00, phi: -0.00
Ramping... r: 0.100000 T, theta: 90.00, phi: 0.00
Ramping... r: 0.099000 T, theta: 90.00, phi: 0.00
Ramping... r: 0.084369 T, theta: 82.86, phi: 11.52
Ramping... r: 0.076622 T, theta: 75.26, phi: 20.29
Ramping... r: 0.077474 T, theta: 67.38, phi: 27.21
Ramping... r: 0.080937 T, theta: 61.35, phi: 28.87
Ramping... r: 0.087963 T, theta: 53.60, phi: 29.53
Ramping... r: 0.093565 T, theta: 49.15, phi: 29.83
Ramping... r: 0.097914 T, theta: 46.26, phi: 29.94
Ramping... r: 0.099167 T, theta: 45.51, phi: 29.94
Ramping... r: 0.099729 T, theta: 45.14, phi: 29.98
Ramping... r: 0.099809 T, theta: 45.06, phi: 29.98
Ramping... r: 0.099915 T, theta: 45.04, phi: 30.05
Ramping... r: 0.099985 T, theta: 45.00, phi: 30.05
Done... r: 0.099985 T, theta: 45.00, phi: 30.05

STEP C

[9]:
mips.theta_target(45)
mips.phi_target(30)
mips.r_target(0)

mips.ramp(mode='simul')

# since simul mode is non-blocking,
# we can read out during the ramp
while mips.is_ramping():
    print(f"Ramping... r: {mips.r_measured():.6f} T, "
          f"theta: {mips.theta_measured():.2f}, "
          f"phi: {mips.phi_measured():.2f}")
    sleep(1)
print(f"Done... r: {mips.r_measured():.6f} T, "
      f"theta: {mips.theta_measured():.2f}, "
      f"phi: {mips.phi_measured():.2f}")
Ramping... r: 0.099985 T, theta: 45.00, phi: 30.05
Ramping... r: 0.099985 T, theta: 45.00, phi: 30.05
Ramping... r: 0.099985 T, theta: 45.00, phi: 30.05
Ramping... r: 0.079511 T, theta: 36.45, phi: 25.40
Ramping... r: 0.060216 T, theta: 31.76, phi: 17.06
Ramping... r: 0.045938 T, theta: 27.09, phi: 8.25
Ramping... r: 0.033194 T, theta: 18.38, phi: 6.58
Ramping... r: 0.021813 T, theta: 8.91, phi: 9.46
Ramping... r: 0.009555 T, theta: 6.13, phi: 11.31
Ramping... r: 0.002929 T, theta: 8.09, phi: 14.04
Ramping... r: 0.001315 T, theta: 8.75, phi: 0.00
Ramping... r: 0.000608 T, theta: 9.46, phi: 0.00
Ramping... r: 0.000200 T, theta: 0.00, phi: 0.00
Ramping... r: 0.000100 T, theta: 0.00, phi: 0.00
Ramping... r: 0.000100 T, theta: 0.00, phi: 0.00
Ramping... r: 0.000000 T, theta: 0.00, phi: 0.00
Ramping... r: 0.000000 T, theta: 0.00, phi: 180.00
Ramping... r: 0.000000 T, theta: 0.00, phi: -180.00
Ramping... r: 0.000000 T, theta: 0.00, phi: -180.00
Ramping... r: 0.000000 T, theta: 0.00, phi: -0.00
Ramping... r: 0.000000 T, theta: 0.00, phi: -0.00
Ramping... r: 0.000000 T, theta: 0.00, phi: -0.00
Ramping... r: 0.000000 T, theta: 0.00, phi: -0.00
Ramping... r: 0.000000 T, theta: 0.00, phi: -180.00
Ramping... r: 0.000000 T, theta: 0.00, phi: 180.00
Ramping... r: 0.000000 T, theta: 0.00, phi: 180.00
Ramping... r: 0.000100 T, theta: 180.00, phi: -180.00
Ramping... r: 0.000000 T, theta: 0.00, phi: -0.00
Ramping... r: 0.000000 T, theta: 0.00, phi: -0.00
Ramping... r: 0.000000 T, theta: 0.00, phi: 0.00
Ramping... r: 0.000000 T, theta: 0.00, phi: 0.00
Ramping... r: 0.000000 T, theta: 0.00, phi: 180.00
Ramping... r: 0.000100 T, theta: 180.00, phi: 180.00
Ramping... r: 0.000000 T, theta: 0.00, phi: 180.00
Ramping... r: 0.000100 T, theta: 180.00, phi: 180.00
Ramping... r: 0.000000 T, theta: 0.00, phi: 0.00
Ramping... r: 0.000000 T, theta: 0.00, phi: -0.00
Ramping... r: 0.000000 T, theta: 0.00, phi: 0.00
Ramping... r: 0.000000 T, theta: 0.00, phi: 0.00
Ramping... r: 0.000000 T, theta: 0.00, phi: 180.00
Done... r: 0.000000 T, theta: 0.00, phi: 0.00

Third example: safe ramp away from the origin

At the origin, we can not meaningfully measure what theta and phi is, but the target values are persistent.

If we ramp up again and measure, we should thus get back to our target values. We use blocking safe ramp for this (just to also test/show a blocking ramp).

[10]:
mips.r_target(0.05)

mips.ramp(mode='safe')

print('Ramped back out again.')
print(f'Field values are: theta: {mips.theta_measured()}, phi: {mips.phi_measured()}')
Ramped back out again.
Field values are: theta: 44.95982350797425, phi: 30.046435246810503

That’s it for now! Happy sweeping.

[11]:
# sweep back down for good measures
mips.x_target(0)
mips.y_target(0)
mips.z_target(0)

mips.ramp(mode='safe')

mips.close()