progressbar

- Modelling -

Introduction


Throughout our project, we have developed our mathematical and computational models in order to facilitate experimental design, predict the function of our system, and to characterise the effect of our individual genetic components on its effectiveness as a whole.

Our computer-aided design approach composes three parts, each serving their distinct purposes:

  • Gene Regulatory Network (GRN) model: a system of ordinary differential equations modelling our engineered bacterium's intracellular dynamics.
  • Agent-based model (ABM): a computational model of our system, incorporating our GRN model to simulate each individual bacterium and its interactions with its environment.
  • Population model: a system of partial differential equations, modelling our system at a higher level of abstraction and greater scale.

Gene Regulatory Network


Equations

In our first model, we utilise a system of ordinary differential equations representing the bacterium's gene regulatory network, in order to inform parameters for optimal uptake and cell stress.

We first defined a function of time representing the production of LutH, composing two Hill equations—the first representing the natural LutH production as exists in the wild-type, which we assume to be inhibited by lanthanide concentration, and the second representing our engineered LutH production, induced by IPTG:

PH(t)=XmaxH11+(L(t)KL)nH1+XmaxH21+KIIHnH2

We then defined a function representing the production of tLanM. We define PH(t) and PM(t) to use different IPTG concentrations, as ideally the promoters would use different inducers. The function composes a single Hill equation, representing the IPTG-induced tLanM production:

PM(t)=XmaxM1+KIIMnM

Using these two production functions, we form a function representing the stress in the system. Stress is increased by lanthanide ion concentration and greater production of LutH and tLanM. As the lanthanide concentration approaches Ld, stress approaches infinity:

S ( t ) =L(t)µ(PH(t)nHS+PM(t)nMs)LdL(t)

We then use this function in χ(t), representing the proportion to which he rate of LutH and LanM production are scaled:

χ(t)=11 + S(t)

We then represent the formation of tLanM-Ln complexes by a proportion of the total tLanM and Ln present in the system:

h(t)=δM(t)L(t)

Rate of change of LutH is then represented by a differential equation, whereby production of LutH is scaled down by stress, while degraded LutH is removed from the system at a constant rate:

dH(t)dt=χ(t)PH(t)αH(t)

Rate of change of tLanM is implemented similarly, with tLanM production being scaled down by stress, then decayed tLanM being removed. We also subtract the tLanM in tLanM-Ln complexes.

dM(t)dt=χ(t)PM(t)γM(t)h(t)

Rate of change of lanthanides in the cell is represented in 3 parts, composing the subtraction of decayed lanthanides and those sequestered by the tLanM, with a term representing uptake from the environment. We use the Michaelis-Menten enzymatic kinetics function as we assume that the mechanics will be largely similar, scaled by the amount of LutH in the system:

dL(t)dt=H(t)VmaxLoutKmm+LoutβL(t)h(t)

Parameters

δ:The rate of formation of tLanM-Ln3+ complexes
XmaxH1:The maximum rate of wild-type LutH production
KL:Dissociation constant for lanthanides
nH1:Hill coefficient for wild-type LutH production
XmaxH2:The maximum rate of IPTG-induced LutH production
KI:Dissociation constant for IPTG
nH2:Hill coefficient for IPTG-induced LutH production
XmaxM:The maximum rate of IPTG-induced tLanM production
IH:[IPTG] for LutH
nM:Hill coefficient for IPTG-induced tLanM production
IM:[IPTG] for tLanM
nHs:Degree to which LutH production affects stress
nMs:Degree to which LanM production affects stress
µ:Degree to which lanthanide concentration affects stress
Ld:Concentration of intracellular lanthanides resulting in cell death
α:Rate of decay of LutH
Vmax:Limiting rate of lanthanide uptake
Lout:Concentration of extracellular lanthanides
Kmm:Michaelis-Menten constant for uptake
β:Rate of decay of Lanthanides
γ:Rate of decay of tLanM

Simulation

In order to prepare our model for analysis, we implemented the ODE system in Python, using scipy to solve our equations.

We first defined our ODE system, implementing a step response for the lanthanides:

grn.py

# Define the system of ODEs
def ode_system(t, y, params):
    H, L, M = y
    L_d, X_max_H1, X_max_H2, X_max_M, K_L, K_I, alpha, V_max, L_out, K_mm, beta, delta, gamma, I_H, I_M, n_H1, n_H2, n_M, n_H_s, n_M_s, mu = params

    # Step function
    L_out = L_out * np.heaviside((t - 80), 0)  # Simulate L_out at t > 80

    # Production terms
    P_H = X_max_H1 / (1 + (L / K_L) ** n_H1)  +  X_max_H2 / (1 + K_I / I_H ** n_H2)
    P_M = X_max_M / (1 + K_I / I_M ** n_M)

    # Stress function
    S = (P_H ** n_H_s + P_M ** n_M_s) * (L ** mu) / (L_d - L)
    chi = 1 / (1 + S)

    # Differential equations
    dH_dt = chi * P_H - alpha * H 
    dL_dt = (V_max * H * L_out) / (K_mm + L_out) - beta * L - delta * M * L
    dM_dt = chi * P_M - gamma * M - delta * M * L

    return [dH_dt, dL_dt, dM_dt]
      

We then set our initial conditions and parameters:

grn.py

# Initial conditions
y0 = [0.1, 0.0, 0.1]  # Initial concentrations for H, L, M

# Time points
t_span = (0, 200)                   # Start and end times
t_eval = np.linspace(0, 200, 2000)  # Time points where solution is evaluated

# Parameters
params = (L_d, X_max_H1, X_max_H2, X_max_M, K_L, K_I, alpha, V_max, L_out, K_mm,
          beta, delta, gamma, I_H, I_M, n_H1, n_H2, n_M, n_H_s, n_M_s, mu)
      

And finally solved the equations:

grn.py

sol = solve_ivp(ode_system, t_span, y0, args=(params,), t_eval=t_eval, method='RK45')

    if sol.success:
        print("ODE solver executed successfully.")

        # Extract the results
        H, L, M = sol.y

        # Plot the results
        plt.figure(figsize=(12, 8))
        plt.plot(sol.t, H, label='LutH (H)')
        plt.plot(sol.t, L, label='Free intracellular lanthanides (L)')
        plt.plot(sol.t, M, label='Unbound truncated lanM (M)')
        plt.xlabel('Time')
        plt.ylabel('Concentration')
        plt.legend()
        plt.ylim([0, 1500])
        plt.show()
      


Optimisation - DNA design

One of the main purposes of our GRN model was to find optimum parameters for our system, and so we generated several heatmaps for our various parameters to find values for low stress, whilst maintaining effective uptake:

