Welcome to The Smell Engine’s documentation!

About

The Smell Engine is a python framework for controlling an olfactometer to generate odors. See paper for more detail: [link to paper]

Smell Engine

class olfactometer.smell_engine.SmellEngine(total_flow_rate=4000, n_odorants=3, data_container=None, debug_mode=True, write_flag=False, PID_mode=False, look_up_table_path=None, oms=None)

Bases: object

add_molecule_id(id)

Add Odorant Molecule to system

id

int values uniquely identifying OM

append(val)
automation_set_mfc_setpoints(mfc_setpoints)

Override mfc setpoints in order B, A, C

ids

List[] specifying each MFCs flow rate relative to its max.

calculate_target_achieved_error()
close_smell_engine()
get_desired_concentrations()
get_mfc_setpoints()
get_molecular_ids()
get_valve_duty_cycles()
initialize_equipment()

Configure olfactometer equipment via Smell Composer component(s). First we specify molecules by CID number and define their vapor pressure, densities, and solution. Instantiate the olfactometer. Determines the jars and dilutions required to achieve the target specified per molecule. Perform optimization to configure olfactometer Format values for ValveDriver to execute.

initialize_olfactometer()

Initialize Smell Driver instance which instantiates a ValveDriver instance. This instance is then assigned to the Smell Composer so the communication line of odorant mixtures is established.

debug_mode

Flag denoting physical vs simulated hardware.

initialize_smell_engine_system(with_nidaq=True)

Presumes user has manually set OM ids before initializing system pipeline

property om_dilutions
property om_ids
print_data_binary(concentration)

A debug method for printing binary representations of concentrations (digital states of olfactometer).

Parameters

concentration (list of float) – List of concentration states.

set_desired_concentrations(concentrations)

Reads and writes concentration values to Smell Engine and Valve Driver

concentrations

List containing concentration values

set_mfc_setpoints(mfc_setpoints)

Override mfc setpoints

ids

mfc setpoints

set_odorant_molecule_dilutions(dilutions)

Assign PubChemID’s on startup to the Valve Driver cid’s.

ids

List containing uniquely identifiable int values of OM ids

set_odorant_molecule_ids(ids)

Assign PubChemID’s on startup to the Valve Driver cid’s.

ids

List containing uniquely identifiable int values of OM ids

set_olfactometer_target_outflow(m_flowrate)
set_starting_concentration(starting_vector)
set_valve_duty_cycles(valves)

Override valve duty cycles for system tests.

ids

Valve states

Odorant Molecules (PubChem)

Classes for odorants, mixtures, chemical orders, etc.

class olfactometer.odorants.Molecule(cid=None, name=None, fill=False, vapor_press=None, dens=None)

Bases: object

This module extracts various chemical and physical properties of odorous molecular compounds using PUG REST API. Portions of the codebase have benefitted from work by Maxim Shevelev <mdshev7@gmail.com>

For details about PubChem’s PUG REST API please visit https://pubchem.ncbi.nlm.nih.gov/pug_rest/PUG_REST.html

fill_details()

Populate odorant molecule properties to include molecular weight and cid/name.

get_addtional_om_props(required_properties)

Found method as courtesy of Maxim Shevelev (Github: @mawansui) 1. Accepts the compound name 2. Gets the CID for this compound 3. Searches PubChem for data for the specified CID 4. Cycles through the fetched data to select required fields 5. Returns a dictionary with specified properties of specified compound

get_cid_by_name(compound_name)
  1. Accepts the compound name

  2. Searches PubChem by this name

  3. Returns the compound’s PubChem CID

get_cid_from_api()

Obtain pub chem ID from OM name.

get_density()

Obtain odorant molecule density by searching density property and extra additional properties from PubChem.

get_vapor_pressure()

Obtain odorant molecule Vapor Pressure from PubChem.

pubchem_parsing(url)

Get the link to PubChem API, parse it for JSON and then translate that to Python dictionary; This is just to follow the DRY principle

Smell Controller

class olfactometer.smell_controller.SmellController(olfactometer, data_container=None, valve_driver=None, kdtree_flag=False, kdtree=None, smell_data_frame=None)

Bases: object

classmethod calc_conc(variables, n_jars, max_flow_rates, A)

This method is invoked externally to calculate concentration given olfactometer configuration variables: MUST BE np.array! Contains olfactometer mfc values and valve duty cycles n_jars: assigneed from class instance max_flow_rates: assigneed from class instance A: Gas Phase Concentration of jars x odorants

