Nuclear Deformation Simulation¶
Import the necessary packages¶
[1]:
import parmed as pmd
import json
import sys
from sys import platform
import mdtraj as md
import simtk.openmm.app as mmapp
import mdtraj.reporters
import simtk.unit as u
import random
from openNucleome import OpenNucleome
from openNucleome.utils import coor_transformation, final_frame
import warnings
import numpy as np
import pandas as pd
Important parameters¶
We defined the important parameters in the next block. First, we set the transition probability between dP particles and P particles as 0.2, and the transition frequency as 4000. When creating the system, we set type 6, 7 as the dP and P particles, respectively, so we kept using 6, 7 here. In this example, we ran a simulation with total length of 100,000 steps, and output one configuration and the energy every 2000 steps.
[2]:
prob_P_dP = 0.2 # Transition probability from P to dP
prob_dP_P = 0.2 # Transition probability from dP to P
transition_freq = 4000
sampling_freq = 2000
dP_type = 6
P_type = 7
total_steps = 100000
Initialize the system¶
We first set up an example “model” of class “OpenNucleome” with the conserved temperature, damping coefficient, timestep, and the mass scale. In this folder, we also included the initial configuration “human.pdb” used for the simulation and created a system according to the initial configuration.
In this example, we moved all the lamina beads, and would consider the dynamics of the membrane, and that is why we set “True” (on) for membrane dynamics. Consequently, we need to designate the bonds between specific lamina beads, so we included a text file, which logs all the pairs linked by the bonds, for the variable “membrane_bond”.
[3]:
model = OpenNucleome(1.0, 0.1, 0.005, 1.0) # 1.0: temperature (LJ reduced unit);
# 0.1: damping coefficient (LJ reduced unit);
# 0.005: timestep (LJ reduced unit);
# 1.0: mass_scale
PDB_file = "strength_one/human.pdb" #The initial configuration
# Generate new elements and construct topology as well
# flag_membrane: True for including lamina dynamics, False for excluding lamina dynamics;
# lam_bond: A file contains the lamina bond when membrane_dynamics is on.
warnings.filterwarnings("ignore")
model.create_system(PDB_file, flag_membrane = True, lam_bond = 'input_params/lamina_bond.txt')
Add the force field¶
Different from the sphere nucleus situation, we used a csv file to log all the flags of interactions between chromosomes and chromosomes, or chromosomes and nuclear landmarks.
The column from “bond” to “lam_squeeze” is boolen, which means the switches of the corresponding interactions. For example, if the spec-chrom is True, it means in the simulation, the interaction between speckle and chromosome will be present; if the inter is False, it means we exclude the interchromosomal interactions between chromosome. Remember some potentials require the corresponding parameter files, such as ideal requires ideal_param_file and spec-chrom requires the chr_spec_param, so if you turn those potentials on, you should also include the corresponding parameter files.
In this example, we turn every interaction on, and set the strength of force squeezing the nucleus as 1.0 (k) when loading the customized force field. The users can set their specific strengths.
Because during the simulations, we would transit the dP speckle and P speckle particles, here, we logged the start index and end index of speckle particle, and computed the number of speckle particles.
[4]:
# Add the default force field
# Add the customized force field
# In this customized setting, I logged all the flags for different interactions
# (True means on, False means off, and NaN means N/A)
# and all the corresponding files required for interactions.
force_field = pd.read_csv('input_params/input.csv', sep=' ', header=0, index_col=0)
model.load_customized_settings(force_field, 'input_params', k = 1.0)
index_spec_spec_potential = 6 #The 6-th potential is speckle-speckle interaction in the default settings
start_spec_index = model.N_chr_nuc+1
end_spec_index = model.N_chr_nuc_spec+1
N_spec = end_spec_index-start_spec_index
force_field
[4]:
bond | angle | softcore | ideal | compt | inter | spec-spec | spec-chrom | nuc-nuc | nuc-spec | ... | lam-chrom | hard-wall | lam-lam | lam_squeeze | ideal_param_file | compt_param_file | interchr_param_file | chr_spec_param | chr_nuc_param | chr_lam_param | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
chromosome | True | True | True | True | True | True | NaN | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | ideal_param_file.txt | compt_param_file.txt | interchr_param_file.txt | NaN | NaN | NaN |
speckle | NaN | NaN | NaN | NaN | NaN | NaN | True | True | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | chr_spec_param.txt | NaN | NaN |
nucleolus | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | True | True | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | chr_nuc_param.txt | NaN |
lamina | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | ... | True | True | True | True | NaN | NaN | NaN | NaN | NaN | chr_lam_param.txt |
4 rows × 21 columns
Perform the simulation¶
We first created the simulation with a specific Platform, and here, we used “CUDA” but users can also use “CPU”, “Reference”, and “OpenCL” according to their hardware. Before the simulation, we minimized the energy to make the system much more reasonable and stable. After randomly setting velocity, we started our simulation with a total length of 100,000 steps and output the configuration and energy every 2000 steps, and change the speckle types every 4000 steps as we mentioned in the previous blocks.
[5]:
#model.save_system("model_before_simulation_0.xml")
simulation = model.create_simulation(platform_type = "CUDA") # Users can also use CPU, Reference, OpenCL.
simulation.context.setPositions(model.chr_positions)
simulation.minimizeEnergy()
simulation.reporters.append(mdtraj.reporters.DCDReporter('results/step_100000.dcd', sampling_freq))
def setVelocity(context):
sigma = u.sqrt(1.0*u.kilojoule_per_mole / model.chr_system.getParticleMass(1))
velocs = u.Quantity(1.0 * np.random.normal(size=(model.chr_system.getNumParticles(), 3)),
u.meter) * (sigma / u.meter)
context.setVelocities(velocs)
setVelocity(simulation.context)
simulation.reporters.append(mmapp.statedatareporter.StateDataReporter(sys.stdout, sampling_freq, step=True,
potentialEnergy=True, kineticEnergy=True, temperature=True, progress=True,
remainingTime=True, separator='\t', totalSteps = total_steps))
for i in range(total_steps//transition_freq):
simulation.step(transition_freq)
# Change the type of speckles every 4000 steps, non-equilibrium scheme.
# Do the chemical modification, and change the spec-spec potential on the fly.
for j in np.random.randint(start_spec_index-1, end_spec_index-1, N_spec):
if model.compart_type[j] == dP_type-1:
model.compart_type[j] = P_type-1 if random.random() < prob_dP_P else dP_type-1
else:
model.compart_type[j] = dP_type-1 if random.random() < prob_P_dP else P_type-1
# Update the context after changing the type of speckles.
for m in range(model.chr_system.getNumParticles()):
model.chr_system.getForce(index_spec_spec_potential).setParticleParameters(m, [model.compart_type[m]])
model.chr_system.getForce(index_spec_spec_potential).updateParametersInContext(simulation.context)
# Keep the final result of bead types in case constructing the configuration for the continuous simulation.
np.savetxt('results/compt_final_frame.txt', (np.array(model.compart_type)+1).reshape((-1,1)), fmt='%d')
#"Progress (%)" "Step" "Potential Energy (kJ/mole)" "Kinetic Energy (kJ/mole)" "Temperature (K)" "Time Remaining"
2.0% 2000 123136.44445905171 100752.69234185581 114.52055593216552 --
4.0% 4000 138145.93252829043 99854.70038122084 113.4998532971868 9:06
6.0% 6000 142575.6182367836 104465.90880812544 118.741183730089 8:38
8.0% 8000 141094.42452352808 107327.50563145665 121.99381799170929 8:21
10.0% 10000 139697.16552712512 107342.47070395519 122.01082804258003 8:13
12.0% 12000 137654.04464475974 106994.70033531352 121.61553482482218 8:03
14.0% 14000 138386.55393252496 105782.62134612219 120.2378251433289 7:53
16.0% 16000 137409.19082230702 106199.98460544148 120.71222112592687 7:42
18.0% 18000 138944.10917304258 106093.39691125305 120.59106821467195 7:32
20.0% 20000 137593.22835381792 106262.0393691768 120.78275567816587 7:21
22.0% 22000 138695.92867257126 105989.21260070454 120.47264710962654 7:10
24.0% 24000 138082.96422167047 105636.71725733916 120.07198324896527 6:59
26.0% 26000 138577.68014618923 105942.89960543014 120.42000543978695 6:48
28.0% 28000 138547.97569732778 105614.2894846135 120.04649071927538 6:37
30.0% 30000 139048.79707005448 105953.13915726854 120.43164422721559 6:26
32.0% 32000 139369.4478256774 106096.93739319986 120.5950925037643 6:15
34.0% 34000 139349.70734011347 105551.45826302844 119.9750736061584 6:04
36.0% 36000 138652.69678783504 106307.39016938012 120.83430366889978 5:53
38.0% 38000 138994.0196299976 105924.91785091767 120.39956647706012 5:42
40.0% 40000 138711.95389602106 106045.77361075555 120.53693718629414 5:31
42.0% 42000 139755.55680584465 106157.32762501454 120.66373506566056 5:20
44.0% 44000 139418.1188406263 105861.9173488944 120.32795695131931 5:09
46.0% 46000 139318.77307771594 106084.84309645879 120.58134552039144 4:58
48.0% 48000 139156.1582248191 106000.44885852029 120.48541880298527 4:47
50.0% 50000 139394.24871664512 106154.93144347834 120.66101144572286 4:36
52.0% 52000 138976.02488041663 105917.33440921981 120.39094675743841 4:24
54.0% 54000 139636.7751483859 105824.53815483821 120.2854698873599 4:13
56.0% 56000 139109.49119912702 105962.1255783129 120.44185864342542 4:02
58.0% 58000 139591.83060822415 105548.44699685683 119.9716508502199 3:51
60.0% 60000 139158.00653002597 105773.10164969457 120.22700457961139 3:40
62.0% 62000 139141.23690284294 106340.19938493858 120.87159626642863 3:29
64.0% 64000 138657.07116640714 106162.69666414094 120.66983778441957 3:18
66.0% 66000 138872.56066384647 105709.55038510424 120.154769029554 3:07
68.0% 68000 139552.46901895298 106237.45014421445 120.75480633361563 2:56
70.0% 70000 140314.6318439555 105998.06802734527 120.48271263103683 2:45
72.0% 72000 139878.4964976344 105824.37540777247 120.28528490089441 2:34
74.0% 74000 140049.54976571468 105978.09999204957 120.46001596209643 2:23
76.0% 76000 139040.28804717027 105698.80343463195 120.14255350742695 2:12
78.0% 78000 139441.61604379857 105796.50827126215 120.25360971791694 2:01
80.0% 80000 139103.81283076043 105733.9975154245 120.1825568622178 1:50
82.0% 82000 139735.49375639576 105520.89870734174 119.94033808470546 1:39
84.0% 84000 139625.44875567633 105857.33796241746 120.32275179130842 1:28
86.0% 86000 139302.36985220472 106414.25964148348 120.95577686304458 1:17
88.0% 88000 139197.9629622838 105156.448308775 119.52608550957876 1:06
90.0% 90000 138969.3590577207 105676.13902677708 120.1167920063982 0:55
92.0% 92000 138261.92996336846 105622.26065619843 120.05555115210468 0:44
94.0% 94000 138923.98136101887 106060.5908358773 120.55377918643204 0:33
96.0% 96000 138863.37451388055 105655.18511185612 120.0929747373017 0:22
98.0% 98000 138649.21396001056 106102.68049923926 120.60162040571038 0:11
100.0% 100000 138531.91066385998 106315.62779116184 120.84366696236692 0:00
Post-process the output files¶
We converted the trajectory with the default OpenMM unit to the nex trajectory with the reduced unit, and saved the final configuration according to the last frame.
[6]:
coor_transformation('results/step_100000.dcd', 'results/reduced_traj.dcd')
final_frame('results/step_100000.dcd', 'results/compt_final_frame.txt', 'results/final_config.pdb')