2D contour map of max induced LutH and LanM production rates' effect on free intracellular lanthanides 2D contour map of max induced LutH and LanM production rates' effect on stress
Effect of maximum transcriptional rates of induced LutH and LanM on intracellular free lanthanide concentration (left) and stress (right).

From this we were able to determine the ratios of XmaxH2 to XmaxM, such that the cell would both not reach the death threshold (where stress is negative in the stress plot), and would not produce so much tLanM that the lanthanide concentration would never reach the threshold required for chemotaxis (where L is 0 in the lanthanides plot).

This ratio was of particular importance to the wet lab team, because the only available, characterised inducible constructs for M. extorquens were exclusively IPTG-inducible. Therefore, the control over expression levels was at the level of DNA design and not titration of IPTG, which is much more time-consuming and expensive to characterise in the lab than in the model.



Optimisation - LanM affinity

Beyond the scope of our project, another future potential use of our GRN model was to predict the effects of different affinities of LanM. There exist multiple variations of LanM, such as those described in Mattocks et al. (2023), each with varying affinities for lanthanides, and so it would be useful to preemptively select for one resulting in a most optimal system.

To do this, we varied δ—an indirect measure of affinity—to observe its effects on stress and lanthanide concentration:

graph showing effects on Ln of varying delta. graph showing effects on stress of varying delta.
Effect of varying delta on stress and lanthanide uptake

The parameter delta is the rate of complex formation between lanthanides and LanM. This is indirectly related to the affinity of LanM variants for their associated lanthanides. By varying the parameter, we can identify ranges of acceptable values that are neither too low to be functional or otherwise stressful. This can also be used to calculate required ratios of lanM and lutH expression for different LanMs, as has been explained above.


Analysis

From solving the ODE system for these values, we produced a graph of the concentrations of our species over time:

graph showing GRN results of simulation.
GRN simulation results

We found that the on the introduction of lanthanides to the system (time = 80), the concentration of tLanM would rapidly decrease, while lanthanide concentration stayed close to 0. This was in line with tLanM-Ln complexes forming and sequestering all lanthanides whilst depleting tLanM. Once tLanM was fully depleted, lanthanide concentration began to rapidly increase, decreasing LutH production in the process due to stress.

Beyond the scope of our project, another future potential use of our GRN model was to predict the effects of different affinities of LanM. There exist multiple variations of LanM, such as those described in Mattocks et al. (2023), each with varying affinities for lanthanides, and so it would be useful to preemptively select for one resulting in a most optimal system.

To do this, we varied δ—an indirect measure of affinity—to observe its effects on stress and lanthanide concentration:

graph showing effects on Ln of varying delta. graph showing effects on stress of varying delta.
Effect of varying delta on stress and lanthanide uptake

The parameter delta is the rate of complex formation between lanthanides and LanM. This is indirectly related to the affinity of LanM variants for their associated lanthanides. By varying the parameter, we can identify ranges of acceptable values that are neither too low to be functional or otherwise stressful. This can also be used to calculate required ratios of lanM and lutH expression for different LanMs, as has been explained above.

Agent-Based Model


Overview

In order to perform a more granular analysis on the behaviour of our system's emergent dynamics, we chose to incorporate our GRN model into an agent-based model. This would involve simulating each individual bacterium within a population, each implementing their own system of ordinary differential equations, such that we would be able to accurately simulate the effect of our GRN on the behaviour of the bacteria. This would then allow us to selectively alter or disable various aspects of our GRN, most notably in order to determine the significance of each of our engineered genetic components towards a fully functioning and efficient system as a whole.

With the core design of the model in place, we began to look into methods of performing such a simulation. Our research led us into discussions with Dr Criseida Zamora, who advised us on the suitability of BSim2.0, a software package written in Java for agent-based modelling of bacterial populations. BSim was especially suitable for our project by allowing us to seamlessly integrate our GRN into a configurable computational model, and by its implementation of chemical fields, able to represent our concentrations of extracellular species over time.

Diagram of ABM design, showing the incorporation of the GRN into the ABM.
Design of our ABM

BeaconBacterium

Parameters
taxisThreshold [Ln] in cell required to initiate chemotaxis
deathThreshold Value of stress required to initiate cell death, fitted from experimental data
growthRate Maximum rate of cell growth
kGrowth Degree to which stress affects growth, fitted from experimental data
foodUptakeRate Rate of food uptake
Methods
action Executes on every tick: diffuse, grow, die, chemotaxis, replicate, uptake food, uptake Ln
calculateSurfaceAreaGrowthRate Returns growth rate, scaled down by stress
pEndRun Returns probability of ending a run, which is lower when undergoing chemotaxis
uptakeLn Solves ODE system, setting new values for intracellular [Ln] and subtracting from the extracellular Ln field
uptakeFood Subtracts food from field
getFreeLnIn Returns quantity of Ln inside cell
getFreeLnInConc Returns [free Ln] inside cell
getFreeLnInConcAvg Returns [free Ln] inside cell, averaged over the past n seconds
getTotalLnInConc Returns [total Ln] inside cell (including sequestered Ln)
getSequesteredLnInConc Returns [sequestered Ln]
getStress Returns value of stress from GRN
BeaconBacterium

BeaconBacterium$BeaconGRN

Parameters
deltaThe rate of formation of tLanM-Ln3+ complexes
k_LDissociation constant for lanthanides
n_H1Hill coefficient for wild-type LutH production
k_IDissociation constant for IPTG
n_H2:Hill coefficient for engineered LutH production
I_H[IPTG] for LutH
n_MHill coefficient for engineered tLanM production
I_M[IPTG] for tLanM
muDegree to which lanthanide concentration affects stress
L_lethalConcentration of intracellular lanthanides resulting in cell death
alphaRate of decay of LutH
V_maxLimiting rate of lanthanide uptake
L_out:Concentration of extracellular lanthanides
k_mmMichaelis-Menten constant for uptake
deltaRate of decay of Lanthanides
gammaRate of decay of tLanM
Methods
derivativeSystemDefines the differential equations
HillActivatory Hill function
HillInhibInhibitory Hill function
setExternalLnLevelSets L_out
BeaconGRN

BSimBeacon

Parameters
exportSet to false to preview, true to export to files
exportPathFolder to export files to
useMultithreadingSet to true to enable multithreading
threadsNumber of threads for multithreading
boundSizeSimulation bound size
multSystem scale
simTimeTime to run simulation for
cXX-position of chemoattractant
cYY-position of chemoattractant
cCConcentration of chemoattractant
lCConcentration of lanthanides
fCConcentration of food
cdiffusivityDiffusivity of chemoattractant
lnDiffusivityDiffusivity of lanthanides
lnTimeTime at which lanthanides are introduced
decayRateRate of decay of chemoattractant
populationInitial population of bacteria
fieldresResolution of chemical fields
Methods
runRuns the simulation
BSimBeacon


