Uniform Compartment – Reference Guide

This guide is for versions 1.0 Beta 39+

Source code

Class ExcessiveTimeStepHard

    Used to raise Exceptions arising from excessively large time steps
    (that lead to negative concentration values, i.e. "HARD" errors)
    



Class ExcessiveTimeStepSoft

    Used to raise Exceptions arising from excessively large time steps
    (that lead to norms regarded as excessive because of user-specified values, i.e. "SOFT" errors)
    



Class UniformCompartment

    Used to simulate the dynamics of reactions (in a single compartment.)
    This might be thought of as a "zero-dimensional system"
    
nameargumentsreturns
__init__chem_data=None, names=None, preset="mid"
        Note: AT MOST 1 of the following 2 arguments can be passed
        :param chem_data:   [OPTIONAL 1] Object of type "ChemData" (with data
                                         about the chemicals and their reactions)
        :param names:       [OPTIONAL 2] A single name, or list or tuple of names, of the chemicals;
                                         the reactions can be added later, with calls to add_reaction().
                                         Providing a list is useful to make the chemicals appear in a particular desired order

        :param preset:  String with code that can be adjusted make the time resolution finer or coarser;
                        it will stay in effect from now on, unless explicitly changed later
        

TO SET AND READ DATA

nameargumentsreturns
get_chem_dataselfChemData

        :return:    Object of type "ChemData"
        
nameargumentsreturns
set_concconc: Union[list, tuple, dict], snapshot=TrueNone
        Set the concentrations of ALL the chemicals at once

        :param conc:    EITHER
                            (1) a list or tuple of concentration values for ALL the registered chemicals,
                                in their index order
                            OR
                            (2) a dict indexed by the chemical names, for some or all of the chemicals
                                Anything not specified will be set to zero.
                                EXAMPLE:  {"A": 12.4, "B": 0.23, "E": 2.6}

                        Note: any previous values will get over-written

        :param snapshot:[OPTIONAL] If True (default), add to the history
                            a snapshot of this state being set
        :return:        None
        
nameargumentsreturns
set_single_concconc, species_index=None, species_name=None, snapshot=TrueNone
        Set the concentrations of 1 chemical
        Note: if both species_index and species_name are provided, species_name is used

        :param conc:            A non-negative number with the desired concentration value
                                    of the chemical specified below.  (Any previous value will get over-written)
        :param species_index:   (OPTIONAL) An integer that indexes the chemical of interest (numbering starts at 0)
        :param species_name:    (OPTIONAL) A name for the chemical of interest.
                                    If both species_index and species_name are provided, species_name is used
                                    At least one of "species_index" and "species_name" must be specified
        :param snapshot:        (OPTIONAL) boolean: if True, add to the history
                                    a snapshot of this state being set.  Default: True
        :return:                None
        
nameargumentsreturns
get_system_concselfnp.array
        Retrieve the concentrations of ALL the chemicals as a Numpy array

        :return:        A Numpy array with the concentrations of ALL the chemicals,
                        in their index order
                            EXAMPLE:  array([12.3, 4.56, 0.12])
                                      The 0-th chemical has concentration 12.3, and so on...
        
nameargumentsreturns
get_chem_concname: strfloat
        Return the current system concentration of the given chemical, specified by its name.
        If no chemical by that name exists, an Exception is raised

        :param name:    The name of a chemical species
        :return:        The current system concentration of the above chemical
        
nameargumentsreturns
get_conc_dictspecies=None, system_data=Nonedict
        Retrieve the concentrations of the requested chemicals (by default all),
        as a dictionary indexed by the chemical's name

        :param species:     (OPTIONAL) list or tuple of names of the chemical species; by default, return all
        :param system_data: (OPTIONAL) a Numpy array of concentration values, in the same order as the
                                index of the chemical species; by default, use the SYSTEM DATA
                                (which is set and managed by various functions)
        :return:            A dictionary, indexed by chemical name, of the concentration values;
                                EXAMPLE: {"A": 1.2, "D": 4.67}
        
