Source code for cytocalc.csmsimulation
#! /usr/bin/env python3
# A CSMSimulation is a collection of CSMFrames and the
# simulation parameters (read from configurations.cym)
import cytocalc.csmframe
import freud
import warnings
import numpy as np
import pandas as pd
from scipy.stats import linregress
from bisect import insort
from typing import List
[docs]
class CSMSimulation:
"""
Contains a set of CSMFrames that form a simulation.
Also contains the simulation parameters.
"""
def __init__(self):
self.frames = [] # list containing cytosim frames
self.params = {} # dict containing simulation
[docs]
def add_frame(self, csmframe):
""" Adds a CSMFrame to the simulation
at the correct time (frame1.time < frame2.time)"""
insort(self.frames, csmframe, key=lambda x: x.time)
[docs]
def get_np_object(self, start_frame='0', end_frame=None):
""" Constructs a numpy object from csm frame data
of shape (n_frames,n_particles,3) """
np_frames = [frame._npobj for frame in self.frames[start_frame:end_frame]]
return np.stack(np_frames)
[docs]
def contraction_rate(self, start_frame=None, end_frame=None, r_sq = 0.9):
""" Obtain the contraction rate (rate of decrease in radius of network)
between start_frame and end_frame """
time = []
radius = []
for frame in self.frames[start_frame:end_frame]:
time.append(frame.time)
radius.append(frame.compute_radius())
fit = linregress(time, radius)
# warn if R^2 value is low
if (fit.rvalue)**2 <= r_sq:
warnings.warn('R-square value is too low!')
return fit.slope
[docs]
def compute_msd(self, start_frame : int = 0, end_frame : int = -1):
"""
Compute Mean Squared Displacement (direct) of objects.
:param start_frame: int, frame number for starting MSD calculation, defaults to the first frame
:param end_frame: int, frame at which MSD calculation ends, defaults to the last (-1) frame
:return: dict, with time difference as key and corresponding MSD as value
"""
init_frame = self.frames[start_frame]
init_disp = init_frame._npobj
msd_vs_time = {}
for frame in self.frames[start_frame+1:end_frame]:
time = round(frame.time - init_frame.time,4) # time diff between start and curr frame
disp = frame._npobj - init_disp
msd = np.mean(np.sum(disp**2, axis=1))
msd_vs_time[time] = msd
return msd_vs_time
[docs]
def compute_time_avg_msd(self,
start_frame : int = 0,
end_frame : int = -1,
box_size : List[float] = []):
"""
Computes a 'time averaged MSD' i.e using all distinct displacements to extract more data.
See [1] for futher details
[1] https://en.wikipedia.org/wiki/Mean_squared_displacement#Definition_of_MSD_for_time_lags
:param start_frame: int, frame number for starting MSD calculation, defaults to the first frame
:param end_frame: int, frame at which MSD calculation ends, defaults to the last (-1) frame
:param box_size: List[float], dimensions of box (assumes periodicty), ignore if non-periodic
:return: dict, with time difference as key and corresponding MSD as value
"""
init_frame = self.frames[start_frame]
sim_array = self.get_np_object(start_frame,end_frame)
# freud isn't thread_safe, breaks if we multithread elsewhere
# so we disable multithreading in freud
freud.parallel.NumThreads(1)
msd_vs_DeltaT = {}
if not box_size:
# no box size specified, assume non-periodic
t_avg_msd = freud.msd.MSD().compute(sim_array).msd
else:
box = freud.Box.from_box(box_size, dimensions=3) # our position data is always in 3D
t_avg_msd = freud.msd.MSD(box).compute(sim_array).msd
# return timestamps along with MSD
for count,frame in enumerate(self.frames[start_frame:end_frame]):
time = frame.time - init_frame.time # time diff between start and curr frame
msd_vs_DeltaT[time] = t_avg_msd[count]
return msd_vs_DeltaT
[docs]
def bound_couple_vs_time(self, start_frame : int = 0, end_frame: int = -1):
"""
Get the number of bound hands as a function of time
:param start_frame: int, frame number for starting calculation, defaults to the first frame
:param end_frame: int, frame at which calculation ends, defaults to the last (-1) frame
:return: dict, with time as key and corresponding bound crosslinker count `s value
"""
AA_vs_time = {}
for frame in self.frames[start_frame:end_frame]:
AA_vs_time[frame.time] = int(frame.data['AA'].iloc[0])
return AA_vs_time