GRN

As a starting point, we created the BeaconBacterium class as a subclass of BSimBacterium. This was so that our class would inherit the growth, replication, stochastic diffusion, and run-and-tumble behaviour of BSimBacterium.

There is however, insufficient characterisation of Mex's movement patterns, and so we assumed that the qualities of Mex were similar enough to E. coli that we could largely maintain these same behaviours, despite Mex having only a single flagellum. There would additionally be little benefit to implementing a chemotactic system specific to Mex, considering our limited time frame and that for the purposes of our model, it would have minor, if any effect on our analysis.

Our first consideration was the incorporation of the GRN, via the class BeaconGRN, implementing the BSimOdeSystem interface. We use the same parameters in this system as in our GRN ODEs model:

BeaconGRN::derivativeSystem
	  
public double[] derivativeSystem(double t, double[] y) {
    double[] dy = new double[numEq];

    double H = y[0];
    double L = y[1];
    double M = y[2];

    double h = delta*M*L; // Sequestered Ln/LanM complexes
    double MM = (v_max*L_out) / (k_M + L_out); // Michaelis-Menten kinetics

    double P_H = HillInhib(L, n_H1, k_L) // Natural LutH production rate
	    + Hill(I_H, n_H2, k_I);      // IPTG induced LutH production rate
    double P_M = Hill(I_M, n_M, k_I);    // IPTG induced tLanM production rate

    // Stress function
    s = (P_H + P_M) * Math.pow(L, mu) / (L_lethal - L);
    double chi = 1 / (1 + s);

    dy[0] = chi*P_H - alpha*H;     // Scale LutH production by stress, subtract decay
    dy[1] = H*MM - beta*L - h;     // Scale Ln uptake by [LutH], subtract decay and sequestered Ln/LanM complexes
    dy[2] = chi*P_M - gamma*M - h; // Scale LanM production by stress, subtract decay and sequestered Ln/LanM complexes

    // Store uptake without decay or sequestered to determine value to subtract from env. Ln
    dy[3] = H*MM;
    // Total Ln in cell, including sequestered
    dy[4] = H*MM - beta*L;

    return dy;
}
protected static double Hill(double Ligand, double n, double K_d) {
    final double d = 1 + K_d / Math.pow(Ligand, n);
    assert d != 0: "Denominator of Hill equation cannot be 0";
    return 1 / d;
}
protected static double HillInhib(double Ligand, double n, double K_d) {
    final double d = 1 + Math.pow(Ligand/K_d, n);
    assert d != 0: "Denominator of Hill equation cannot be 0";
    return 1 / d;
}
	
	



High-level behaviour

We first defined the high-level behaviour of the bacterium, to be executed on each tick in the simulation. The method first checks if the bacterium should die (i.e, stress, s in the GRN, is higher than a threshold, deathThreshold):

BeaconBacterium::action

/** Diffuse, grow, die, chemotaxis, replicate */
@Override
public void action() {
    // If dead, do nothing
    if(this.isDead() || this.checkShouldDie()) {
	this.die();
        return;
    }
	

It then sets the growth rate of the bacterium, based on stress and availability of food. The method then halts movement and growth in absence of food, to represent bacteriostasis, before calling super.action() to perform the movement, growth, stochastic diffusion processes of BSimBacterium:

BeaconBacterium::action

    // Decrease growth rate with increasing stress, or halt growth if no food
    this.setSurfaceAreaGrowthRate(this.calculateSurfaceAreaGrowthRate());
    // Stop moving if in stasis
    this.forceMagnitude = this.checkShouldStasis() ? 
	    0 : this.initalForceMagnitude;
    // Movement, growth, diffusion
    super.action();
      

Finally, the bacterium uptakes lanthanides and food from the environment. The uptake of food is represented as a constant decrease in the concentration of food in the BSimChemicalField foodField, which is defined in the simulation:

BeaconBacterium::action

    // Adjust internal and external [Ln]
    this.uptakeLn();

    // Take food from environment
    this.uptakeFood();
}
    



Lanthanide uptake

The uptakeLn() method is implemented in a similar manner, though in much more detail. In sequential order, the method behaves as follows:

  1. Set Lout in the GRN model to the extracellular [Ln] at the current position in space.
  2. Solve the ODEs in the GRN.
  3. Store the stress value derived from the GRN model, for later use in determining stress levels for, eg death.
  4. Store the amount of lanthanides uptaken by the bacterium, and the current concentration of free intracellular lanthanides. This will be useful for later use in determining whether to activate chemotaxis.
  5. Store the free and total intracellular lanthanide values from the GRN.
  6. Subtract the uptaken lanthanides from the extracellular field of lanthanides at the current spatial position, representing the decrease in lanthanides in the environment.
BeaconBacterium::uptakeLn

/** Removes Ln from environment, and updates internal [Ln] */
public void uptakeLn() {
    // Subtract from environment the difference in [Ln] now vs previous timestep,
    // and update BeaconBacterium internal [Ln] value to reflect this
    this.lnField.addQuantity(this.getPosition(), - this.updateInternalLnLevel());
}

/**
    * Solves GRN for current time, updating this.y, this.lnUValues, this.lnIn
    * @return uptaken [Ln] from environment
    */
public double updateInternalLnLevel() {
    // Set environmental [Ln] in GRN to current Ln Field value at bacterium's position
    this.getGRN().setExternalLnLevel(this.lnField.getConc(this.getPosition()));
    // Commented out to avoid terrible performance, dubious behaviour
    // // divide by number of bacteria in Ln field cell to avoid magic lanthanides
    // / this.getNumberOfBacteriaInSamePosition()); 

    // Solve ODEs
    this.y = BSimOdeSolver.rungeKutta45(this.grn, 
	    this.sim.getTime()/60, this.y, this.sim.getDt()/60);

    // Store new stress value
    this.stressValues.memorise(this.grn.s);

    // Store new uptaken [Ln] value
    this.lnUValues.memorise(this.y[3]);
    this.lnFreeConcValues.memorise(this.getFreeLnInConc());

    // Set internal [Ln] to value from ODEs in GRN
    // this.setLnIn(this.getLnIn() + L_outDelta*sim.getDt());
    this.setFreeLnIn(this.y[1]);  // Set free [Ln] in cell
    this.setTotalLnIn(this.y[4]); // Set total [Ln] in cell

    // Return value for amount of Ln uptaken from environment
    return this.lnUValues.getFirst() - this.lnUValues.getLast();
}
    



Chemotaxis

We then needed to be able to activate chemotaxis upon the intracellular lanthanide concentration surpassing a specified threshold.