nameargumentsreturns
clear_reactionsselfNone
        Get rid of all reactions; start again with "an empty slate" (but still with reference
        to the same data object about the chemicals)

        :return:    None
        

TO VISUALIZE SYSTEM

nameargumentsreturns
describe_stateselfNone
        Print out various data on the current state of the system
        :return:        None
        

TO SET AND DESCRIBE REACTIONS

nameargumentsreturns
add_reaction**kwargsint
        Register a new SINGLE chemical reaction,
        optionally including its kinetic and/or thermodynamic data.

        For details, see ChemData.add_reaction()

        :param kwargs:  Any arbitrary named arguments
        :return:        Integer index of the newly-added reaction
        
nameargumentsreturns
describe_reactions**kwargsNone
        Print out a user-friendly plain-text form of ALL the reactions.

        For details, see ChemData.describe_reactions()

        :param kwargs:  Any arbitrary named arguments
        :return:        None
        
nameargumentsreturns
number_of_reactionsselfint
        Return the number of registered chemical reactions

        :return:    The number of registered chemical reactions
        
nameargumentsreturns
plot_reaction_networkgraphic_component :str, unpack=FalseNone
        Send a plot of the network of reactions to the HTML log file,
        also including a brief summary of all the reactions

        EXAMPLE of usage:  plot_reaction_network("vue_cytoscape_2")

        :param graphic_component:   The name of a Vue component that accepts a "graph_data" argument,
                                        an object with the following keys
                                        'structure', 'color_mapping' and 'caption_mapping'
                                        For more details, see ChemData.prepare_graph_network()
        :param unpack:              Use True for Vue components that require their data unpacked into individual arguments;
                                        False for that accept a single data argument, named "graph_data"
        :return:                    None
        

TO PERFORM THE REACTIONS

nameargumentsreturns
specify_stepstotal_duration=None, time_step=None, n_steps=None(float, int)
        If either the time_step or n_steps is not provided (but at least 1 of them must be present),
        determine the other one from total_duration

        Their desired relationship is: total_duration = time_step * n_steps

        :param total_duration:  Float with the overall time advance (i.e. time_step * n_steps)
        :param time_step:       Float with the size of each time step
        :param n_steps:         Integer with the desired number of steps
        :return:                The pair (time_step, n_steps)
        
nameargumentsreturns
single_compartment_reactduration=None, target_end_time=None, stop=None, initial_step=None, n_steps=None, max_steps=None, snapshots=None, silent=False, variable_steps=True, explain_variable_steps=None, reaction_duration=NoneNone
        Perform ALL the (previously-registered) reactions in the single compartment -
        based on the INITIAL concentrations stored in self.system

        Update the system state and the system time accordingly
        (object attributes self.system and self.system_time)

        :param duration:        The overall time advance for the reactions (it may be exceeded in case of variable steps)
        :param reaction_duration: [OBSOLETE OLD NAME for "duration"; being phased out]
        :param target_end_time: The final time at which to stop the reaction; it may be exceeded in case of variable steps
                                    If both `target_end_time` and `duration` are specified, an error will result

        :param initial_step:    The suggested size of the first step (it might be reduced automatically,
                                    in case of "hard" errors resulting from overly-large steps)

        :param stop:            Pair of the form (termination_keyword, termination_parameter), to indicate
                                    the criterion to use to stop the reaction
                                    EXAMPLES:
                                        ("conc_below", (chem_name, conc))  Stop when conc first dips below
                                        ("conc_above", (chem_name, conc))  Stop when conc first rises above

        :param n_steps:         The desired number of steps

        :param max_steps:       (OPTIONAL) Max numbers of steps; if reached, it'll terminate regardless of any other criteria

        :param snapshots:       (OPTIONAL) Dict that may contain any the following keys:
                                        -"frequency" (default 1)
                                        -"species" (default None, meaning all species)
                                        -"initial_caption" (default: "1st reaction step")
                                        -"final_caption" (default: "last reaction step")
                                    If provided, take a system snapshot after running a multiple
                                    of "frequency" reaction steps (default 1, i.e. at every step.)
                                    EXAMPLE: snapshots={"frequency": 2, "species": ["A", "H"]}

        :param silent:              If True, less output is generated

        :param variable_steps:      If True, the steps sizes will get automatically adjusted, based on thresholds
        :param explain_variable_steps:  If not None, a brief explanation is printed about how the variable step sizes were chosen,
                                            when the System time inside that range;
                                            only applicable if variable_steps is True

        :return:                None.   The object attributes self.system and self.system_time get updated
        