clean_valve_mfc_values(values=None, print_flow_rates=False)

Convert dictionary that looks like: {‘w1MFC_A_High’: 0.8840516038671588, ‘w2MFC_A_High’: 0, ‘w1MFC_B_Low’: 0.11551628411711523, ‘w2MFC_B_Low’: 0, ‘fMFC_A_High’: array(1.9601) * cc/min, ‘fMFC_B_Low’: array(0.01) * cc/min, ‘MFC_Carrier’: array(2198.0299) * cc/min}

find_odorant_id_by_index(m_index)
get_max_flow_rates()
get_vapor_concs_dense(target_odorants)
kdtree_lookup(target_dense, mfc_names, F)
lls_olfactometer_scheduler(vapor_concs_dense, target_outflow_rate_ccm, target_dense)
property max_outflow_rate

The maximum possible flow rate if all connected-in-parallel MFCs are at their maximum flow rates

mfc_flow_rates_to_voltages(values)
property olfactometer_schedule
optimization_report()

Report on optimization quality

optimize(target_outflow_concs=None, target_outflow_rate=None, report=False)

Find the valve duty cycles and MFC settings that bring this route closest to target. This will be done by varying actual valve positions and flow rates, so it should only be done on a virtual olfactometer

property outflow_fractions
property outflow_rates
classmethod residuals(variables, x, n_jars, max_flow_rates, total_vapor, fixed_A, fixed_B, alpha)

This method is invoked by the non-linear least squares solver. wNA: The fraction of time that valve N is in state A wNB: The fraction of the remaining time that valve N is in state B fA: The flow rate through MFC A fB: The flow rate through MFC B

property target_outflow
property target_outflow_concs
property target_outflow_rate
update_target(report=False)

Executed externally, ideally through the SmellEngineCommunicator class. This method takes the assigned target concentrations and flow rate then message passes target states through the Smell Engine pipeline.

update_target_from_logical()
olfactometer.smell_controller.calc_conc_jit(variables, n_jars, max_flow_rates, A)

Valve Driver

class olfactometer.valve_driver.ValveDriver(olfactometer=None, data_container=None, debug_mode=False, PID_mode=False)

Bases: object

ValveDriver is responsible for interfacing with NI-DAQmx hardware using the nidaqmx API. Provides continuous virtual communication lines with MFCs (analog) and olfactometer valves (digital).

olf

List of mfc voltage values indexed accurately.

cids

A ordered list of concentration ids for each valve.

abort()

Resets NI-DAQmx device to initialized state. All prior tasks and defined channel lines are aborted.

close_tasks()

Closes all created/open NIDAQ tasks.

convert_concentration_format(data_input, debug=False)

