Examples

The following examples are also available in the examples directory.

Load and save data

This example shows how to load/save methods and measurements and how to inspect the data.

load_save_data.py
from pathlib import Path

import pandas as pd

import pypalmsens as ps

examples_dir = Path(__file__).parent

# load a method file
method = ps.load_method_file(examples_dir / 'PSDummyCell_LSV.psmethod', as_method=True)

# save the method file
ps.save_method_file(examples_dir / 'PSDummyCell_LSV_copy.psmethod', method)

# load a session file
measurements = ps.load_session_file(examples_dir / 'Demo CV DPV EIS IS-C electrode.pssession')

for measurement in measurements:
    print(f'loaded measurement: {measurement.title}, {measurement.timestamp}')
    print(f'number of curves: {len(measurement.curves)}')
    for curve in measurement.curves:
        print(f'curve title: {curve.title}')
        print(f'number of points: {len(curve.x_array)}')
        print(f'number of peaks: {len(curve.peaks)}')
    print(f'Has EIS fit results: {"yes" if len(measurement.eis_fit) > 0 else "no"}')

# save the session file
ps.save_session_file(
    examples_dir / 'Demo CV DPV EIS IS-C electrode_copy.pssession', [measurements[0]]
)

# convert measurments to pandas dataframes
frames = []
frame_names = []

for measurement in measurements:
    dataset = measurement.dataset

    frames.append(dataset.to_dataframe())
    frame_names.append(measurement.title)

df = pd.concat(frames, keys=frame_names)
print(df)

Manual control

This example shows how to discover devices, establish a connection and control an instrument manually.

manual_control.py
import pypalmsens as ps

instruments = ps.discover()
print(instruments)

with ps.connect(instruments[0]) as manager:
    manager.set_cell(True)
    print('cell enabled')

    manager.set_potential(1)
    print('set potential to 1V')

    manager.set_current_range(ps.settings.CURRENT_RANGE.cr_1_mA)
    print('set cell to to 1mA currrent range')

    current = manager.read_current()
    print('current = ' + str(current) + ' µA')

    manager.set_cell(False)
    print('cell disabled')

Manual control async

This example shows how to discover devices, establish a connection and control an instrument manually using the asynchronous instrument manager.

manual_control_async.py
import asyncio

import pypalmsens as ps


async def main():
    instruments = await ps.discover_async()
    print(instruments)

    async with await ps.connect_async(instruments[0]) as manager:
        await manager.set_cell(True)
        print('cell enabled')

        await manager.set_potential(1)
        print('set potential to 1V')

        await manager.set_current_range(ps.settings.CURRENT_RANGE.cr_1_mA)
        print('set cell to to 1mA currrent range')

        current = await manager.read_current()
        print(f'current = {current} µA')

        await manager.set_cell(False)
        print('cell disabled')


asyncio.run(main())

Measure CA

This example shows how to set up and run a chronoamperometry measurement.

measurement_CA.py
import pypalmsens as ps


def new_data_callback(new_data):
    for point in new_data:
        print(point)


instruments = ps.discover()
print(instruments)

with ps.connect(instruments[0]) as manager:
    manager.callback = new_data_callback

    serial = manager.get_instrument_serial()
    print(serial)

    # Chronoamperometry measurement using helper class
    method = ps.ChronoAmperometry(
        interval_time=0.01,
        potential=1.0,
        run_time=10.0,
    )

    measurement = manager.measure(method)

print(measurement)

Measure CA async

This example shows how to set up and run a chronoamperometry measurement using the asynchronous instrument manager.

measurement_CA_async.py
import asyncio

import pypalmsens as ps


def new_data_callback(new_data):
    for point in new_data:
        print(point)


async def main():
    instruments = await ps.discover_async()
    print(instruments)

    async with await ps.connect_async(instruments[0]) as manager:
        manager.callback = new_data_callback

        serial = await manager.get_instrument_serial()
        print(serial)

        # Chronoamperometry measurement using helper class
        method = ps.ChronoAmperometry(
            interval_time=0.02,
            potential=1.0,
            run_time=2.0,
        )

        measurement = await manager.measure(method)

    print(measurement)


asyncio.run(main())

Measure CV

This example shows how to set up and run a cyclic voltammetry measurement.

measurement_CV.py
import pypalmsens as ps


def new_data_callback(new_data):
    for point in new_data:
        print(point)


instruments = ps.discover()
print(instruments)

with ps.connect(instruments[0]) as manager:
    manager.callback = new_data_callback

    serial = manager.get_instrument_serial()
    print(serial)

    method = ps.CyclicVoltammetry(
        current_range=ps.settings.CurrentRange(
            max=ps.settings.CURRENT_RANGE.cr_1_A,  # 1 A range
            min=ps.settings.CURRENT_RANGE.cr_1_uA,  # 1 µA range
            start=ps.settings.CURRENT_RANGE.cr_1_mA,  # 1 mA range
        ),
        equilibration_time=2,  # seconds
        begin_potential=-2,  # V
        vertex1_potential=-2,  # V
        vertex2_potential=3,  # V
        step_potential=0.05,  # V
        scanrate=5,  # V/s
        n_scans=3,  # number of scans
    )

    measurement = manager.measure(method)