nameargumentsreturns
reaction_step_commondelta_time: float, conc_array=None, variable_steps=False, explain_variable_steps=None, step_counter=1(np.array, float, float)
        This is the common entry point for both single-compartment reactions,
        and the reaction component of reaction-diffusions in 1D, 2D and 3D.

        "Compartments" may or may not correspond to the "bins" of the higher layers;
        the calling code might have opted to merge some bins into a single "compartment".

        Using the given concentration data for all the applicable species in a single compartment,
        do a single reaction time step for ALL the reactions -
        based on the INITIAL concentrations (prior to this reaction step),
        which are used as the basis for all the reactions.

        Return the increment vector for all the chemical species concentrations in the compartment

        NOTES:  * the actual system concentrations are NOT changed
                * this method doesn't decide on step sizes - except in case of ("hard" or "soft") aborts, which are
                    followed by repeats with a smaller step.  Also, it makes suggestions
                    to the calling module about the next step to best take (whether as a result of an abort,
                    or for other considerations)

        :param delta_time:      The requested time duration of the reaction step
        :param conc_array:      [OPTIONAL]All initial concentrations at the start of the reaction step,
                                    as a Numpy array for ALL the chemical species, in their index order.
                                    If not provided, self.system is used instead
        :param variable_steps:  If True, the step sizes will get automatically adjusted with an adaptive algorithm
        :param explain_variable_steps:  If not None, a brief explanation is printed about how the variable step sizes were chosen,
                                            when the System time inside that range;
                                            only applicable if variable_steps is True
        :param step_counter:

        :return:                The triplet:
                                    1) increment vector for the concentrations of ALL the chemical species,
                                        in their index order, as a Numpy array
                                        EXAMPLE (for a single-reaction reactant and product with a 3:1 stoichiometry):
                                            array([7. , -21.]) 
                                    2) time step size actually taken - which might be smaller than the requested one
                                        because of reducing the step to avoid negative-concentration errors
                                    3) recommended_next_step : a suggestions to the calling module
                                       about the next step to best take
        
nameargumentsreturns
validate_incrementdelta_conc, baseline_conc: float, rxn_index :int, species_index: int, delta_timeNone
        Examine the requested concentration change given by delta_conc
        (typically, as computed by an ODE solver),
        relative to the baseline (pre-reaction) value baseline_conc,
        for the given SINGLE chemical species and SINGLE reaction.

        If the concentration change would render the concentration negative,
        raise an Exception (of custom type "ExcessiveTimeStepHard")

        :param delta_conc:              The change in concentration computed by the ode solver
                                            (for the specified chemical, in the given reaction)
        :param baseline_conc:           The initial concentration

        [The remaining arguments are ONLY USED for error printing]
        :param rxn_index:               The index (0-based) to identify the reaction of interest (ONLY USED for error printing)
        :param species_index:           The index (0-based) to identify the chemical species of interest (ONLY USED for error printing)
        :param delta_time:              The time duration of the reaction step (ONLY USED for error printing)

        :return:                        None (an Exception is raised if a negative concentration is detected)
        
