pyLoopSage 1.0.22__tar.gz → 1.0.23__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.23/MANIFEST.in +1 -0
- {pyloopsage-1.0.22/pyLoopSage.egg-info → pyloopsage-1.0.23}/PKG-INFO +2 -1
- {pyloopsage-1.0.22 → pyloopsage-1.0.23}/README.md +1 -0
- {pyloopsage-1.0.22 → pyloopsage-1.0.23}/loopsage/__init__.py +1 -1
- {pyloopsage-1.0.22 → pyloopsage-1.0.23}/loopsage/args_definition.py +1 -0
- {pyloopsage-1.0.22 → pyloopsage-1.0.23}/loopsage/md.py +24 -16
- {pyloopsage-1.0.22 → pyloopsage-1.0.23}/loopsage/run.py +1 -1
- {pyloopsage-1.0.22 → pyloopsage-1.0.23}/loopsage/stochastic_simulation.py +17 -17
- {pyloopsage-1.0.22 → pyloopsage-1.0.23/pyLoopSage.egg-info}/PKG-INFO +2 -1
- {pyloopsage-1.0.22 → pyloopsage-1.0.23}/pyLoopSage.egg-info/SOURCES.txt +1 -1
- {pyloopsage-1.0.22 → pyloopsage-1.0.23}/setup.py +1 -1
- pyloopsage-1.0.22/loopsage/knots.py +0 -193
- {pyloopsage-1.0.22 → pyloopsage-1.0.23}/LICENSE +0 -0
- {pyloopsage-1.0.22 → pyloopsage-1.0.23}/loopsage/em.py +0 -0
- {pyloopsage-1.0.22 → pyloopsage-1.0.23}/loopsage/forcefields/classic_sm_ff.xml +0 -0
- {pyloopsage-1.0.22 → pyloopsage-1.0.23}/loopsage/initial_structures.py +0 -0
- {pyloopsage-1.0.22 → pyloopsage-1.0.23}/loopsage/plots.py +0 -0
- {pyloopsage-1.0.22 → pyloopsage-1.0.23}/loopsage/preproc.py +0 -0
- {pyloopsage-1.0.22 → pyloopsage-1.0.23}/loopsage/utils.py +0 -0
- {pyloopsage-1.0.22 → pyloopsage-1.0.23}/loopsage/vizualization_tools.py +0 -0
- {pyloopsage-1.0.22 → pyloopsage-1.0.23}/pyLoopSage.egg-info/dependency_links.txt +0 -0
- {pyloopsage-1.0.22 → pyloopsage-1.0.23}/pyLoopSage.egg-info/entry_points.txt +0 -0
- {pyloopsage-1.0.22 → pyloopsage-1.0.23}/pyLoopSage.egg-info/requires.txt +0 -0
- {pyloopsage-1.0.22 → pyloopsage-1.0.23}/pyLoopSage.egg-info/top_level.txt +0 -0
- {pyloopsage-1.0.22 → pyloopsage-1.0.23}/setup.cfg +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
exclude loopsage/knots.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyLoopSage
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.23
|
|
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
|
|
@@ -305,6 +305,7 @@ An example, illustrated with Chimera software, simulated trajectory of structure
|
|
|
305
305
|
| ANGLE_FF_STRENGTH | Angle force strength. | float | 200.0 |
|
|
306
306
|
| LE_FF_LENGTH | Equilibrium distance of loop forces. | float | 0.1 |
|
|
307
307
|
| LE_FF_STRENGTH | Interaction Strength of loop forces. | float | 50000.0 |
|
|
308
|
+
| CONTINUOUS_TOP | True if the topological constraints are applied continuously during the simulation. | bool | False |
|
|
308
309
|
| EV_P | Probability that excluded volume is disabled. Enable it only in case of topoisomerase activity simulation. | float | 0.0 |
|
|
309
310
|
| EV_FF_STRENGTH | Excluded-volume strength. | float | 100.0 |
|
|
310
311
|
| EV_FF_POWER | Excluded-volume power. | float | 3.0 |
|
|
@@ -260,6 +260,7 @@ An example, illustrated with Chimera software, simulated trajectory of structure
|
|
|
260
260
|
| ANGLE_FF_STRENGTH | Angle force strength. | float | 200.0 |
|
|
261
261
|
| LE_FF_LENGTH | Equilibrium distance of loop forces. | float | 0.1 |
|
|
262
262
|
| LE_FF_STRENGTH | Interaction Strength of loop forces. | float | 50000.0 |
|
|
263
|
+
| CONTINUOUS_TOP | True if the topological constraints are applied continuously during the simulation. | bool | False |
|
|
263
264
|
| EV_P | Probability that excluded volume is disabled. Enable it only in case of topoisomerase activity simulation. | float | 0.0 |
|
|
264
265
|
| EV_FF_STRENGTH | Excluded-volume strength. | float | 100.0 |
|
|
265
266
|
| EV_FF_POWER | Excluded-volume power. | float | 3.0 |
|
|
@@ -170,6 +170,7 @@ args = ListOfArgs([
|
|
|
170
170
|
Arg('ANGLE_FF_STRENGTH', help="Angle force strength.", type=float, default='200.0', val='200.0'),
|
|
171
171
|
Arg('LE_FF_LENGTH', help="Equillibrium distance of loop forces.", type=float, default='0.1', val='0.1'),
|
|
172
172
|
Arg('LE_FF_STRENGTH', help="Interaction Strength of loop forces.", type=float, default='50000.0', val='50000.0'),
|
|
173
|
+
Arg('CONTINUOUS_TOP', help="True if topoisomerase disables EV in a continuous region rather than a discrete set of points.", type=bool, default='False', val='False'),
|
|
173
174
|
Arg('EV_P', help="Probability that randomly excluded volume may be disabled.", type=float, default='0.0', val='0.0'),
|
|
174
175
|
Arg('EV_FF_STRENGTH', help="Excluded-volume strength.", type=float, default='100.0', val='100.0'),
|
|
175
176
|
Arg('EV_FF_POWER', help="Excluded-volume power.", type=float, default='3.0', val='3.0'),
|
|
@@ -36,18 +36,19 @@ 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
|
|
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, continuous_topoisomerase=False):
|
|
40
40
|
'''
|
|
41
41
|
This is the basic function that runs the molecular simulation pipeline.
|
|
42
42
|
'''
|
|
43
43
|
# Parameters
|
|
44
44
|
self.p_ev = p_ev
|
|
45
|
+
self.continuous_topoisomerase = continuous_topoisomerase
|
|
45
46
|
|
|
46
47
|
# Define initial structure
|
|
47
48
|
print('Building initial structure...')
|
|
48
|
-
points = compute_init_struct(self.N_beads,mode='rw')
|
|
49
|
-
write_mmcif(points,self.path+'/LE_init_struct.cif')
|
|
50
|
-
generate_psf(self.N_beads,self.path+'/other/LE_init_struct.psf')
|
|
49
|
+
points = compute_init_struct(self.N_beads, mode='rw')
|
|
50
|
+
write_mmcif(points, self.path+'/LE_init_struct.cif')
|
|
51
|
+
generate_psf(self.N_beads, self.path+'/other/LE_init_struct.psf')
|
|
51
52
|
print('Done brother ;D\n')
|
|
52
53
|
|
|
53
54
|
# Define System
|
|
@@ -79,32 +80,39 @@ class MD_LE:
|
|
|
79
80
|
start = time.time()
|
|
80
81
|
heats = list()
|
|
81
82
|
for i in range(self.N_steps):
|
|
82
|
-
# Define probabilities that EV would be disabled
|
|
83
|
-
if
|
|
83
|
+
# Define probabilities or regions that EV would be disabled
|
|
84
|
+
if self.continuous_topoisomerase:
|
|
85
|
+
region_length = max(1, int(self.p_ev * self.N_beads)) # Determine region length from p_ev
|
|
86
|
+
start_idx = np.random.randint(0, self.N_beads - region_length -1)
|
|
87
|
+
end_idx = start_idx + region_length
|
|
88
|
+
self.ps_ev = np.zeros(self.N_beads)
|
|
89
|
+
self.ps_ev[start_idx:end_idx] = 1
|
|
90
|
+
elif p_ev > 0:
|
|
91
|
+
self.ps_ev = np.random.rand(self.N_beads)
|
|
84
92
|
self.change_loop(i)
|
|
85
93
|
self.change_ev()
|
|
86
94
|
self.simulation.step(sim_step)
|
|
87
95
|
self.state = self.simulation.context.getState(getPositions=True)
|
|
88
96
|
PDBxFile.writeFile(pdb.topology, self.state.getPositions(), open(self.path+f'/ensemble/MDLE_{i+1}.cif', 'w'))
|
|
89
|
-
heats.append(get_heatmap(self.state.getPositions(),save=False))
|
|
97
|
+
heats.append(get_heatmap(self.state.getPositions(), save=False))
|
|
90
98
|
end = time.time()
|
|
91
99
|
elapsed = end - start
|
|
92
|
-
print(f'Everything is done! Simulation finished
|
|
100
|
+
print(f'Everything is done! Simulation finished successfully!\nMD finished in {elapsed/60:.2f} minutes.\n')
|
|
93
101
|
|
|
94
|
-
self.avg_heat = np.average(heats,axis=0)
|
|
95
|
-
self.std_heat = np.std(heats,axis=0)
|
|
102
|
+
self.avg_heat = np.average(heats, axis=0)
|
|
103
|
+
self.std_heat = np.std(heats, axis=0)
|
|
96
104
|
|
|
97
105
|
if plots:
|
|
98
|
-
np.save(self.path+f'/other/avg_heatmap.npy',self.avg_heat)
|
|
99
|
-
np.save(self.path+f'/other/std_heatmap.npy',self.std_heat)
|
|
100
|
-
self.plot_heat(self.avg_heat,f'/plots/avg_heatmap.svg')
|
|
101
|
-
self.plot_heat(self.std_heat,f'/plots/std_heatmap.svg')
|
|
106
|
+
np.save(self.path+f'/other/avg_heatmap.npy', self.avg_heat)
|
|
107
|
+
np.save(self.path+f'/other/std_heatmap.npy', self.std_heat)
|
|
108
|
+
self.plot_heat(self.avg_heat, f'/plots/avg_heatmap.svg')
|
|
109
|
+
self.plot_heat(self.std_heat, f'/plots/std_heatmap.svg')
|
|
102
110
|
return self.avg_heat
|
|
103
111
|
|
|
104
112
|
def change_ev(self):
|
|
105
|
-
ev_strength = (self.ps_ev>self.p_ev).astype(int)*np.sqrt(self.ev_ff_strength) if self.p_ev>0 else np.sqrt(self.ev_ff_strength)*np.ones(self.N_beads)
|
|
113
|
+
ev_strength = (self.ps_ev > self.p_ev).astype(int) * np.sqrt(self.ev_ff_strength) if self.p_ev > 0 else np.sqrt(self.ev_ff_strength) * np.ones(self.N_beads)
|
|
106
114
|
for n in range(self.N_beads):
|
|
107
|
-
self.ev_force.setParticleParameters(n,[ev_strength[n],0.05])
|
|
115
|
+
self.ev_force.setParticleParameters(n, [ev_strength[n], 0.05])
|
|
108
116
|
self.ev_force.updateParametersInContext(self.simulation.context)
|
|
109
117
|
|
|
110
118
|
def change_loop(self,i):
|
|
@@ -86,7 +86,7 @@ def main():
|
|
|
86
86
|
if args.SIMULATION_TYPE=='EM':
|
|
87
87
|
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)
|
|
88
88
|
elif args.SIMULATION_TYPE=='MD':
|
|
89
|
-
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)
|
|
89
|
+
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,args.CONTINUOUS_TOPOISOMERASE)
|
|
90
90
|
elif args.SIMULATION_TYPE==None:
|
|
91
91
|
print('\n3D simulation did not run because it was not specified. Please specify argument SIMULATION_TYPE as EM or MD.')
|
|
92
92
|
else:
|
|
@@ -251,21 +251,21 @@ class StochasticSimulation:
|
|
|
251
251
|
end = time.time()
|
|
252
252
|
elapsed = end - start
|
|
253
253
|
print(f'Computation finished succesfully in {elapsed//3600:.0f} hours, {elapsed%3600//60:.0f} minutes and {elapsed%60:.0f} seconds.')
|
|
254
|
-
|
|
254
|
+
|
|
255
255
|
# Save simulation info
|
|
256
256
|
if save:
|
|
257
257
|
save_dir = os.path.join(self.path, 'other') + '/'
|
|
258
|
-
with open(save_dir + 'info.txt', "w") as
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
258
|
+
with open(save_dir + 'info.txt', "w") as file:
|
|
259
|
+
file.write(f'Number of beads {self.N_beads}.\n')
|
|
260
|
+
file.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
|
+
file.write(f'Bedpe file for CTCF binding is {self.bedpe_file}.\n')
|
|
262
|
+
file.write(f'Initial temperature {T}. Minimum temperature {T_min}.\n')
|
|
263
|
+
file.write(f'Monte Carlo optimization method: {mode}.\n')
|
|
264
|
+
file.write(f'Monte Carlo steps {N_steps}. Sampling frequency {self.MC_step}. Burnin period {burnin}.\n')
|
|
265
|
+
file.write(f'Crossing energy in equilibrium is {np.average(self.Ks[burnin//MC_step:]):.2f}. Crossing coefficient kappa={kappa}.\n')
|
|
266
|
+
file.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
|
+
file.write(f'Binding energy in equilibrium is {np.average(self.Bs[burnin//MC_step:]):.2f}. Binding coefficient b={b}.\n')
|
|
268
|
+
file.write(f'Energy at equillibrium: {np.average(self.Es[self.burnin//MC_step:]):.2f}.\n')
|
|
269
269
|
np.save(save_dir + 'Ms.npy', self.Ms)
|
|
270
270
|
np.save(save_dir + 'Ns.npy', self.Ns)
|
|
271
271
|
np.save(save_dir + 'ufs.npy', self.ufs)
|
|
@@ -292,9 +292,9 @@ class StochasticSimulation:
|
|
|
292
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)
|
|
293
293
|
corr_exp_heat(sim_heat,self.bedpe_file,self.region,self.chrom,self.N_beads,self.path)
|
|
294
294
|
|
|
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):
|
|
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,continuous_topoisomerase=False):
|
|
296
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)
|
|
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)
|
|
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,continuous_topoisomerase=continuous_topoisomerase)
|
|
298
298
|
corr_exp_heat(sim_heat,self.bedpe_file,self.region,self.chrom,self.N_beads,self.path)
|
|
299
299
|
|
|
300
300
|
def main():
|
|
@@ -311,12 +311,12 @@ def main():
|
|
|
311
311
|
region, chrom = [15550000,16850000], 'chr6'
|
|
312
312
|
|
|
313
313
|
# Definition of data
|
|
314
|
-
output_name='
|
|
314
|
+
output_name='tmp'
|
|
315
315
|
bedpe_file = '/home/skorsak/Data/HiChIP/Maps/hg00731_smc1_maps_2.bedpe'
|
|
316
316
|
|
|
317
317
|
sim = StochasticSimulation(region,chrom,bedpe_file,out_dir=output_name,N_beads=1000,N_lef=N_lef,N_lef2=N_lef2)
|
|
318
318
|
Es, Ms, Ns, Bs, Ks, Fs, ufs = sim.run_energy_minimization(N_steps,MC_step,burnin,T,T_min,mode=mode,viz=True,save=True,f=f,f2=f2, b=b, kappa=kappa, lef_rw=lew_rw)
|
|
319
|
-
sim.
|
|
319
|
+
sim.run_MD('CUDA',continuous_topoisomerase=True, p_ev=0.1)
|
|
320
320
|
|
|
321
321
|
if __name__=='__main__':
|
|
322
|
-
main()
|
|
322
|
+
main()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyLoopSage
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.23
|
|
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
|
|
@@ -305,6 +305,7 @@ An example, illustrated with Chimera software, simulated trajectory of structure
|
|
|
305
305
|
| ANGLE_FF_STRENGTH | Angle force strength. | float | 200.0 |
|
|
306
306
|
| LE_FF_LENGTH | Equilibrium distance of loop forces. | float | 0.1 |
|
|
307
307
|
| LE_FF_STRENGTH | Interaction Strength of loop forces. | float | 50000.0 |
|
|
308
|
+
| CONTINUOUS_TOP | True if the topological constraints are applied continuously during the simulation. | bool | False |
|
|
308
309
|
| EV_P | Probability that excluded volume is disabled. Enable it only in case of topoisomerase activity simulation. | float | 0.0 |
|
|
309
310
|
| EV_FF_STRENGTH | Excluded-volume strength. | float | 100.0 |
|
|
310
311
|
| EV_FF_POWER | Excluded-volume power. | float | 3.0 |
|
|
@@ -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.23', # 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',
|
|
@@ -1,193 +0,0 @@
|
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|