pyLoopSage 1.0.18__tar.gz → 1.0.20__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyloopsage-1.0.18/pyLoopSage.egg-info → pyloopsage-1.0.20}/PKG-INFO +7 -6
- {pyloopsage-1.0.18 → pyloopsage-1.0.20}/README.md +3 -3
- {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/__init__.py +1 -1
- {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/args_definition.py +1 -1
- {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/em.py +2 -2
- pyloopsage-1.0.20/loopsage/knots.py +193 -0
- {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/md.py +3 -4
- {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/preproc.py +2 -0
- {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/run.py +2 -2
- {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/stochastic_simulation.py +24 -25
- {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/utils.py +7 -7
- {pyloopsage-1.0.18 → pyloopsage-1.0.20/pyLoopSage.egg-info}/PKG-INFO +7 -6
- {pyloopsage-1.0.18 → pyloopsage-1.0.20}/pyLoopSage.egg-info/SOURCES.txt +1 -0
- {pyloopsage-1.0.18 → pyloopsage-1.0.20}/pyLoopSage.egg-info/requires.txt +1 -1
- {pyloopsage-1.0.18 → pyloopsage-1.0.20}/setup.py +2 -2
- {pyloopsage-1.0.18 → pyloopsage-1.0.20}/LICENSE +0 -0
- {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/forcefields/classic_sm_ff.xml +0 -0
- {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/initial_structures.py +0 -0
- {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/plots.py +0 -0
- {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/vizualization_tools.py +0 -0
- {pyloopsage-1.0.18 → pyloopsage-1.0.20}/pyLoopSage.egg-info/dependency_links.txt +0 -0
- {pyloopsage-1.0.18 → pyloopsage-1.0.20}/pyLoopSage.egg-info/entry_points.txt +0 -0
- {pyloopsage-1.0.18 → pyloopsage-1.0.20}/pyLoopSage.egg-info/top_level.txt +0 -0
- {pyloopsage-1.0.18 → pyloopsage-1.0.20}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pyLoopSage
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.20
|
|
4
4
|
Summary: An energy-based stochastic model of loop extrusion in chromatin.
|
|
5
5
|
Home-page: https://github.com/SFGLab/pyLoopSage
|
|
6
6
|
Author: Sebastian Korsak
|
|
@@ -12,7 +12,7 @@ Classifier: Operating System :: POSIX :: Linux
|
|
|
12
12
|
Requires-Python: >=3.10
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist: numpy
|
|
15
|
+
Requires-Dist: numpy==1.24.3
|
|
16
16
|
Requires-Dist: numba
|
|
17
17
|
Requires-Dist: scipy
|
|
18
18
|
Requires-Dist: pandas
|
|
@@ -38,6 +38,7 @@ Dynamic: description
|
|
|
38
38
|
Dynamic: description-content-type
|
|
39
39
|
Dynamic: home-page
|
|
40
40
|
Dynamic: license
|
|
41
|
+
Dynamic: license-file
|
|
41
42
|
Dynamic: requires-dist
|
|
42
43
|
Dynamic: requires-python
|
|
43
44
|
Dynamic: summary
|
|
@@ -178,7 +179,7 @@ You also need to download the reference genome from: ftp://ftp.1000genomes.ebi.a
|
|
|
178
179
|
|
|
179
180
|
*Alternativelly, it is possible to import a .bedpe file without the last two columns (CTCF orientation). In this case, CTCF would act as an orientation independent barrier. This might affect slightly the results, but it is an easier option, if you do not want to run a CTCF motif finding script.*
|
|
180
181
|
|
|
181
|
-
Then, we define the main parameters of the simulation `N_beads,N_coh,kappa,f,b` or we can choose the default ones (take care because it might be the case that they are not the appropriate ones and they need to be changed), the parameters of Monte Carlo `N_steps, MC_step, burnin, T`, and we initialize the class `LoopSage()`. The command `sim.run_energy_minimization()` corresponds to the stochastic Monte Carlo simulation, and it produces a set of cohesin constraints as a result (`Ms, Ns`). Note that the stochastic simulation has two modes: `Annealing` and `Metropolis`. We feed cohesin constraints to the molecular simulation part of and we run `
|
|
182
|
+
Then, we define the main parameters of the simulation `N_beads,N_coh,kappa,f,b` or we can choose the default ones (take care because it might be the case that they are not the appropriate ones and they need to be changed), the parameters of Monte Carlo `N_steps, MC_step, burnin, T`, and we initialize the class `LoopSage()`. The command `sim.run_energy_minimization()` corresponds to the stochastic Monte Carlo simulation, and it produces a set of cohesin constraints as a result (`Ms, Ns`). Note that the stochastic simulation has two modes: `Annealing` and `Metropolis`. We feed cohesin constraints to the molecular simulation part of and we run `EM_LE()` or `EM_LE()` simulation which produces a trajectory of 3d-structures, and the average heatmap. `MD_LE()` function can produce an actual trajectory and a `.dcd` video of how simulation changes over time. However, the implementation needs a high amount of memory since we need to define a bond for each time step, and it may not work for large systems. `EM_LE()` is suggested for large simulations, because it does not require so big amount of memory.
|
|
182
183
|
|
|
183
184
|
### Running LoopSage from command-line
|
|
184
185
|
To run LoopSage from command-line, you only need to type a command
|
|
@@ -307,8 +308,8 @@ An example, illustrated with Chimera software, simulated trajectory of structure
|
|
|
307
308
|
| EV_P | Probability that excluded volume is disabled. Enable it only in case of topoisomerase activity simulation. | float | 0.0 |
|
|
308
309
|
| EV_FF_STRENGTH | Excluded-volume strength. | float | 100.0 |
|
|
309
310
|
| EV_FF_POWER | Excluded-volume power. | float | 3.0 |
|
|
310
|
-
| FRICTION | Friction coefficient of the Langevin integrator. | float | 0.1
|
|
311
|
-
| TOLERANCE | Stopping condition for energy minimization.
|
|
311
|
+
| FRICTION | Friction coefficient of the Langevin integrator. | float | 0.1 |
|
|
312
|
+
| TOLERANCE | Stopping condition for energy minimization. | float | 0.001 |
|
|
312
313
|
| VIZ_HEATS | True to visualize the output average heatmap. | bool | True |
|
|
313
314
|
| SIM_TEMP | Temperature of the 3D simulation (EM or MD). | Quantity | 310 kelvin |
|
|
314
315
|
| SIM_STEP | Amount of simulation steps for loop force adjustments. | int | 1000 |
|
|
@@ -134,7 +134,7 @@ You also need to download the reference genome from: ftp://ftp.1000genomes.ebi.a
|
|
|
134
134
|
|
|
135
135
|
*Alternativelly, it is possible to import a .bedpe file without the last two columns (CTCF orientation). In this case, CTCF would act as an orientation independent barrier. This might affect slightly the results, but it is an easier option, if you do not want to run a CTCF motif finding script.*
|
|
136
136
|
|
|
137
|
-
Then, we define the main parameters of the simulation `N_beads,N_coh,kappa,f,b` or we can choose the default ones (take care because it might be the case that they are not the appropriate ones and they need to be changed), the parameters of Monte Carlo `N_steps, MC_step, burnin, T`, and we initialize the class `LoopSage()`. The command `sim.run_energy_minimization()` corresponds to the stochastic Monte Carlo simulation, and it produces a set of cohesin constraints as a result (`Ms, Ns`). Note that the stochastic simulation has two modes: `Annealing` and `Metropolis`. We feed cohesin constraints to the molecular simulation part of and we run `
|
|
137
|
+
Then, we define the main parameters of the simulation `N_beads,N_coh,kappa,f,b` or we can choose the default ones (take care because it might be the case that they are not the appropriate ones and they need to be changed), the parameters of Monte Carlo `N_steps, MC_step, burnin, T`, and we initialize the class `LoopSage()`. The command `sim.run_energy_minimization()` corresponds to the stochastic Monte Carlo simulation, and it produces a set of cohesin constraints as a result (`Ms, Ns`). Note that the stochastic simulation has two modes: `Annealing` and `Metropolis`. We feed cohesin constraints to the molecular simulation part of and we run `EM_LE()` or `EM_LE()` simulation which produces a trajectory of 3d-structures, and the average heatmap. `MD_LE()` function can produce an actual trajectory and a `.dcd` video of how simulation changes over time. However, the implementation needs a high amount of memory since we need to define a bond for each time step, and it may not work for large systems. `EM_LE()` is suggested for large simulations, because it does not require so big amount of memory.
|
|
138
138
|
|
|
139
139
|
### Running LoopSage from command-line
|
|
140
140
|
To run LoopSage from command-line, you only need to type a command
|
|
@@ -263,8 +263,8 @@ An example, illustrated with Chimera software, simulated trajectory of structure
|
|
|
263
263
|
| EV_P | Probability that excluded volume is disabled. Enable it only in case of topoisomerase activity simulation. | float | 0.0 |
|
|
264
264
|
| EV_FF_STRENGTH | Excluded-volume strength. | float | 100.0 |
|
|
265
265
|
| EV_FF_POWER | Excluded-volume power. | float | 3.0 |
|
|
266
|
-
| FRICTION | Friction coefficient of the Langevin integrator. | float | 0.1
|
|
267
|
-
| TOLERANCE | Stopping condition for energy minimization.
|
|
266
|
+
| FRICTION | Friction coefficient of the Langevin integrator. | float | 0.1 |
|
|
267
|
+
| TOLERANCE | Stopping condition for energy minimization. | float | 0.001 |
|
|
268
268
|
| VIZ_HEATS | True to visualize the output average heatmap. | bool | True |
|
|
269
269
|
| SIM_TEMP | Temperature of the 3D simulation (EM or MD). | Quantity | 310 kelvin |
|
|
270
270
|
| SIM_STEP | Amount of simulation steps for loop force adjustments. | int | 1000 |
|
|
@@ -178,4 +178,4 @@ args = ListOfArgs([
|
|
|
178
178
|
Arg('VIZ_HEATS', help="Visualize the output average heatmap.", type=bool, default='True', val='True'),
|
|
179
179
|
Arg('SIM_TEMP', help="The temperature of the 3D simulation (EM or MD).", type=Quantity, default='310 kelvin', val='310 kelvin'),
|
|
180
180
|
Arg('SIM_STEP', help="This is the amount of simulation steps that are perform each time that we change the loop forces. If this number is too high, the simulation is slow, if is too low it may not have enough time to adapt the structure to the new constraints.", type=int, default='1000', val='1000'),
|
|
181
|
-
])
|
|
181
|
+
])
|
|
@@ -41,13 +41,13 @@ class EM_LE:
|
|
|
41
41
|
self.ev_ff_power = ev_ff_power
|
|
42
42
|
self.tolerance = tolerance
|
|
43
43
|
|
|
44
|
-
def run_pipeline(self,plots=False, friction=0.1, ff_path=default_xml_path, integrator_step = 100 * mm.unit.femtosecond, temperature = 310):
|
|
44
|
+
def run_pipeline(self,plots=False, friction=0.1, ff_path=default_xml_path, integrator_step = 100 * mm.unit.femtosecond, temperature = 310, init_struct='rw'):
|
|
45
45
|
'''
|
|
46
46
|
This is the basic function that runs the molecular simulation pipeline.
|
|
47
47
|
'''
|
|
48
48
|
# Define initial structure
|
|
49
49
|
print('Building initial structure...')
|
|
50
|
-
points = compute_init_struct(self.N_beads,mode=
|
|
50
|
+
points = compute_init_struct(self.N_beads,mode=init_struct)
|
|
51
51
|
write_mmcif(points,self.path+'/LE_init_struct.cif')
|
|
52
52
|
generate_psf(self.N_beads,self.path+'/other/LE_init_struct.psf')
|
|
53
53
|
print('Done brother ;D\n')
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import topoly as tp
|
|
2
|
+
import numpy as np
|
|
3
|
+
import matplotlib
|
|
4
|
+
from tqdm import tqdm
|
|
5
|
+
import matplotlib
|
|
6
|
+
import matplotlib.pyplot as plt
|
|
7
|
+
from mpl_toolkits.mplot3d import Axes3D
|
|
8
|
+
from mpl_toolkits.mplot3d.art3d import Line3DCollection
|
|
9
|
+
import matplotlib.colors as mcolors
|
|
10
|
+
import scipy.interpolate as si
|
|
11
|
+
from scipy.spatial.distance import pdist, squareform
|
|
12
|
+
from scipy.integrate import dblquad
|
|
13
|
+
from scipy.interpolate import splprep, splev, CubicSpline
|
|
14
|
+
from .uts import *
|
|
15
|
+
|
|
16
|
+
def trivial_knot(N=100, radius=1.0):
|
|
17
|
+
"""
|
|
18
|
+
Generate a 3D trivial (unknot) structure as a simple circle.
|
|
19
|
+
|
|
20
|
+
Parameters:
|
|
21
|
+
N (int): Number of points in the loop.
|
|
22
|
+
radius (float): Radius of the circular unknot.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
numpy.ndarray: Nx3 array representing the 3D coordinates of the unknot.
|
|
26
|
+
"""
|
|
27
|
+
t = np.linspace(0, 2 * np.pi, N) # Parameter along the circle
|
|
28
|
+
x = radius * np.cos(t) # Circle in the xy-plane
|
|
29
|
+
y = radius * np.sin(t)
|
|
30
|
+
z = np.zeros_like(t) # No variation in z (flat circle)
|
|
31
|
+
|
|
32
|
+
return np.vstack((x, y, z)).T # Return as Nx3 matrix
|
|
33
|
+
|
|
34
|
+
def toroidal_knot(p, q, num_points=500, R=2, r=1):
|
|
35
|
+
"""
|
|
36
|
+
Generate a toroidal (p, q) knot.
|
|
37
|
+
|
|
38
|
+
Parameters:
|
|
39
|
+
p (int): Number of turns around the torus' central axis.
|
|
40
|
+
q (int): Number of turns around the torus' inner axis.
|
|
41
|
+
num_points (int): Number of points to plot.
|
|
42
|
+
R (float): Major radius of the torus (distance from center to torus tube).
|
|
43
|
+
r (float): Minor radius of the torus tube.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
np.ndarray: Array of shape (num_points, 3) representing 3D coordinates of the knot.
|
|
47
|
+
"""
|
|
48
|
+
t = np.linspace(0, 2 * np.pi, num_points, endpoint=False)
|
|
49
|
+
|
|
50
|
+
# Parametric equations for the toroidal knot
|
|
51
|
+
x = (R + r * np.cos(q * t)) * np.cos(p * t)
|
|
52
|
+
y = (R + r * np.cos(q * t)) * np.sin(p * t)
|
|
53
|
+
z = r * np.sin(q * t)
|
|
54
|
+
V = np.column_stack((x, y, z))
|
|
55
|
+
V = np.vstack((V,[x[0],y[0],z[0]]))
|
|
56
|
+
return V
|
|
57
|
+
|
|
58
|
+
def trefoil_knot(N=200, scale=0.8, offset=(0, 0, 0), rotation=None):
|
|
59
|
+
"""Generate a 3D Trefoil Knot with transformations."""
|
|
60
|
+
t = np.linspace(0, 2 * np.pi-np.pi/36, N)
|
|
61
|
+
x = scale * (np.sin(t) + 2 * np.sin(2 * t)) + offset[0]
|
|
62
|
+
y = scale * (np.cos(t) - 2 * np.cos(2 * t)) + offset[1]
|
|
63
|
+
z = scale * (-np.sin(3 * t)) + offset[2]
|
|
64
|
+
|
|
65
|
+
V = np.vstack((x, y, z)).T
|
|
66
|
+
|
|
67
|
+
if rotation is not None:
|
|
68
|
+
V = np.dot(V, rotation) # Apply rotation matrix
|
|
69
|
+
|
|
70
|
+
return V
|
|
71
|
+
|
|
72
|
+
def cinquefoil_knot(N=300, scale=0.8, offset=(0, 0, 0), rotation=None):
|
|
73
|
+
"""Generate a 3D Cinquefoil (5_1) Knot with transformations."""
|
|
74
|
+
t = np.linspace(0, 2 * np.pi, N)
|
|
75
|
+
x = scale * (np.sin(2*t) + 2*np.sin(3*t))
|
|
76
|
+
y = scale * (np.cos(2*t) - 2*np.cos(3*t))
|
|
77
|
+
z = scale * (-np.sin(5*t))
|
|
78
|
+
|
|
79
|
+
V = np.vstack((x, y, z)).T
|
|
80
|
+
if rotation is not None:
|
|
81
|
+
V = np.dot(V, rotation) # Apply rotation
|
|
82
|
+
return V + np.array(offset) # Apply position offset
|
|
83
|
+
|
|
84
|
+
def random_rotation_matrix():
|
|
85
|
+
"""Generate a random 3D rotation matrix."""
|
|
86
|
+
theta, phi, z = np.random.uniform(0, 2*np.pi-np.pi/36, 3)
|
|
87
|
+
r = np.sqrt(z)
|
|
88
|
+
V = np.array([
|
|
89
|
+
[np.cos(theta) * np.cos(phi), -np.sin(theta), np.cos(theta) * np.sin(phi)],
|
|
90
|
+
[np.sin(theta) * np.cos(phi), np.cos(theta), np.sin(theta) * np.sin(phi)],
|
|
91
|
+
[-np.sin(phi), 0, np.cos(phi)]
|
|
92
|
+
])
|
|
93
|
+
return V
|
|
94
|
+
|
|
95
|
+
def smooth_linkage(start, end, N=50):
|
|
96
|
+
"""Generate a smooth linking curve between two knots."""
|
|
97
|
+
t = np.linspace(0, 1, N)
|
|
98
|
+
x = (1 - t) * start[0] + t * end[0]
|
|
99
|
+
y = (1 - t) * start[1] + t * end[1]
|
|
100
|
+
z = (1 - t) * start[2] + t * end[2]
|
|
101
|
+
return np.vstack((x, y, z)).T
|
|
102
|
+
|
|
103
|
+
def generate_knotted_structure(num_knots=5, N_per_knot=300):
|
|
104
|
+
"""Generate a large 3D knotted structure by linking multiple knots."""
|
|
105
|
+
knots = []
|
|
106
|
+
previous_end = None
|
|
107
|
+
|
|
108
|
+
for i in range(num_knots):
|
|
109
|
+
knot_type = np.random.choice(["trefoil", "figure_eight"])
|
|
110
|
+
offset = previous_end + np.random.uniform(-3.0, 3.0, size=3) if previous_end is not None else np.random.uniform(-5, 5, size=3)
|
|
111
|
+
scale = np.random.uniform(0.5, 1.0) # Vary knot sizes
|
|
112
|
+
rotation = random_rotation_matrix()
|
|
113
|
+
|
|
114
|
+
if knot_type == "trefoil":
|
|
115
|
+
knot = trefoil_knot(N=N_per_knot, scale=scale, offset=offset, rotation=rotation)
|
|
116
|
+
else:
|
|
117
|
+
knot = cinquefoil_knot(N=N_per_knot, scale=scale, offset=offset, rotation=rotation)
|
|
118
|
+
|
|
119
|
+
if previous_end is not None:
|
|
120
|
+
linkage = smooth_linkage(previous_end, knot[0], N=50) # Smooth connection
|
|
121
|
+
knots.append(linkage)
|
|
122
|
+
|
|
123
|
+
knots.append(knot)
|
|
124
|
+
previous_end = knot[-1]
|
|
125
|
+
|
|
126
|
+
return np.vstack(knots)
|
|
127
|
+
|
|
128
|
+
def smooth_knot_spline(V, num_interp=100, closed=True):
|
|
129
|
+
"""
|
|
130
|
+
Smoothly interpolates a 3D knot using a cubic spline.
|
|
131
|
+
|
|
132
|
+
Parameters:
|
|
133
|
+
V (numpy.ndarray): Nx3 array representing the 3D knot.
|
|
134
|
+
num_interp (int): Number of points in the interpolated knot.
|
|
135
|
+
closed (bool): Whether to enforce periodic boundary conditions for closed knots.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
numpy.ndarray: Smoothed Nx3 array of the interpolated knot.
|
|
139
|
+
"""
|
|
140
|
+
if V.shape[1] != 3:
|
|
141
|
+
raise ValueError("Input array V must have shape (N, 3)")
|
|
142
|
+
|
|
143
|
+
if len(V) < 4:
|
|
144
|
+
raise ValueError("At least 4 points are required for spline fitting.")
|
|
145
|
+
|
|
146
|
+
# Define the parameter t along the curve
|
|
147
|
+
t = np.linspace(0, 1, len(V))
|
|
148
|
+
|
|
149
|
+
# Interpolation points
|
|
150
|
+
t_new = np.linspace(0, 1, num_interp)
|
|
151
|
+
|
|
152
|
+
# Fit cubic splines separately for each coordinate
|
|
153
|
+
cs_x = CubicSpline(t, V[:, 0], bc_type='periodic' if closed else 'not-a-knot')
|
|
154
|
+
cs_y = CubicSpline(t, V[:, 1], bc_type='periodic' if closed else 'not-a-knot')
|
|
155
|
+
cs_z = CubicSpline(t, V[:, 2], bc_type='periodic' if closed else 'not-a-knot')
|
|
156
|
+
|
|
157
|
+
# Evaluate the splines
|
|
158
|
+
x_new = cs_x(t_new)
|
|
159
|
+
y_new = cs_y(t_new)
|
|
160
|
+
z_new = cs_z(t_new)
|
|
161
|
+
|
|
162
|
+
return np.vstack((x_new, y_new, z_new)).T
|
|
163
|
+
|
|
164
|
+
def link_number_ensemble(path,N_ens=1250,step=100):
|
|
165
|
+
nlinks = list()
|
|
166
|
+
Ms = np.load(path+'/other/Ms.npy')
|
|
167
|
+
Ns = np.load(path+'/other/Ns.npy')
|
|
168
|
+
for i in range(N_ens//step,N_ens,step):
|
|
169
|
+
V = uts.get_coordinates_cif(path+f'/ensemble/MDLE_{i+1}.cif')
|
|
170
|
+
ms, ns = Ms[:,i], Ns[:,i]
|
|
171
|
+
N_links, links = calculate_linking_number(V,ms,ns)
|
|
172
|
+
nlinks.append(N_links)
|
|
173
|
+
return nlinks
|
|
174
|
+
|
|
175
|
+
def calculate_linking_number(V,ms,ns):
|
|
176
|
+
links = list()
|
|
177
|
+
for i in tqdm(range(len(ms))):
|
|
178
|
+
for j in range(i+1,len(ms)):
|
|
179
|
+
if (ns[i]-ms[i])>5 and (ns[j]-ms[j])>5 and (((ms[i]<ns[i]) and (ns[i]<ms[j])) or ((ms[j]<ns[j]) and (ns[j]<ms[i]))):
|
|
180
|
+
loop1 = V[ms[i]:ns[i]]
|
|
181
|
+
loop1 = np.vstack((loop1,loop1[0,:]))
|
|
182
|
+
loop1 = smooth_knot_spline(loop1,2*len(loop1))
|
|
183
|
+
l1 = [list(loop1[i]) for i in range(len(loop1))]
|
|
184
|
+
loop2 = V[ms[j]:ns[j]]
|
|
185
|
+
loop2 = np.vstack((loop2,loop2[0,:]))
|
|
186
|
+
loop2 = smooth_knot_spline(loop2,2*len(loop2))
|
|
187
|
+
l2 = [list(loop2[i]) for i in range(len(loop2))]
|
|
188
|
+
links.append(tp.gln(l1,l2))
|
|
189
|
+
links = np.array(links)
|
|
190
|
+
links = links[links>0.5]
|
|
191
|
+
N_links = len(links)
|
|
192
|
+
|
|
193
|
+
return N_links, links
|
|
@@ -36,7 +36,7 @@ class MD_LE:
|
|
|
36
36
|
self.ev_ff_power = ev_ff_power
|
|
37
37
|
self.tolerance = tolerance
|
|
38
38
|
|
|
39
|
-
def run_pipeline(self,run_MD=True, friction=0.1, integrator_step=10 * mm.unit.femtosecond, sim_step=100, ff_path = 'forcefields/classic_sm_ff.xml',temperature=310, p_ev=0, plots=False):
|
|
39
|
+
def run_pipeline(self,run_MD=True, friction=0.1, integrator_step=10 * mm.unit.femtosecond, sim_step=100, ff_path = 'forcefields/classic_sm_ff.xml', init_struct='rw', temperature=310, p_ev=0, plots=False):
|
|
40
40
|
'''
|
|
41
41
|
This is the basic function that runs the molecular simulation pipeline.
|
|
42
42
|
'''
|
|
@@ -78,15 +78,14 @@ class MD_LE:
|
|
|
78
78
|
print('Running molecular dynamics (wait for 100 steps)...')
|
|
79
79
|
start = time.time()
|
|
80
80
|
heats = list()
|
|
81
|
-
for i in range(
|
|
81
|
+
for i in range(self.N_steps):
|
|
82
82
|
# Define probabilities that EV would be disabled
|
|
83
83
|
if p_ev>0: self.ps_ev = np.random.rand(self.N_beads)
|
|
84
|
-
# Change forcefield
|
|
85
84
|
self.change_loop(i)
|
|
86
85
|
self.change_ev()
|
|
87
86
|
self.simulation.step(sim_step)
|
|
88
87
|
self.state = self.simulation.context.getState(getPositions=True)
|
|
89
|
-
PDBxFile.writeFile(pdb.topology, self.state.getPositions(), open(self.path+f'/ensemble/MDLE_{i}.cif', 'w'))
|
|
88
|
+
PDBxFile.writeFile(pdb.topology, self.state.getPositions(), open(self.path+f'/ensemble/MDLE_{i+1}.cif', 'w'))
|
|
90
89
|
heats.append(get_heatmap(self.state.getPositions(),save=False))
|
|
91
90
|
end = time.time()
|
|
92
91
|
elapsed = end - start
|
|
@@ -52,6 +52,8 @@ def binding_vectors_from_bedpe(bedpe_file,N_beads,region,chrom,normalization=Fal
|
|
|
52
52
|
if df[7][i]>=0: R[x] += df[6][i]*df[7][i]
|
|
53
53
|
if df[8][i]>=0: R[y] += df[6][i]*df[8][i]
|
|
54
54
|
else:
|
|
55
|
+
if x>=N_beads: x=N_beads-1
|
|
56
|
+
if y>=N_beads: y=N_beads-1
|
|
55
57
|
L[x] += df[6][i]
|
|
56
58
|
L[y] += df[6][i]
|
|
57
59
|
R[x] += df[6][i]
|
|
@@ -72,9 +72,9 @@ def main():
|
|
|
72
72
|
sim = StochasticSimulation(region,chrom,bedpe_file,out_dir=output_name,N_beads=N_beads,N_lef=N_lef,N_lef2=N_lef2)
|
|
73
73
|
Es, Ms, Ns, Bs, Ks, Fs, ufs = sim.run_energy_minimization(N_steps,MC_step,burnin,T,T_min,mode=mode,viz=args.SAVE_PLOTS,save=args.SAVE_MDT,lef_rw=args.LEF_RW,f=f,f2=f2,b=b,kappa=kappa,lef_drift=args.LEF_DRIFT,cross_loop=args.CROSS_LOOP)
|
|
74
74
|
if args.SIMULATION_TYPE=='EM':
|
|
75
|
-
sim.run_EM(args.PLATFORM,args.ANGLE_FF_STRENGTH,args.LE_FF_LENGTH,args.LE_FF_STRENGTH,args.EV_FF_STRENGTH,args.EV_FF_POWER,args.TOLERANCE,args.FRICTION,args.INTEGRATOR_STEP,args.SIM_TEMP,args.VIZ_HEATS,args.FORCEFIELD_PATH)
|
|
75
|
+
sim.run_EM(args.PLATFORM,args.ANGLE_FF_STRENGTH,args.LE_FF_LENGTH,args.LE_FF_STRENGTH,args.EV_FF_STRENGTH,args.EV_FF_POWER,args.TOLERANCE,args.FRICTION,args.INTEGRATOR_STEP,args.SIM_TEMP,args.INITIAL_STRUCTURE_TYPE,args.VIZ_HEATS,args.FORCEFIELD_PATH)
|
|
76
76
|
elif args.SIMULATION_TYPE=='MD':
|
|
77
|
-
sim.run_MD(args.PLATFORM,args.ANGLE_FF_STRENGTH,args.LE_FF_LENGTH,args.LE_FF_STRENGTH,args.EV_FF_STRENGTH,args.EV_FF_POWER,args.TOLERANCE,args.FRICTION,args.INTEGRATOR_STEP,args.SIM_TEMP,args.SIM_STEP,args.VIZ_HEATS,args.FORCEFIELD_PATH,args.EV_P)
|
|
77
|
+
sim.run_MD(args.PLATFORM,args.ANGLE_FF_STRENGTH,args.LE_FF_LENGTH,args.LE_FF_STRENGTH,args.EV_FF_STRENGTH,args.EV_FF_POWER,args.TOLERANCE,args.FRICTION,args.INTEGRATOR_STEP,args.SIM_TEMP,args.INITIAL_STRUCTURE_TYPE,args.SIM_STEP,args.VIZ_HEATS,args.FORCEFIELD_PATH,args.EV_P)
|
|
78
78
|
elif args.SIMULATION_TYPE==None:
|
|
79
79
|
print('\n3D simulation did not run because it was not specified. Please specify argument SIMULATION_TYPE as EM or MD.')
|
|
80
80
|
else:
|
|
@@ -136,7 +136,7 @@ def slide(m_old, n_old, ms, ns, N_beads, rw=True, drift=True):
|
|
|
136
136
|
m_new = m_old - r1 if m_old + r1>=0 else 0
|
|
137
137
|
n_new = n_old + r2 if n_old + r2<N_beads else N_beads-1
|
|
138
138
|
if np.any(ms==n_new) and drift and n_old-r2>m_old+1:
|
|
139
|
-
n_new = n_old - r2 if n_old + r2
|
|
139
|
+
n_new = n_old - r2 if n_old + r2<N_beads else N_beads-1
|
|
140
140
|
return int(m_new), int(n_new)
|
|
141
141
|
|
|
142
142
|
@njit
|
|
@@ -254,26 +254,25 @@ class StochasticSimulation:
|
|
|
254
254
|
|
|
255
255
|
# Save simulation info
|
|
256
256
|
if save:
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
np.save(
|
|
271
|
-
np.save(
|
|
272
|
-
np.save(
|
|
273
|
-
np.save(
|
|
274
|
-
np.save(
|
|
275
|
-
np.save(
|
|
276
|
-
np.save(self.path+'/other/Ks.npy',self.Ks)
|
|
257
|
+
save_dir = os.path.join(self.path, 'other')
|
|
258
|
+
with open(save_dir + 'info.txt', "w") as f:
|
|
259
|
+
f.write(f'Number of beads {self.N_beads}.\n')
|
|
260
|
+
f.write(f'Number of cohesins {self.N_lef}. Number of cohesins in second family {self.N_lef2}. Number of CTCFs {self.N_CTCF}. \n')
|
|
261
|
+
f.write(f'Bedpe file for CTCF binding is {self.bedpe_file}.\n')
|
|
262
|
+
f.write(f'Initial temperature {T}. Minimum temperature {T_min}.\n')
|
|
263
|
+
f.write(f'Monte Carlo optimization method: {mode}.\n')
|
|
264
|
+
f.write(f'Monte Carlo steps {N_steps}. Sampling frequency {self.MC_step}. Burnin period {burnin}.\n')
|
|
265
|
+
f.write(f'Crossing energy in equilibrium is {np.average(self.Ks[burnin//MC_step:]):.2f}. Crossing coefficient kappa={kappa}.\n')
|
|
266
|
+
f.write(f'Folding energy in equilibrium is {np.average(self.Fs[burnin//MC_step:]):.2f}. Folding coefficient f={f}. Folding coefficient for the second family f2={f2}\n')
|
|
267
|
+
f.write(f'Binding energy in equilibrium is {np.average(self.Bs[burnin//MC_step:]):.2f}. Binding coefficient b={b}.\n')
|
|
268
|
+
f.write(f'Energy at equillibrium: {np.average(self.Es[self.burnin//MC_step:]):.2f}.\n')
|
|
269
|
+
np.save(save_dir + 'Ms.npy', self.Ms)
|
|
270
|
+
np.save(save_dir + 'Ns.npy', self.Ns)
|
|
271
|
+
np.save(save_dir + 'ufs.npy', self.ufs)
|
|
272
|
+
np.save(save_dir + 'Es.npy', self.Es)
|
|
273
|
+
np.save(save_dir + 'Bs.npy', self.Bs)
|
|
274
|
+
np.save(save_dir + 'Fs.npy', self.Fs)
|
|
275
|
+
np.save(save_dir + 'Ks.npy', self.Ks)
|
|
277
276
|
|
|
278
277
|
# Some vizualizations
|
|
279
278
|
if viz: coh_traj_plot(self.Ms,self.Ns,self.N_beads, self.path)
|
|
@@ -288,14 +287,14 @@ class StochasticSimulation:
|
|
|
288
287
|
self.N_CTCF = np.max([np.count_nonzero(self.L),np.count_nonzero(self.R)])
|
|
289
288
|
print('Number of CTCF:',self.N_CTCF)
|
|
290
289
|
|
|
291
|
-
def run_EM(self,platform='CPU',angle_ff_strength=200,le_distance=0.
|
|
290
|
+
def run_EM(self,platform='CPU',angle_ff_strength=200,le_distance=0.1,le_ff_strength=50000.0,ev_ff_strength=100.0,ev_ff_power=3.0,tolerance=0.001,friction=0.1,integrator_step=100*mm.unit.femtosecond,temperature=310,init_struct='rw',save_plots=True,ff_path=default_xml_path):
|
|
292
291
|
em = EM_LE(self.Ms,self.Ns,self.N_beads,self.burnin,self.MC_step,self.path,platform,angle_ff_strength,le_distance,le_ff_strength,ev_ff_strength,ev_ff_power,tolerance)
|
|
293
|
-
sim_heat = em.run_pipeline(plots=save_plots,friction=friction,integrator_step=integrator_step,temperature=temperature,ff_path=ff_path)
|
|
292
|
+
sim_heat = em.run_pipeline(plots=save_plots,friction=friction,integrator_step=integrator_step,temperature=temperature,ff_path=ff_path,init_struct=init_struct)
|
|
294
293
|
corr_exp_heat(sim_heat,self.bedpe_file,self.region,self.chrom,self.N_beads,self.path)
|
|
295
294
|
|
|
296
|
-
def run_MD(self,platform='CPU',angle_ff_strength=200,le_distance=0.
|
|
295
|
+
def run_MD(self,platform='CPU',angle_ff_strength=200,le_distance=0.1,le_ff_strength=50000.0,ev_ff_strength=100.0,ev_ff_power=3.0,tolerance=0.001,friction=0.1,integrator_step=100*mm.unit.femtosecond,temperature=310,init_struct='rw',sim_step=1000,save_plots=True,ff_path=default_xml_path,p_ev=0):
|
|
297
296
|
md = MD_LE(self.Ms,self.Ns,self.N_beads,self.path,platform,angle_ff_strength,le_distance,le_ff_strength,ev_ff_strength,ev_ff_power,tolerance)
|
|
298
|
-
sim_heat = md.run_pipeline(plots=save_plots,sim_step=sim_step,friction=friction,integrator_step=integrator_step,temperature=temperature,ff_path=ff_path,p_ev=p_ev)
|
|
297
|
+
sim_heat = md.run_pipeline(plots=save_plots,sim_step=sim_step,friction=friction,integrator_step=integrator_step,temperature=temperature,ff_path=ff_path,p_ev=p_ev,init_struct=init_struct)
|
|
299
298
|
corr_exp_heat(sim_heat,self.bedpe_file,self.region,self.chrom,self.N_beads,self.path)
|
|
300
299
|
|
|
301
300
|
def main():
|
|
@@ -13,14 +13,14 @@ from scipy.stats.stats import pearsonr, spearmanr, kendalltau
|
|
|
13
13
|
from tqdm import tqdm
|
|
14
14
|
|
|
15
15
|
def make_folder(folder_name):
|
|
16
|
-
|
|
16
|
+
if not os.path.exists(folder_name):
|
|
17
17
|
os.mkdir(folder_name)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
elif os.path.isdir(folder_name):
|
|
19
|
+
print(f'Directory with name "{folder_name}" already exists! No problem lets continue!')
|
|
20
|
+
else:
|
|
21
|
+
raise IOError(f'File with name "{folder_name}" already exists! Please change the name of the folder!')
|
|
22
|
+
for subfolder in ['plots', 'other', 'ensemble']:
|
|
23
|
+
os.makedirs(os.path.join(folder_name, subfolder), exist_ok=True)
|
|
24
24
|
return folder_name
|
|
25
25
|
|
|
26
26
|
############# Creation of mmcif and psf files #############
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pyLoopSage
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.20
|
|
4
4
|
Summary: An energy-based stochastic model of loop extrusion in chromatin.
|
|
5
5
|
Home-page: https://github.com/SFGLab/pyLoopSage
|
|
6
6
|
Author: Sebastian Korsak
|
|
@@ -12,7 +12,7 @@ Classifier: Operating System :: POSIX :: Linux
|
|
|
12
12
|
Requires-Python: >=3.10
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist: numpy
|
|
15
|
+
Requires-Dist: numpy==1.24.3
|
|
16
16
|
Requires-Dist: numba
|
|
17
17
|
Requires-Dist: scipy
|
|
18
18
|
Requires-Dist: pandas
|
|
@@ -38,6 +38,7 @@ Dynamic: description
|
|
|
38
38
|
Dynamic: description-content-type
|
|
39
39
|
Dynamic: home-page
|
|
40
40
|
Dynamic: license
|
|
41
|
+
Dynamic: license-file
|
|
41
42
|
Dynamic: requires-dist
|
|
42
43
|
Dynamic: requires-python
|
|
43
44
|
Dynamic: summary
|
|
@@ -178,7 +179,7 @@ You also need to download the reference genome from: ftp://ftp.1000genomes.ebi.a
|
|
|
178
179
|
|
|
179
180
|
*Alternativelly, it is possible to import a .bedpe file without the last two columns (CTCF orientation). In this case, CTCF would act as an orientation independent barrier. This might affect slightly the results, but it is an easier option, if you do not want to run a CTCF motif finding script.*
|
|
180
181
|
|
|
181
|
-
Then, we define the main parameters of the simulation `N_beads,N_coh,kappa,f,b` or we can choose the default ones (take care because it might be the case that they are not the appropriate ones and they need to be changed), the parameters of Monte Carlo `N_steps, MC_step, burnin, T`, and we initialize the class `LoopSage()`. The command `sim.run_energy_minimization()` corresponds to the stochastic Monte Carlo simulation, and it produces a set of cohesin constraints as a result (`Ms, Ns`). Note that the stochastic simulation has two modes: `Annealing` and `Metropolis`. We feed cohesin constraints to the molecular simulation part of and we run `
|
|
182
|
+
Then, we define the main parameters of the simulation `N_beads,N_coh,kappa,f,b` or we can choose the default ones (take care because it might be the case that they are not the appropriate ones and they need to be changed), the parameters of Monte Carlo `N_steps, MC_step, burnin, T`, and we initialize the class `LoopSage()`. The command `sim.run_energy_minimization()` corresponds to the stochastic Monte Carlo simulation, and it produces a set of cohesin constraints as a result (`Ms, Ns`). Note that the stochastic simulation has two modes: `Annealing` and `Metropolis`. We feed cohesin constraints to the molecular simulation part of and we run `EM_LE()` or `EM_LE()` simulation which produces a trajectory of 3d-structures, and the average heatmap. `MD_LE()` function can produce an actual trajectory and a `.dcd` video of how simulation changes over time. However, the implementation needs a high amount of memory since we need to define a bond for each time step, and it may not work for large systems. `EM_LE()` is suggested for large simulations, because it does not require so big amount of memory.
|
|
182
183
|
|
|
183
184
|
### Running LoopSage from command-line
|
|
184
185
|
To run LoopSage from command-line, you only need to type a command
|
|
@@ -307,8 +308,8 @@ An example, illustrated with Chimera software, simulated trajectory of structure
|
|
|
307
308
|
| EV_P | Probability that excluded volume is disabled. Enable it only in case of topoisomerase activity simulation. | float | 0.0 |
|
|
308
309
|
| EV_FF_STRENGTH | Excluded-volume strength. | float | 100.0 |
|
|
309
310
|
| EV_FF_POWER | Excluded-volume power. | float | 3.0 |
|
|
310
|
-
| FRICTION | Friction coefficient of the Langevin integrator. | float | 0.1
|
|
311
|
-
| TOLERANCE | Stopping condition for energy minimization.
|
|
311
|
+
| FRICTION | Friction coefficient of the Langevin integrator. | float | 0.1 |
|
|
312
|
+
| TOLERANCE | Stopping condition for energy minimization. | float | 0.001 |
|
|
312
313
|
| VIZ_HEATS | True to visualize the output average heatmap. | bool | True |
|
|
313
314
|
| SIM_TEMP | Temperature of the 3D simulation (EM or MD). | Quantity | 310 kelvin |
|
|
314
315
|
| SIM_STEP | Amount of simulation steps for loop force adjustments. | int | 1000 |
|
|
@@ -6,7 +6,7 @@ with open('README.md', 'r', encoding='utf-8') as f:
|
|
|
6
6
|
|
|
7
7
|
setup(
|
|
8
8
|
name='pyLoopSage', # Package name
|
|
9
|
-
version='1.0.
|
|
9
|
+
version='1.0.20', # Version of the software
|
|
10
10
|
description='An energy-based stochastic model of loop extrusion in chromatin.',
|
|
11
11
|
long_description=long_description,
|
|
12
12
|
long_description_content_type='text/markdown',
|
|
@@ -20,7 +20,7 @@ setup(
|
|
|
20
20
|
'loopsage': ['forcefields/*'],
|
|
21
21
|
},
|
|
22
22
|
install_requires=[ # List your package dependencies here
|
|
23
|
-
'numpy',
|
|
23
|
+
'numpy==1.24.3',
|
|
24
24
|
'numba',
|
|
25
25
|
'scipy',
|
|
26
26
|
'pandas',
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|