simcats 1.1.0__py3-none-any.whl → 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- simcats/__init__.py +4 -3
- simcats/_default_configs.py +129 -13
- simcats/_simulation.py +451 -69
- simcats/config_samplers/_GaAs_v1_random_variations_v3_config_sampler.py +1059 -0
- simcats/config_samplers/__init__.py +9 -0
- simcats/distortions/_distortion_interfaces.py +1 -1
- simcats/distortions/_dot_jumps.py +8 -6
- simcats/distortions/_random_telegraph_noise.py +4 -4
- simcats/distortions/_transition_blurring.py +5 -5
- simcats/distortions/_white_noise.py +2 -2
- simcats/ideal_csd/geometric/_generate_lead_transition_mask.py +3 -3
- simcats/ideal_csd/geometric/_get_electron_occupation.py +5 -5
- simcats/ideal_csd/geometric/_ideal_csd_geometric.py +5 -5
- simcats/ideal_csd/geometric/_ideal_csd_geometric_class.py +9 -9
- simcats/ideal_csd/geometric/_tct_bezier.py +5 -5
- simcats/sensor/__init__.py +10 -6
- simcats/sensor/{_generic_sensor.py → _sensor_generic.py} +1 -1
- simcats/sensor/_sensor_interface.py +164 -11
- simcats/sensor/_sensor_rise_glf.py +229 -0
- simcats/sensor/_sensor_scan_sensor_generic.py +929 -0
- simcats/sensor/barrier_function/__init__.py +9 -0
- simcats/sensor/barrier_function/_barrier_function_glf.py +280 -0
- simcats/sensor/barrier_function/_barrier_function_interface.py +43 -0
- simcats/sensor/barrier_function/_barrier_function_multi_glf.py +157 -0
- simcats/sensor/deformation/__init__.py +9 -0
- simcats/sensor/deformation/_sensor_peak_deformation_circle.py +109 -0
- simcats/sensor/deformation/_sensor_peak_deformation_interface.py +65 -0
- simcats/sensor/deformation/_sensor_peak_deformation_linear.py +77 -0
- simcats/support_functions/__init__.py +11 -3
- simcats/support_functions/_generalized_logistic_function.py +146 -0
- simcats/support_functions/_linear_algebra.py +171 -0
- simcats/support_functions/_parameter_sampling.py +108 -19
- simcats/support_functions/_pixel_volt_transformation.py +24 -0
- simcats/support_functions/_reset_offset_mu_sens.py +43 -0
- {simcats-1.1.0.dist-info → simcats-2.0.0.dist-info}/METADATA +93 -29
- simcats-2.0.0.dist-info/RECORD +53 -0
- {simcats-1.1.0.dist-info → simcats-2.0.0.dist-info}/WHEEL +1 -1
- simcats-1.1.0.dist-info/RECORD +0 -37
- /simcats/sensor/{_gaussian_sensor_peak.py → _sensor_peak_gaussian.py} +0 -0
- /simcats/sensor/{_lorentzian_sensor_peak.py → _sensor_peak_lorentzian.py} +0 -0
- {simcats-1.1.0.dist-info → simcats-2.0.0.dist-info/licenses}/LICENSE +0 -0
- {simcats-1.1.0.dist-info → simcats-2.0.0.dist-info}/top_level.txt +0 -0
simcats/_simulation.py
CHANGED
|
@@ -2,19 +2,20 @@
|
|
|
2
2
|
This module contains the simulation class, that can be used to perform charge stability diagram (CSD) simulations.
|
|
3
3
|
Additionally, it provides default configurations for this class.
|
|
4
4
|
|
|
5
|
-
@author: f.hader
|
|
5
|
+
@author: f.hader, b.papajewski
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from copy import deepcopy
|
|
9
9
|
from functools import partial
|
|
10
|
-
from typing import Union, Tuple, Dict, List
|
|
10
|
+
from typing import Union, Tuple, Dict, List, Optional
|
|
11
|
+
import warnings
|
|
11
12
|
|
|
12
13
|
import numpy as np
|
|
13
14
|
|
|
14
15
|
from simcats.distortions import SensorResponseDistortionInterface, OccupationDistortionInterface, \
|
|
15
16
|
SensorPotentialDistortionInterface
|
|
16
17
|
from simcats.ideal_csd import IdealCSDInterface
|
|
17
|
-
from simcats.sensor import SensorInterface, SensorGeneric
|
|
18
|
+
from simcats.sensor import SensorInterface, SensorGeneric, SensorScanSensorInterface
|
|
18
19
|
|
|
19
20
|
__all__ = []
|
|
20
21
|
|
|
@@ -30,14 +31,16 @@ class Simulation:
|
|
|
30
31
|
"""
|
|
31
32
|
|
|
32
33
|
def __init__(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
self,
|
|
35
|
+
volt_limits_g1: Optional[np.ndarray] = None,
|
|
36
|
+
volt_limits_g2: Optional[np.ndarray] = None,
|
|
37
|
+
volt_limits_sensor_g1: Optional[np.ndarray] = None,
|
|
38
|
+
volt_limits_sensor_g2: Optional[np.ndarray] = None,
|
|
39
|
+
ideal_csd_config: Optional[IdealCSDInterface] = None,
|
|
40
|
+
sensor: SensorInterface = SensorGeneric(),
|
|
41
|
+
occupation_distortions: Optional[List[OccupationDistortionInterface]] = None,
|
|
42
|
+
sensor_potential_distortions: Optional[List[SensorPotentialDistortionInterface]] = None,
|
|
43
|
+
sensor_response_distortions: Optional[List[SensorResponseDistortionInterface]] = None,
|
|
41
44
|
):
|
|
42
45
|
"""
|
|
43
46
|
Initializes an object of the class to perform the simulation of charge stability diagrams (CSDs) for a double
|
|
@@ -48,37 +51,52 @@ class Simulation:
|
|
|
48
51
|
smaller voltages, the distortions is also applied in this direction.
|
|
49
52
|
|
|
50
53
|
Args:
|
|
51
|
-
volt_limits_g1 (
|
|
54
|
+
volt_limits_g1 (Optional[np.ndarray]): Voltage limits of (plunger) gate 1 (second-/x-axis). Defines the
|
|
52
55
|
range in which data can be queried during the simulation. If set to None, all voltage values are
|
|
53
56
|
allowed. This can potentially lead to problems, if the structures are not available for some regions
|
|
54
57
|
and no restriction is applied. Default is None. \n
|
|
55
58
|
Example: \n
|
|
56
59
|
[min_V1, max_V1]
|
|
57
|
-
volt_limits_g2 (
|
|
60
|
+
volt_limits_g2 (Optional[np.ndarray]): Voltage limits of (plunger) gate 2 (first-/y-axis). Defines the
|
|
58
61
|
range in which data can be queried during the simulation. If set to None, all voltage values are
|
|
59
62
|
allowed. This can potentially lead to problems, if the structures are not available for some regions
|
|
60
63
|
and no restriction is applied. Default is None. \n
|
|
61
64
|
Example: \n
|
|
62
65
|
[min_V2, max_V2]
|
|
63
|
-
|
|
64
|
-
|
|
66
|
+
volt_limits_sensor_g1 (Optional[np.ndarray]): Voltage limits of sensor gate 1 (second-/x-axis). Defines the
|
|
67
|
+
range in which data can be queried during the simulation. If set to None, all voltage values are
|
|
68
|
+
allowed. This can potentially lead to problems, if the structures are not available for some regions
|
|
69
|
+
and no restriction is applied. Default is None. \n
|
|
70
|
+
Example: \n
|
|
71
|
+
[min_V1, max_V1]
|
|
72
|
+
volt_limits_sensor_g2 (Optional[np.ndarray]): Voltage limits of sensor gate 2 (first-/y-axis). Defines the
|
|
73
|
+
range in which data can be queried during the simulation. If set to None, all voltage values are
|
|
74
|
+
allowed. This can potentially lead to problems, if the structures are not available for some regions
|
|
75
|
+
and no restriction is applied. Default is None. \n
|
|
76
|
+
Example: \n
|
|
77
|
+
[min_V2, max_V2]
|
|
78
|
+
ideal_csd_config (Optional[IdealCSDInterface]): Implementation of the IdealCSDInterface (from the ideal_csd
|
|
79
|
+
module). Used to generate the ideal CSD data during the simulation. Default is None.
|
|
65
80
|
sensor (SensorInterface): Implementation of the SensorInterface (from the sensor module). Used to calculate
|
|
66
81
|
the sensor potential & response based on ideal CSD data. Default is SensorGeneric().
|
|
67
|
-
occupation_distortions (
|
|
82
|
+
occupation_distortions (Optional[List[OccupationDistortionInterface]]): List of implementations of the
|
|
68
83
|
OccupationDistortionInterface. This distortion type affects the occupations and lead transitions of the
|
|
69
84
|
CSD (the "structure"). The supplied implementations are applied in the order they appear in the list.
|
|
70
85
|
Default is None.
|
|
71
|
-
sensor_potential_distortions (
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
sensor_response_distortions (
|
|
76
|
-
|
|
86
|
+
sensor_potential_distortions (Optional[List[SensorPotentialDistortionInterface]]): List of implementations
|
|
87
|
+
of the SensorPotentialDistortionInterface. This distortion type affects the sensor potential, which is
|
|
88
|
+
calculated based on the occupations and (plunger) gate voltages. The supplied implementations are
|
|
89
|
+
applied in the order they appear in the list. Default is None.
|
|
90
|
+
sensor_response_distortions (Optional[List[SensorResponseDistortionInterface]]): List of implementations of
|
|
91
|
+
the SensorResponseDistortionInterface. This distortions type affects the sensor response, which is
|
|
77
92
|
calculated based on the sensor potential. The supplied implementations are applied in the order they
|
|
78
93
|
appear in the list. Default is None.
|
|
79
94
|
"""
|
|
80
95
|
self.volt_limits_g1 = volt_limits_g1
|
|
81
96
|
self.volt_limits_g2 = volt_limits_g2
|
|
97
|
+
self.volt_limits_sensor_g1 = volt_limits_sensor_g1
|
|
98
|
+
self.volt_limits_sensor_g2 = volt_limits_sensor_g2
|
|
99
|
+
|
|
82
100
|
self.ideal_csd_config = ideal_csd_config
|
|
83
101
|
self.sensor = sensor
|
|
84
102
|
self.occupation_distortions = occupation_distortions
|
|
@@ -86,13 +104,15 @@ class Simulation:
|
|
|
86
104
|
self.sensor_response_distortions = sensor_response_distortions
|
|
87
105
|
|
|
88
106
|
def measure(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
107
|
+
self,
|
|
108
|
+
sweep_range_g1: np.ndarray,
|
|
109
|
+
sweep_range_g2: np.ndarray,
|
|
110
|
+
volt_sensor_g1: Optional[float] = None,
|
|
111
|
+
volt_sensor_g2: Optional[float] = None,
|
|
112
|
+
resolution: Union[int, np.ndarray] = np.array([100, 100]),
|
|
93
113
|
) -> Tuple[np.ndarray, np.ndarray, np.ndarray, Dict]:
|
|
94
114
|
"""
|
|
95
|
-
Simulates
|
|
115
|
+
Simulates CSD measurement for the specified voltage sweep(s) at the desired resolution. If two resolutions are
|
|
96
116
|
supplied, a 2D scan is performed, and if only one resolution is supplied, a 1D scan is performed.
|
|
97
117
|
|
|
98
118
|
Args:
|
|
@@ -102,6 +122,12 @@ class Simulation:
|
|
|
102
122
|
sweep_range_g2 (np.ndarray): Voltage sweep range of (plunger) gate 2 (first-/y-axis). \n
|
|
103
123
|
Example: \n
|
|
104
124
|
[min_V2, max_V2]
|
|
125
|
+
volt_sensor_g1 (Optional[float]): Voltage applied at sensor gate 1 (second-/x-axis). \n
|
|
126
|
+
Example: \n
|
|
127
|
+
[min_V1, max_V1]
|
|
128
|
+
volt_sensor_g2 (Optional[float]): Voltage applied at sensor gate 2 (first-/y-axis). \n
|
|
129
|
+
Example: \n
|
|
130
|
+
[min_V2, max_V2]
|
|
105
131
|
resolution (Union[int, np.ndarray]): The desired resolution (in pixels) for the two gates. If only one value
|
|
106
132
|
is supplied, a 1D sweep is performed. Then, both gates are swept simultaneously. Default is
|
|
107
133
|
np.array([100, 100]). \n
|
|
@@ -132,30 +158,58 @@ class Simulation:
|
|
|
132
158
|
# Check if voltage range is restricted. If it is restricted, check if the start & stop voltage
|
|
133
159
|
# of the scan are in the allowed range.
|
|
134
160
|
if (
|
|
135
|
-
|
|
136
|
-
|
|
161
|
+
self.__volt_limits_g1 is not None
|
|
162
|
+
and (
|
|
137
163
|
np.min(sweep_range_g1) < self.__volt_limits_g1[0]
|
|
138
164
|
or np.min(sweep_range_g1) > self.__volt_limits_g1[1]
|
|
139
165
|
or np.max(sweep_range_g1) < self.__volt_limits_g1[0]
|
|
140
166
|
or np.max(sweep_range_g1) > self.__volt_limits_g1[1]
|
|
141
|
-
|
|
142
|
-
|
|
167
|
+
)
|
|
168
|
+
or (
|
|
143
169
|
self.__volt_limits_g2 is not None
|
|
144
170
|
and (
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
171
|
+
np.min(sweep_range_g2) < self.__volt_limits_g2[0]
|
|
172
|
+
or np.min(sweep_range_g2) > self.__volt_limits_g2[1]
|
|
173
|
+
or np.max(sweep_range_g2) < self.__volt_limits_g2[0]
|
|
174
|
+
or np.max(sweep_range_g2) > self.__volt_limits_g2[1]
|
|
149
175
|
)
|
|
150
|
-
|
|
176
|
+
)
|
|
151
177
|
):
|
|
152
178
|
raise ValueError(
|
|
153
|
-
f"The voltages defined by sweep_range_g1 ({sweep_range_g1}) must be in the range of the limits defined"
|
|
179
|
+
f"The voltages defined by sweep_range_g1 ({sweep_range_g1}) must be in the range of the limits defined "
|
|
154
180
|
f"by volt_limits_g1 ({self.__volt_limits_g1}) and the voltages defined by sweep_range_g2 "
|
|
155
181
|
f"({sweep_range_g2}) must be in the range of the limits defined by volt_limits_g2 "
|
|
156
182
|
f"({self.__volt_limits_g2})."
|
|
157
183
|
)
|
|
158
184
|
|
|
185
|
+
if (
|
|
186
|
+
self.__volt_limits_sensor_g1 is not None
|
|
187
|
+
and (
|
|
188
|
+
volt_sensor_g1 < self.__volt_limits_sensor_g1[0]
|
|
189
|
+
or volt_sensor_g1 > self.__volt_limits_sensor_g1[1]
|
|
190
|
+
)
|
|
191
|
+
or (
|
|
192
|
+
self.__volt_limits_sensor_g2 is not None
|
|
193
|
+
and (
|
|
194
|
+
volt_sensor_g2 < self.__volt_limits_sensor_g2[0]
|
|
195
|
+
or volt_sensor_g2 > self.__volt_limits_sensor_g2[1]
|
|
196
|
+
)
|
|
197
|
+
)
|
|
198
|
+
):
|
|
199
|
+
raise ValueError(
|
|
200
|
+
f"The voltages defined by volt_sensor_g1 ({volt_sensor_g1}) must be in the range of the limits defined "
|
|
201
|
+
f"by volt_limits_sensor_g1 ({self.__volt_limits_sensor_g1}) and the voltages defined by volt_sensor_g2 "
|
|
202
|
+
f"({volt_sensor_g2}) must be in the range of the limits defined by volt_limits_sensor_g2 "
|
|
203
|
+
f"({self.__volt_limits_sensor_g2})."
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
if (volt_sensor_g1 is None) != (volt_sensor_g2 is None):
|
|
207
|
+
raise ValueError(
|
|
208
|
+
f"It is not permitted to pass only one of the two sensor gate voltages. Either both parameters "
|
|
209
|
+
f"volt_sensor_g1 ({volt_sensor_g1}) and volt_sensor_g2 ({volt_sensor_g2}) must be specified or both "
|
|
210
|
+
f"must be equal to None."
|
|
211
|
+
)
|
|
212
|
+
|
|
159
213
|
# check if the resolution has at most two entries
|
|
160
214
|
if not type(resolution) == int and len(resolution) > 2:
|
|
161
215
|
raise ValueError(
|
|
@@ -165,9 +219,9 @@ class Simulation:
|
|
|
165
219
|
|
|
166
220
|
# Check if one gate is kept at a fixed voltage if a 2D scan is requested. This is only possible in 1D scans.
|
|
167
221
|
if (
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
222
|
+
not type(resolution) == int
|
|
223
|
+
and len(resolution) == 2
|
|
224
|
+
and (sweep_range_g1[0] == sweep_range_g1[1] or sweep_range_g2[0] == sweep_range_g2[1])
|
|
171
225
|
):
|
|
172
226
|
raise ValueError(
|
|
173
227
|
f"At least one of the voltage ranges 'sweep_range_g1' and 'sweep_range_g2' defines a fixed voltage. "
|
|
@@ -203,21 +257,39 @@ class Simulation:
|
|
|
203
257
|
next distortions.
|
|
204
258
|
"""
|
|
205
259
|
occupations, lead_transitions = generate_csd(volt_limits_g1, volt_limits_g2, resolution)
|
|
206
|
-
noise_function(occupations, lead_transitions, volt_limits_g1, volt_limits_g2, generate_csd,
|
|
260
|
+
noise_function(occupations, lead_transitions, volt_limits_g1, volt_limits_g2, generate_csd,
|
|
261
|
+
freeze=True)
|
|
207
262
|
return occupations, lead_transitions
|
|
208
|
-
|
|
263
|
+
|
|
264
|
+
generate_csd = partial(generate_csd_with_noise, generate_csd=generate_csd,
|
|
265
|
+
noise_function=i.noise_function)
|
|
209
266
|
|
|
210
267
|
# calculate the sensor potential from the distorted occupations
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
268
|
+
if isinstance(self.__sensor, SensorScanSensorInterface):
|
|
269
|
+
# additionally pass sensor voltages if sensor is capable of sensor scans
|
|
270
|
+
potential = self.__sensor.sensor_potential(
|
|
271
|
+
occupations=occupations, volt_limits_g1=sweep_range_g1, volt_limits_g2=sweep_range_g2,
|
|
272
|
+
volt_limits_sensor_g1=volt_sensor_g1, volt_limits_sensor_g2=volt_sensor_g2
|
|
273
|
+
)
|
|
274
|
+
else:
|
|
275
|
+
potential = self.__sensor.sensor_potential(
|
|
276
|
+
occupations=occupations, volt_limits_g1=sweep_range_g1, volt_limits_g2=sweep_range_g2
|
|
277
|
+
)
|
|
214
278
|
|
|
215
279
|
# Add distortions to the sensor potential
|
|
216
280
|
if self.__sensor_potential_distortions is not None:
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
281
|
+
if occupations.ndim == 3 and potential.ndim == 2 or occupations.ndim == 2 and potential.ndim == 1:
|
|
282
|
+
for i in self.__sensor_potential_distortions:
|
|
283
|
+
potential = i.noise_function(
|
|
284
|
+
mu_sens=potential, volt_limits_g1=sweep_range_g1, volt_limits_g2=sweep_range_g2
|
|
285
|
+
)
|
|
286
|
+
# SensorScanSensor implementations have more than one potential that has to be distorted
|
|
287
|
+
elif occupations.ndim == 3 and potential.ndim == 3 or occupations.ndim == 2 and potential.ndim == 2:
|
|
288
|
+
for i in self.__sensor_potential_distortions:
|
|
289
|
+
for pot_num in range(potential.shape[0]):
|
|
290
|
+
potential[pot_num] = i.noise_function(
|
|
291
|
+
mu_sens=potential[pot_num], volt_limits_g1=sweep_range_g1, volt_limits_g2=sweep_range_g2
|
|
292
|
+
)
|
|
221
293
|
|
|
222
294
|
# Add the sensor function
|
|
223
295
|
csd = self.__sensor.sensor_response(potential)
|
|
@@ -229,10 +301,15 @@ class Simulation:
|
|
|
229
301
|
sensor_response=csd, volt_limits_g1=sweep_range_g1, volt_limits_g2=sweep_range_g2
|
|
230
302
|
)
|
|
231
303
|
|
|
232
|
-
# create a dictionary containing all the metadata
|
|
233
304
|
metadata = {
|
|
234
305
|
"sweep_range_g1": deepcopy(sweep_range_g1),
|
|
235
306
|
"sweep_range_g2": deepcopy(sweep_range_g2),
|
|
307
|
+
"volt_sensor_g1": deepcopy(volt_sensor_g1),
|
|
308
|
+
"volt_sensor_g2": deepcopy(volt_sensor_g2),
|
|
309
|
+
"volt_limits_g1": deepcopy(self.volt_limits_g1),
|
|
310
|
+
"volt_limits_g2": deepcopy(self.volt_limits_g2),
|
|
311
|
+
"volt_limits_sensor_g1": deepcopy(self.volt_limits_sensor_g1),
|
|
312
|
+
"volt_limits_sensor_g2": deepcopy(self.volt_limits_sensor_g2),
|
|
236
313
|
"resolution": deepcopy(resolution),
|
|
237
314
|
"ideal_csd_config": deepcopy(self.ideal_csd_config),
|
|
238
315
|
"sensor": deepcopy(self.sensor),
|
|
@@ -243,8 +320,251 @@ class Simulation:
|
|
|
243
320
|
|
|
244
321
|
return csd, occupations, lead_transitions, metadata
|
|
245
322
|
|
|
323
|
+
def measure_sensor_scan(
|
|
324
|
+
self,
|
|
325
|
+
sweep_range_sensor_g1: np.ndarray,
|
|
326
|
+
sweep_range_sensor_g2: np.ndarray,
|
|
327
|
+
volt_g1: Optional[float] = None,
|
|
328
|
+
volt_g2: Optional[float] = None,
|
|
329
|
+
resolution: Union[int, np.ndarray] = np.array([100, 100]),
|
|
330
|
+
) -> Tuple[np.ndarray, np.ndarray, np.ndarray, Dict]:
|
|
331
|
+
"""
|
|
332
|
+
Simulates the measurement of a sensor scan under the specified voltage sweep(s) at the desired resolution. If
|
|
333
|
+
two resolutions are supplied, a 2D scan is performed, and if only one resolution is supplied, a 1D scan is
|
|
334
|
+
performed.
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
sweep_range_sensor_g1 (np.ndarray): Voltage sweep range of sensor gate 1 (second-/x-axis). \n
|
|
338
|
+
Example: \n
|
|
339
|
+
[min_V1, max_V1]
|
|
340
|
+
sweep_range_sensor_g2 (np.ndarray): Voltage sweep range of sensor gate 2 (first-/y-axis). \n
|
|
341
|
+
Example: \n
|
|
342
|
+
[min_V2, max_V2]
|
|
343
|
+
volt_g1 (Optional[float]): Voltage applied at (plunger) gate 1 (second-/x-axis).
|
|
344
|
+
volt_g2 (Optional[float]): Voltage applied at (plunger) gate 2 (first-/y-axis).
|
|
345
|
+
resolution (Union[int, np.ndarray]): The desired resolution (in pixels) for the two gates. If only one value
|
|
346
|
+
is supplied, a 1D sweep is performed. Then, both gates are swept simultaneously. Default is
|
|
347
|
+
np.array([100, 100]). \n
|
|
348
|
+
Example: \n
|
|
349
|
+
[res_g1, res_g2]
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
Tuple[np.ndarray, np.ndarray, np.ndarray, Dict]: Numpy array of the measured sensor signal, that is called
|
|
353
|
+
sensor scan. Numpy array, the conductive area mask as a label mask that indicates the non-conductive area,
|
|
354
|
+
sensor oscillation regime and fully conductive area. Numpy array as a second label mask, that marks the
|
|
355
|
+
peaks of the Coulomb peaks as integers. Dictionary with metadata (all parameters of the system & the
|
|
356
|
+
measurement). The axes of the three arrays match to the supplied sweep range. If the sweep is f.e. performed
|
|
357
|
+
from high to low voltage, the values in the array are also arranged in that way (lowest index would then map
|
|
358
|
+
to the highest voltage). In general: axis 0 = y-axis = sensor_g2 = sensor_V2 and axis 1 = x-axis = sensor_g1
|
|
359
|
+
= sensor_V1.
|
|
360
|
+
"""
|
|
361
|
+
if not isinstance(self.__sensor, SensorScanSensorInterface):
|
|
362
|
+
raise ValueError(
|
|
363
|
+
"The current sensor does not support the measurement of sensor scans. Only sensors that implement the "
|
|
364
|
+
"SensorScanSensorInterface are supported."
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
# check if the sensor gate voltage ranges have the correct number of entries
|
|
368
|
+
if not (len(sweep_range_sensor_g1) == 2 and len(sweep_range_sensor_g2) == 2):
|
|
369
|
+
raise ValueError(
|
|
370
|
+
"The sweep ranges for the sensor gates g1 and g2 must consist of exactly 2 values each. At least "
|
|
371
|
+
f"one sweep range violates this."
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
# Check if sensor voltage range is restricted. If it is restricted, check if the start & stop voltage
|
|
375
|
+
# of the scan are in the allowed range.
|
|
376
|
+
if (
|
|
377
|
+
self.__volt_limits_sensor_g1 is not None
|
|
378
|
+
and (
|
|
379
|
+
np.min(sweep_range_sensor_g1) < self.__volt_limits_sensor_g1[0]
|
|
380
|
+
or np.min(sweep_range_sensor_g1) > self.__volt_limits_sensor_g1[1]
|
|
381
|
+
or np.max(sweep_range_sensor_g1) < self.__volt_limits_sensor_g1[0]
|
|
382
|
+
or np.max(sweep_range_sensor_g1) > self.__volt_limits_sensor_g1[1]
|
|
383
|
+
)
|
|
384
|
+
or (
|
|
385
|
+
self.__volt_limits_sensor_g2 is not None
|
|
386
|
+
and (
|
|
387
|
+
np.min(sweep_range_sensor_g2) < self.__volt_limits_sensor_g2[0]
|
|
388
|
+
or np.min(sweep_range_sensor_g2) > self.__volt_limits_sensor_g2[1]
|
|
389
|
+
or np.max(sweep_range_sensor_g2) < self.__volt_limits_sensor_g2[0]
|
|
390
|
+
or np.max(sweep_range_sensor_g2) > self.__volt_limits_sensor_g2[1]
|
|
391
|
+
)
|
|
392
|
+
)
|
|
393
|
+
):
|
|
394
|
+
raise ValueError(
|
|
395
|
+
f"The voltages defined by sweep_range_sensor_g1 ({sweep_range_sensor_g1}) must be in the range of the "
|
|
396
|
+
f"limits defined by volt_limits_sensor_g1 ({self.__volt_limits_sensor_g1}) and the voltages defined by "
|
|
397
|
+
f"sweep_range_sensor_g2 ({sweep_range_sensor_g2}) must be in the range of the limits defined by "
|
|
398
|
+
f"volt_limits_sensor_g2 ({self.__volt_limits_sensor_g2})."
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
# Check if one sensor gate is kept at a fixed voltage if a 2D scan is requested.
|
|
402
|
+
# This is only possible in 1D scans.
|
|
403
|
+
if (
|
|
404
|
+
not type(resolution) == int
|
|
405
|
+
and len(resolution) == 2
|
|
406
|
+
and (sweep_range_sensor_g1[0] == sweep_range_sensor_g1[1] or sweep_range_sensor_g2[0] ==
|
|
407
|
+
sweep_range_sensor_g2[1])
|
|
408
|
+
):
|
|
409
|
+
raise ValueError(
|
|
410
|
+
f"At least one of the voltage ranges 'sweep_range_sensor_g1' and 'sweep_range_sensor_g2' defines a "
|
|
411
|
+
f"fixed voltage. This is only supported for 1D sweeps (only one resolution), but two resolutions were "
|
|
412
|
+
f"specified."
|
|
413
|
+
)
|
|
414
|
+
if (
|
|
415
|
+
volt_g1 is not None
|
|
416
|
+
and self.__volt_limits_g1 is not None
|
|
417
|
+
and (
|
|
418
|
+
volt_g1 < self.__volt_limits_g1[0]
|
|
419
|
+
or volt_g1 > self.__volt_limits_g1[1]
|
|
420
|
+
)
|
|
421
|
+
or (
|
|
422
|
+
volt_g2 is not None
|
|
423
|
+
and self.__volt_limits_g2 is not None
|
|
424
|
+
and (
|
|
425
|
+
volt_g2 < self.__volt_limits_g2[0]
|
|
426
|
+
or volt_g2 > self.__volt_limits_g2[1]
|
|
427
|
+
)
|
|
428
|
+
)
|
|
429
|
+
):
|
|
430
|
+
raise ValueError(
|
|
431
|
+
f"The voltages defined by volt_g1 ({volt_g1}) must be in the range of the "
|
|
432
|
+
f"limits defined by volt_limits_g1 ({self.__volt_limits_g1}) and the voltages defined by "
|
|
433
|
+
f"volt_g2 ({volt_g2}) must be in the range of the limits defined by "
|
|
434
|
+
f"volt_limits_g2 ({self.__volt_limits_g2})."
|
|
435
|
+
)
|
|
436
|
+
if self.ideal_csd_config and ((volt_g1 is None) or (volt_g2 is None)):
|
|
437
|
+
warnings.warn(
|
|
438
|
+
f"If an ideal_csd_config is specified, it is assumed that the two voltages volt_g1 ({volt_g2}) and "
|
|
439
|
+
f"volt_g2 ({volt_g2}) are specified and are not equal to None. \n"
|
|
440
|
+
f"The ideal_csd_config is ignored and an occupation of 0 is assumed."
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
if (volt_g1 is None) != (volt_g2 is None):
|
|
444
|
+
raise ValueError(
|
|
445
|
+
f"It is not permitted to transfer only one of the two double dot (plunger) gate voltages. Both "
|
|
446
|
+
f"parameters volt_g1 ({volt_g1}) and volt_g2 ({volt_g2}) must be specified or be equal to None."
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
# check if the resolution has at most two entries
|
|
450
|
+
if not type(resolution) == int and len(resolution) > 2:
|
|
451
|
+
raise ValueError(
|
|
452
|
+
f"The specified resolution ({resolution}) has more than two entries. The resolution must either be a "
|
|
453
|
+
f"single value (for 1D scans) or contain two entries (for 2D scans)."
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
# Check if one resolution is 1 (or smaller) for a 2D scan. A resolution of 1 indicates that the corresponding
|
|
457
|
+
# gate is kept at a fixed voltage. This only makes sense for a 1D scan
|
|
458
|
+
if not type(resolution) == int and len(resolution) == 2 and (resolution[0] <= 1 or resolution[1] <= 1):
|
|
459
|
+
raise ValueError(
|
|
460
|
+
f"The specified resolution ({resolution}) indicates that a 2D scan should be performed, but at least "
|
|
461
|
+
f"one of the two entries is smaller or equal to 1. A resolution of 1 means that the corresponding gate "
|
|
462
|
+
f"is not swept. Thus, it describes a 1D scan of a single gate. Please specify just one resolution and "
|
|
463
|
+
f"a fixed voltage for the corresponding gate, to perform a single gate sweep."
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
if self.ideal_csd_config is None or volt_g1 is None or volt_g2 is None:
|
|
467
|
+
if type(resolution) == int:
|
|
468
|
+
occupations = np.zeros((resolution, 2))
|
|
469
|
+
else:
|
|
470
|
+
occupations = np.zeros((resolution[1], resolution[0], 2))
|
|
471
|
+
undistorted_occupations = deepcopy(occupations)
|
|
472
|
+
else:
|
|
473
|
+
# setup clean data function pointer
|
|
474
|
+
generate_csd = deepcopy(self.__ideal_csd_config.get_csd_data)
|
|
475
|
+
|
|
476
|
+
# Perform simulation
|
|
477
|
+
occupations_csd, lead_transitions = generate_csd(
|
|
478
|
+
volt_limits_g1=(volt_g1, volt_g1), volt_limits_g2=(volt_g2, volt_g2), resolution=(1)
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
if len(resolution) == 1:
|
|
482
|
+
resolution = int(resolution[0])
|
|
483
|
+
|
|
484
|
+
if type(resolution) == int:
|
|
485
|
+
occupations = np.ones((resolution, 2))
|
|
486
|
+
occupations[:, 0] = occupations[:, 0] * occupations_csd[0, 0]
|
|
487
|
+
occupations[:, 1] = occupations[:, 1] * occupations_csd[0, 1]
|
|
488
|
+
else:
|
|
489
|
+
occupations = np.ones((resolution[1], resolution[0], 2))
|
|
490
|
+
occupations[:, :, 0] = occupations[:,:,0] * occupations_csd[0, 0]
|
|
491
|
+
occupations[:, :, 1] = occupations[:,:,1] * occupations_csd[0, 1]
|
|
492
|
+
|
|
493
|
+
undistorted_occupations = deepcopy(occupations)
|
|
494
|
+
|
|
495
|
+
# could apply occupation distortions here, if sweeping the DQD & Sensor gates simultaneously is implemented
|
|
496
|
+
|
|
497
|
+
# Calculate the sensor potential from the distorted occupations
|
|
498
|
+
potential = self.__sensor.sensor_potential(
|
|
499
|
+
occupations=occupations, volt_limits_g1=volt_g1, volt_limits_g2=volt_g2,
|
|
500
|
+
volt_limits_sensor_g1=sweep_range_sensor_g1, volt_limits_sensor_g2=sweep_range_sensor_g2
|
|
501
|
+
)
|
|
502
|
+
|
|
503
|
+
# Calculate the potential using the undistorted occupations for the calculation of the labels
|
|
504
|
+
potential_undistorted_occ = self.__sensor.sensor_potential(
|
|
505
|
+
occupations=undistorted_occupations, volt_limits_g1=volt_g1, volt_limits_g2=volt_g2,
|
|
506
|
+
volt_limits_sensor_g1=sweep_range_sensor_g1, volt_limits_sensor_g2=sweep_range_sensor_g2
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
# Calculate the labels
|
|
510
|
+
conductive_mask, coulomb_peak_mask = self.__sensor.get_sensor_scan_labels(
|
|
511
|
+
volt_limits_g1=volt_g1,
|
|
512
|
+
volt_limits_g2=volt_g2,
|
|
513
|
+
volt_limits_sensor_g1=sweep_range_sensor_g1,
|
|
514
|
+
volt_limits_sensor_g2=sweep_range_sensor_g2,
|
|
515
|
+
potential=potential_undistorted_occ
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
# Add distortions to the sensor potential
|
|
519
|
+
if self.__sensor_potential_distortions is not None:
|
|
520
|
+
if occupations.ndim == 3 and potential.ndim == 2 or occupations.ndim == 2 and potential.ndim == 1:
|
|
521
|
+
for i in self.__sensor_potential_distortions:
|
|
522
|
+
potential = i.noise_function(
|
|
523
|
+
mu_sens=potential,
|
|
524
|
+
volt_limits_g1=sweep_range_sensor_g1,
|
|
525
|
+
volt_limits_g2=sweep_range_sensor_g2
|
|
526
|
+
)
|
|
527
|
+
elif occupations.ndim == 3 and potential.ndim == 3 or occupations.ndim == 2 and potential.ndim == 2:
|
|
528
|
+
for i in self.__sensor_potential_distortions:
|
|
529
|
+
for pot_num in range(potential.shape[0]):
|
|
530
|
+
potential[pot_num] = i.noise_function(
|
|
531
|
+
mu_sens=potential[pot_num],
|
|
532
|
+
volt_limits_g1=sweep_range_sensor_g1,
|
|
533
|
+
volt_limits_g2=sweep_range_sensor_g2
|
|
534
|
+
)
|
|
535
|
+
|
|
536
|
+
# Add the sensor function
|
|
537
|
+
scan = self.__sensor.sensor_response(potential)
|
|
538
|
+
|
|
539
|
+
# add distortions to the sensor signal
|
|
540
|
+
if self.__sensor_response_distortions is not None:
|
|
541
|
+
for i in self.__sensor_response_distortions:
|
|
542
|
+
scan = i.noise_function(
|
|
543
|
+
sensor_response=scan, volt_limits_g1=sweep_range_sensor_g1, volt_limits_g2=sweep_range_sensor_g1
|
|
544
|
+
)
|
|
545
|
+
|
|
546
|
+
# create a dictionary containing all the metadata
|
|
547
|
+
metadata = {
|
|
548
|
+
"sweep_range_sensor_g1": deepcopy(sweep_range_sensor_g1),
|
|
549
|
+
"sweep_range_sensor_g2": deepcopy(sweep_range_sensor_g2),
|
|
550
|
+
"volt_g1": deepcopy(volt_g1),
|
|
551
|
+
"volt_g2": deepcopy(volt_g2),
|
|
552
|
+
"volt_limits_g1": deepcopy(self.volt_limits_g1),
|
|
553
|
+
"volt_limits_g2": deepcopy(self.volt_limits_g2),
|
|
554
|
+
"volt_limits_sensor_g1": deepcopy(self.volt_limits_sensor_g1),
|
|
555
|
+
"volt_limits_sensor_g2": deepcopy(self.volt_limits_sensor_g2),
|
|
556
|
+
"resolution": deepcopy(resolution),
|
|
557
|
+
"ideal_csd_config": deepcopy(self.ideal_csd_config),
|
|
558
|
+
"sensor": deepcopy(self.sensor),
|
|
559
|
+
"occupation_distortions": deepcopy(self.occupation_distortions),
|
|
560
|
+
"sensor_potential_distortions": deepcopy(self.sensor_potential_distortions),
|
|
561
|
+
"sensor_response_distortions": deepcopy(self.sensor_response_distortions),
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
return scan, conductive_mask, coulomb_peak_mask, metadata
|
|
565
|
+
|
|
246
566
|
@property
|
|
247
|
-
def volt_limits_g1(self) ->
|
|
567
|
+
def volt_limits_g1(self) -> Optional[np.ndarray]:
|
|
248
568
|
"""
|
|
249
569
|
Returns the current plunger 1 voltage limit configuration of the system. This configuration can then be adjusted
|
|
250
570
|
and is directly used as new configuration, as the object is returned as call by reference
|
|
@@ -255,7 +575,7 @@ class Simulation:
|
|
|
255
575
|
return self.__volt_limits_g1
|
|
256
576
|
|
|
257
577
|
@volt_limits_g1.setter
|
|
258
|
-
def volt_limits_g1(self, volt_limits_g1:
|
|
578
|
+
def volt_limits_g1(self, volt_limits_g1: Optional[np.ndarray]) -> None:
|
|
259
579
|
"""
|
|
260
580
|
Updates the plunger 1 voltage limit configuration of the system according to the supplied values.
|
|
261
581
|
|
|
@@ -264,7 +584,7 @@ class Simulation:
|
|
|
264
584
|
"""
|
|
265
585
|
# check datatype of voltage limits
|
|
266
586
|
if not (
|
|
267
|
-
|
|
587
|
+
volt_limits_g1 is None or (isinstance(volt_limits_g1, (np.ndarray, list)) and volt_limits_g1.size == 2)
|
|
268
588
|
):
|
|
269
589
|
raise ValueError(
|
|
270
590
|
f"The provided volt_limits_g1 configuration is not supported. Must be either None or "
|
|
@@ -273,7 +593,7 @@ class Simulation:
|
|
|
273
593
|
self.__volt_limits_g1 = deepcopy(volt_limits_g1)
|
|
274
594
|
|
|
275
595
|
@property
|
|
276
|
-
def volt_limits_g2(self) ->
|
|
596
|
+
def volt_limits_g2(self) -> Optional[np.ndarray]:
|
|
277
597
|
"""
|
|
278
598
|
Returns the current (plunger) gate 2 voltage limit configuration of the system. This configuration can then be
|
|
279
599
|
adjusted and is directly used as new configuration, as the object is returned as call by reference
|
|
@@ -284,7 +604,7 @@ class Simulation:
|
|
|
284
604
|
return self.__volt_limits_g2
|
|
285
605
|
|
|
286
606
|
@volt_limits_g2.setter
|
|
287
|
-
def volt_limits_g2(self, volt_limits_g2:
|
|
607
|
+
def volt_limits_g2(self, volt_limits_g2: Optional[np.ndarray]) -> None:
|
|
288
608
|
"""
|
|
289
609
|
Updates the plunger 2 voltage limit configuration of the system according to the supplied values.
|
|
290
610
|
|
|
@@ -293,7 +613,7 @@ class Simulation:
|
|
|
293
613
|
"""
|
|
294
614
|
# check datatype of voltage limits
|
|
295
615
|
if not (
|
|
296
|
-
|
|
616
|
+
volt_limits_g2 is None or (isinstance(volt_limits_g2, (np.ndarray, list)) and volt_limits_g2.size == 2)
|
|
297
617
|
):
|
|
298
618
|
raise ValueError(
|
|
299
619
|
f"The provided volt_limits_g2 configuration is not supported. Must be either None or "
|
|
@@ -302,10 +622,70 @@ class Simulation:
|
|
|
302
622
|
self.__volt_limits_g2 = deepcopy(volt_limits_g2)
|
|
303
623
|
|
|
304
624
|
@property
|
|
305
|
-
def
|
|
625
|
+
def volt_limits_sensor_g1(self) -> Optional[np.ndarray]:
|
|
626
|
+
"""
|
|
627
|
+
Returns the current sensor gate 1 voltage limit configuration of the system. This configuration can then be
|
|
628
|
+
adjusted and is directly used as new configuration, as the object is returned as call by reference
|
|
629
|
+
|
|
630
|
+
Returns:
|
|
631
|
+
np.ndarray: The allowed voltage range for sensor gate 1.
|
|
632
|
+
"""
|
|
633
|
+
return self.__volt_limits_sensor_g1
|
|
634
|
+
|
|
635
|
+
@volt_limits_sensor_g1.setter
|
|
636
|
+
def volt_limits_sensor_g1(self, volt_limits_sensor_g1: Optional[np.ndarray]) -> None:
|
|
637
|
+
"""
|
|
638
|
+
Updates the sensor gate 1 voltage limit configuration of the system according to the supplied values.
|
|
639
|
+
|
|
640
|
+
Args:
|
|
641
|
+
volt_limits_sensor_g1 (np.ndarray): The allowed voltage range for sensor gate 1.
|
|
642
|
+
"""
|
|
643
|
+
# check datatype of voltage limits
|
|
644
|
+
if not (
|
|
645
|
+
volt_limits_sensor_g1 is None or (
|
|
646
|
+
isinstance(volt_limits_sensor_g1, (np.ndarray, list)) and volt_limits_sensor_g1.size == 2)
|
|
647
|
+
):
|
|
648
|
+
raise ValueError(
|
|
649
|
+
f"The provided volt_limits_sensor_g1 configuration is not supported. Must be either None or "
|
|
650
|
+
"a numpy array of size 2."
|
|
651
|
+
)
|
|
652
|
+
self.__volt_limits_sensor_g1 = deepcopy(volt_limits_sensor_g1)
|
|
653
|
+
|
|
654
|
+
@property
|
|
655
|
+
def volt_limits_sensor_g2(self) -> Optional[np.ndarray]:
|
|
656
|
+
"""
|
|
657
|
+
Returns the current sensor gate 2 voltage limit configuration of the system. This configuration can then be
|
|
658
|
+
adjusted and is directly used as new configuration, as the object is returned as call by reference
|
|
659
|
+
|
|
660
|
+
Returns:
|
|
661
|
+
np.ndarray: The allowed voltage range for sensor gate 2.
|
|
662
|
+
"""
|
|
663
|
+
return self.__volt_limits_sensor_g2
|
|
664
|
+
|
|
665
|
+
@volt_limits_sensor_g2.setter
|
|
666
|
+
def volt_limits_sensor_g2(self, volt_limits_sensor_g2: Optional[np.ndarray]) -> None:
|
|
667
|
+
"""
|
|
668
|
+
Updates the sensor gate 2 voltage limit configuration of the system according to the supplied values.
|
|
669
|
+
|
|
670
|
+
Args:
|
|
671
|
+
volt_limits_sensor_g2 (np.ndarray): The allowed voltage range for sensor gate 2.
|
|
672
|
+
"""
|
|
673
|
+
# check datatype of voltage limits
|
|
674
|
+
if not (
|
|
675
|
+
volt_limits_sensor_g2 is None or (
|
|
676
|
+
isinstance(volt_limits_sensor_g2, (np.ndarray, list)) and volt_limits_sensor_g2.size == 2)
|
|
677
|
+
):
|
|
678
|
+
raise ValueError(
|
|
679
|
+
f"The provided volt_limits_sensor_g2 configuration is not supported. Must be either None or "
|
|
680
|
+
"a numpy array of size 2."
|
|
681
|
+
)
|
|
682
|
+
self.__volt_limits_sensor_g2 = deepcopy(volt_limits_sensor_g2)
|
|
683
|
+
|
|
684
|
+
@property
|
|
685
|
+
def ideal_csd_config(self) -> Optional[IdealCSDInterface]:
|
|
306
686
|
"""
|
|
307
687
|
Returns the current ideal CSD configuration of the system. This configuration can then be adjusted and
|
|
308
|
-
is directly used as new configuration, as the object is returned as call by reference
|
|
688
|
+
is directly used as new configuration, as the object is returned as call by reference.
|
|
309
689
|
|
|
310
690
|
Returns:
|
|
311
691
|
IdealCSDInterface: Implementation of the IdealCSDInterface (part of module ideal_csd)
|
|
@@ -313,7 +693,7 @@ class Simulation:
|
|
|
313
693
|
return self.__ideal_csd_config
|
|
314
694
|
|
|
315
695
|
@ideal_csd_config.setter
|
|
316
|
-
def ideal_csd_config(self, ideal_csd_config:
|
|
696
|
+
def ideal_csd_config(self, ideal_csd_config: Optional[IdealCSDInterface]) -> None:
|
|
317
697
|
"""
|
|
318
698
|
Updates the ideal CSD configuration of the system according to the supplied implementation.
|
|
319
699
|
|
|
@@ -384,8 +764,8 @@ class Simulation:
|
|
|
384
764
|
"""
|
|
385
765
|
# check occupation distortions config for implementations of the correct interface
|
|
386
766
|
if not (
|
|
387
|
-
|
|
388
|
-
|
|
767
|
+
occupation_distortions is None
|
|
768
|
+
or all(isinstance(x, OccupationDistortionInterface) for x in occupation_distortions)
|
|
389
769
|
):
|
|
390
770
|
raise ValueError(
|
|
391
771
|
f"The provided occupation distortion configuration is not supported, as not all list "
|
|
@@ -405,7 +785,8 @@ class Simulation:
|
|
|
405
785
|
return self.__sensor_potential_distortions
|
|
406
786
|
|
|
407
787
|
@sensor_potential_distortions.setter
|
|
408
|
-
def sensor_potential_distortions(self, sensor_potential_distortions: Union[
|
|
788
|
+
def sensor_potential_distortions(self, sensor_potential_distortions: Union[
|
|
789
|
+
List[SensorPotentialDistortionInterface], None]):
|
|
409
790
|
"""
|
|
410
791
|
Updates the sensor potential distortion configuration of the system according to the supplied values.
|
|
411
792
|
|
|
@@ -414,8 +795,8 @@ class Simulation:
|
|
|
414
795
|
"""
|
|
415
796
|
# check sensor potential distortions config for implementations of the correct interface
|
|
416
797
|
if not (
|
|
417
|
-
|
|
418
|
-
|
|
798
|
+
sensor_potential_distortions is None
|
|
799
|
+
or all(isinstance(x, SensorPotentialDistortionInterface) for x in sensor_potential_distortions)
|
|
419
800
|
):
|
|
420
801
|
raise ValueError(
|
|
421
802
|
f"The provided sensor potential distortion configuration is not supported, as not all "
|
|
@@ -435,7 +816,8 @@ class Simulation:
|
|
|
435
816
|
return self.__sensor_response_distortions
|
|
436
817
|
|
|
437
818
|
@sensor_response_distortions.setter
|
|
438
|
-
def sensor_response_distortions(self, sensor_response_distortions: Union[
|
|
819
|
+
def sensor_response_distortions(self, sensor_response_distortions: Union[
|
|
820
|
+
List[SensorResponseDistortionInterface], None]) -> None:
|
|
439
821
|
"""
|
|
440
822
|
Updates the sensor response distortion configuration of the system according to the supplied values.
|
|
441
823
|
|
|
@@ -444,8 +826,8 @@ class Simulation:
|
|
|
444
826
|
"""
|
|
445
827
|
# check sensor response distortions config for implementations of the correct interface
|
|
446
828
|
if not (
|
|
447
|
-
|
|
448
|
-
|
|
829
|
+
sensor_response_distortions is None
|
|
830
|
+
or all(isinstance(x, SensorResponseDistortionInterface) for x in sensor_response_distortions)
|
|
449
831
|
):
|
|
450
832
|
raise ValueError(
|
|
451
833
|
f"The provided sensor response distortion configuration is not supported, as not all "
|
|
@@ -455,6 +837,6 @@ class Simulation:
|
|
|
455
837
|
|
|
456
838
|
def __repr__(self):
|
|
457
839
|
return (
|
|
458
|
-
|
|
459
|
-
|
|
840
|
+
self.__class__.__name__
|
|
841
|
+
+ f"(volt_limits_g1={self.volt_limits_g1}, volt_limits_g2={self.volt_limits_g2}, volt_limits_sensor_g1={self.__volt_limits_sensor_g1}, volt_limits_sensor_g2={self.__volt_limits_sensor_g2},ideal_csd_config={self.ideal_csd_config}, sensor={self.sensor}, occupation_distortions={self.occupation_distortions}, sensor_potential_distortions={self.sensor_potential_distortions}, sensor_response_distortions={self.sensor_response_distortions})"
|
|
460
842
|
)
|