Getting Started

Installation

To install PICOcode, clone the git repository:

git clone https://github.com/picoexperiment/PICOcode.git

This downloads the repository as PICOcode under your current working directory and configures the copy for Git tracking. This clone can be made on your own computer or in your $HOME directory on a shared system like ComputeCanada (or both). Directions for each case follow.

Creating a virtual environment

It is common practice to create a virtual environment for python and install packages within it. This has several advantages, including keeping separate sets of installed packages in different environments, and saving you a huge headache later if something goes wrong with your python installation, since you can just delete the folder which contains the environment instead of having to purge and reinstall python.

To create a virtual environment, first download the virtualenv package by calling

pip install virtualenv

or pip3 install virtualenv if you have python 2 and 3 installed, e.g. on some versions of Ubuntu. Then, create a virtual evironment by calling

python -m virtualenv <path>

where <path> is the name of a new folder in which the environment will be created. Then, activate it by calling source <path>/bin/activate on Unix/Mac OS, or <path>Scriptsactivate.bat on Windows from the command prompt.

On your own computer

When the git clone download is complete you can install PICOcode and its dependencies with:

pip install -e PICOcode --user

The optional --user flag installs the packages in ~/.local/lib/; you may skip this if using a virtual environment. If you’d prefer a system-wide installation, you can run pip with root/admin privileges (e.g. prepend sudo). The optional -e flag installs the package in editable mode so that edits to the local code are automatically linked to the user’s python environment, and is recommended if you plan on developing.

On ComputeCanada

If you don’t yet have a ComputeCanada (CC) account linked to PICO, see docdb:4287 for instructions, including creating a python virtual environment.

When you’re able to ssh to graham.computecanada.ca you’ll find the PICO-60 and PICO-40L raw data grouped by runID under the PICO project directory: ~/projects/rrg-kenclark/pico/30l-16-data/ and ~/projects/rrg-kenclark/pico/40l-19-data/.

Detailed instructions for setting up your Python environment on CC can be found at the CC Python wiki page, but it should be sufficient to run the following:

module load python/3.7.7 scipy-stack/2020a

This loads the CC-provided Lmod modules for Python 3.7.7 and most of the packages required to use PICOcode into your current shell. This can be added to your ~/.bashrc to have it loaded automatically on each login. Keep in mind that non-interactive bash shells don’t source this file, so you’ll likely need to add this line to any CC bash scripts you write.

Finally, install PICOcode by running the following in the directory where you cloned the GitHub repository:

pip install -e PICOcode

At this point all PICOcode requirements should be satisfied, and the package should be ready to use.

Initial Setup

There are two files which may require some setup after installing PICOcode.

PICOcode.conf

The first time that PICOcode is imported, a config file is created in the user’s home directory depending on the system:

Linux/Mac OS

$HOME/.PICOcode.conf

Windows

C:Users<user>Documents.PICOcode.conf

This config file is structured as a JSON file containing two entries:

  • ARCHIVE_DIR: the directory which contains raw PICO data, either in directories or as archives, in subfolders denoted by a data_series, i.e. raw data exists as <ARCHIVE_DIR>/<data_series>/<runID> or <ARCHIVE_DIR>/<data_series>/<runID>.tar

  • SCRATCH_DIR, which contains extracted data if the runs are archived.

By default these point to the relevant locations on the Graham node of Compute Canada, which PICO uses for data processing. If you are using PICOcode on your own computer, you should edit this file to point to convenient directories.

librefprop

The NIST REFPROP program is used to compute various fluid properties, which is mainly used to calculate the Seitz threshold. The use of REFPROP (and its python wrapper ctREFPROP) depend on a file called librefprop, the format of which depends on the system in use.

Several versions have been compiled and are provided in PICOcode/REFPROP/lib. When first loaded, PICOcode will try each of these pre-compiled librefprop files until one works; if none of them do, it will throw an error. If this occurs, you may have to compile your own version (see instructions here). Please contact Colin if you are having issues with this.

Accessing PICO Data with PICOcode

