ecoli.analysis.multigeneration.selected_fluxes
Plot selected metabolic fluxes across an entire lineage (one seed, all generations) as a single continuous trace versus cumulative time.
This is the multigeneration analog of ecoli.analysis.single.selected_fluxes. Because this is a multigeneration analysis, history_sql spans every generation of a single (experiment, variant, lineage_seed), so each reaction’s flux is drawn as across all generations (with some initial time steps in each cell omitted, see the _drop_second_time_step() function for more info on this).
- ecoli.analysis.multigeneration.selected_fluxes._drop_second_time_step(flux_data)[source]
Remove the second timestep (first remaining timestep after “remove_first=True” is used) of each cell.
The first two timesteps of each cell are not representative of the cell’s own metabolism and are trimmed before plotting/averaging. The reasoning below is for sims run with the metabolism.py process (ecoli/processes/metabolism.py). Note: the listeners in the metabolism_redux processes behave the same way.
First timestep: removed upstream by “remove_first=True” in the read_stacked_columns call:
Metabolism computes values at a single instant from the current cell’s state, so it only solves when next_update_time <= global_time (in metabolism.py update_condition()), and listeners are emitted at the start of a step, before that step’s solve (each row is emitted before that increment’s solve, so the first row of a cell is never computed by that cell, it is a carried over value from previous parent cell’s last solve (or is an uninitialized default for generation 1)).
The base_reaction_fluxes listener is declared with listener_schema: updater “set” with no divider, so at division vivarium’s default “set” divider copies the parent’s value to each daughter unchanged. So a daughter’s first flux equals the parent’s flux at the division instant, not the parent’s last recorded row, which was emitted one solve earlier (or the schema default (~0) for generation 1, which has no parent). The parent’s post-solve value is never emitted on the parent side (it only shows up as the daughter’s first row). Because the first timestep value represents the parent’s metabolism (or an uninitialized default for generation 1) and not the daughter’s, it is filtered out from the plot and the average calculation.
- Second timestep – removed by this function:
The first actual solve after division tends to overshoot. The homeostatic driving target is (target_conc-current_conc)/timestep (in metabolism.py). At cell birth, counts were halved at division and other processes have already advanced a step or two without metabolism replenishing pools, so the difference between the current_conc and target_conc might be larger than usual. This can drive an unusually large flux near the start of the cell, and thus can cause a large-magnitude spike at the start of a cell that is uncommon throughout the rest of its lifetime, so it is dropped here from the plot too. Since remove_first already dropped the first timestep, the earliest row remaining per cell is this second timestep. Note: this will not remove spikes that occur later in a cell’s lifetime.
- Parameters:
flux_data (DataFrame) – Polars DataFrame (already filtered with remove_first=True) including the cell identity columns (experiment_id, variant, lineage_seed, generation, agent_id) and time.
- Returns:
flux_data with the minimum-time row of each cell removed (the second timestep of each cell).
- Return type:
DataFrame
- ecoli.analysis.multigeneration.selected_fluxes._extract_average_fluxes(flux_data, reaction_set_ids)[source]
Compute the mean and standard deviation of flux for each reaction.
Statistics are computed over whatever rows are present in
flux_data; callers that want to exclude the unreliable first two timesteps of each cell should pass data already filtered withremove_first=Trueand_drop_second_time_step().
- ecoli.analysis.multigeneration.selected_fluxes._mark_generations(ax, gen_bounds)[source]
Draw dotted vertical lines at generation boundaries (division events).
- Parameters:
ax – Matplotlib axis to annotate.
gen_bounds (DataFrame) – Polars DataFrame with “generation” and “t_start” columns, sorted by generation.
- Return type:
None
- ecoli.analysis.multigeneration.selected_fluxes.esc_latex(s)[source]
Helper function to escape a string for LaTeX.
- ecoli.analysis.multigeneration.selected_fluxes.plot(params, conn, history_sql, config_sql, success_sql, sim_data_paths, validation_data_paths, outdir, variant_metadata, variant_names)[source]
All options have default values (do not need to be explicitly provided).
- Parameters:
Dictionary of parameters given under analysis name in configuration JSON. Config options look like this:
{ // show_enzyme_counts: whether to show enzyme counts along with fluxes "show_enzyme_counts": true, // show_flux_averages: whether to show flux averages along with fluxes "show_flux_averages": false, // Height of each row and width of each column in inches. // Defaults to 3 and 4 inches, respectively. // Overidden if "figsize" is provided! "row_height": 3, "col_width": 4, // figsize: custom figure size "figsize": [12, 9], // mark_generations: whether to draw dotted vertical lines at // generation boundaries and label each generation (default true). "mark_generations": true, // plot_reactions: list of reaction-sets to plot in each row. "plot_reactions" : [ // "Reaction-sets" can be: // (1) single strings, for the ID of a single reaction "PGLUCISOM-RXN", // (2) lists of string IDs ["3PGAREARR-RXN", "RXN-15513"], // (3) dictionaries of string IDs paired with human-readable labels {"PEPDEPHOS-RXN" : "PEP kinase", "PEPSYNTH-RXN": "PEP synthase"} ] }
conn (DuckDBPyConnection)
history_sql (str)
config_sql (str)
success_sql (str)
outdir (str)