nameargumentsreturns
compute_all_reaction_ratesrxn_list=Nonedict
        Compute the reaction rates, at the current chemical concentrations,
        for all the specified reaction (by default, all.)
        Return a dict with the rates indexed by the reaction number.

        For background info: https://life123.science/reactions

        :param rxn_list:    OPTIONAL list of reactions (specified by their integer index);
                                if None, do all the reactions.  EXAMPLE: [1, 3, 7]

        :return:            A dict of the reactions rates, as defined by compute_reaction_rate(),
                                at the current concentrations of chemicals in the system (as stored in self.system).
                                The dict is indexed by the reaction number, and contains as many entries as the
                                number of reactions being included in the simulation
        

MACROMOLECULE DYNAMICS

nameargumentsreturns
set_macromoleculesdata=NoneNone
        Specify the macromolecules, and their counts, to be included in the system.
        The fractional occupancy is set to 0 at all binding sites of all the specified macromolecules.
        Any previous data gets over-written.

        Note: to set a single fractional occupancy value, use set_occupancy()

        :param data:    A dict mapping macromolecule names to their counts
                            EXAMPLE:  {"M1": 1, "M2": 3, "M3": 1}
                        If any of the requested macromolecules isn't registered, an Exception will be raised
                        If data=None, then the set of registered macromolecules is used,
                            and all their counts are set to 1
        :return:        None.
                        The object variables self.macro_system and self.macro_system_state get set
        
nameargumentsreturns
set_occupancymacromolecule, site_number: int, fractional_occupancy: floatNone
        Set the fractional occupancy at the given binding site of the specified macromolecule,
        using the requested value.
        If the specified macromolecule hasn't yet been added to the dynamical system state,
        automatically add it with count 1

        :param macromolecule:           Name of a previously-registered macromolecule
        :param site_number:             Integer to identify a binding site on the macromolecule
        :param fractional_occupancy:    A number between 0. and 1., inclusive
        :return:                        None
        
nameargumentsreturns
get_occupancymacromolecule, site_numberfloat
        Get the fractional occupancy at the given binding site of the specified macromolecule.

        :param macromolecule:           Name of a previously-registered macromolecule
        :param site_number:             Integer to identify a binding site on the macromolecule
        :return:                        A number between 0. and 1., representing the fractional occupancy
        
nameargumentsreturns
update_occupancyselfNone
        Update the fractional occupancy at all binding sites,
        based on the current system concentrations of the relevant ligands

        :return:    None
        
