"""Curve fit of cell density versus shear stress"""
from scipy.optimize import curve_fit
from matplotlib.figure import Figure
from numpy import ndarray
from numpy import linspace
from numpy import histogram
from numpy import exp
from numpy import sqrt
from numpy import power
from numpy import pi as π
[docs]
def fit(
distances: ndarray, n_bins: int = 21,
) -> tuple[ndarray, ndarray, ndarray, ndarray, ndarray]:
"""
Fits cell density to sigmoid curve.
Expects the relative `distances` of the cells from the center and
`n_bins`, the number of bins in the histogram.
Returns `(x_data, y_data, x_fit, y_fit, parameters)`, which represent
the binned data of shear stress vs. normalized cell density, the
coordinates of the corresponding fit curve, and the fit parameters.
"""
# Create histogram.
bins = linspace(0, 1, num=n_bins, endpoint=True)
(counts, edges) = histogram(distances, bins)
# Normalize cell density.
bin_area = π * (bins[1:]**2 - bins[:-1]**2)
density = counts / bin_area
density = density / density[0]
# Calculate midpoints of the bins.
x_bins = (bins[:-1] + bins[1:]) / 2
# Convert bin position to corresponding shear stress τ.
r = 9.5 # radius in mm, taken from Mariana's script
ρ = 997 # buffer density in kg/m³
μ = 0.00089 # buffer viscosity in N·s/m²
ω = 261.8 # velocity in radians: 104.72 dyn per 1000 rpm
τ = 0.8 * r * 0.001 * sqrt(ρ * μ * power(ω, 3)) * 10
x_data = τ * x_bins
y_data = density
# Fit to sigmoid curve.
(parameters, _) = curve_fit(sigmoid, x_data, y_data, method='dogbox')
x_fit = linspace(0, x_data.max(), 100)
y_fit = sigmoid(x_fit, *parameters)
return (x_data, y_data, x_fit, y_fit, parameters)
[docs]
def sigmoid(x: ndarray, a: float, b: float, c: float) -> ndarray:
r"""
Fit function according to [Boettiger (2007)].
- `a` is the plateau maximum and constrained to 0.95 < a < 1.05.
- `b` is the slope of the curve at the inflection point. It reflects
the distribution.
- `c` is $\tau_{50}$, the mean shear stress required for cell
detachment and is proportional to the number of adhesive bonds.
[Boettiger (2007)]: https://doi.org/10.1016/S0076-6879(07)26001-X
"""
return a / (1 + exp(b * (x - c)))
[docs]
def plot(
figure: Figure,
x_data: ndarray, y_data: ndarray,
x_fit: ndarray, y_fit: ndarray, parameters: ndarray,
):
"""Plots binned data and fit curve onto given Matplotlib `figure`."""
(a, b, c) = parameters
formula = fr'$\frac{{{a:.2f}}}{{ 1 + \exp({b:.2f} \cdot (x - {c:.2f}))}}$'
axes = figure.add_subplot()
axes.plot(x_data, y_data, 'o', label='data',
markersize=7, color='tab:blue')
axes.plot(x_fit, y_fit, '-', label=f'fit: {formula}',
linewidth=2, color='tab:orange', zorder=-1)
axes.set_xlabel('shear stress [dyn/cm²]')
axes.set_xscale('log')
axes.set_xlim(1, 10_000)
axes.set_ylim(0, 1.5)
axes.set_ylabel('normalized cell density')
axes.legend()