More biologically accurate would be to model the production of methyl-accepting chemotaxis proteins (MCPs), however we felt that this would add unnecessary complexity to our model. Instead, we change the probability of ending a run, depending on the lanthanide concentration, and if the bacterium is moving up the chemoattractant gradient. When both the lanthanide concentration is greater than the threshold, and the bacterium is moving up the concentration gradient, the probability of ending a run decreases.

We can then observe chemotaxis as an emergent behaviour in the final simulation by the mean x-position of the bacteria increasing over time, as the bacteria migrate up the chemoattractant gradient, where the gradient is along the x-axis.

Graph demonstrating chemotaxis in the ABM
Graph demonstrating chemotaxis in the ABM

BeaconBacterium::pEndRunUp

/** @return probability of ending run, using chemotaxis value if [Ln] has passed threshold,
  * and if the bacterium is moving up the chemoattractant gradient. Otherwise use default */
@Override
public double pEndRun() {
    return (this.goal != null && this.movingUpGradient()
	    // Only activate chemotaxis when Ln has passed threshold
	    && this.getFreeLnInConcAvg() > this.getTaxisThreshold()) ? 
		    pEndRunUp : pEndRunElse; // pEndRunUp for chemotaxis
}
      



Growth and Replication

Growth is inherited from BSimBacterium as a constant increase in radius over time, however we were interested in the potential effect of the lanthanide concentration as an inhibitor to growth. This being the case, we vary the growth rate by a function of stress. As the stress approaches the death threshold, scaled by a constant parameter kGrowth, the growth rate decreases from the base rate growthRate:

BeaconBacterium::calculateGrowthRate

/** @return growth rate, scaled down by stress or 0 if in stasis */
public double calculateSurfaceAreaGrowthRate() {
    // Do not grow if no food
    if (this.checkShouldStasis()) {
	return 0;
    }
    // Otherwise grow less with more stress
    return this.growthRate / 
	    (1 + (this.getStress()
		    / (this.getDeathThreshold() * this.kGrowth)));
}
	

Demonstration of growth in the ABM
Simulated cell growth and replication

To implement cell division, we use a similar method to BSimBacterium::replicate, whereby the bacterium spawns a child bacterium, setting both bacteriums' radii such that their surface areas are half that of the original bacterium. We additionally split the lanthanides contained in each bacterium evenly between the two, and scale various values accordingly so as to maintain an effective chemotaxis and death threshold checking system:

BeaconBacterium::replicate

public void replicate() {
    this.setRadiusFromSurfaceArea(this.surfaceArea(this.replicationRadius)/2);
    BeaconBacterium child = new BeaconBacterium(this.sim, new Vector3d(this.position), 
	    this.lnField, this.foodField, this.goal, this.childList);
    child.setRadius(radius);
    child.setSurfaceAreaGrowthRate(surfaceAreaGrowthRate);
    this.childList.add(child);

    // Split Ln equally
    this.setFreeLnIn(this.getFreeLnIn() / 2);
    child.setFreeLnIn(this.getFreeLnIn() / 2);
    this.setTotalLnIn(this.getTotalLnIn() / 2);
    child.setTotalLnIn(this.getTotalLnIn() / 2);

    // Split LutH, LanM, Ln equally
    for (int i = 0; i < child.y.length; ++i) {
	this.y[i] /= 2;
	child.y[i] = this.y[i];
    }
    for (int i = 0; i < this.lnUValues.size(); ++i) {
	this.lnUValues.set(i, this.lnUValues.get(i)/2);
	child.lnUValues.set(i, this.lnUValues.get(i));
    }
}
			



Simulation

With the BeaconBacterium implemented, we could begin to build our simulation. We defined 3 chemical fields, representing the chemoattractant, lanthanides, and food (lanthanides are introduced later as a step response):

BSimBeacon::run

final BSimChemicalField cField = new BSimChemicalField(sim, new int[]{this.fieldres,1,1}, this.cDiffusivity, this.decayRate);
final BSimChemicalField lnField = new BSimChemicalField(sim, new int[]{this.fieldres,this.fieldres,1}, this.lnDiffusivity, 0.0);
final BSimChemicalField foodField = new BSimChemicalField(sim, new int[]{this.fieldres,this.fieldres,1}, 10.0, 0.0);

// Homogenous food plane
foodField.linearGradient(0, this.fC/scale, this.fC/scale);

// Linear gradient for chemoattractant
cField.linearGradient(0, 0, this.cC);
cField.update();
    

We then initialise the bacteria as randomly distributed across the space, in random positions and rotations. Once the simulation time has reached the parameter lnTime, the lanthanides field is set as a homogenous plane:

BSimBeacon::run

// Ln step response
if (sim.getTime() == BSimBeacon.this.lnTime) {
    lnField.linearGradient(0,
        BSimBeacon.this.lC*BSimBeacon.this.mult,
        BSimBeacon.this.lC*BSimBeacon.this.mult);
    lnField.update();
}
	

Simulation before and after step response, purple representing lanthanides


Analysis

In order to evaluate the effectiveness of our system, we implemented a procedure to calculate the total lanthanide concentrations, including lanthanides in the environment and bacteria, so that we could observe the total distribution of lanthanides over time:

BSimBeacon$_::during

@Override
public void during() {
    String buffer = this.sim.getFormattedTime() + "\n";
    double[][] lnConcs = new double[lnField.getBoxes()[0]][lnField.getBoxes()[1]];
    // Add internal [Ln]s to appropriate cells
    for (BSimBacterium b : bacteria) {
	lnConcs[(int) ((b.getPosition().getX()/this.sim.getBound().getX())*lnConcs.length)]
		[(int) ((b.getPosition().getY()/this.sim.getBound().getY())*lnConcs[0].length)]
		    += ((BeaconBacterium) b).getTotalLnIn();
    }
    // Add external [Ln]s to appropriate gridcells, write to buffer
    for (int i = 0; i < lnConcs[0].length; ++i) {
	for (int j = 0; j < lnConcs.length; ++j) {
	    lnConcs[j][i] += lnField.getConc(i, j, 0);
	    buffer += lnConcs[j][i] + (j == lnConcs.length - 1 ? "" : ",");
	}
	buffer += "\n";
    }
    buffer += "\n";
    this.write(buffer);
}
    

This then produced a csv file representing the lanthanide concentrations across the plane over time:

Distribution of lanthanides over time
Simulated distribution of lanthanides over time

