nibcq.LCREIS

class nibcq.LCREIS(device: nibcq._device.Device, test_parameters: LCREISTestParameters, measurement_callback: Callable[[nibcq.measurement.LCRMeasurementResult], None] | None = None)

Bases: nibcq._lcr_acir.LCRACIR

LCR-based EIS (Electrochemical Impedance Spectroscopy) measurement handler.

This class extends LCRACIR to provide multi-frequency impedance spectroscopy capabilities using the NI PXIe-4190 LCR Meter. Unlike SMU-based EIS which uses waveform generation and FFT analysis, the LCR meter performs hardware-based impedance measurements directly at each frequency.

EIS measurements characterize the frequency-dependent impedance behavior of electrochemical systems by sweeping from high to low frequencies and measuring the complex impedance at each point. The class provides methods for generating Nyquist and Bode plots from the measurement data.

The LCR meter handles all signal generation and analysis internally, making measurements simpler and faster than SMU-based methods while providing direct impedance results without signal processing.

Inherits from LCRACIR (which inherits from Measurement) to follow the Template Method pattern: validate → lock → workflow (configure → measure). Overrides _run_workflow() to implement the multi-frequency sweep.

Parameters:
DEVICE_FAMILY

DeviceFamily.LCR_METER - required device type.

Type:

DeviceFamily

MEASUREMENT_TIMEOUT

21.0 seconds - timeout per frequency measurement.

Type:

float

Examples

>>> device = Device.create(DeviceFamily.LCR_METER, "PXI1Slot2")
>>> params = LCREISTestParameters(
...     frequency_sweep_characteristics={
...         10000.0: 0.07,
...         1000.0: 0.07,
...         100.0: 0.07,
...     },
... )
>>> lcr_eis = LCREIS(device, params)
>>> results = lcr_eis.run()
>>> nyquist, bode_mag, bode_phase = lcr_eis.get_plots()
property test_parameters: LCREISTestParameters

Get the EIS test parameters.

Returns:

The test parameters including frequency sweep configuration.

Return type:

LCREISTestParameters

property current_frequency: float | None

Get the current measurement frequency (status during sweep).

This property can be read during a sweep to determine which frequency is currently being measured. Useful for progress reporting.

Returns:

The current frequency in Hz, or None if no sweep is active.

Return type:

float | None

property measurement_callback: Callable[[nibcq.measurement.LCRMeasurementResult], None] | None

Get the optional callback invoked after each frequency measurement.

Returns:

The callback, or None if not set.

Return type:

Callable[[LCRMeasurementResult], None] | None

property frequency_list: List[float]

Get the frequency sweep frequencies as a descending sorted list.

Returns all frequencies defined in the frequency sweep characteristics, sorted in descending order (highest to lowest frequency), regardless of the order they were defined in the input dict. This order is used during EIS measurements to start with high frequencies and sweep down to low frequencies.

Returns:

A list of frequencies in Hz, sorted in descending order.

Return type:

list[float]

Examples

>>> lcr_eis.frequency_list
[10000.0, 5000.0, 1000.0, 500.0, 100.0]
property result: List[nibcq.measurement.LCRMeasurementResult]

Get the results of the last EIS measurement.

Returns the list of measurement results from the most recent frequency sweep. Each result corresponds to one frequency point in the sweep.

Returns:

The measurement results for all frequencies.

Return type:

list[LCRMeasurementResult]

Raises:

RuntimeError – If no measurement has been performed yet.

get_plots() tuple[nibcq._eis.PlotSeries, nibcq._eis.PlotSeries, nibcq._eis.PlotSeries]

Return plotting datasets for Nyquist and Bode plots.

Generates three plot series from the measurement results:

  1. Nyquist (Cole-Cole) plot: Resistance vs negative Reactance

  2. Bode magnitude plot: Frequency vs Impedance magnitude

  3. Bode phase plot: Frequency vs Phase angle

All series are sorted by frequency in ascending order for consistent plotting conventions.

Returns:

A 3-tuple containing:
  • Nyquist plot data: PlotSeries(R, -X)

  • Bode magnitude data: PlotSeries(frequency, abs(Z))

  • Bode phase data: PlotSeries(frequency, theta in degrees)

Return type:

tuple[PlotSeries, PlotSeries, PlotSeries]

Examples