Read in a list of concentrations, convert each individual concentration A/B/off state into a sequence of binary control signals for olfactometer. :param d_in: :type d_in: list of `

Returns

A list of 5 concentration values and there states in A/B.

format_bits(valve_index, state)

Each valve state is identified by A, B, None. The valve state is represented as a 32 bit unsigned integer. If state A, first 16 bits are used to configure digital state of olfactometer. If state B, last 16 bits are used.

Parameters
  • valve_index (int) – Integer indexing of valve state (non-zero).

  • state (str) – A,B,None.

Returns

Binary representation of olfactometer state.

Return type

uint32

Raises

Exception – Invalid state requested.

generate_analog_frame_writes(analog_states)

Creates list of analog states to be written to MFCs.

Parameters

analog_states (dictionary of (int, float)) – A index identifier for MFC and the requested voltage values supplied.

Returns

List of voltage values per MFC.

Return type

(list of float)

generate_digital_frame_writes(valve_durations)

Create list (of size samples_per_frame) to populate with 32 bit values representative of the digital states for each valve.

To turn a respective valve on, it’s index location within first 16 bits of a 32-bit write must be set to high. To turn a respective valve off, it’s index location within last 16 bits of a 32-bit write must be set to high.

Parameters

valve_durations (dictionary of (int, float)) – Dictionary of valve indexes and their respective states (time in ms for A,B,off).

Returns

Writes will be written to olfactometer as multiline/channel write.

Return type

(list of uint32)

init_analog_in_task(num_samples=50, task_name='Analog_In', channel_name='analog_in_channel')

Initializing an Analog Input Task to read samples from the specified channel.

Parameters
  • num_samples (int) – Specify # of samples per channel, default is 50

  • task_name (str) – Task name, used for uniquely identifying task instance.

Returns

Success/error status represented with 1/-1.

Raises

nidaqmx.DaqError – An error occured when trying to add an analog voltage virtual communication channel.

init_analog_task(mfc_flat_list, task_name='Analog_Task', channel_name='analog_channel')

Initializing an Analog Output Task and Channel communication line. List of analog channels [0:3] along with voltage rates are initialized via nidaqmx API.

Parameters
  • mfc_flat_list – A 1D list containing analog channels and voltages.

  • task_name (str) – Task name, used for uniquely identifying task instance.

Returns

Success/error status represented with 1/-1.

Raises

nidaqmx.DaqError – An error occured when trying to add an analog voltage virtual communication channel.

init_digital_task(task_name='DigitalTask', channel_name='digital_channel')

Create task object with digital out on port0. Configure task to use 1 channel for all liens of communication. Append to list of tasks and update state of olfactometer.

Parameters
  • task_name (str) – Task name, used for uniquely identifying task instance.

  • channel_name (str) – Channel name, used for unuquely identifying different channel comm lines.

Returns

Success/error status represented with 1/-1.

Raises

nidaqmx.DaqError – An error occured when trying to add an analog voltage virtual communication channel.

initialize()

Reading and saving device information from NI-DAQmx hardware. Existing device references and tasks are reset.

Returns

Success/error status.

Raises
  • nidaqmx.DaqError – An error occurred when attempting to read NIDAQ hardware information.

  • Error codes can be found in the nidaqmx errors.py system file.

issue_odorants(valve_mfc_values)

Executed externally to update olfactometer schedule of odor samples, this method converts the olfactometer schedule into digital and analog control signals. Olfactometer Schedule Examples: {‘valves’:

[

(Valve Number, time in state A, time in state B) (1, 0.6523873263385962, 0.3433522990024116), (2, 0.6523929695332448, 0.34373085569547107)

], ‘mfcs’:

{MyMediumMFC: MFC_A_High (1.0 L/min): array(value) * V, MyLowMFC: MFC_B_Low (10.0 cc/min): array(value) * V, MyHighMFC: MFC_Carrier (10.0 L/min): array(values) * V}

}

Parameters

valve_mfc_values (dictionary of (str, float)) – Expected dictionary of mfc voltages and valve state durations.

print_data_binary(concentration)

A debug method for printing binary representations of concentrations (digital states of olfactometer).

Parameters

concentration (list of float) – List of concentration states.

set_mfc_setpoints(setpoints)

Toggle override for analog mfc setpoints and assign user defined setpoints

Parameters

setpoints – List containing voltage values per mfc

set_task_clock(task, samples_per_frame=50, frames_per_s=1, repeats=1)

Sets rate, number of samples, and source of the Sample Clock for developer-specified task.

Parameters
  • task (nidaqmx.task.Task) – Task object to which sample clock is being configured for.

  • samples_per_frame (int) – Samples generated per frame.

  • frames_per_s (int) – Frames per second for sampling.

  • repeats (int, optional) – Duplicating samples across each channel.

set_valve_states(valve_states)

Toggle overrides for digital valve states and assign user-defined valve states

Parameters

valve_states – A list of tuples containing high/low toggles for valve

specify_analog_frame_writes(analog_states)

Provide a defined list of analog states to be written to MFCs.

Parameters

analog_states (list of (float, int)) – A index identifier for MFC and the requested voltage values supplied.

Returns

List of voltage values per MFC.

Return type

(list of float)

specify_digital_frame_writes(valve_states)

Given valve states, samples are generated with half containing given valve state other half being off

Parameters

valve_states (list) – List of tuples representing A,B states for each valve

timer_pause()

Halts thread instance.

timer_resume()

Continues thread after being paused.

timer_run()

The ‘update’ method for the thread instance. Writes digital valve states to olfactometer at the defined timer_interval rate. If writing values is unsuccessfull thread instance halts.

timer_setup(interval=None)

Configuration of thread instance. Thread runs at a user-defined time interval to which it issues commands to hardware.

Parameters

interval (float) – Rate at which thread executs write commands to olfactometer.

timer_start()

Starts thread instance.

timer_stop()

Pauses thread, turns off all valve, ends defined virtual communication, and releases Task object instance from memory.

write_output(digital_values, analog_values=[])

This method will write a list of valve commands to olfactometer.

Parameters

None

Returns

Success/error message. 0 success, -1 error.

Return type

int

write_zeroes()

This method will write a list of valve commands to olfactometer.

Returns

A list of 5 concentration values and there states in A/B.

Indices and tables