PICO data comes in two forms: raw and reconstructed (a.k.a. recon). Raw data are generally time series of transducer data for a given run or event (e.g. pressure, temperature, acoustics, pictures), while recon data consists of processed raw data which is used to categorize events (e.g. pressure or Seitz threshold, AP to discriminate neutrons/alphas, Dytran to discriminate multiples).

Raw and reconstructed data are accessed using the Event and ReconFile classes. The use of these are described below.

The Event class

Note that the Event class is loaded to PICOcode’s namespace upon importing PICOcode, so it may be called with:

>>> PICOcode.Event(...).

This class is used to access raw data for individual PICO events. PICO data is stored with one directory per run, and one subdirectory per event (starting at 0, with up to 100 events per run), with some files related to run conditions stored in the run folder:

<runID>
├── <runID>.txt
├── DAQ30l_Setup.xml
├── DAQversion.txt
├── RunParameters.txt
├── 0
│   ├── Event.txt
│   ├── fastDAQ_0.bin
│   ├── fastDAQ_0_cal.txt
│   ├── PLClog.txt
│   ├── slowDAQ_0.txt
│   ├── temperature.txt
│   └── Images
│       ├── cam0_image30.png
│       ├── cam0_image31.png
│       ├── ...
│       ├── cam0_image70.png
│       ├── cam1_image30.png
│       ├── ...
│       └── cam3_image70.png
├── 1
│   └── ...
└── ...

PICO-40L, PICO-60, and some PICO-2L data are curently stored as archives on Compute Canada (CC) at:

Experiment Name

Data Series

Location on CC

PICO-40L

40l-19-data

/project/6007972/pico/40l-19-data

PICO-60

30l-16-data

/project/6007972/pico/30l-16-data

PICO-2L

2l-16-data

/project/6007972/pico/2l-16-data

The Event class is called by passing the full path to the run as the first argument, followed by the event number, followed by the name of the data to load as strings (either as a list, or as comma-separated values passed to *args). The requested data may include any or all of the following:

Load option

File location

Description of data

event

<run>/<event>/Event.txt

When the event began, trigger info, pressure setpoint, etc.

fastDAQ

<run>/<event>/fastDAQ_0.bin <run>/<event>/fastDAQ_1.bin <run>/<event>/fastDAQ_0_cal.txt <run>/<event>/fastDAQ_1_cal.txt

Data collected by the fastDAQ system. Includes signals from piezos, Dytrans.

slowDAQ

<run>/<event>/slowDAQ_0.txt

Data collected by the pressure cart PLC. Includes signals from pressure transducers, position transducers, valve states, DIO, etc. Similar data to PLC, but lower level and higher timing resolution.

temperature

<run>/<event>/temperature.txt

Data collected by the temperature PLC. Includes all RTDs, chiller setpoints, chiller loop flow data, humidity sensors.

PLC

<run>/<event>/PLClog.txt

Data collected over modbus from pressure cart. Lower timing resolution than slowDAQ, and contains some other info, e.g. PID values, sensor rms, etc.

DAQsettings

<run>/DAQ30l_Setup.xml

XML file containing run info from the DAQ VI. includes info such as setpoint, time spent compressed between events, pressure scan info (if used), etc.

rundata

<run>/<run>.txt

Text file containing all info from each events’ “Event.txt” file. Same info as the event option, but for all events in the run.

camdata

Currently Unimplemented.

Once the Event class has been loaded with one or more options, they are accessed via Event.<option>.<parameter>. For example, to access the data for PT4:

>>> event = PICOcode.Event("20200928_0", 24, 'slowDAQ')
>>> PT4 = event.slowDAQ.PT4

If working on a Graham node (a Compute Canada resource), or if you have altered your PICOcode.conf file appropriately, the path to the run directory may be substituted by the run ID only, and the Event class will search the locations contained in PICOcode.conf.

Example usage of the Event class