We found that our simulated bacteria would behave as predicted:

  1. Simulation start:
    no lanthanides in either plane or bacteria
  2. Ln introduced:
    homogenous plane of lanthanides, none in bacteria
  3. Pickup phase:
    internal free lanthanide concentration is below threshold, so chemotaxis does not occur, bacteria remain evenly spread. Concentrated spots appear where bacteria have picked up lanthanides
  4. Chemotaxis phase:
    [Ln] has passed threshold, bacteria begin to migrate up the chemoattractant gradient (towards the right side of the plane).
  5. Simulation end:
    The majority of lanthanides are distributed in the right side of the plane. Some remain spread due to stochasticity and the random nature of run-and-tumble dynamics, made especially noticeable due to the small simulation bound size. Over a larger space we observe less spread.



Assessing the influence of our genetic components

In order to determine the importance of each of our individual genetic components on the effectiveness of the system to migrate lanthanides across the space, we selectively disabled parts of the GRN and compared the behaviours of each simulation:

Graphs demonstrating effect of components on bacterial movement. Lanthanide concentration represented in red.

Distributions of lanthanides across the x-axis, in low lanthanide concentration.

All components present

No tLanM production

No induced LutH production

No tLanM or induced LutH production


Graphs showing distribution of lanthanides over time. Lanthanides on y-axis, x-position on x-axis. Move slider to show change over time.

We found that in the low lanthanide concentrations such as those we use in our experiments, there were minor differences between results; namely, that the migration of lanthanides occurred at a slightly higher rate in the bacteria with induced tLanM and LutH production, than in the bacteria with solely the tar MCP.

On the other hand, in higher lanthanide concentrations as might be used in an industrial process, the differences are much more pronounced:

Distributions of lanthanides across the x-axis, in high lanthanide concentration.

All components present

No tLanM production

No induced LutH production

No tLanM or induced LutH production


Graphs showing distribution of lanthanides over time. Lanthanides on y-axis, x-position on x-axis. Move slider to show change over time.

Here we found that in the full engineered system, the bacteria have the ability to carry a greater quantity of lanthanides and so movement occurs at a higher rate in comparison to the simulation implementing solely our engineered chemotaxis.

We also found that when expressed independently of each other, the induced LutH and tLanM systems failed to deliver at all. This is respectively due to the lack of tLanM to sequester lanthanides resulting in unduly stress and therefore cell death, and the overabundance of tLanM compared to lanthanides resulting in all free lanthanides being sequestered, thus the cells being unable to initiate chemotaxis.

Population Model


We additionally wanted a model to simulate the entire population across a larger space, in order to be able to visualise our system's behaviour on a much grander scale, potentially even to the scale of an industrial implementation, and also to inform us as to the parameters of our experimental design. To do this we used a system of partial differential equations.


Parameters

NameValueUnitsReference
Chemoattractant diffusivity600µm2s-1 B10NUMB3R5 [2024] Diffusion coefficient of glucose in water, Diffusion coefficient of glucose in water - Generic - BNID 104089. Available at: https://bionumbers.hms.harvard.edu/bionumber.aspx?id=104089&ver=7 (Accessed: July 2024)
Bacteria diffusivity200 to 1900µm2s-1Licata NA, Mohari B, Fuqua C, Setayeshgar S. (2016) Diffusion of Bacterial Cells in Porous Media. Biophys J. 110(1):247-57.
Chemoattractant sensitivity10e-5 to 10e-3cm2s-1Berg, H. (2004) E. coli in Motion. Berlin: Springer-Verlag.
Food sensitivity10e-5 to 10e-3cm2s-1Berg, H. (2004) E. coli in Motion. Berlin: Springer-Verlag.
Maximum lanthanides uptake100µMVu HN, Subuyuj GA, Vijayakumar S, Good NM, Martinez-Gomez NC, Skovran E. (2016) Lanthanide-Dependent Regulation of Methanol Oxidation Systems in Methylobacterium extorquens AM1 and Their Contribution to Methanol Growth. J Bacteriol. 198(8):1250-9.
Average extracellular lanthanide concentration50µMVu HN, Subuyuj GA, Vijayakumar S, Good NM, Martinez-Gomez NC, Skovran E. (2016) Lanthanide-Dependent Regulation of Methanol Oxidation Systems in Methylobacterium extorquens AM1 and Their Contribution to Methanol Growth. J Bacteriol. 198(8):1250-9.
Rate of death of cells1.83 ± 0.20Per 1000 cells per hourBrot, L. et al. (2021) Death rate during the exponential growth of E. Coli, arXiv.org. Available at: https://arxiv.org/pdf/2112.06073 (Accessed: July 2024)
Food diffusion coefficient1000µm2s-1B10NUMB3R5 [2024] Diffusion coefficient of glucose in water, Diffusion coefficient of glucose in water - Generic - BNID 104089. Available at: https://bionumbers.hms.harvard.edu/bionumber.aspx?id=104089&ver=7 (Accessed: July 2024)
Maximum specific grow rate0.14 to 0.25Per hourHu, B., Yang, YM., Beck, D.A.C. et al. (2016) Comprehensive molecular characterization of Methylobacterium extorquens AM1 adapted for 1-butanol tolerance. Biotechnol Biofuels 9, 84 and Carroll SM, Xue KS, Marx CJ. (2014) Laboratory divergence of Methylobacterium extorquens AM1 through unintended domestication and past selection for antibiotic resistance. BMC Microbiol. 14:2.
Half saturation constant0.2 to 2.5MmolBelkhelfa S, Roche D, Dubois I, Berger A, Delmas VA, Cattolico L, Perret A, Labadie K, Perdereau AC, Darii E, Pateau E, de Berardinis V, Salanoubat M, Bouzon M, Döring V. (2019) Continuous Culture Adaptation of Methylobacterium extorquens AM1 and TK 0001 to Very High Methanol Concentrations. Front Microbiol. 10:1313.
Yield coefficient0.44Gram of cell per gram of methanolNayak. D. and Marx C.J. (2014) Genetic and Phenotypic Comparison of Facultative Methylotrophy between Methylobacterium extorquens Strains PA1 and AM1. Plos One.

With consistent units (ommitted parameters have been determined indirectly by fitting the model with the wet lab data):

Chemoattractant diffusivity600 µm2s-1
Bacteria diffusivity200 to 1900 µm2s-1
Chemoattractant sensitivity103 to 105 µm2s-1
Food sensitivity103 to 105 µm2s-1
Maximum lanthanides uptake100 µM
Average lanthanides concentration50 µM
Average rate of death of cells(5.08 ± 0.55) ×10−4 per second per 1000 cells
Diffusion coefficient of food600 µm2s-1
Maximum specific growth rate(3.89 to 6.94)×10−5 per second
Half saturation constant0.2 to 2.5 mM
Diffusion Coefficient of Food0.44 g cells/g methanol



Hyperparameters

  • Grid size. dx and dy are expressed in µm.
  • Timestep size. Expressed in seconds.
  • Lanthanides lag.


Assumptions

