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.
Files changed (24) hide show
  1. {pyloopsage-1.0.18/pyLoopSage.egg-info → pyloopsage-1.0.20}/PKG-INFO +7 -6
  2. {pyloopsage-1.0.18 → pyloopsage-1.0.20}/README.md +3 -3
  3. {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/__init__.py +1 -1
  4. {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/args_definition.py +1 -1
  5. {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/em.py +2 -2
  6. pyloopsage-1.0.20/loopsage/knots.py +193 -0
  7. {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/md.py +3 -4
  8. {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/preproc.py +2 -0
  9. {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/run.py +2 -2
  10. {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/stochastic_simulation.py +24 -25
  11. {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/utils.py +7 -7
  12. {pyloopsage-1.0.18 → pyloopsage-1.0.20/pyLoopSage.egg-info}/PKG-INFO +7 -6
  13. {pyloopsage-1.0.18 → pyloopsage-1.0.20}/pyLoopSage.egg-info/SOURCES.txt +1 -0
  14. {pyloopsage-1.0.18 → pyloopsage-1.0.20}/pyLoopSage.egg-info/requires.txt +1 -1
  15. {pyloopsage-1.0.18 → pyloopsage-1.0.20}/setup.py +2 -2
  16. {pyloopsage-1.0.18 → pyloopsage-1.0.20}/LICENSE +0 -0
  17. {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/forcefields/classic_sm_ff.xml +0 -0
  18. {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/initial_structures.py +0 -0
  19. {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/plots.py +0 -0
  20. {pyloopsage-1.0.18 → pyloopsage-1.0.20}/loopsage/vizualization_tools.py +0 -0
  21. {pyloopsage-1.0.18 → pyloopsage-1.0.20}/pyLoopSage.egg-info/dependency_links.txt +0 -0
  22. {pyloopsage-1.0.18 → pyloopsage-1.0.20}/pyLoopSage.egg-info/entry_points.txt +0 -0
  23. {pyloopsage-1.0.18 → pyloopsage-1.0.20}/pyLoopSage.egg-info/top_level.txt +0 -0
  24. {pyloopsage-1.0.18 → pyloopsage-1.0.20}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: pyLoopSage
3
- Version: 1.0.18
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 `MD_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. *We would like to advise to our users that in case that they would like to use molecular dynamics (`MD`), it is better to choose small `MC_step`, because the trajectory of LEFs should be continuous.*
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. | float | 0.001 |
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 `MD_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. *We would like to advise to our users that in case that they would like to use molecular dynamics (`MD`), it is better to choose small `MC_step`, because the trajectory of LEFs should be continuous.*
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. | float | 0.001 |
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 |
@@ -1,4 +1,4 @@
1
1
  # Importing specific functions or classes from submodules
2
2
  from .run import main
3
3
 
4
- __version__ = "1.0.18"
4
+ __version__ = "1.0.20"
@@ -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='rw')
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(1,self.N_steps):
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>=0 else 0
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
- f = open(self.path+'/other/info.txt', "w")
258
- f.write(f'Number of beads {self.N_beads}.\n')
259
- 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')
260
- f.write(f'Bedpe file for CTCF binding is {self.bedpe_file}.\n')
261
- f.write(f'Initial temperature {T}. Minimum temperature {T_min}.\n')
262
- f.write(f'Monte Carlo optimization method: {mode}.\n')
263
- f.write(f'Monte Carlo steps {N_steps}. Sampling frequency {MC_step}. Burnin period {burnin}.\n')
264
- f.write(f'Crossing energy in equilibrium is {np.average(self.Ks[burnin//MC_step:]):.2f}. Crossing coefficient kappa={kappa}.\n')
265
- 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')
266
- f.write(f'Binding energy in equilibrium is {np.average(self.Bs[burnin//MC_step:]):.2f}. Binding coefficient b={b}.\n')
267
- f.write(f'Energy at equillibrium: {np.average(self.Es[burnin//MC_step:]):.2f}.\n')
268
- f.close()
269
-
270
- np.save(self.path+'/other/Ms.npy',self.Ms)
271
- np.save(self.path+'/other/Ns.npy',self.Ns)
272
- np.save(self.path+'/other/ufs.npy',self.ufs)
273
- np.save(self.path+'/other/Es.npy',self.Es)
274
- np.save(self.path+'/other/Bs.npy',self.Bs)
275
- np.save(self.path+'/other/Fs.npy',self.Fs)
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.0,le_ff_strength=300000.0,ev_ff_strength=10.0,ev_ff_power=3.0,tolerance=0.001,friction=0.1,integrator_step=100*mm.unit.femtosecond,temperature=310,save_plots=True,ff_path=default_xml_path):
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.0,le_ff_strength=300000.0,ev_ff_strength=10.0,ev_ff_power=3.0,tolerance=0.001,friction=0.1,integrator_step=100*mm.unit.femtosecond,temperature=310,sim_step=1000,save_plots=True,ff_path=default_xml_path,p_ev=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
- try:
16
+ if not os.path.exists(folder_name):
17
17
  os.mkdir(folder_name)
18
- os.mkdir(folder_name+'/plots')
19
- os.mkdir(folder_name+'/other')
20
- os.mkdir(folder_name+'/ensemble')
21
- except OSError as error:
22
- a=1
23
- # print(f'Directory with name "{folder_name}" already exists! No problem lets continue!')
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.2
1
+ Metadata-Version: 2.4
2
2
  Name: pyLoopSage
3
- Version: 1.0.18
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 `MD_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. *We would like to advise to our users that in case that they would like to use molecular dynamics (`MD`), it is better to choose small `MC_step`, because the trajectory of LEFs should be continuous.*
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. | float | 0.001 |
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 |
@@ -5,6 +5,7 @@ loopsage/__init__.py
5
5
  loopsage/args_definition.py
6
6
  loopsage/em.py
7
7
  loopsage/initial_structures.py
8
+ loopsage/knots.py
8
9
  loopsage/md.py
9
10
  loopsage/plots.py
10
11
  loopsage/preproc.py
@@ -1,4 +1,4 @@
1
- numpy
1
+ numpy==1.24.3
2
2
  numba
3
3
  scipy
4
4
  pandas
@@ -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.18', # Version of the software
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