bfee2 2.5.0__py3-none-any.whl → 3.0.0__py3-none-any.whl

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.

Potentially problematic release.


This version of bfee2 might be problematic. Click here for more details.

BFEE2/postTreatment.py CHANGED
@@ -500,3 +500,159 @@ class postTreatment:
500
500
  errors[5] = math.sqrt(errors[0]**2 + errors[1]**2 +errors[2]**2 + errors[3]**2 + errors[4]**2)
501
501
 
502
502
  return contributions, errors
503
+
504
+ def _LDDMReadColvarsTmp(self, file_path):
505
+ """ Read an Colvars Tmp file in binding free energy calculations,
506
+ get the force constants and centers constants of restraints for LDDM
507
+
508
+ Args:
509
+ file_path (str): path to the Colvars.tmp file
510
+
511
+ Returns:
512
+ Tuple[List, List]: lists of force constants and centers
513
+ """
514
+
515
+ force_constants = []
516
+ centers = []
517
+
518
+ with open(file_path, 'r') as colvars_tmp_file:
519
+ center_line = False
520
+ for line in colvars_tmp_file.readlines():
521
+ splited_line = line.strip().split()
522
+
523
+ if len(splited_line) < 2:
524
+ center_line = False
525
+ continue
526
+
527
+ if splited_line[1].startswith('$afc_'):
528
+ force_constants.append(float(splited_line[1].replace('$afc_', '')))
529
+ center_line = True
530
+ continue
531
+
532
+ if center_line:
533
+ centers.append(float(splited_line[1]))
534
+
535
+ continue
536
+
537
+ return force_constants, centers
538
+
539
+ def _LDDMBoundStateFreeEnergy(
540
+ self,
541
+ colvars_tmp_path,
542
+ cvtrj_path,
543
+ fepout_path,
544
+ steps_per_window,
545
+ equilbration_steps_per_window,
546
+ num_windows,
547
+ temperature = 300,
548
+ jobtype = 'fep'
549
+ ):
550
+ """ Calculate bound state free-energy contribution and error
551
+
552
+ Args:
553
+ colvars_tmp_path (str): path to colvars tmp file
554
+ cvtrj_path (str): path to colvars.traj file
555
+ fepout_path (str): path to fepout file
556
+ steps_per_window (int): steps per FEP window
557
+ equilbration_steps_per_window (int): equilibration steps per FEP window
558
+ num_windows (int): number of windows
559
+ temperature (float): temperature of the simulation, defaults to 300
560
+ jobType (str, optional): Type of the post-treatment method. 'fep' or 'bar'.
561
+ Defaults to 'fep'.
562
+
563
+ Returns:
564
+ Tuple[float, float, float]: free-energy contribution of decoupling the molecule,
565
+ error, and contribution of the restraints
566
+ """
567
+ force_contants, centers = self._LDDMReadColvarsTmp(colvars_tmp_path)
568
+ colvars_parser = py_bar.ColvarsParser(cvtrj_path, steps_per_window, equilbration_steps_per_window,
569
+ force_contants, centers,
570
+ np.linspace(0, 1, num_windows))
571
+ window, deltaU = colvars_parser.get_data()
572
+ b = py_bar.FEPAnalyzer(window, deltaU, temperature)
573
+
574
+ window2, deltaU2 = py_bar.NAMDParser(fepout_path).get_data()
575
+ success = b.MergeData(window2, deltaU2)
576
+ if not success:
577
+ raise RuntimeError('Failed in merging fepout and colvars.traj! Probably wrong number of windows or crupted simulations!')
578
+
579
+ if jobtype == 'fep':
580
+ result = b.FEP_free_energy()
581
+ else:
582
+ result = b.BAR_free_energy(block_size=50, n_bootstrap=20)
583
+
584
+ #windows = b.Window_boundaries()
585
+ #with open(fepout_path + ".convergence.data", "w") as convergence_file:
586
+ # convergence_file.write(f" lambda dA stdev_A \n")
587
+ # for window, dA, stdA in zip(windows, result[1], resul[2]):
588
+ # convergence_file.write(f" {window} {dA:.4f} {stdA:.4f} \n")
589
+
590
+ return -np.sum(result[1]), np.sqrt(np.sum(np.power(result[2], 2))), colvars_parser.get_restraint_contribution()
591
+
592
+ def _LDDMFreeStateFreeEnergy(self, fepout_path, temperature = 300, jobtype = 'fep'):
593
+ """ Calculate free state free-energy contribution and error
594
+
595
+ Args:
596
+ fepout_path (str): path to fepout file
597
+ temperature (float, optional): temperature of the simulation. Defaults to 300.
598
+ jobType (str, optional): Type of the post-treatment method. 'fep' or 'bar'.
599
+ Defaults to 'fep'.
600
+
601
+ Returns:
602
+ Tuple[float, float]: free-energy contribution and the error of decoupling the molecule
603
+ """
604
+ window, deltaU = py_bar.NAMDParser(fepout_path).get_data()
605
+ b = py_bar.FEPAnalyzer(window, deltaU, temperature)
606
+
607
+ if jobtype == 'fep':
608
+ result = b.FEP_free_energy()
609
+ else:
610
+ result = b.BAR_free_energy(block_size=50, n_bootstrap=20)
611
+
612
+ # convergence file
613
+ #windows = b.Window_boundaries()
614
+ #with open(fepout_path + "convergence.data", "w") as convergence_file:
615
+ # convergence_file.write(f" lambda dA stdev_A \n")
616
+ # for window, dA, stdA in zip(windows, result_fep[1], result_fep[2]):
617
+ # convergence_file.write(f" {window} {dA:.4f} {stdA:.4f} \n")
618
+
619
+ return np.sum(result[1]), np.sqrt(np.sum(np.power(result[2], 2)))
620
+
621
+ def LDDMBindingFreeEnergy(
622
+ self,
623
+ colvars_tmp_path,
624
+ cvtrj_path,
625
+ step1_fepout_path,
626
+ steps_per_window,
627
+ equilbration_steps_per_window,
628
+ num_windows,
629
+ step3_fepout_path,
630
+ temperature = 300,
631
+ jobType = 'fep'):
632
+ """calculate binding free energy for LDDM
633
+
634
+ Args:
635
+ colvars_tmp_path (str): path to colvars tmp file of step 1
636
+ cvtrj_path (str): path to colvars.traj file of step 1
637
+ step1_fepout_path (str): path to fepout file of step 1
638
+ steps_per_window (int): steps per FEP window of step 1
639
+ equilbration_steps_per_window (int): equilibration steps per FEP window of step 1
640
+ num_windows (int): number of windows of step 1
641
+ step3_fepout_path (str): path to fepout file of step 3
642
+ temperature (float): temperature of the simulation, defaults to 300
643
+ jobType (str, optional): Type of the post-treatment method. 'fep' or 'bar'.
644
+ Defaults to 'fep'.
645
+
646
+ Returns:
647
+ tuple:
648
+ np.array, float, 2: (contributions for step1, and step 3)
649
+ np.array, float, 2: errors corresponding each contribution
650
+ """
651
+
652
+ step1_dG, step1_error, step3_dG_restraint = self._LDDMBoundStateFreeEnergy(
653
+ colvars_tmp_path, cvtrj_path, step1_fepout_path, steps_per_window, equilbration_steps_per_window,
654
+ num_windows, temperature, jobType
655
+ )
656
+ step3_dG_molecule, step3_error = self._LDDMFreeStateFreeEnergy(step3_fepout_path, temperature, jobType)
657
+
658
+ return np.array([step1_dG, step1_error]), np.array([step3_dG_molecule + step3_dG_restraint, step3_error])
@@ -37,7 +37,8 @@ class configTemplate:
37
37
  OPLSMixingRule = False,