nameargumentsreturns
sigmoidconc: float, Kd: floatfloat
        Return an estimate of fractional occupancy (between 0 and 1)
        on a particular binding site on a particular macromolecule,
        from the concentration of the ligand (such as a Transcription Factor)
        and its affinity to that binding site.

        A sigmoid curve is expected.

        Based on fig. 3A of the 2019 paper "Low-Affinity Binding Sites and the
        Transcription Factor Specificity Paradox in Eukaryotes"
        (https://doi.org/10.1146/annurev-cellbio-100617-062719):

            - at extremely low concentration, the occupancy is 0
            - when the concentration is 10% of Kd, the occupancy is about 0.1
            - when the concentration matches Kd, the occupancy is 1/2 by definition
            - when the concentration is 10 times Kd, the occupancy is about 0.9
            - at concentrations beyond that, the occupancy saturates to 1.0

        :param conc:    Concentration of the ligand (such as a Transcription Factor), in microMolars
        :param Kd:      Binding-side Affinity, in microMolars
        :return:        Estimated binding-site fractional occupancy : a value between
                            0. (no occupancy at all during the previous time step) and 1. (continuous occupancy)
        
nameargumentsreturns
logisticx: float, x0 = 0., k = 1.float
        Compute the value of the Logistic function, in the range (0, 1), at the given point
        See: https://en.wikipedia.org/wiki/Logistic_function

        :param x:
        :param x0:
        :param k:
        :return:    The value of the Logistic function at the given point x
        

FOR DIAGNOSTICS

nameargumentsreturns
enable_diagnosticsself
        Turn on the diagnostics mode

        :return: None
        
nameargumentsreturns
pause_diagnosticsself
        Turn off the overall diagnostics mode; existing diagnostics data, if any, is left untouched

        :return:    None
        
nameargumentsreturns
get_diagnosticsself

        :return:    Object of type life123.diagnostics.Diagnostics
        

GRAPHICS

nameargumentsreturns
plot_historychemicals=None, colors=None, title=None, title_prefix=None, range_x=None, range_y=None, y_label=None, vertical_lines_to_add=None, show_intervals=False, show=Falsego.Figure
        Using plotly, draw the plots of concentration values over time, based on history data that gets
        automatically saved when running reactions.

        Note: if this plot is to be later combined with others, use PlotlyHelper.combine_plots()
              EXAMPLE:
                    from life123 import PlotlyHelper
                    p1 = plot_history(various args, show=False)
                    p2 = plot_history(various args, show=False)
                    PlotlyHelper.combine_plots([p1, p2], other optional args)

        :param chemicals:       (OPTIONAL) Name, or list of names, of the chemicals whose concentration changes are to be plotted;
                                    if None, then display all
        :param colors:          (OPTIONAL) Either a single color (string with standard plotly name, such as "red"),
                                    or list of names to use, in order; if None, then use the hardwired defaults
        :param title:           (OPTIONAL) Title for the plot;
                                    if None, use default titles that will vary based on the # of reactions; EXAMPLES:
                                    "Changes in concentrations for 5 reactions"
                                    "Reaction `A <-> 2 B` .  Changes in concentrations with time"
                                    "Changes in concentration for `2 S <-> U` and `S <-> X`"
        :param title_prefix:    (OPTIONAL) If present, it gets prefixed (followed by ".  ") to the title,
                                    whether the title is specified by the user or automatically generated
        :param range_x:         (OPTIONAL) list of the form [t_start, t_end], to initially show only a part of the timeline.
                                    Note: it's still possible to zoom out, and see the excluded portion
        :param range_y:         (OPTIONAL) list of the form [y_min, y_max], to initially show only a part of the y values.
                                    Note: it's still possible to zoom out, and see the excluded portion
        :param y_label:          (OPTIONAL) Caption to use for the y-axis.
                                    By default, the name in `the chemicals` argument, in square brackets, if only 1 chemical,
                                    or "Concentration" if more than 1 (a legend also shown)
        :param vertical_lines_to_add: (OPTIONAL) Ignored if the argument `show_intervals` is specified.
                                    List or tuple or Numpy array or Pandas series
                                    of x-coordinates at which to draw thin vertical dotted gray lines.
                                    If the number of vertical line is so large as to overwhelm the plot,
                                    only a sample of them is shown.
                                    Note that vertical lines, if requested, go into the plot's "layout";
                                    as a result they might not appear if this plot is later combined with another one.
        :param show_intervals:  (OPTIONAL) If True, it over-rides any value passed to the `vertical_lines` argument,
                                    and draws thin vertical dotted gray lines at all the x-coords
                                    of the data points in the saved history data;
                                    also, it adds a comment to the title.
        :param show:            If True, the plot will be shown
                                    Note: on JupyterLab, simply returning a plot object (without assigning it to a variable)
                                          leads to it being automatically shown

        :return:                A plotly "Figure" object
        
nameargumentsreturns
plot_step_sizesshow_intervals=FalseNone
        Using plotly, draw the plot of the step sizes vs. time
        (only meaningful when the variable-step option was used).
        The same scale as plot_history() will be used.
        This function requires the diagnostics option to be turned on, prior to running the simulation

        :param show_intervals:  If True, will add to the plot thin vertical dotted gray lines
                                    at the time steps
        :return:                None
        

HISTORY

nameargumentsreturns
add_snapshotspecies=None, caption="", time=None, system_data=NoneNone
        Preserve some or all the chemical concentrations into the history,
        linked to the passed time (by default the current System Time),
        with an optional caption.

        EXAMPLES:  add_snapshot()
                    add_snapshot(species=['A', 'B']), caption="Just prior to infusion")

        :param species:     (OPTIONAL) list of name of the chemical species whose concentrations we want to preserve for later use.
                                If not specified, save all
        :param caption:     (OPTIONAL) caption to attach to this preserved data
        :param time:        (OPTIONAL) time value to attach to the snapshot (default: current System Time)
        :param system_data: (OPTIONAL) a Numpy array of concentration values, in the same order as the
                                index of the chemical species; by default, use the SYSTEM DATA
                                (which is set and managed by various functions)
        :return:            None
        
