nibcq.ParallelACIR
- class nibcq.ParallelACIR(leader: nibcq._device.Device, followers: collections.abc.Sequence[nibcq._device.Device], test_parameters: nibcq._acir.ACIRTestParameters, test_frequency: float = 1000.0, multi_device_type: nibcq.enums.MultiDeviceMode = MultiDeviceMode.PARALLEL)
Bases:
nibcq.measurement.ParallelMeasurement,nibcq._acir.ACIRDefines an ACIR measurement with multiple SMUs working in parallel.
This class enables AC impedance measurements using multiple SMU devices connected in parallel to achieve higher current amplitudes than a single SMU can provide.
Unlike single-device ACIR measurements, parallel ACIR does NOT support switching configurations due to hardware current limitations of switch matrices (typically limited to ~2A). Parallel ACIR is designed for direct-connection, high-current testing scenarios.
- Key Features:
Multiple SMUs synchronized via hardware triggers (PXI backplane).
Current capacity scales linearly with number of devices (e.g., 3 SMUs = 3x current).
Voltage measured from leader device (4-wire sense for accuracy).
Current contributions summed across all devices.
Single frequency measurement per run (frequency sweep requires EIS).
- Hardware Requirements:
All devices must be NI PXIe-4139 or compatible SMUs.
Devices must be in the same PXI chassis for trigger routing.
One device designated as leader, others as followers.
Example
>>> leader_device = Device.create(DeviceFamily.SMU, "PXI1Slot2") >>> follower1 = Device.create(DeviceFamily.SMU, "PXI1Slot3") >>> >>> params = ACIRTestParameters( ... current_amplitude=4.0, ... powerline_frequency=PowerlineFrequency.FREQ_50_HZ, ... ) >>> >>> parallel_acir = ParallelACIR( ... leader=leader_device, ... followers=[follower1], ... test_parameters=params, ... test_frequency=1000.0, ... ) >>> >>> compensation = parallel_acir.load_compensation_file() >>> result = parallel_acir.run(compensation) >>> print(f"Impedance: {result.impedance}")
- Parameters:
leader (nibcq._device.Device)
followers (collections.abc.Sequence[nibcq._device.Device])
test_parameters (nibcq._acir.ACIRTestParameters)
test_frequency (float)
multi_device_type (nibcq.enums.MultiDeviceMode)
- current_frequency = 1000.0
Get the current frequency of the ACIR Measurement.
- Returns:
The current configured frequency in Hz
- Return type:
- Raises:
FrequencyError – If frequency has not been set
- test_parameters
Get the current test parameters for the measurement.
Returns the configuration parameters that define how the measurement should be performed, including settings like powerline frequency and other measurement-specific parameters.
- Returns:
The current test parameters configuration
- Return type:
Examples
>>> measurement = Measurement(device) >>> params = measurement.test_parameters >>> print(params.powerline_frequency) PowerlineFrequency.FREQ_60_HZ
- property all_measurement_data: collections.abc.Iterator[tuple[nibcq._device.Device, nibcq.measurement.SMUMeasurement | None]]
Get raw measurement data from all individual devices.
Returns per-device voltage and current measurements as captured during the measurement phase, before the parallel multidevice operation is applied.
- Returns:
- Iterator of tuples pairing each
Device with its corresponding SMUMeasurement. Yields tuples with None measurements if no measurement has been performed yet.
- Return type:
Iterator[tuple[Device, SMUMeasurement | None]]
Example
>>> for device, measurement in parallel_acir.all_measurement_data: ... print(f"Device: {device.product}") ... print(f"Voltage samples: {len(measurement.voltage_values)}")
- property calibration_file_paths: list[tuple[nibcq._device.Device, str]]
Get the default calibration file paths for all parallel devices.
Returns a list of tuples pairing each Device with its default calibration diary file path. Calibration is managed per-device via the Calibrator class, so this property simply aggregates the paths.
- property measurement_data: nibcq.measurement.SMUMeasurement
Get sanitized multidevice measurement data after parallel combination.
Applies the same signal conditioning as single-device ACIR. First, edge effects are removed for frequencies above 60 Hz with more than 10 periods by trimming the first two periods from the signal. Then DC offset is removed by subtracting the mean from both voltage and current waveforms to center them around zero.
In parallel mode, voltage values come from the leader device (4-wire remote sense) and current values are the sum of all devices’ individual currents.
- Returns:
- Sanitized combined measurement data, or None if
no multidevice data is available yet.
- Return type:
- generate_compensation_file_path() str
Generate compensation file path for parallel ACIR measurement.
Uses the leader device’s serial number with parallel-specific naming:
z_{method}_Leader_SN{serial_number}.jsonThis differs from single-device ACIR which uses:
z_{method}_{serial_number}.jsonThe parallel naming convention makes it clear which device was the leader during compensation data collection.
- Returns:
- The generated file path for compensation data, or None if no
compensation is needed.
- Return type:
- Raises:
CompensationMethodError – If compensation_method is not supported.
- run(compensation: nibcq.compensation.Compensation) nibcq._acir.SMUResult
Execute the complete parallel ACIR measurement workflow.
Full measurement cycle: 1. Validate temperature if compensation requires it. 2. Lock all device sessions via a shared ExitStack context manager. 3. Configure all devices for synchronized ACIR measurement. 4. Execute the measurement (initiate, fetch, gang). 5. Perform DFT-based impedance calculation on the multidevice waveform. 6. Apply compensation to the raw impedance result. 7. Release all session locks (guaranteed even if an earlier step raised).
- Parameters:
compensation (Compensation) – Compensation data for error correction. Supports NO_COMPENSATION, SHORT, and GOLDEN_DUT methods, matching the single-device ACIR workflow.
- Returns:
- Measurement result containing the compensated complex impedance,
magnitude, phase, real and imaginary components.
- Return type:
- Raises:
FrequencyError – If frequency or test parameters are invalid.
SMUParameterError – If device configuration fails.
CompensationMethodError – If compensation data is incompatible.
RuntimeError – If measurement or DFT analysis fails.
Example
>>> compensation = parallel_acir.load_compensation_file() >>> result = parallel_acir.run(compensation) >>> print(f"Impedance magnitude: {result.z:.4f} Ohms")
- write_compensation_file(compensation_file_path: str | None = None, kit_file_path: str | None = None, comment: str | None = None) str
Create a compensation file for the parallel ACIR measurement setup.
Same workflow as single-device ACIR, but locks all device sessions via a shared ExitStack during the measurement to prevent concurrent access. The compensation file is saved based on the leader device’s serial number.
- Parameters:
compensation_file_path (str, optional) – Optional file path for the compensation data. If None, auto-generates path using leader serial number.
kit_file_path (str, optional) – Optional path to known impedance table for KIT compensation subtraction.
comment (str, optional) – Optional comment to embed in the compensation file.
- Returns:
Path to the created compensation file.
- Return type:
- Raises:
CompensationMethodError – If NO_COMPENSATION is selected or method is unsupported.
FrequencyError – If the test frequency is out of range.
SMUParameterError – If device configuration or measurement fails.
- abstractmethod run_with_switching(compensation=None)
Parallel ACIR does not support switching.
Switching support is limited to 3A, which a single SMU can already provide. For current targets up to 3A, use single-device ACIR with switching instead.
- Raises:
NotImplementedError – Always raised.
- get_all_devices()
Yield every parallel device, followers first, leader last.
This generator avoids storing a redundant copy of the device references that already live in
ParallelMeasurement’s fields containing the followers and the leader.- Yields:
Device – The next device in followers-then-leader order.
- property result: float | SMUResult | list[SMUResult] | list[tuple[nibcq.switch.SMUCellData, SMUResult]] | list[tuple[nibcq.switch.SMUCellData, list[SMUResult]]] | list[tuple[str, tuple[datetime.datetime, datetime.datetime, float]]] | Any
Get the result of the last measurement.
Returns the measurement result from the most recent measurement operation. The result type depends on the specific measurement implementation and whether switching was used:
- Single measurements:
float for OCV and DCIR
SMUResult for ACIR
list[SMUResult] for EIS
- Switching measurements:
list[tuple[SMUCellData, SMUResult]] for ACIR with switching
list[tuple[SMUCellData, list[SMUResult]]], for EIS with switching
list[tuple[str, tuple[datetime, datetime, float]]], for OCV with switching
- Returns:
float | SMUResult | list[SMUResult] | list[tuple[SMUCellData, SMUResult]] | list[tuple[SMUCellData, list[SMUResult]]] | list[tuple[str, tuple[datetime, datetime, float]]] | Any: The measurement result(s). For switching measurements, returns a list of tuples where each tuple contains the cell data and its corresponding results.
- Raises:
RuntimeError – If no measurement has been performed yet or if the result is not available
- Return type:
Union[float, SMUResult, list[SMUResult], list[tuple[nibcq.switch.SMUCellData, SMUResult]], list[tuple[nibcq.switch.SMUCellData, list[SMUResult]]], list[tuple[str, tuple[datetime.datetime, datetime.datetime, float]]], Any]
Examples
>>> measurement.run(test_parameters) >>> result = measurement.result >>> print(f"Measurement result: {result}") >>> >>> # For switching measurements >>> switching_results = measurement.run_with_switching(compensation) >>> for cell_data, cell_results in measurement.result: ... print(f"Cell {cell_data.cell_serial_number}: {len(cell_results)} results")
- DEVICE_FAMILY: nibcq.enums.DeviceFamily = None
Device family for ACIR and similar measurement types.
- Type:
- property acceptable_temperature_delta: float
Get the acceptable temperature delta for compensation validation.
Returns the maximum allowed temperature difference from the device’s temperature capability. This is a pass-through property that delegates to the underlying TemperatureCapability.
- Returns:
The acceptable temperature delta in degrees, or NaN if no temperature capability
- Return type:
Examples
>>> measurement = EIS(device) >>> measurement.acceptable_temperature_delta = 2.5 >>> delta = measurement.acceptable_temperature_delta
- property temperature: float
Get the latest temperature reading from the device.
- Returns:
The most recent temperature measurement, or NaN if no temperature capability
- Return type:
- property temperature_range: CenteredRange
Get the latest temperature reading from the device, coupled with the user-set delta.
- Returns:
A CenteredRange representing the most recent temperature measurement (NaN if not available), along with the acceptable temperature delta (NaN if not set).
- Return type:
- measure_temperature() CenteredRange
Get a new temperature reading from the device.
- Returns:
Current temperature reading, or NaN if no temperature capability
- Return type:
- validate_temperature(target_temperature: CenteredRange) bool
Validate the current temperature against the compensation file’s target.
Delegates to the device’s temperature capability for validation. The capability handles all validation logic including checking if thermocouple is configured, using overridden delta values if set, and printing appropriate warnings.
- Parameters:
target_temperature (CenteredRange) – The target temperature parameters for validation
- Returns:
- True if thermocouple is configured and temperature is within range.
False if thermocouple is not configured (capability missing or not set up), or if target temperature/delta is NaN.
- Return type:
- Raises:
TemperatureError – If the current temperature exceeds the target ± delta range (only raised when capability is configured)
Examples
>>> measurement = EIS(device) >>> measurement.measure_temperature() >>> target = compensation.temperature_parameter >>> is_valid = measurement.validate_temperature(target)
- FREQUENCY_LIMIT: Final[float] = 10000.0
Frequency limit for ACIR and similar measurement types.
- Type:
- SENSITIVITY_FREQUENCY_LIMIT: Final[float] = 7500.0
Frequency limit for DEVICE_CURRENT_LIMIT A Current measurements.
- Type:
- CURRENT_LIMIT: Final[float] = 2.0
High Frequency current limit for ACIR and similar measurement types.
- Type:
- DEVICE_CURRENT_LIMIT: Final[float] = 3.0
Moderate Frequency current limit for ACIR and similar measurement types.
- Type:
- property raw_data: nibcq.measurement.SMUMeasurement
Get the raw measurement data read from the device.
Only allows measurement to be read, not access it directly. Returns unprocessed data as captured from the device.
- Returns:
- Raw measurement data containing tone frequency, voltage values,
and current values
- Return type:
- Raises:
SMUParameterError – If no measurement data is available or measurement is incomplete
- static validate_current_amplitude(target_frequency: float, current_amplitude: float) bool
Validate that the current amplitude is within acceptable limits.
- Parameters:
- Returns:
Always returns True when validation passes.
- Return type:
- Raises:
CurrentAmplitudeError – If the current amplitude is not positive or exceeds the defined maximum limit (defined in ACIR.CURRENT_LIMIT constant).
- load_compensation_file(file_path: str | None = None) nibcq.compensation.Compensation
Load compensation file based on compensation method and device serial number.
Creates and returns a compensation object with the appropriate compensation data. For NO_COMPENSATION, creates a default compensation object. For other methods, loads compensation data from file.
- Parameters:
file_path (str, optional) – Optional specific file path to read from. If None, generates path automatically
- Returns:
The loaded compensation object
- Return type:
- Raises:
FileNotFoundError – If compensation file is not found and compensation is required
ValueError – If compensation file is invalid and compensation is required
CompensationMethodError – If compensation method is not supported by the nibcq Python API
- property has_switch_capability: bool
Check if the device has switch capability.
- Returns:
True if switch capability is available, False otherwise
- Return type:
- connect_channel(channel: SMUCellData) None
Connect to a specific DUT channel using the device’s switch capability.
- Parameters:
channel (SMUCellData) – SwitchChannel containing connection information
- Raises:
RuntimeError – If no switch capability is available
- Return type:
None
- disconnect_all() None
Disconnect all channels using the device’s switch capability.
- Raises:
RuntimeError – If no switch capability is available
- Return type:
None
- wait_for_debounce() None
Wait for switch relays to settle using the device’s switch capability.
- Raises:
RuntimeError – If no switch capability is available
- Return type:
None
- property switch_cells: List[str] | List[SMUCellData]
Get the configured switch cells from the device.
- Returns:
DUT channel names or SMUCellData objects, which contain the DUT and switch channel information.
- Return type:
list[str] | list[SMUCellData]