BCLIF
BCLIF is the LIF-scan container exposed by
BCExperiment as exp.lif whenever the experiment
folder contains a lif/ subdirectory. On construction it reads
lif/lifparams.csv and lif/processing.csv and pulls the
DelayPoints / DelayStart / DelayStep and
LaserPoints / LaserStart / LaserStep rows out of the experiment
header so the full delay × laser scan grid is known up-front. Per-point
trace files are read lazily through get_trace; opening an LIF
experiment is therefore cheap regardless of grid size.
The class provides three aggregating helpers — delay_slice,
laser_slice, and image — that integrate every present scan point
with one processing-override surface, returning numpy arrays sized
against the full scan axes. Scan grids are routinely incomplete:
acquisitions can stop partway through, leaving some (lIndex, dIndex)
positions with no trace file. Those positions are filled with np.nan
by default; pass fill=0.0 (or any other numeric value) when a
zero-baseline image is preferred. The has_ref attribute reports
whether any point in the scan recorded a reference channel, derived
from the refsize column of lifparams.csv.
The on-disk format that BCLIF reads — the lif/ subdirectory
layout, the meaning of each lifparams.csv column, the
processing.csv integration-gate settings — is documented in detail
on the LIF Data Storage
user-guide page.
When the experiment’s LIF setup runs the tunable laser through one or
more optical conversion stages — a doubling crystal, a mixing crystal —
before the beam reaches the sample, BCLIF also exposes the recorded
frequency-conversion topology, read from liftopology.csv via
BCExperiment.liftopology (None for a bare laser with no
conversion stages); see the file’s column reference on the
LIF Data Storage page. The
boolean has_topology reports whether a topology was recorded;
stages, final_stage, and laser_key name every stage in it,
the stage whose output is the excitation beam reaching the sample, and
the laser the topology was built around.
Three accessors translate between the laser’s tuning value and any
beam in the topology, in any of cm-1 / nm / GHz / eV:
fundamental answers “what laser setting produced this beam value?”;
at_stage is the inverse, answering “what beam value does this laser
setting produce?”; and stage_frequencies returns a table of every
beam in the topology for a single laser setting or excitation-beam
value. All three fall back to the identity relationship (the laser
setting is the excitation-beam value) when has_topology is
False.
API Reference
- class blackchirp.BCLIF(path: str, sep: str, header: pandas.DataFrame, liftopology: pandas.DataFrame | None = None)
Container for a complete LIF scan.
Loads
lif/lifparams.csvandlif/processing.csvand reads the scan-axis parameters (DelayStart,LaserStart, etc.) from the supplied experiment header. Exposes per-point access viaget_trace()and aggregating helpers (delay_slice(),laser_slice(),image()) that integrate every present point with a single processing-override surface.This class is not intended to be instantiated directly; it is constructed by
BCExperimentwhenever the experiment folder contains alif/subdirectory.- Parameters:
path – Experiment folder path.
sep – CSV delimiter for the experiment.
header – Experiment header DataFrame (
BCExperiment.header) used to readLifConfigscan-axis rows.
- lifparams
Contents of
lif/lifparams.csv.- Type:
pd.DataFrame
- proc
Contents of
lif/processing.csvparsed to typed values (ints, floats, bools).- Type:
dict
- delay_points
Number of delay-axis points.
- Type:
int
- delay_start
First delay value, in
delay_units.- Type:
float
- delay_step
Delay-axis step, in
delay_units.- Type:
float
- delay_units
Units of
delay_start/delay_step.- Type:
str
- laser_points
Number of laser-axis points.
- Type:
int
- laser_start
First laser value, in
laser_units.- Type:
float
- laser_step
Laser-axis step, in
laser_units.- Type:
float
- laser_units
Units of
laser_start/laser_step.- Type:
str
- has_ref
Trueif anylifparamsrow hasrefsize > 0.- Type:
bool
- numtraces
Number of populated scan points (
len(lifparams)).- Type:
int
- has_topology
Trueif a non-identity frequency-conversion topology (liftopology.csv) was recorded for this experiment.- Type:
bool
- stages
Conversion-stage hardware keys in DAG order (empty when
has_topologyisFalse).- Type:
list[str]
- final_stage
Hardware key of the stage that produces the output (excitation) beam, or
Nonefor the identity case.- Type:
str | None
- laser_key
Hardware key of the tunable laser the topology was captured against, or
Nonefor the identity case.- Type:
str | None
- at_stage(fundamental, at='final', side='output', unit='cm-1')
Evaluate the beam at one location for a given grating fundamental.
Inverse of
fundamental(). Accepts a scalar or a NumPy array of fundamentals and matches the input shape on output, so a whole laser sweep can be mapped in one call.- Parameters:
fundamental – Grating fundamental value(s) in
unit.at – A stage’s hardware key,
"final", or"laser".side –
"output"or"input"(primary input) of the stage.unit – Unit of both
fundamentaland the result.
- Returns:
The beam value(s) at
at/sideinunit(scalar or array, matchingfundamental).- Raises:
KeyError – If
atnames an unknown stage.
Example
>>> lif.at_stage(560.0, at="final", side="output", unit="nm") 280.0
- delay_axis() Tuple[numpy.ndarray, str]
Return the delay-axis sample values and their units string.
- delay_slice(l_index: int, fill=numpy.nan, **proc) Tuple[numpy.ndarray, numpy.ndarray]
Integrate every present trace at one laser index.
- Parameters:
l_index – Laser-axis index to slice.
fill – Value substituted at delay indices that have no acquired trace (
np.nanby default; pass0.0to represent missing points as zero).**proc – Optional processing overrides, forwarded to
BCLifTrace.integrate().
- Returns:
Tuple
(delay_axis, integrals). Length of both isdelay_points.
- fundamental(value, at='final', side='output', unit='cm-1')
Solve for the grating fundamental from a beam value at one location.
Because every beam is affine in the tunable fundamental, this is an exact inversion (no numerics). Accepts a scalar or a NumPy array and matches the input shape on output.
- Parameters:
value – Beam value(s) at the location, expressed in
unit.at – A stage’s hardware key,
"final"(the output beam), or"laser"(the fundamental itself).side –
"output"or"input"of the stage (the primary, tunable-path input for a two-input mixing stage). Ignored forat="laser".unit – Unit of both
valueand the returned fundamental — one ofcm-1,nm,GHz,eV(or aheader.csvlabel such ascm⁻¹).
- Returns:
The grating fundamental in
unit(scalar or array, matchingvalue). For an experiment with no conversion, the fundamental equalsvalueatatin{"final", "laser"}.- Raises:
ValueError – If the location does not depend on the tunable laser (e.g. a fixed mixing beam), so it cannot be inverted.
KeyError – If
atnames an unknown stage.
Example
>>> lif.fundamental(280.0, at="final", side="output", unit="nm") 560.0
- get_trace(l_index: int, d_index: int) BCLifTrace
Load a single
BCLifTracefrom disk.- Parameters:
l_index – Laser-axis index.
d_index – Delay-axis index.
- Returns:
A freshly loaded
BCLifTrace.- Raises:
KeyError – If no row exists in
lifparams.csvfor the requested(l_index, d_index)pair (i.e. the scan point was not acquired).
- image(fill=numpy.nan, **proc) Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray]
Integrate every present trace into a 2D delay × laser image.
- Parameters:
fill – Value substituted at
(d, l)positions that have no acquired trace.**proc – Optional processing overrides, forwarded to
BCLifTrace.integrate().
- Returns:
Tuple
(delay_axis, laser_axis, integrals)whereintegralshas shape(delay_points, laser_points).
- laser_axis() Tuple[numpy.ndarray, str]
Return the laser-axis sample values and their units string.
- laser_slice(d_index: int, fill=numpy.nan, **proc) Tuple[numpy.ndarray, numpy.ndarray]
Integrate every present trace at one delay index.
- Parameters:
d_index – Delay-axis index to slice.
fill – Value substituted at laser indices that have no acquired trace.
**proc – Optional processing overrides, forwarded to
BCLifTrace.integrate().
- Returns:
Tuple
(laser_axis, integrals). Length of both islaser_points.
- stage_frequencies(value=None, *, fundamental=None, at='final', side='output', unit='cm-1') pandas.DataFrame
Tabulate every stage’s inputs and output for a single frequency.
Give it either a known beam
valueat a location (at/side) or afundamentaldirectly; it solves for the fundamental and evaluates the whole graph. This is the single-frequency, descriptive counterpart tofundamental()/at_stage()— for a bulk sweep, call those with an array (an(N, M)grid isnp.stack([lif.at_stage(f, at=s) for s in lif.stages], axis=1)).- Parameters:
value – A known beam value at
at/side, inunit. Mutually exclusive withfundamental.fundamental – The grating fundamental in
unit. Mutually exclusive withvalue.at – Location that
valuerefers to (stage key,"final", or"laser").side –
"output"or"input"thatvaluerefers to.unit – Unit for the input and every value in the returned frame.
- Returns:
A
pandas.DataFrameindexed by"laser"followed by each stage’s hardware key, with columnsop,isfinal,input0,input1(NaNfor single-input stages), andoutput, all inunit. For an experiment with no conversion, only thelaserrow is returned.- Raises:
ValueError – If neither or both of
value/fundamentalare given, or a non-scalar is passed.KeyError – If
atnames an unknown stage.