Grid and space assumptions

  • The space is represented on a 2D square grid with uniform grid spacing, which assumes that bacterial motion, nutrient diffusion, and chemotaxis are isotropic and homogeneous across space.

Bacterial behaviour

  • The bacterial population is modelled using a concentration field u, and it is assumed that this concentration follows diffusion and chemotactic behaviour.
  • Bacteria are attracted to a chemotactic agent and nutrients via gradients in chemoattractant (v) and nutrient (f) concentrations.
  • Bacteria undergo logistic growth based on the nutrient availablility and their maximum growth rate (r) up to a saturation point.
  • The bacteria die at a constant rate (kd) in the absence of food, and they can take up lanthanides (l), which activates chemotaxis.

Chemotaxis sensitivity

  • The chemotactic sensitivity to nutrients and the chemoattractant is a decreasing function of the cell concentration to account for overcrowding.

Lanthanides behaviour

  • Lanthanides diffuse through the system with a constant diffusion coefficient DI and are taken up by bacteria with a rate proportional to the bacterial concentration (omega * u * l)
  • The uptake of lanthanides has a delayed effect on chemotaxis, modelled by a lag period (lag) in time before the chemotaxis behaviour is expressed.
  • The average uptake per cell (lavg= 50) is considered constant.

Nutrient behaviour

  • Nutrient diffusion occurs with a constant diffusion coefficient Df, and it is consumed by bacteria at a rate proportional to the bacterial concentration.
  • The nutrient consumption follows Michaelis-Menten kinetics, where the consumption rate saturates at higher nutrient concentrations (f/(h + f)) with half-saturation constant h).

Chemoattractant dynamics

  • The chemoattractant concentration v diffuses according to a constant diffusion coefficient Dv without additional production or decay terms.
  • The chemoattractant is initially set up with a localised peak, simulating a source of attraction in the bacterial environment.

Cell activation

  • A subpopulation of active cells (a) is considered, which responds both to chemoattractant and nutrient gradients, and undergoes diffusion and death similar to the bacterial population.
  • Active cells diffuse like the bacterial population but have a distinct behaviour influenced by chemoattractant uptake.

Time-stepping

  • The simulation uses a fixed time-step size for all processes, assuming that the timescales of diffusion, chemotaxis, bacterial growth, and lanthanide uptake are comparable.

Boundary conditions

  • Cell concentration (u and a) are constrained to have zero gradients on the boundaries (no-flux boundary conditions), meaning that no material flows in or out of the boundaries.

Initial conditions

  • Randomised initial conditions for bacterial (u), nutrient (f), and lanthanide (l) concentrations, with a Gaussian distribution for chemoattractant (v), assume some initial variability or noise in the system.



Limitations

Simplified spatial representation

  • 2D grid assumption: the model assumes a 2D space, while bacterial motion and chemotaxis typically occur in three dimensions. This could lead to inaccuracies when extrapolating results to real-world 3D environments.
  • Uniform grid spacing: the grid spacing is uniform, which may not capture finer spatial variations in concentration gradients, especially in regions with steep changes.

Constant diffusion coefficients

  • Fixed diffusion coefficients: the diffusion coefficients for bacterial concentration (Du), chemoattractant (Dv), nutrients (Df), and lanthanides (Dl) are assumed to be constant. In reality, diffusion rates may vary with environmental factors such as temperature, viscosity, and the presence of obstacles or other microorganisms.
  • The model assumes isotropic diffusion (same in all directions), but in real systems, bacterial motion could be influenced by external forces or environmental heterogeneity, leading to anisotropic behaviour.

Over-simplified chemotactic response

  • Constant sensitivity: the chemotactic sensitivity parameters for nutrients account only for overcrowding effect. In real bacterial systems, the sensitivity to chemoattractant and nutrients may change over time or depend on the bacterial state, adaptation, or saturation effects.
  • No receptor saturation: the model assumes that the chemotactic response scales linearly with the chemoattractant gradient, without considering potential receptor saturation or desensitization at high chemoattractant concentrations.

Nutrient consumption and growth

  • Simplified nutrient kinetics: the nutrient consumption follows a Michaelis-Menten-like relationship, which may not fully capture the complexity of bacterial metabolism, including the possibility of cooperative uptake or the presence of multiple nutrient sources.
  • Constant growth and death rates: the maximum growth rate (r) and death rate (kd) are constant, while in real bacterial systems, growth rates can vary depending on nutrient availability, population density, environmental stress, and quorum sensing.

Lanthanide uptake

  • Lag period: the model assumes a fixed lag period (lag) between lanthanide uptake and chemotaxis adjustment, but in reality, this delay could vary dynamically depending on the concentration of lanthanides or the physiological state of the bacteria.
  • No lanthanide-dependent feedback: the model does not account, apart from chemotaxis, for any feedback mechanisms where lanthanide uptake could influence other aspects of bacterial behaviour, such as metabolism or motility, beyond the fixed uptake rate.
  • Constant lanthanides concentration: every cell is modelled uptaking a constant number of lanthanides that is released after the cell death.

Boundary conditions

No-flux boundaries: the zero-gradient (no-flux) boundary conditions prevent any flow of material in or out of the system, which may not accurately represent real-world systems where bacteria or chemicals could diffuse beyond the boundaries of the modelled region.

Absence of environmental factors

  • No environmental variations: the model does not account for external environmental factors like fluid flow, temperature gradients, or mechanical forces, which can influence bacterial motility and chemotaxis in natural or industrial settings.
  • No interaction with other species: the model focuses only on a single bacterial population, but real environments often involve interactions with other microorganisms, bacteriophages, or physical structures, which could alter chemotactic behaviour.

Simplified active cell dynamics

  • No cell-state transitions: the model assumes two distinct populations—active cells (a) and non-active cells (u), but it doesn't account for transitions between these states based on environmental conditions or intracellular signalling.
  • Fixed yield coefficient: the yield coefficient (Yc) for the conversion of nutrients into biomass is constant, which ignores variations in metabolic efficiency based on nutrient quality or bacterial energy state.

Absence of detailed biophysical processes

  • No intracellular dynamics: the model focuses on population-level behaviour (concentration fields) but ignores detailed intracellular processes like gene expression, receptor dynamics, and energy metabolism, which play key roles in bacterial chemotaxis and growth.
  • No cell-cell interactions: the model does not account for direct interactions between individual bacteria, such as collision, quorum sensing, or biofilm formation, which can significantly alter chemotactic and growth dynamics in dense populations.

Time-stepping and numerical stability

Fixed time step: a fixed time step is used for solving the partial differential equations (PDEs). While convenient for simulations, it may lead to numerical instability if the time step is too large relative to the rate of diffusion or chemotactic response, especially in regions with steep gradients.