nameargumentsreturns
get_historyt_start=None, t_end=None, head=None, tail=None, t=None, columns=Nonepd.DataFrame
        Retrieve and return a Pandas dataframe with the system history that had been saved
        using add_snapshot().
        Optionally, restrict the result with a start and/or end times,
        or by limiting to a specified numbers of rows at the end

        :param t_start: (OPTIONAL) Start time in the "SYSTEM TIME" column.  Watch out for roundoff errors!
        :param t_end:   (OPTIONAL) End time.  Watch out for roundoff errors!
        :param head:    (OPTIONAL) Number of records to return,
                                   from the start of the history dataframe.
        :param tail:    (OPTIONAL) Number of records to consider, from the end of the history dataframe
        :param t:       (OPTIONAL) Individual time to pluck out from the dataframe;
                                   the row with closest time will be returned.
                                   If this parameter is specified, an extra column - called "search_value" -
                                   is inserted at the beginning of the dataframe.
                                   If either the "head" or the "tail" arguments are passed, this argument will get ignored
        :param columns: (OPTIONAL) Name, or list of names, of the column(s) to return; if not specified, all are returned.
                                   Make sure to include "SYSTEM TIME" in the list, if the time variable needs to be included

        :return:        A Pandas dataframe
        
nameargumentsreturns
get_historical_concentrationsrow=None, t=None, df=Nonenp.array
        Typically used to retrieve a system snapshot from its history.

        Return a Numpy array with ALL the chemical concentrations (in their index order)
        from one row in the given Pandas data frame (by default, the system history);
        the row can be identified either by it row number, or by the system time.

        :param row: (OPTIONAL) Integer with the zero-based row number of the system history
                        (which is a Pandas data frame)
        :param t:   (OPTIONAL) Individual time to pluck out from the dataframe;
                        the row with closest time will be returned.
                        Exactly one of "t" and "row" must be specified
        :param df:  (OPTIONAL) A Pandas data frame with concentration information in columns that have
                        the names of the chemicals (if None, the system history is used)
        :return:    A Numpy array of floats.  EXAMPLE: array([200., 40.5])
        

RESULT ANALYSIS

nameargumentsreturns
is_in_equilibriumrxn_index=None, conc=None, tolerance=1, explain=True, verbose=NoneUnion[bool, dict]
        Ascertain whether the given concentrations (by default the current System concentrations)
        are in equilibrium for the specified reactions (by default, for all reactions)

        :param rxn_index:   The integer index (0-based) to identify the reaction of interest;
                                if None, then check all the reactions
        :param conc:        Dict with the concentrations of the species involved in the reaction(s).
                            The keys are the chemical names
                                EXAMPLE: {'A': 23.9, 'B': 36.1}
                            If None, then use the current System concentrations instead
        :param tolerance:   Allowable relative tolerance, as a PERCENTAGE,
                                to establish satisfactory match with expected values
        :param explain:     If True, print out details about the analysis,
                                incl. the formula(s) being used to check the equilibrium
                                EXAMPLES:   "([C][D]) / ([A][B])"
                                            "[B] / [A]^2"
        :param verbose:     Alternate name for argument `explain`

        :return:            Return True if ALL the reactions are close enough to an equilibrium,
                                as allowed by the requested tolerance;
                                otherwise, return a dict of the form {False: [list of reaction indexes]}
                                for all the reactions that failed the criterion
                                EXAMPLE:  {False: [3, 6]}
        
