1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
|
"""Test the embedding circuit module (qmix.circuit).
This module has a class (qmix.circuit.EmbeddingCircuit) that is used to
contain all the information about the embedding circuit (i.e., the Thevenin
voltages, the Thevenin impedances, the signal frequencies, the simulation
parameters, etc.). The tests for this class are pretty trivial because the
class is used like a struct.
"""
import tempfile
import numpy as np
import pytest
import qmix
from qmix.circuit import *
def test_saving_and_importing_methods():
"""Build an embedding circuit, save it to file, load it from file and make
sure that the two circuits match."""
# Build a quick circuit
cct = EmbeddingCircuit(2, 2)
# Voltages
cct.vt[1, 1] = 0.3
cct.vt[1, 2] = 0.1
cct.vt[2, 1] = 0.01
cct.vt[2, 2] = 0.001
# Impedances
cct.zt[1, 1] = 1. + 1j * 0.1
cct.zt[1, 2] = 2. + 1j * 0.2
cct.zt[2, 1] = 3. + 1j * 0.3
cct.zt[2, 2] = 4. + 1j * 0.4
# Signal names
cct.set_name('LO', 1, 1)
cct.set_name('RF', 2, 1)
# Save circuit to file
_, path = tempfile.mkstemp()
cct.save_info(path)
# Try printing
cct.print_info()
print(cct)
cct.name = 'Test'
print(cct)
# Try locking arrays
cct.lock()
with pytest.raises(ValueError):
cct.freq[1] = 0.5
cct.unlock()
# Read from file
cct2 = read_circuit(path)
# Compare values
np.testing.assert_array_equal(cct.freq, cct2.freq)
np.testing.assert_array_equal(cct.vt, cct2.vt)
np.testing.assert_array_equal(cct.zt, cct2.zt)
assert cct.num_f == cct2.num_f
assert cct.num_p == cct2.num_p
def test_power_settings():
"""Build an embedding circuit, set the available power, read the available
power and make sure the two values match."""
# Build quick circuit instance
cct = EmbeddingCircuit(2, 2, vgap=3e-3, rn=10, fgap=700e9)
# Voltages
cct.vt[1, 1] = 0.3
cct.vt[1, 2] = 0.1
cct.vt[2, 1] = 0.01
cct.vt[2, 2] = 0.001
# Impedances
cct.zt[1, 1] = 1. + 1j * 0.1
cct.zt[1, 2] = 2. + 1j * 0.2
cct.zt[2, 1] = 3. + 1j * 0.3
cct.zt[2, 2] = 4. + 1j * 0.4
# Signal names
cct.set_name('LO', 1, 1)
cct.set_name('RF', 2, 1)
# Set frequency
cct.set_freq(400e9, 1, units='Hz')
cct.set_freq(700e9, 2, units='Hz')
# Try setting frequency with different units
freq1 = cct.freq[1]
cct.set_freq(400e9 / sc.tera, 1, units='THz')
assert freq1 == pytest.approx(cct.freq[1])
cct.set_freq(400e9 / sc.giga, 1, units='GHz')
assert freq1 == pytest.approx(cct.freq[1])
cct.set_freq(400e9 / sc.mega, 1, units='MHz')
assert freq1 == pytest.approx(cct.freq[1])
cct.set_freq(freq1, 1, units='norm')
assert freq1 == pytest.approx(cct.freq[1])
cct.set_freq(freq1 * 3e-3, 1, units='V')
assert freq1 == pytest.approx(cct.freq[1])
cct.set_freq(freq1 * 3, 1, units='mV')
assert freq1 == pytest.approx(cct.freq[1])
# Also try non-sense units
with pytest.raises(ValueError):
cct.set_freq(1, 1, units='GV')
# Test normalized frequency (recall fgap=700e9)
assert cct.freq[2] == 1.
# Try printing
cct.print_info()
print(cct)
# Set available power (in units dBm) using set_available_power method
power_dbm = -50
cct.set_available_power(power_dbm, 1, 1, units='dBm')
# Read available power using available_power method
assert power_dbm == pytest.approx(cct.available_power(1, 1, units='dBm'))
assert power_dbm - 30. == pytest.approx(cct.available_power(1, 1, units='dBW'))
# Set available power (in units W) using set_available_power method
power_watts = 1e-9
cct.set_available_power(power_watts, 2, 1, units='W')
# Read available power using available_power method
assert power_watts == pytest.approx(cct.available_power(2, 1, units='W'))
assert power_watts == pytest.approx(cct.available_power(2, 1, units='mW') * sc.milli)
assert power_watts == pytest.approx(cct.available_power(2, 1, units='uW') * sc.micro)
assert power_watts == pytest.approx(cct.available_power(2, 1, units='nW') * sc.nano)
assert power_watts == pytest.approx(cct.available_power(2, 1, units='pW') * sc.pico)
assert power_watts == pytest.approx(cct.available_power(2, 1, units='fW') * sc.femto)
# Set the available power using different units
cct.set_available_power(power_watts / sc.milli, 2, 1, units='mW')
assert power_watts == pytest.approx(cct.available_power(2, 1, units='W'))
cct.set_available_power(power_watts / sc.micro, 2, 1, units='uW')
assert power_watts == pytest.approx(cct.available_power(2, 1, units='W'))
cct.set_available_power(power_watts / sc.nano, 2, 1, units='nW')
assert power_watts == pytest.approx(cct.available_power(2, 1, units='W'))
cct.set_available_power(power_watts / sc.pico, 2, 1, units='pW')
assert power_watts == pytest.approx(cct.available_power(2, 1, units='W'))
cct.set_available_power(power_watts / sc.femto, 2, 1, units='fW')
assert power_watts == pytest.approx(cct.available_power(2, 1, units='W'))
cct.set_available_power(10*np.log10(power_watts), 2, 1, units='dBW')
assert power_watts == pytest.approx(cct.available_power(2, 1, units='W'))
# Try using incorrect units with both methods
with pytest.raises(ValueError):
cct.set_available_power(power_watts, 1, 1, 'test')
with pytest.raises(ValueError):
cct.available_power(1, 1, 'test')
# Try getting power when real component is zero
cct.zt[1, 1] = 0. + 1j * 0.1
cct.vt[1, 1] = 1.
assert cct.available_power(1, 1, units='W') == 0.
def test_setting_alpha():
"""The EmbeddingCircuit class includes a method to set the drive level
of the SIS junction. Note: This is only an approximation. The method
assumes an impedance for the junction in order to do this.
Try setting the drive level to alpha=1, run a simulation to calculate
the junction voltage, and then check the value."""
# Use polynomial model for the response function
resp = qmix.respfn.RespFnPolynomial(50)
# Build embedding circuit
cct = EmbeddingCircuit(1, 1)
cct.freq[1] = 0.3
cct.zt[1, 1] = 0.3 - 1j * 0.3
# Set drive level to alpha=1
alpha_set = 1.
cct.set_alpha(alpha_set, 1, 1, zj=0.6)
# Harmonic balance to calculate junction voltage (vj)
vj = qmix.harmonic_balance.harmonic_balance(cct, resp)
# Check value in middle of first photon step
idx = np.abs(cct.vb - (1 - cct.freq[1] / 2)).argmin()
alpha = np.abs(vj[1, 1, idx]) / cct.freq[1]
assert 0.9 < alpha < 1.1 # only has to be approximate
def test_junction_properties():
"""Test junction properties."""
# Junction properties
rn = 10.
vgap = 3e-3
fgap = vgap * sc.e / sc.h
# Set Vgap
cct1 = EmbeddingCircuit(1, 1, vgap=vgap, rn=rn)
# Set fgap
cct2 = EmbeddingCircuit(1, 1, fgap=fgap, rn=rn)
# Check
assert cct1.vgap == cct2.vgap
assert cct1.fgap == cct2.fgap
if __name__ == "__main__":
test_setting_alpha()
|