First, import PICOcode.
>>> import PICOcode
In this example, load fastDAQ, slowDAQ, and PLC data for the PICO-40L run 20200721_0 event 22.
>>> event = PICOcode.Event("20200721_0", 22, "fastDAQ", "slowDAQ", "PLC")
Loading  fastDAQ
Loading  slowDAQ
Loading  PLC
Load options may alternatively be passed as a list (for compatibility with old code). Use verbose=False to suppress info about loading data:
>>> event = PICOcode.Event("20200721_0", 22, ["fastDAQ", "slowDAQ", "PLC"], verbose=False)
If we wanted to plot the pressure of the freon for this event versus time, we can use matplotlib:
>>> import matplotlib.pyplot as plt
>>> plt.plot( event.slowDAQ.elapsed_time, event.slowDAQ.PT4 )
>>> plt.show()

Which would produce the following plot (with some added elements):

_images/Example_Plotting_PT4.png

The ReconFile class

Note that the ReconFile class is loaded to PICOcode’s namespace upon importing PICOcode, so it may be called with:

>>> PICOcode.ReconFile(...).

This class is used to access reconstructed PICO data, i.e. data that has been processed from its “raw” form. These data are stored in “recon files” located at /project/6007972/pico/recon/<branch>/<data-series>/output/, where <branch> is either current or devel, and <data-series> specifies the run series, e.g. 40l-19.

Within the recon output directory, each processed run has an associated recon folder containing its processed data. In addition, concatenated files exist in the output directory with data from all runs. The current list of processed files are:

File Prefix

Recon type

Data description

merged_all

2

Merged file containing data from all files.

merged_lvl1_xyz

2

Merged file with all data except XYZLookup

abub3hs

8

Pixel coordinates for bubbles in each event.

Dytran

1

Dytran info, t0, and fit parameters.

FastDAQ

1

Piezo info, t0, power, and AP parameters.

History

1

Pressure and temperature data at trigger, Seitz threshold, alarm conditions.

xyz_L1

2

Level one 3D reconstruction.

XYZLookup

2

Level two 3D reconstruction.

If the recon file being loaded is for a run (and it is located in output/<run>/), then the file prefix is appended with _<run>.txt. If the file is the concatenated file (located in output/), then the prefix is instead appended with _all.txt.

All data from recon files are loaded into numpy arrays. This is useful for making cuts; see the example below for how to do so.

Example usage of the ReconFile class

First, import PICOcode
>>> import PICOcode
An example of loading the merged_all file from the output directory in the current branch, then printing the freon pressure at the time of the trigger:
>>> recon = PICOcode.ReconFile("/project/6007972/pico/recon/current/40l-19/output/merged_all_all.txt")
>>> print(recon.PT4)
[45.95491 45.01076 52.85783 ... 56.51385 56.51385 45.03973]
>>> print(type(recon.PT4))
<class 'numpy.ndarray'>
Making a cut to scan for events at 35 psi, and which triggered within 1 PSI of the intended setpoint, then printing them one per line:
>>> cut = (recon.pset==35) & ( abs(recon.pset - recon.PT4) <= 1 )
>>> for run, ev in zip(recon.run[cut], recon.ev[cut]): print(run, ev)
20200107_3 0
20200107_3 1
20200107_3 2
20200107_3 3
20200107_3 4
20200107_3 5
20200109_0 0
...
Update the cut to only include data after run 20200713_7 (this is the first run in which four cameras were recording data). Load the level two reconstruction, and plot X versus Y for events from the previous cut in which the bubble nucleated at least 5 mm from the wall (note this is ignoring the jar dome):
>>> cut = cut & (recon.run>"20200713_7")
>>> import matplotlib.pyplot as plt
>>> xyz = PICOcode.ReconFile("/project/6007972/pico/recon/current/40l-19/output/XYZLookup_all.txt")
>>> R = 145 # Outer jar radius, in mm
>>> for run, ev in zip(recon.run[cut], recon.ev[cut]):
...     xyzcut = (xyz.run==run) & (xyz.ev==ev)
...     if xyz.cR2[cut]**0.5 < R-5: plt.plot( xyz.cX[cut], xyz.cY[cut], 'ob' )

The above example would produce the following plot (with some added decoration):

_images/Example_ReconFile.png

Note that XYZLookup_all.txt should be merged into merged_all_all.txt, but this is not currently possible. In the future (when it is merged), the above example will not be useful.