Mathematics

Variables

u(x,y,t)bacterial concentration at position (x,y) and time t
v(x,y,t)chemoattractant concentration at position (x,y) and time t
f(x,y,t)nutrient concentration at position (x,y) and time t
l(x,y,t)lanthanide concentration at position (x,y) and time t
a(x,y,t)active bacterial concentration at position (x,y) and time t

Equations


  1. Bacterial Population:

    ut=g(u)+·(Duuλ(u)f)τ(u,l(x,y,tlag))

    Where:

    • g(u) is the growth function.
    • Du is the diffusion coefficient for bacterial cells.
    • λ(u) is the chemotactic sensitivity to nutrients.
    • τ(u,l(x,y,t-lag)) represents the transition to the active state when chemotaxis is enabled.


  2. Chemoattractant:

    vt=k(u,v)+·(Dvv)

    Where:

      k(u,v) models the kinetics of the chemoattractant
      Dv is the diffusion coefficient of the chemoattractant


  3. Nutrients:

    ft=·(Dff)-1Ycr(fh+f)(u+a)

    Where:

    • Df is the diffusion coefficient for nutrients.
    • Yc is the yield coefficient for cell growth.
    • r is the maximum growth rate


  4. Lanthanides concentration:

    lt=·(Dll)-ωul+kdlavg

    Where:

    • Dl is the diffusion coefficient for lanthanides.
    • ω is the rate at which cells uptake lanthanides
    • kd is the death rate of cells in the absence of nutrients
    • lavg is the average amount of lanthanides per cell


  5. Active cells

    at=·(Dua-λf-χ(v)v)+τ(u,l(x,y,t-lag))-kda

    Where:

    • χ(v) is the chemotactic sensitivity dependent on the chemoattractant concentration.



Description

The model provided for engineered chemotaxis of Methylobacterium extorquens AM1 after lanthanide uptake is fundamentally an extension of the classic Keller-Segel model for chemotaxis. The Keller-Segel model describes how cells (such as bacteria) move in response to a chemical signal (the chemoattractant) in their environment.

The Keller-Segel model is composed of two primary partial differential equations:

  • Cell density u(x,t): describes how the concentration of cells changes over time due to diffusion and chemotaxis.

  • ut=·(Duu-χ(u,v)v)+growth term


  • Chemoattractant concentration v(x,t): describes the concentration of the chemoattractant that cells are attracted to, which also diffuses through space and may be produced or degraded.

  • vt=·(Dvv)+productiondecay term

Where:

  • Du is the diffusion coefficient of the cells.
  • χ(u,v) is the chemotactic sensitivity, a function that models the strength of chemotaxis.
  • Δu represents the diffusion of cells.
  • ∇⋅(χ(u,v)∇v) is chemotaxis, or movement in response to the chemoattractant gradient.
  • Dv is the diffusion coefficient of the chemoattractant.

Key differences

While the Keller-Segel model focusses on simple bacterial chemotaxis to a chemical signal, this model introduces additional complexity:

  • Chemotaxis towards nutrients: we include chemotaxis not only towards the chemoattractant, but also to nutrients, coupling bacterial movement with nutrient availability.
  • Lanthanide uptake: the absorption and release of lanthanides influences cell behaviour, introducing a delay (lag) before chemotaxis can occur after lanthanide uptake.
  • Active cells: our model distinguishes between the general bacterial cells u, and actively chemotactic cells a, with the latter only exhibiting chemotactic behaviour after lanthanide uptake.



Simulation

In order to solve our PDE system, we used the Python library FiPy, a finite volume PDE solver developed by NIST.

We first initialised our parameters, initial conditions, and boundary conditions:

population.py

# Define the grid
nx = ny = 120
dx = dy = 100 
mesh = Grid2D(dx=dx, dy=dy, nx=nx, ny=ny)
np.random.seed(70)

# Define the parameters
Du = 1000      # Diffusion coefficient for u 200 to 1900 µm^2/s 
Dv = 600       # Diffusion coefficient for v 600 µm^2/s
chi = 1000     # Chemotactic sensitivity 10e3 to 10e5 µm^2/s
alpha = 0.1    # Production rate of v
beta = 0.05    # Decay rate of v
r = 0.00005    # Maximum specific grow rate (3.89 to 6.94)x10^-5 per second
c_max = 1      # Maximum concentration of cells
Df = 1200      # Diffision term for nutrients 600 µm^2/s
h = 1          # Half saturation constant 0.2 to 2.5 mM 
theta = 1000   # Chemotactic sensitivity for food
Yc = 0.44      # Yield coefficient gram of cells per gram of food 
kd = 0.0005    # Rate at which cells die in the absence of food (5.08±0.55)x10ˆ4 per second per 1000 cells
omega = 0.01   # Uptake rate of lenthanides scaling factor
Dl = 600       # Diffusion coefficient lenthanides 
lag = 5        # Lag between lenthanides uptake and chemotaxis towards chemoattractant
lenc = []      # Lanthanides concentration
max_v = 50     # Maximum chemiotractor concentration in mMol
max_f = 50     # Maximum initial concentration of nutrients in mMol
lavg = 50      # Average uptake of lanthanides per cell 50 mMol

chem_centre = 0 # Centre of the chemoattractant 

# Define the variables
u = CellVariable(name="u", mesh=mesh, value=1.0)       # Bacterial Concentration 
v = CellVariable(name="v", mesh=mesh, value=0.0)       # Chemoattractant Concentration 
f = CellVariable(name="f", mesh=mesh, value=0.0)       # Nutrients Concentration 
l = CellVariable(name="l", mesh=mesh, value=0.0)       # Lanthanides Concentration
a = CellVariable(name="a", mesh=mesh, value=0.0)       # Concentration of active cells 
diff = CellVariable(name="diff", mesh=mesh, value=0.0) # Difference between Lanthanides concentration

# Initial conditions (localized peak in the center)
x, y = mesh.cellCenters
x_centre, y_centre = nx/2, ny/2
r_squared = (x-x_centre)**2 + (y-y_centre)**2                  

# Set the initial conditions 
u.setValue(0.05 * np.random.rand(nx*ny)) # Initial bacteria concentration 0.01 - 0.1 bacteria/µm^2

v.setValue(max_v * np.exp(-((x - nx*dx/2 + chem_centre)**2 + (y - ny*dy/2 + chem_centre)**2) / (2*(2000**2))))

f.setValue(50)

# f.setValue( 0.5 * max_f * np.ones(nx*ny) + 0.5 * max_f * np.random.rand(nx*ny))
l.setValue(0.3 * np.random.rand(nx*ny))
lenc.append(l.value.copy())

a.setValue(0.0) # Initialize active cells to 0