nameargumentsreturns
reaction_in_equilibriumrxn_index :int, conc, tolerance, explain :boolbool
        Ascertain whether the given concentrations are in equilibrium for the specified SINGLE reaction;
        return True or False, accordingly.

        Pathological case: if at least one of the reactants AND at least one of the products have zero
        concentration, then the reaction is "stuck" - and thus regarded in "equilibrium"

        :param rxn_index:   The integer index (0-based) to identify the reaction of interest
        :param conc:        Dictionary with the concentrations of the species involved in the reaction.
                            The keys are the chemical names
                                EXAMPLE: {'A': 23.9, 'B': 36.1}
        :param tolerance:   Allowable relative tolerance, as a PERCENTAGE,
                                to establish satisfactory match with expected values
        :param explain:     If True, print out the formula being used, as well as the concentrations (reactants, then products)
                                and some other info.
                                EXAMPLES of formulas:   "([C][D]) / ([A][B])"
                                                        "[B] / [A]^2"
        :return:            True if the given reaction is close enough to an equilibrium,
                                as allowed by the requested tolerance
        
nameargumentsreturns
find_equilibrium_concrxn_index :intdict
        Determine the equilibrium concentrations that would be reached by the chemicals
        participating in the specified reversible reaction, given their current concentrations,
        IN THE ABSENCE of any other reaction.

        RESTRICTIONS:  currently limited to just aA + bB <-> cC + dD reversible reactions,
                       first-order in all chemicals (some of the terms may be missing.)
                       An Exception will be raised in all other cases!

        :param rxn_index:   The integer index (0-based) to identify the reaction of interest
        :return:            A dictionary of the equilibrium concentrations of the
                                chemicals involved in the specified reaction
                            EXAMPLE:  {'A': 24.0, 'B': 36.0, 'C': 1.8}
        
nameargumentsreturns
reach_thresholdchem :str, thresholdUnion[float, None]

        :param chem:        The name of the chemical of interest
        :param threshold:   A number with the concentration value being sought
        :return:            The time at which the linearly-interpolated concentration of the specified chemical
                                first reaches the given threshold;
                                or None if it never does (in which case a message is printed)
        
nameargumentsreturns
curve_intersectchem1 :str, chem2 :str, t_start, t_end, explain=False(float, float)
        Find and return the intersection of the concentrations of the chemicals chem1 and chem2,
        in the time interval [t_start, t_end]
        If more than one is present, only the first (smallest value of x-coord) one will be returned;
        so, the specified time interval should be narrow enough to bracket the intersection of interest

        :param chem1:   The name of the 1st chemical of interest
        :param chem2:   The name of the 2nd chemical of interest
        :param t_start: The start of the time interval being considered
        :param t_end:   The end of the time interval being considered
        :param explain: [OPTIONAL] If True, print out some details of the computation
        :return:        The pair (time of intersection, common value) ;
                            if not found, a message is printed, and None is returned
        
nameargumentsreturns
extract_delta_concentrationsdf, row_from :int, row_to :int, chem_list: [str]np.array
        Extract the concentration changes of the specified chemical species from a Pandas dataframe
        of concentration values

        EXAMPLE:  extract_delta_concentrations(my_dataframe, 7, 8, ['A', 'B'])

        :param df:          A Pandas dataframe of concentration values (it MUST contain columns
                                with the names given in chem_list
        :param row_from:    Row number of the first row of data we're interested in
        :param row_to:      Row number of the last row of data we're interested in
        :param chem_list:   A list of names of chemicals

        :return:            A Numpy array of floats