38
38
  GaWTM = False,
39
39
  CUDASOAIntegrator = False,
40
- timestep = 2.0
40
+ timestep = 2.0,
41
+ LDDMStep1 = False
41
42
  ):
42
43
  """the namd config file template
43
44
 
@@ -68,6 +69,7 @@ class configTemplate:
68
69
  GaWTM (bool, optional): Whether this is an GaWTM-eABF simulation. Default to False
69
70
  CUDASOAIntegrator (bool, optional): Whether CUDASOA integrator is used. Default to False
70
71
  timestep (float, optional): timestep of the simulation. Default to 2.0
72
+ LDDMStep1 (bool, optional): whether this is the 1st step of a LDDM simulation. Default to False.
71
73
 
72
74
  Returns:
73
75
  str: a NAMD config string if succeed, and empty string otherwise
@@ -259,8 +261,13 @@ run norepeat {numSteps} \n'
259
261
  else:
260
262
  # currently the alchemical route is somewhat hard-coded
261
263
  # this will be improved in the future
264
+ if LDDMStep1:
265
+ configString += f'\
266
+ source ./fep_lddm.tcl \n'
267
+ else:
268
+ configString += f'\
269
+ source ../fep.tcl \n'
262
270
  configString += f'\
263
- source ../fep.tcl \n\
264
271
  alch on \n\
265
272
  alchType FEP \n\
266
273
  alchFile {fepFile} \n\
@@ -269,6 +276,7 @@ alchOutFile {outputPrefix}.fepout \n\
269
276
  alchOutFreq 50 \n\
270
277
  alchVdwLambdaEnd 0.7 \n\
271
278
  alchElecLambdaStart 0.5 \n\
279
+ alchDecouple on \n\
272
280
  alchEquilSteps 100000 \n'
273
281
 
274
282
  if fepForward:
@@ -566,7 +574,6 @@ colvar {{ \n\
566
574
  indexGroup ligand \n\
567
575
  centerReference on \n\
568
576
  rotateReference on \n\
569
- enableFitGradients no \n\
570
577
  fittingGroup {{ \n\
571
578
  indexGroup protein \n\
572
579
  }} \n\
@@ -637,7 +644,6 @@ colvar {{ \n\
637
644
  indexGroup reference \n\
638
645
  centerReference on \n\
639
646
  rotateReference on \n\
640
- enableFitGradients no \n\
641
647
  fittingGroup {{ \n\
642
648
  indexGroup protein \n\
643
649
  }} \n\
@@ -647,7 +653,6 @@ colvar {{ \n\
647
653
  indexGroup ligand \n\
648
654
  centerReference on \n\
649
655
  rotateReference on \n\
650
- enableFitGradients no \n\
651
656
  fittingGroup {{ \n\
652
657
  indexGroup protein \n\
653
658
  }} \n\
@@ -707,7 +712,6 @@ colvar {{ \n\
707
712
  centerReference on \n\
708
713
  rotateReference on \n\
709
714
  centerToOrigin on \n\
710
- enableFitGradients on \n\
711
715
  fittingGroup {{ \n\
712
716
  indexGroup protein \n\
713
717
  }} \n\
@@ -0,0 +1,312 @@
1
+ ##############################################################
2
+ # LDDM variant FEP SCRIPT
3
+ # Haohao Fu <fhh2626@gmail.com>
4
+ # Jerome Henin <henin@ibpc.fr>
5
+ #
6
+ # Important: This version should only be used for the LDDM strategy!
7
+ # Use the NAMD version of fep.tcl for other simulations.
8
+ #
9
+ # Changes:
10
+ # 2020-05-03: added prev_lambda option to runFEP, improved doc
11
+ # 2018-04-18: added interleaved double-wide sampling (IDWS)
12
+ # 2010-04-24: added runFEPmin
13
+ # 2009-11-17: changed for NAMD 2.7 keywords
14
+ # 2008-06-25: added TI routines
15
+ # 2007-11-01: fixed runFEP to handle backwards transformations
16
+ # (i.e. dLambda < 0)
17
+ # 2024-11-04: support LDDM version of runFEP
18
+ ##############################################################
19
+
20
+ ##############################################################
21
+ ## Example NAMD input: calculation with smaller windows at
22
+ ## the ends
23
+ #
24
+ # source fep.tcl
25
+ #
26
+ # alch on
27
+ # alchFile system.fep
28
+ # alchCol B
29
+ # alchOutFreq 10
30
+ # alchOutFile system.fepout
31
+ # alchEquilSteps 500
32
+ #
33
+ # set nSteps 5000
34
+ #
35
+ ## A) Simple schedule: 20 windows from 0 to 1, in a single run
36
+ #
37
+ # runFEP 0.0 1.0 0.05 $nSteps
38
+ #
39
+ ## B) Same thing, in two NAMD separate runs with a restart
40
+ #
41
+ ## First run
42
+ # runFEP 0.0 0.5 0.05 $nSteps
43
+ #
44
+ ## Restart
45
+ # runFEP 0.5 1.0 0.05 $nSteps
46
+ #
47
+ ## C) Lambda schedule with narrower windows at the end points
48
+ ## Using two explicit lists of lambda points
49
+ #
50
+ # set init {0.0 0.05 0.1}
51
+ # set end {0.9 0.95 1.0}
52
+ #
53
+ # runFEPlist $init $nSteps
54
+ # runFEP 0.1 0.9 0.1 $nSteps
55
+ # runFEPlist $end $nSteps
56
+ #
57
+ ## Alternately, in one step:
58
+ #
59
+ # runFEPlist [concat $init [FEPlist 0.1 0.9 0.1] $end] $nSteps
60
+ #
61
+ ##############################################################
62
+
63
+ ##############################################################
64
+ ## Special usage for Interleaved Double-Wide sampling
65
+ ## A) Simple schedule: 20 windows from 0 to 1, in a single run
66
+ #
67
+ # runFEP 0.0 1.0 0.05 $nSteps true
68
+ #
69
+ ## B) Same thing, in two NAMD separate runs with a restart
70
+ #
71
+ ## First run
72
+ # runFEP 0.0 0.5 0.05 $nSteps true
73
+ #
74
+ ## Restart - need to tell the script the previous lambda point: 0.45
75
+ # runFEP 0.5 1.0 0.05 $nSteps true 0.45
76
+ #
77
+ ## C) Example of a piecewise calculation with restarts
78
+ # and a nonlinear lambda schedule
79
+ #
80
+ ## Run individual points 0, 0.05 then the series from 0.1 to 0.5
81
+ #
82
+ # runFEPlist [concat {0. 0.05} [FEPlist 0.1 0.5 0.1]] $numSteps true
83
+ #
84
+ ## Continue series from 0.5 to 0.9, sampling backward dE from 0.4
85
+ #
86
+ # runFEPlist [FEPlist 0.5 0.9 0.1] $numSteps true 0.4
87
+ #
88
+ ## Add two values 0.95 and 1, sampling backward dE from 0.9
89
+ ## (automatically adds final backward window from 1. to 0.95)
90
+ #
91
+ # runFEPlist {0.95 1.} $numSteps true 0.9
92
+ #
93
+ ##############################################################
94
+
95
+ ##############################################################
96
+ # proc runFEPlist { lambdaList nSteps {IDWS} {prev_lambda} }
97
+ #
98
+ # Run n FEP windows joining (n + 1) lambda-points
99
+ # Provide prev_lambda value if continuing a sequential
100
+ # transformation and using IDWS
101
+ ##############################################################
102
+
103
+ proc runFEPlist { lambdaList nSteps { IDWS false } { prev_lambda -1 } } {
104
+ set epsilon 1e-12
105
+
106
+ # Keep track of window number
107
+ global win
108
+ if {![info exists win]} {
109
+ set win 1
110
+ }
111
+
112
+ set l1 [lindex $lambdaList 0]
113
+ foreach l2 [lrange $lambdaList 1 end] {
114
+ print [format "Running FEP window %3s: Lambda1 %-6s Lambda2 %-6s \[dLambda %-6s\]"\
115
+ $win $l1 $l2 [expr $l2 - $l1]]
116
+ firsttimestep 0
117
+ alchLambda $l1
118
+ alchLambda2 $l2
119
+
120
+ if { $IDWS && ($prev_lambda >= 0.) } {
121
+ alchLambdaIDWS $prev_lambda
122
+ }
123
+
124
+ cv reset
125
+ cv configfile "./colvars_files/colvars_[expr $win - 1].in"
126
+
127
+ run $nSteps
128
+
129
+ # Keep track of previous value to set is as target for backward calculation in IDWS
130
+ set prev_lambda $l1
131
+ set l1 $l2
132
+ incr win
133
+ }
134
+
135
+ if { $IDWS && ($l1 > [expr {1. - $epsilon}] || $l1 < $epsilon)} {
136
+ # If the list ends at 1 or zero, we add a final window, which is backward from the end point
137
+ # to complete double-wide sampling
138
+ # this will be look like "forward" sampling in the fepout file ("FepEnergy:" keyword)
139
+ print [format "Running FEP window %3s: Lambda1 %-6s Lambda2 %-6s \[dLambda %-6s\]"\
140
+ $win $l1 $l2 [expr $l2 - $l1]]
141
+ firsttimestep 0
142
+ alchLambda $l1
143
+ alchLambda2 $prev_lambda
144
+ alchLambdaIDWS -1
145
+
146
+ cv reset
147
+ cv configfile "./colvars_files/colvars_[expr $win - 1].in"
148
+
149
+ run $nSteps
150
+ }
151
+ }
152
+
153
+
154
+ ##############################################################
155
+ # proc runFEP { start stop dLambda nSteps {IDWS} {prev_lambda} }
156
+ #
157
+ # run FEP windows of width dLambda between values start and stop
158
+ ##############################################################
159
+
160
+ proc runFEP { start stop dLambda nSteps { IDWS false } { prev_lambda -1 } } {
161
+
162
+ runFEPlist [FEPlist $start $stop $dLambda] $nSteps $IDWS $prev_lambda
163
+ }
164
+
165
+
166
+ ##############################################################
167
+ # proc FEPlist { start stop dLambda nSteps }
168
+ #
169
+ # Create list of FEP windows
170
+ ##############################################################
171
+
172
+ proc FEPlist { start stop dLambda } {
173
+ set epsilon 1e-15
174
+
175
+ if { ($stop < $start) && ($dLambda > 0) } {
176
+ set dLambda [expr {-$dLambda}]
177
+ }
178
+
179
+ if { $start == $stop } {
180
+ set ll [list $start $start]
181
+ } else {
182
+ set ll [list $start]
183
+ set l2 [increment $start $dLambda]
184
+
185
+ if { $dLambda > 0} {
186
+ # A small workaround for numerical rounding errors
187
+ while { [expr {$l2 <= ($stop + $epsilon) } ] } {
188
+ lappend ll $l2
189
+ set l2 [increment $l2 $dLambda]
190
+ }
191
+ } else {
192
+ while { [expr {$l2 >= ($stop - $epsilon) } ] } {
193
+ lappend ll $l2
194
+ set l2 [increment $l2 $dLambda]
195
+ }
196
+ }
197
+ }
198
+
199
+ return $ll
200
+ }
201
+
202
+
203
+ ##############################################################
204
+ ##############################################################
205
+
206
+ proc runFEPmin { start stop dLambda nSteps nMinSteps temp} {
207
+ set epsilon 1e-15
208
+
209
+ if { ($stop < $start) && ($dLambda > 0) } {
210
+ set dLambda [expr {-$dLambda}]
211
+ }
212
+
213
+ if { $start == $stop } {
214
+ set ll [list $start $start]
215
+ } else {
216
+ set ll [list $start]
217
+ set l2 [increment $start $dLambda]
218
+
219
+ if { $dLambda > 0} {
220
+ # A small workaround for numerical rounding errors
221
+ while { [expr {$l2 <= ($stop + $epsilon) } ] } {
222
+ lappend ll $l2
223
+ set l2 [increment $l2 $dLambda]
224
+ }
225
+ } else {
226
+ while { [expr {$l2 >= ($stop - $epsilon) } ] } {
227
+ lappend ll $l2
228
+ set l2 [increment $l2 $dLambda]
229
+ }
230
+ }
231
+ }
232
+
233
+ if { $nMinSteps > 0 } {
234
+ alchLambda $start
235
+ alchLambda2 $start
236
+ minimize $nMinSteps
237
+ reinitvels $temp
238
+ }
239
+
240
+ runFEPlist $ll $nSteps
241
+ }
242
+
243
+ ##############################################################
244
+ ##############################################################
245
+
246
+ proc runTIlist { lambdaList nSteps } {
247
+ # Keep track of window number
248
+ global win
249
+ if {![info exists win]} {
250
+ set win 1
251
+ }
252
+
253
+ foreach l $lambdaList {
254
+ print [format "Running TI window %3s: Lambda %-6s " $win $l ]
255
+ firsttimestep 0
256
+ alchLambda $l
257
+ run $nSteps
258
+ incr win
259
+ }
260
+ }
261
+
262
+
263
+ ##############################################################
264
+ ##############################################################
265
+
266
+ proc runTI { start stop dLambda nSteps } {
267
+ set epsilon 1e-15
268
+
269
+ if { ($stop < $start) && ($dLambda > 0) } {
270
+ set dLambda [expr {-$dLambda}]
271
+ }
272
+
273
+ if { $start == $stop } {
274
+ set ll [list $start $start]
275
+ } else {
276
+ set ll [list $start]
277
+ set l2 [increment $start $dLambda]
278
+
279
+ if { $dLambda > 0} {
280
+ # A small workaround for numerical rounding errors
281
+ while { [expr {$l2 <= ($stop + $epsilon) } ] } {
282
+ lappend ll $l2
283
+ set l2 [increment $l2 $dLambda]
284
+ }
285
+ } else {
286
+ while { [expr {$l2 >= ($stop - $epsilon) } ] } {
287
+ lappend ll $l2
288
+ set l2 [increment $l2 $dLambda]
289
+ }
290
+ }
291
+ }
292
+
293
+ runTIlist $ll $nSteps
294
+ }
295
+
296
+ ##############################################################
297
+ # Increment lambda and try to correct truncation errors around
298
+ # 0 and 1
299
+ ##############################################################
300
+
301
+ proc increment { lambda dLambda } {
302
+ set epsilon 1e-15
303
+ set new [expr { $lambda + $dLambda }]
304
+
305
+ if { [expr $new > - $epsilon && $new < $epsilon] } {
306
+ return 0.0
307
+ }
308
+ if { [expr ($new - 1) > - $epsilon && ($new - 1) < $epsilon] } {
309
+ return 1.0
310
+ }
311
+ return $new
312
+ }
@@ -146,4 +146,159 @@ amberToGromacs(
146
146
  pbcVector[0],
147
147
  '${inputPrefix}_gmx',
148
148
  )
149
+ ''')
150
+
151
+ genarateLDDMFilesTemplate = string.Template('''
152
+ import numpy as np
153
+ import os, sys
154
+
155
+ TEMPERATURE = ${temperature}
156
+ WINDOWS = ${windows}
157
+ BOLTZMANN = 0.0019872041
158
+
159
+ def parseDat(filename, temperature=300.0):
160
+ """Parse a dat (histogram) file, return the most probable CV value
161
+ and the corresponding force constant for restraints
162
+
163
+ Args:
164
+ filename (str): the dat file to be parsed with
165
+ temperature (float): the temperature of the simulation
166
+
167
+ Returns:
168
+ Tuple(float, float): the most probable CV value and the force constant
169
+ """
170
+
171
+ data = np.loadtxt(filename)
172
+ CVs = data[:,0]
173
+ counts = data[:,1]
174
+
175
+ beta = 1 / (-BOLTZMANN * temperature)
176
+
177
+ maxCV = -1
178
+ maxCount = -1
179
+ maxIndex = -1
180
+ for index, (i, j) in enumerate(zip(CVs, counts)):
181
+ if j > maxCount:
182
+ maxCV = i
183
+ maxCount = j
184
+ maxIndex = index
185
+
186
+ # fitting
187
+ assert (maxIndex - 2 >=0 and maxIndex + 2 < len(counts)), f"maxIndex is: {maxIndex}, length is: {len(counts)}"
188
+ X = [CVs[maxIndex - 2], CVs[maxIndex], CVs[maxIndex + 2]]
189
+ y = [beta * np.log(counts[maxIndex - 2]), beta * np.log(counts[maxIndex]), beta * np.log(counts[maxIndex + 2])]
190
+
191
+ forceConstant = np.polyfit(X, y, 2)
192
+
193
+ return maxCV, forceConstant[0]
194
+
195
+ def showForceConstant(temperature=300):
196
+ """ Print force constants obtained from histogram.dat files
197
+
198
+ Args:
199
+ temperature (int, optional): _description_. Defaults to 300.
200
+ """
201
+ #print(parseDat("eq.histogram1.dat", temperature))
202
+ print(parseDat("../000_eq/output/eq.histogram2.dat", temperature))
203
+ print(parseDat("../000_eq/output/eq.histogram3.dat", temperature))
204
+ print(parseDat("../000_eq/output/eq.histogram4.dat", temperature))
205
+ print(parseDat("../000_eq/output/eq.histogram5.dat", temperature))
206
+ print(parseDat("../000_eq/output/eq.histogram6.dat", temperature))
207
+ print(parseDat("../000_eq/output/eq.histogram7.dat", temperature))
208
+
209
+ def updateForceConstant(filename, CVs, newForceConstants):
210
+ """ Update force constant of the CVs in a colvars file named filename.
211
+ Generates a new files named filename.tmp.
212
+
213
+ Args:
214
+ filename (str): path to the colvars files
215
+ CVs (list[str]): list of CVs
216
+ newForceConstants (List[float]): new force constants
217
+ """
218
+ # whether parsing a harmonic block
219
+ # either None or the index of the corresponding CV
220
+ inBlock = None
221
+ with open(filename, 'r') as oldColvarsFile:
222
+ # Python cannot modify text file in place
223
+ with open(filename + '.tmp', 'w') as newColvarsFile:
224
+ for line in oldColvarsFile.readlines():
225
+ if line.strip().lower().startswith('colvarstrajfrequency'):
226
+ newColvarsFile.write(f'colvarsTrajFrequency 50\\n')
227
+ continue
228
+
229
+ splitedLine = line.strip().split()
230
+
231
+ if inBlock is None:
232
+ newColvarsFile.write(line)
233
+ else:
234
+ if not line.strip().lower().startswith('forceconstant'):
235
+ newColvarsFile.write(line)
236
+ else:
237
+ # 'colvars' not in CVs
238
+ if inBlock == -1:
239
+ newColvarsFile.write(line)
240
+ else:
241
+ newColvarsFile.write(f' ForceConstant $$afc_{newForceConstants[inBlock]}\\n')
242
+
243
+ if len(splitedLine) > 0:
244
+ if splitedLine[0] == '}':
245
+ inBlock = None
246
+
247
+ if line.strip().lower().startswith('harmonic'):
248
+ inBlock = -1
249
+
250
+ if line.strip().lower().startswith('colvars'):
251
+ for index, CV in enumerate(CVs):
252
+ if splitedLine[1].lower() == CV.lower():
253
+ inBlock = index
254
+
255
+ def updateAllForceConstants(temperature=300):
256
+ """ update all the force constants of the colvars.in file,
257
+ based on histogram.dat files
258
+
259
+ Args:
260
+ temperature (int, optional): temperature. Defaults to 300.
261
+ """
262
+ newForceConstants = []
263
+ newForceConstants.append(parseDat("../000_eq/output/eq.histogram2.dat", temperature)[1])
264
+ newForceConstants.append(parseDat("../000_eq/output/eq.histogram3.dat", temperature)[1])
265
+ newForceConstants.append(parseDat("../000_eq/output/eq.histogram4.dat", temperature)[1])
266
+ newForceConstants.append(parseDat("../000_eq/output/eq.histogram5.dat", temperature)[1])
267
+ newForceConstants.append(parseDat("../000_eq/output/eq.histogram6.dat", temperature)[1])
268
+ newForceConstants.append(parseDat("../000_eq/output/eq.histogram7.dat", temperature)[1])
269
+
270
+ updateForceConstant("./colvars.in",
271
+ ["eulerTheta", "eulerPhi", "eulerPsi", "polarTheta", "polarPhi", "r"],
272
+ newForceConstants)
273
+
274
+ def generateColvarsFiles(template_file, windows, prefix):
275
+
276
+ if os.path.exists(f"./{prefix}"):
277
+ print(f"Error, ./{prefix} exists!")
278
+ sys.exit(1)
279
+
280
+ os.mkdir(prefix)
281
+
282
+ with open(template_file, "r") as inputFile:
283
+ lines = inputFile.readlines()
284
+
285
+ for i in range(windows):
286
+ with open(f"./{prefix}/colvars_{i}.in", "w") as new_colvars_file:
287
+ for j in range(len(lines)):
288
+ splitedLine = lines[j].strip().split()
289
+ if len(splitedLine) < 2 or (not splitedLine[1].startswith("$$afc_")):
290
+ new_colvars_file.write(lines[j])
291
+ else:
292
+ new_colvars_file.write(f" forceConstant {float(splitedLine[1].split('_')[1]) * (float(i) / windows)}\\n")
293
+
294
+ with open(f"./{prefix}/colvars_{windows}.in", "w") as new_colvars_file:
295
+ for j in range(len(lines)):
296
+ splitedLine = lines[j].strip().split()
297
+ if len(splitedLine) < 2 or (not splitedLine[1].startswith("$$afc_")):
298
+ new_colvars_file.write(lines[j])
299
+ else:
300
+ new_colvars_file.write(f" forceConstant {float(splitedLine[1].split('_')[1])}\\n")
301
+
302
+ updateAllForceConstants(TEMPERATURE)
303
+ generateColvarsFiles("colvars.in.tmp", WINDOWS, "colvars_files")
149
304
  ''')