print(measurement)

Measure EIS

This example shows how to set up and run a EIS measurement.

measurement_EIS.py
import pypalmsens as ps


def new_data_callback(new_data):
    for point in new_data:
        print(point)


instruments = ps.discover()
print(instruments)

with ps.connect(instruments[0]) as manager:
    manager.callback = new_data_callback

    serial = manager.get_instrument_serial()
    print(serial)

    # EIS measurement using helper class
    method = ps.ElectrochemicalImpedanceSpectroscopy()

    measurement = manager.measure(method)

print(measurement)

MethodSCRIPT sandbox

This example shows how to set up and run a MethodSCRIPT Sandbox measurement.

measurement_MethodSCRIPT_sandbox.py
import pypalmsens as ps


def new_data_callback(new_data):
    for point in new_data:
        print(point)


script = """e
var c
var p
var e
var l
var r
var j
var o
var d
var n
set_gpio_cfg 0x0f 1
set_pgstat_chan 0
set_pgstat_mode 3
set_acquisition_frac_autoadjust 50
set_max_bandwidth 0
cell_off
set_range ba 210m
set_autoranging ba 210m 210m
set_range ab 4200m
set_autoranging ab 210m 4200m
meas_loop_ocp o 200m 1
pck_start
    pck_add o
pck_end
endloop
set_range ba 2100u
set_autoranging ba 2100n 2100u
set_range ab 4200m
set_autoranging ab 4200m 4200m
store_var d -200m ab
add_var d o
store_var n 200m ab
add_var n o
set_e d
cell_on
set_gpio 10i
meas_loop_acv p c e l r j d n 10m 200m 10m 100
pck_start
    pck_add p
    pck_add c
    pck_add e
    pck_add l
    pck_add r
    pck_add j
pck_end
endloop
on_finished:
cell_off

"""

instruments = ps.discover()
print(instruments)

with ps.connect(instruments[0]) as manager:
    manager.callback = new_data_callback

    serial = manager.get_instrument_serial()
    print(serial)

    method = ps.MethodScript(script=script)

    measurement = manager.measure(method)

print(measurement)

Stream data to CSV

This example shows how to set up and run a chronoamperometry measurement and write the results to a CSV file in real-time.

measurement_stream_to_csv.py
import csv

import pypalmsens as ps


def stream_to_csv_callback(new_data):
    for point in new_data:
        csv_writer.writerow([point['index'], point['x'], point['y']])
        # csv_writer.writerow([point['frequency'], point['zre'], point['zim']]) #for EIS


csv_file = open('test.csv', 'w', newline='')
csv_writer = csv.writer(csv_file, delimiter=' ')

instruments = ps.discover()
print(instruments)

with ps.connect(instruments[0]) as manager:
    manager.callback = stream_to_csv_callback

    serial = manager.get_instrument_serial()
    print(serial)

    # Chronoamperometry measurement using helper class
    method = ps.ChronoAmperometry(
        interval_time=0.0004,
        potential=1.0,
        run_time=10.0,
    )

    measurement = manager.measure(method)

print(measurement)

csv_file.close()

SWV versus OCP

This example shows how to set up and run a square wave voltammetry measurement versus OCP.

measurement_SWV_vs_OCP.py
import pypalmsens as ps


def new_data_callback(new_data):
    for point in new_data:
        print(point)


instruments = ps.discover()
print(instruments)

with ps.connect(instruments[0]) as manager:
    manager.callback = new_data_callback

    method = ps.SquareWaveVoltammetry(
        pretreatment=ps.settings.Pretreatment(
            conditioning_potential=2.0,  # V
            conditioning_time=2,  # seconds
        ),
        versus_ocp=ps.settings.VersusOCP(
            mode=3,  # versus begin and end potential
            max_ocp_time=1,  # seconds
        ),
        begin_potential=-0.5,  # V
        end_potential=0.5,  # V
        step_potential=0.01,  # V
        amplitude=0.08,  # V
        frequency=50,  # Hz
    )

    measurement = manager.measure(method)

print(measurement)
print(f'ocp: {measurement.ocp_value}')

Multiplexer

This example shows how to set up and control a multiplexer and run consecutive and alternating multiplexer measurments.

multiplexer.py
import pypalmsens as ps


def new_data_callback(new_data):
    for point in new_data:
        print(point)


instruments = ps.discover()
print(instruments)