>>> nyquist, bode_mag, bode_phase = lcr_eis.get_plots()
>>> # Plot Nyquist
>>> plt.plot(nyquist.x, nyquist.y)
>>> plt.xlabel("R (Ohm)")
>>> plt.ylabel("-X (Ohm)")
>>> # Plot Bode magnitude
>>> plt.semilogx(bode_mag.x, bode_mag.y)
>>> plt.xlabel("Frequency (Hz)")
>>> plt.ylabel("abs(Z) (Ohm)")
Raises:

RuntimeError – If no measurement has been performed yet.

Return type:

tuple[nibcq._eis.PlotSeries, nibcq._eis.PlotSeries, nibcq._eis.PlotSeries]

property measurement_frequency: float

Get the current measurement frequency.

Returns:

The measurement frequency in Hz.

Return type:

float

static validate_current_amplitude(current_amplitude: float) bool

Validate that the current amplitude is within acceptable limits.

Parameters:

current_amplitude (float) – The current amplitude to validate in Amperes RMS.

Returns:

Always returns True when validation passes.

Return type:

bool

Raises:

LCRParameterError – If the current amplitude is outside the valid range (7.08 nA to 707 mA).

create_compensation(params: nibcq.lcr_compensation.LCRCompensationParameters, skip_prompts: bool = False) nibcq.measurement.LCRMeasurementResult

Generate LCR compensation data and verify with a measurement.

This method performs the complete 7-step compensation generation flow as defined in the LabVIEW “Create Compensation” example:

  1. Session init (already done via Device.create)

  2. Custom cable compensation (if enabled)

  3. Set cable length

  4. Open/Short compensation (if enabled)

  5. Load compensation (if enabled)

  6. Configure session for verification measurement

  7. Verification measurement (initiate, wait, measure, reset)

The compensation data is stored on the device’s onboard memory and remains available for subsequent measurements until the device is reset or powered off.

User prompts are displayed before each step requiring physical connection changes (open, short, load). These prompts ensure the user has made the correct physical setup before compensation data is captured.

Parameters:
  • params (LCRCompensationParameters) – LCRCompensationParameters with all configuration options.

  • skip_prompts (bool) – If True, skips user prompts for physical connections. User must ensure correct connections are made before each step. Useful for automated testing or scripted calibration sequences. Follows the Calibrator.self_calibrate(force=…) pattern.

Returns:

Verification measurement result after

compensation is applied. Can be used to verify compensation quality (should show near-zero impedance for a short).

Return type:

LCRMeasurementResult

Raises:
  • LCRParameterError – If parameters are invalid (e.g., load comp without open AND short).

  • nidcpower.Error – If hardware operation fails.

Examples

>>> # Interactive compensation with user prompts
>>> lcr = LCRACIR(device, test_params)
>>> result = lcr.create_compensation(
...     LCRCompensationParameters(
...         generate_open=True,
...         generate_short=True,
...         enable_open=True,
...         enable_short=True,
...     )
... )
>>> print(f"Verification Z: {result.z_magnitude:.6f} Ohm")
>>>
>>> # Automated compensation (for testing)
>>> result = lcr.create_compensation(params, skip_prompts=True)
property compensation: Compensation | None

Get the compensation object for error correction.

Returns:

The compensation data, or None if no compensation is applied.

Return type:

Compensation | None

run(compensation: Compensation | None = None, **kwargs)

Run the measurement process (Template Method).

Defines the invariant measurement workflow:

  1. Optionally set compensation from argument.

  2. Store any extra keyword arguments in _run_kwargs so hook methods can access them.

  3. Validate preconditions (temperature, etc.) — before locking.

  4. Lock all device sessions.

  5. Execute the workflow (configure → measure → calculate → compensate).

  6. Release locks.

  7. Clear _run_kwargs.

  8. Return result.

Subclasses customize behavior by overriding the abstract/hook methods: _configure, _measure, _calculate, _apply_compensation, _validate_preconditions, _lock_sessions, _run_workflow.

Parameters:
  • compensation (Compensation | None) – Optional compensation data for error correction. If provided, sets self._compensation before running. If None, uses the previously set self._compensation (which may also be None, meaning no compensation is applied).

  • kwargs (Any) – Additional keyword arguments (**kwargs) forwarded to hook methods via self._run_kwargs. Subclasses may read specific keys from this dict inside their _run_workflow or other hooks.

Returns:

The measurement result (type depends on the specific subclass).

Return type:

Any

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:

float

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:

float

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:

CenteredRange

measure_temperature() CenteredRange

Get a new temperature reading from the device.

Returns:

Current temperature reading, or NaN if no temperature capability

Return type:

CenteredRange

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:

bool

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)