# Set boundary conditions

# Impose low flux on the left and right boundaries (Neumann BC)
u.faceGrad.constrain(0, mesh.exteriorFaces)
a.faceGrad.constrain(0, mesh.exteriorFaces)
f.faceGrad.constrain(0, mesh.exteriorFaces)
l.faceGrad.constrain(0, mesh.exteriorFaces)

u.constrain(0.01, mesh.exteriorFaces)
a.constrain(0, mesh.exteriorFaces)
l.constrain(0, mesh.exteriorFaces)
	

Then defined our equations:

population.py


# Define the PDEs
eq_u = (TransientTerm(var=u) == 
        -((theta * (1/(1+(1+u+a)**2)) * u * f.grad[0]).grad[0]      # Attraction to nutrients
          + (theta * ((1/(1+(1+u+a)**2))) * u * f.grad[1]).grad[1]) #
        + ((Du * u.grad[0]).grad[0] + (Du * u.grad[1]).grad[1])     # Cell diffusion term
        + (r * (f/(h + f)) * (u+a) - kd/1000 * u)                   # Cell growth term 
        + diff * u/20)                                              # Cell Activation term
        

eq_v = (TransientTerm(var=v) == ((Dv * v.grad[0]).grad[0] + (Dv * v.grad[1]).grad[1])) # Chemoattractant Diffusion term 

eq_f = (TransientTerm(var=f) == 
        ((Df * f.grad[0]).grad[0] + (Df * f.grad[1]).grad[1]) # Nutrients Diffusion term 
        - 100/Yc * r * (f/(h + f)) * (u+a))                   # Nutrients consumption


eq_l = (TransientTerm(var=l) == 
        ((Dl * l.grad[0]).grad[0] + (Dl * l.grad[1]).grad[1]) # Lanthanides diffusion term 
        - omega * u * l                                       # Lanthanide absorption by cells 
        + kd/1000 * a * lavg)                                 # Lanthanides release by cells 

eq_a = (TransientTerm(var=a) == 
        -(((chi * (((max_v-v)/max_v)/(1+(1+u+a)**2)) ) * a * v.grad[0]).grad[0]    # Chemotaxis
          + ((chi * (((max_v-v)/max_v)/(1+(1+u+a)**2)) ) * a * v.grad[1]).grad[1]) # 
        -((theta * ((1/(1+(1+u+a)**2))) * a * f.grad[0]).grad[0]                   # Attraction to nutrients
          + (theta * (1/(1+(1+u+a)**2)) * a * f.grad[1]).grad[1])                  #
        + ((Du * a.grad[0]).grad[0] + (Du * a.grad[1]).grad[1])                    # Cell diffusion term
        - diff * u/20                                                              # Cell activation term
        - kd/1000 * a)                                                             # Cell death
        
	

And finally solved and visualised the system:

population.py

# Visualisation
for step in tqdm(range(steps), desc="Time-stepping"):
    
    vconvection_x = (chi * (((max_v-v)/max_v)/(1+(1+u+a)**2)) ) * a * v.grad[0]
    vconvection_y = (chi * (((max_v-v)/max_v)/(1+(1+u+a)**2))) * a * v.grad[1]


    f_grad = f.grad
    fconvection_x = theta * (1/(1+(u+a)**2)) * a * f_grad[0]
    fconvection_y = theta * (1/(1+(u+a)**2)) * a * f_grad[1]

    # Compute the divergence of the convection term
    div_convection =  - (fconvection_x.grad[0] + fconvection_y.grad[1])


    if len(lanc) >= lag:
        diff.setValue(lanc[-1] - lanc[-lag])
        diff.setValue(np.minimum(0,diff.value))


    # Updating Lanthanides Concentration 
    lanc.append(l.value.copy())
    if len(lanc) > lag:
        lanc.pop(0)
   
    if step % print_step == 0:
        with h5py.File(hdf5_file, "a") as r:
            r["cell"][int(step/print_step)] = u.value.reshape(nx, ny)  
            r["Ln"][int(step/print_step)] = l.value.reshape(nx, ny)  
            r["food"][int(step/print_step)] = f.value.reshape(nx, ny)  
            #r["Divergence"][step/print_step] = div_convection.value.reshape(nx, ny)  
            r["active cells"][int(step/print_step)] = a.value.reshape(nx, ny)
            #r["chem"][step/print_step] = v.value.reshape(nx, ny)  

    # Solve the PDEs
    eq_u.solve(dt=timeStepDuration)
    eq_v.solve(dt=timeStepDuration)
    eq_f.solve(dt=timeStepDuration)
    eq_l.solve(dt=timeStepDuration)
    eq_a.solve(dt=timeStepDuration)

    # Add Gaussian noise to variables
    u.setValue(u.value + noise_level_u * np.random.normal(0, 1, u.value.shape))
    v.setValue(v.value + noise_level_v * np.random.normal(0, 1, v.value.shape))
    f.setValue(f.value + noise_level_f * np.random.normal(0, 1, f.value.shape))
    l.setValue(l.value + noise_level_l * np.random.normal(0, 1, l.value.shape))
    
    u.setValue(np.maximum(u.value, 0))
    v.setValue(np.maximum(v.value, 0))
    f.setValue(np.maximum(f.value, 0))
    l.setValue(np.maximum(l.value, 0))
    a.setValue(np.maximum(a.value, 0))
    f.setValue(np.minimum(f.value, max_f))
        
print("Frames saved.")

	



Analysis

Running the simulation with the chemoattractant emanating from the centre of the plane, we produced the following results:

PDEs noisy radial gradient simulation

As time passes, the environmental lanthanide concentration (top right) decreases as the bacteria uptake the lanthanides, while the cells move towards the centre, bringing the lanthanides with them. This then also represents the net movement of lanthanides towards the centre.

Running the simulation with the chemoattractant emanating from the left side of the plane, we produced the following results:

PDEs noisy linear gradient simulation

This configuration was shown to be comparably effective, showing the cells moving towards the left side.

The population model significantly contributed to our experimental design and wet lab procedures by enabling predictions of assay outcomes under various conditions. It firstly allowed us to anticipate the behavior of the bacterial population in response to different gradients, helping us to forecast the results of future assays. Secondly, the model suggested that a central inoculation point for the chemoattractant would be more efficient for maximising bacterial chemotaxis. This insight directly informed the design of our experiments, ensuring more effective use of resources and time in the wet lab. Lastly, the model's ability to simulate different diffusion coefficients allowed us to experiment with varying media densities. This helped us select the optimal media concentration, improving bacterial motility and chemotactic response, leading to more reliable experimental results. Overall, the model was a powerful tool for both guiding experimental decisions and enhancing the accuracy of the wet lab's experimental outcomes.