with ps.connect(instruments[0]) as manager:
    manager.callback = new_data_callback

    n_multiplexer_channels = manager.initialize_multiplexer(2)
    manager.set_mux8r2_settings()

    for channel in range(n_multiplexer_channels):
        manager.set_multiplexer_channel(channel)

    # When measuring alternatingly the selection is restricted to the first n channels
    altnernating_multiplexer_method = ps.ChronoAmperometry(
        interval_time=0.5,  # seconds
        potential=1.0,  # volts
        run_time=5.0,  # seconds
        multiplexer=ps.settings.Multiplexer(
            mode='alternate',  # 'none', 'consecutive', 'alternate'
            channels=[1, 2],  # 8 channels, 1 and 2 are enabled
            connect_sense_to_working_electrode=False,
            combine_reference_and_counter_electrodes=False,
            use_channel_1_reference_and_counter_electrodes=False,
            set_unselected_channel_working_electrode=0,
        ),
    )
    measurement = manager.measure(altnernating_multiplexer_method)
    print(measurement)

    consecutive_multiplexer_method = ps.SquareWaveVoltammetry(
        begin_potential=-0.5,  # volts
        end_potential=0.5,  # volts
        step_potential=0.01,  # volts
        amplitude=0.1,  # volts
        frequency=10,  # hertz
        multiplexer=ps.settings.Multiplexer(
            mode='consecutive',  # 'none', 'consecutive', 'alternate'
            channels=[1, 2, 7, 8],  # channels 1, 2, 7 and 8 are enabled
            connect_sense_to_working_electrode=False,
            combine_reference_and_counter_electrodes=False,
            use_channel_1_reference_and_counter_electrodes=False,
            set_unselected_channel_working_electrode=0,
        ),
    )

    measurement = manager.measure(consecutive_multiplexer_method)
    print(measurement)

Multichannel measurement

This example shows how to connect to a collection of instruments and run a chronoamperometry measurement on all channels simultaneously.

multichannel_measurement.py
import pypalmsens as ps


def new_data_callback(new_data):
    for point in new_data:
        print(point)


method = ps.ChronoAmperometry(
    interval_time=0.004,
    potential=1.0,
    run_time=5.0,
)

instruments = ps.discover()

print(instruments)

# run multichannel experiment with callback
with ps.InstrumentPool(instruments, callback=new_data_callback) as pool:
    results = pool.measure(method=method)

print(results)

Multichannel CSV writer

This example shows how to connect to a how to use a callback to automatically store data to a csv file while collecting data from collection of instruments.

multichannel_csv_callback.py
import asyncio
import pypalmsens as ps
import csv
import functools


def stream_to_csv_callback(new_data, csv_writer):
    for point in new_data:
        csv_writer.writerow([point['index'], point['x'], point['y']])


async def stream_to_csv(manager, *, method):
    """Measure with a custom csv writer callback."""
    serial = await manager.get_instrument_serial()

    with open(f'{serial}.csv', 'w', newline='') as csv_file:
        csv_writer = csv.writer(csv_file, delimiter=' ')

        callback = functools.partial(stream_to_csv_callback, csv_writer=csv_writer)
        manager.callback = callback

        measurement = await manager.measure(method)

    print(f'Wrote data to {csv_file.name}')

    return measurement


async def main():
    method = ps.ChronoAmperometry(
        interval_time=0.004,
        potential=1.0,
        run_time=5.0,
    )

    instruments = await ps.discover_async(ftdi=True)

    print(instruments)

    # run multichannel experiment with csv writer
    async with ps.InstrumentPoolAsync(instruments) as pool:
        results = await pool.submit(stream_to_csv, method=method)

    print(results)


asyncio.run(main())

Multichannel custom loop

This example shows how to run and set up a sequence of measurements on a collection of channels simultaneously.

multichannel_custom_loop.py
import asyncio
from attrs import evolve
import pypalmsens as ps


async def custom_loop(manager, *, method, steps):
    measurements = []

    for step in steps:
        method = evolve(method, **step)
        measurements.append(await manager.measure(method))

    return measurements


async def main():
    method = ps.ChronoAmperometry(
        interval_time=0.004,
        run_time=5.0,
    )

    steps = [
        {
            'potential': 0.4,
        },
        {
            'potential': 0.6,
        },
        {
            'potential': 1.0,
        },
    ]

    instruments = await ps.discover_async(ftdi=True)

    print(instruments)

    async with ps.InstrumentPoolAsync(instruments) as pool:
        results = await pool.submit(custom_loop, method=method, steps=steps)

    print(results)

    for i, measurements in enumerate(results):
        ps.save_session_file(f'example-{i}.pssession', measurements)


asyncio.run(main())

Multichannel HW sync

This example shows how to connect to a collection of instruments and run a chronopotentiometry measurement on all channels simultaneously using hardware synchronization.

multichannel_HW_sync.py
import asyncio
import pypalmsens as ps


async def main():
    method = ps.ChronoAmperometry(
        interval_time=0.004,
        potential=1.0,
        run_time=5.0,
    )
    method.general.use_hardware_sync = True

    instruments = await ps.discover_async(ftdi=True)

    print(instruments)

    async with ps.InstrumentPoolAsync(instruments) as pool:
        results = await pool.measure(method)

    print(results)


asyncio.run(main())