-
Notifications
You must be signed in to change notification settings - Fork 2
/
calcf_vgauss.py
1706 lines (1588 loc) · 70 KB
/
calcf_vgauss.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env python
import numpy as np
import argparse, os, sys
from glob import glob
from copy import deepcopy
from errors import FCAM_Error
from numba import jit
import time
start_time = time.time()
# This is a python program to calculate mean forces based on the Force-Correction Analysis Method.
# If you use this code please cite:
#
# Marinelli, F. and J.D. Faraldo-Gomez, Force-Correction Analysis Method for Derivation of Multidimensional Free-Energy Landscapes from Adaptively Biased Replica Simulations. J Chem Theory Comput, 2021, 17: p. 6775-6788
#
# Manual and tutorial can be downloaded from https://github.com/FCAM-NIH/FCAM
#
### Argparser ###
def parse():
parser = argparse.ArgumentParser()
parser.add_argument("-if", "--inputfile", \
help="input file for analysis", \
type=str, required=True)
parser.add_argument("-temp", "--temp", help="Temperature (required)", \
default=-1.0,type=float, required=False)
parser.add_argument("-skip", "--skip", help="skip points when calculating forces (default 1)", \
default=1,type=int, required=False)
parser.add_argument("-trfr1", "--trajfraction1", help="starting frame fraction of the trajectory (range 0-1) to be used for force calculation (default is 0)", \
default=0.0,type=float, required=False)
parser.add_argument("-trfr2", "--trajfraction2", help="last frame fraction of the trajectory (range 0-1) to be used for force calculation (default is 1)", \
default=1.0,type=float, required=False)
parser.add_argument("-units", "--units", \
help="Choose free energy units specifying (case sensitive) either kj (kj/mol) or kcal (kcal/mol) (in alternative you can set the Boltzmann factor through the option -kb)", \
type=str, required=False)
parser.add_argument("-kb", "--kb", help="Boltzmann factor to define free energy units.", \
default=-1,type=float, required=False)
parser.add_argument("-wf", "--widthfactor", help="Scaling factor of the width (wfact) to assign the force constant (k=kb*temp*(wfact*wfact)/(width*width); default is 1 (width is read in the GRID defined in the input file)", \
default=1.0,type=float, required=False)
parser.add_argument("-colv_time_prec", "--colv_time_prec", help="Precision for reading the time column in COLVAR_FILE and HILLS_FILE", \
default=-1,type=int, required=False)
parser.add_argument("-colvarbias_column", "--read_colvarbias_column", help="read biasing force from COLVAR_FILE at a specified number of columns after the associated CV (e.g. would be 1 if it is right after the CV)", \
default=-1,type=int, required=False)
parser.add_argument("-colvars","--colvars", \
help="Use default parameters for Colvars", \
default=False, dest='do_colv', action='store_true')
parser.add_argument("-internalf","--internalforces", \
help="Provided free energy gradients are based on internal forces", \
default=False, dest='do_intern', action='store_true')
parser.add_argument("-noforce","--nocalcforce", \
help="Do not calculate forces. By default forces calculation is ON", \
default=True, dest='do_force', action='store_false')
parser.add_argument("-nopgradb","--noprintgradbias", \
help="do not print in output bias gradient trajectory ", \
default=True, dest='print_bias', action='store_false')
parser.add_argument("-obgf", "--outbiasgradfile", \
help="output file of bias gradients for each frame", \
default="bias_grad.out",type=str, required=False)
parser.add_argument("-oepf", "--outeffpointsfile", \
help="output file of effective points (e.g. on which forces are going to be calculated)", \
default="eff_points.out",type=str, required=False)
parser.add_argument("-oeff", "--outeffforcefile", \
help="output file of free energy gradient on effective points", \
default="grad_on_eff_points.out",type=str, required=False)
parser.add_argument("-oeff1", "--outeffforcefile1", \
help="output file of free energy gradient on effective points using first half of the trajectory", \
default="grad_on_eff_points1.out",type=str, required=False)
parser.add_argument("-oeff2", "--outeffforcefile2", \
help="output file of of free energy gradient on effective points using second half of the trajectory", \
default="grad_on_eff_points2.out",type=str, required=False)
parser.add_argument("-ocmbeff", "--outcombeffforcefile", \
help="output combined file effective points and forces", \
default="grad_on_eff_points_comb.out",type=str, required=False)
parser.add_argument("-olf", "--outlabelfile", \
help="output file of labels (assigned bins along colvar) ", \
default="label.out",type=str, required=False)
parser.add_argument("-nobound","--noboundaries", \
help="Do not exclude data beyond grid boundaries", \
default=True, dest='do_bound', action='store_false')
parser.add_argument("-nofeffpc","--nofasteffpointcalc", \
help="Do not use algorithm for fast calculation of effective points (through binning)", \
default=True, dest='do_feffpc', action='store_false')
parser.add_argument("-noeffpb","--noeffpointbin", \
help="Do not calculate effective points by binning but use non-overlapping spherical domains", \
default=True, dest='do_effpb', action='store_false')
parser.add_argument("-jceffp","--justcalceffpoints", \
help="Calculate effective points and do nothing else. COLVARS and GRID data must at least be provided", \
default=False, dest='do_jceffp', action='store_true')
parser.add_argument("-jcmetab","--justcalcmetabias", \
help="Calculate metadynamics bias potential and do nothing else. COLVARS, HILLS and GRID data must at least be provided", \
default=False, dest='do_jmetab', action='store_true')
parser.add_argument("-label","--label", \
help="label COLVARS according to the effective foints", \
default=False, dest='do_label', action='store_true')
parser.add_argument("-wrtlabelscrd","--writelabelscoord", \
help="write COLVARS for each label", \
default=False, dest='write_label_coord', action='store_true')
parser.add_argument("-hlfl","--hillfreqlarge", \
help="Metadynamics in which HILLS are stored more frequently than COLVARS", \
default=False, dest='do_hlfl', action='store_true')
parser.add_argument("-gefilt","--gaussianenergyfilter", \
help="Filter data by comparing the calculated gaussian energy with the one reported in the COLVAR file according to -valgefilt", \
default=False, dest='do_gefilt', action='store_true')
parser.add_argument("-colgener", "--colgener", help="Column to read the Gaussian energy in the COLVAR file for filtering (Default is the second last)", \
default=-1,type=int, required=False)
parser.add_argument("-valgefilt", "--valgefilt", help="Difference threshold between calculated gaussian energy and the one reported in the COLVAR file for filtering ", \
default=1000.0,type=float, required=False)
parser.add_argument("-nfr", "--numframerest", help="Number of frames to filter out before and after restart ", \
default=-1,type=int, required=False)
parser.add_argument("-backres","--backrestart", \
help="Filter out just data before a restart according to --numframerest", \
default=False, dest='do_backres', action='store_true')
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
args = parser.parse_args()
return args
args = parse()
# Variables
ifile=args.inputfile
do_colvars=args.do_colv
do_internalf=args.do_intern
do_label=args.do_label
writelabelscoord=args.write_label_coord
do_force=args.do_force
do_boundaries=args.do_bound
do_gefilter=args.do_gefilt
do_just_eff_points=args.do_jceffp
do_just_hills_bias=args.do_jmetab
do_fast_eff_p_calc=args.do_feffpc
do_bin_eff_p_calc=args.do_effpb
do_backrestart=args.do_backres
print_bias=args.print_bias
bias_grad_file=args.outbiasgradfile
labelfile=args.outlabelfile
eff_points_file=args.outeffpointsfile
force_points_file=args.outeffforcefile
force_points_file_comb=args.outcombeffforcefile
force_points_file1=args.outeffforcefile1
force_points_file2=args.outeffforcefile2
temp=args.temp
skip=args.skip
kb=args.kb
units=args.units
widthfact=args.widthfactor
colv_prec=args.colv_time_prec
colvarbias_column=args.read_colvarbias_column
tgefilt=args.valgefilt
nfrestart=args.numframerest
colgener=args.colgener
do_large_hfreq=args.do_hlfl
trajfraction1=args.trajfraction1
trajfraction2=args.trajfraction2
# parameters
if do_colvars:
hcutoff=11.5 # cutoff for Gaussians
else:
hcutoff=6.25 # cutoff for Gaussians
wcutoff=18.75 # cutoff for Gaussians in weight calculation
do_hills_bias=False
do_umbrella_bias=False
if colvarbias_column>0:
if do_colvars==False:
print ("ERROR: reading forces from COLVAR files is supported only for colvars ")
print ("ERROR: if you are using colvars please insert the option -colvars ")
sys.exit()
if trajfraction1>=1.0 or trajfraction1<0.0:
print ("ERROR: please select a trajectory starting point between 0 and 1")
sys.exit()
if trajfraction2>1.0 or trajfraction2<=0.0:
print ("ERROR: please select a trajectory last point between 0 and 1")
sys.exit()
if trajfraction2<=trajfraction1:
print ("ERROR: last point of the trajectory to be used must be larger than the first")
sys.exit()
if do_bin_eff_p_calc==False:
do_fast_eff_p_calc=False
calc_epoints=True # True unless is read from input
calc_force_eff=True # True unless is read from input
ncolvars=0
ndim=0
cvdict={}
ngfiles=0
nefiles=0
nffiles=0
nfcomp=0
read_gfile=False
read_efile=False
read_ffile=False
has_hills=False
gammaf=1
do_us=False
nactive=0
cfile='none'
hfile='none'
# INPUT PARSER
# It reads an input file with relevant information (see manual):
# grid parameters and files with trajectories of collective variables and/or forces, plus other specifications.
# It is structured to be able to read an unlimited number of files. Collective variables specified on the grid can be labeled and recognized
# as specific components on force/gradient trajectory files using a dictionary.
f=open (ifile, 'r')
for line in f:
parts=line.split()
nparts=len(parts)
has_c=False
ncfiles=0
nhfiles=0
nafields=0
nbffields=0
nuscvfields=0
nuskfields=0
nuscfields=0
for i in range (0,nparts):
if str(parts[i])=="COLVAR_FILE":
lc=i
has_c=True
ncfiles=ncfiles+1
if ncfiles>1:
print ("ERROR, COLVAR_FILE must be specified just one time on a single line")
sys.exit()
if has_c:
if ncolvars==0:
cfile=[str(parts[lc+1])]
has_us=False
for i in range (0,nparts):
if str(parts[i])=="US_CVS-CLS":
lus=i
has_us=True
nuscvfields=nuscvfields+1
if nuscvfields>1:
print ("ERROR, US_CVS-CLS must be specified just one time on a single line")
sys.exit()
if has_us:
do_umbrella_bias=True
nact=0
do_us=[True]
for i in range (lus+1,nparts):
if str(parts[i])=="COLVAR_FILE" or str(parts[i])=="US_K" or str(parts[i])=="US_C":
break
nact=nact+1
nactive=[nact]
pippo=np.zeros((nactive[ncolvars]),dtype=np.int64)
npippo=0
for i in range(lus+1,nparts):
if str(parts[i])=="COLVAR_FILE" or str(parts[i])=="US_K" or str(parts[i])=="US_C":
break
pippo[npippo]=int(parts[i])-1
npippo=npippo+1
a_cvs=[pippo]
has_us_k=False
for i in range (0,nparts):
if str(parts[i])=="US_K":
lusk=i
has_us_k=True
nuskfields=nuskfields+1
if has_us_k==False or nuskfields>1:
print ("ERROR, US_K not specified or specified more than one time on a single line")
sys.exit()
if has_us_k:
pippo=np.zeros((nactive[ncolvars]))
npippo=0
for i in range(lusk+1,nparts):
if str(parts[i])=="COLVAR_FILE" or str(parts[i])=="US_CVS-CLS" or str(parts[i])=="US_C":
break
pippo[npippo]=float(parts[i])
npippo=npippo+1
k_cvs=[pippo]
has_us_c=False
for i in range (0,nparts):
if str(parts[i])=="US_C":
lusc=i
has_us_c=True
nuscfields=nuscfields+1
if has_us_c==False or nuscfields>1:
print ("ERROR, US_C not specified or specified more than one time on a single line")
sys.exit()
if has_us_c:
pippo=np.zeros((nactive[ncolvars]))
npippo=0
for i in range(lusc+1,nparts):
if str(parts[i])=="COLVAR_FILE" or str(parts[i])=="US_CVS-CLS" or str(parts[i])=="US_K":
break
pippo[npippo]=float(parts[i])
npippo=npippo+1
c_cvs=[pippo]
else:
do_us=[False]
has_h=False
for i in range (0,nparts):
if str(parts[i])=="HILLS_FILE":
lh=i
has_h=True
nhfiles=nhfiles+1
if nhfiles>1:
print ("ERROR, HILLS_FILE must be specified just one time on a single line")
sys.exit()
if has_h:
do_hills_bias=True
if has_us:
print ("ERROR, reading HILLS_FILE is not compatible with US")
sys.exit()
has_hills=[True]
hfile=[str(parts[lh+1])]
has_a=False
nact=0
has_bf=False
for i in range (0,nparts):
if str(parts[i])=="BF":
lbf=i
nbffields=nbffields+1
has_bf=True
if has_bf==True and nbffields>1:
print ("ERROR, BF (bias factor) specified more than once on a single line")
sys.exit()
if has_bf:
bias_factor=float(parts[lbf+1])
if bias_factor<=0:
print ("ERROR, please select a positive (and non zero) bias factor")
sys.exit()
gammaf=[(bias_factor-1.0)/bias_factor]
else:
gammaf=[1.0]
for i in range (0,nparts):
if str(parts[i])=="HILLS_CVS-CLS":
la=i
has_a=True
nafields=nafields+1
if has_a==False or nafields>1:
print ("ERROR, HILLS_CVS-CLS not specified or specified more than once on a single line")
sys.exit()
for i in range (la+1,nparts):
if str(parts[i])=="COLVAR_FILE" or str(parts[i])=="HILLS_FILE" or str(parts[i])=="BF":
break
nact=nact+1
nactive=[nact]
pippo=np.zeros((nactive[ncolvars]),dtype=np.int64)
npippo=0
for i in range(la+1,nparts):
if str(parts[i])=="COLVAR_FILE" or str(parts[i])=="HILLS_FILE" or str(parts[i])=="BF":
break
pippo[npippo]=int(parts[i])-1
npippo=npippo+1
a_cvs=[pippo]
else:
has_hills=[False]
hfile=['none']
if has_h==False and has_us==False:
nactive=[int(0)]
a_cvs=[int(-2)]
if ncolvars>0:
cfile.append(str(parts[lc+1]))
has_us=False
for i in range (0,nparts):
if str(parts[i])=="US_CVS-CLS":
lus=i
has_us=True
nuscvfields=nuscvfields+1
if nuscvfields>1:
print ("ERROR, US_CVS-CLS must be specified just one time on a single line")
sys.exit()
if has_us:
do_umbrella_bias=True
nact=0
do_us.append(True)
for i in range (lus+1,nparts):
if str(parts[i])=="COLVAR_FILE" or str(parts[i])=="US_K" or str(parts[i])=="US_C":
break
nact=nact+1
nactive.append(nact)
pippo=np.zeros((nactive[ncolvars]),dtype=np.int64)
npippo=0
for i in range(lus+1,nparts):
if str(parts[i])=="COLVAR_FILE" or str(parts[i])=="US_K" or str(parts[i])=="US_C":
break
pippo[npippo]=int(parts[i])-1
npippo=npippo+1
a_cvs.append(pippo)
has_us_k=False
for i in range (0,nparts):
if str(parts[i])=="US_K":
lusk=i
has_us_k=True
nuskfields=nuskfields+1
if has_us_k==False or nuskfields>1:
print ("ERROR, US_K not specified or specified more than one time on a single line")
sys.exit()
if has_us_k:
pippo=np.zeros((nactive[ncolvars]))
npippo=0
for i in range(lusk+1,nparts):
if str(parts[i])=="COLVAR_FILE" or str(parts[i])=="US_CVS-CLS" or str(parts[i])=="US_C":
break
pippo[npippo]=float(parts[i])
npippo=npippo+1
k_cvs.append(pippo)
has_us_c=False
for i in range (0,nparts):
if str(parts[i])=="US_C":
lusc=i
has_us_c=True
nuscfields=nuscfields+1
if has_us_c==False or nuscfields>1:
print ("ERROR, US_C not specified or specified more than one time on a single line")
sys.exit()
if has_us_c:
pippo=np.zeros((nactive[ncolvars]))
npippo=0
for i in range(lusc+1,nparts):
if str(parts[i])=="COLVAR_FILE" or str(parts[i])=="US_CVS-CLS" or str(parts[i])=="US_K":
break
pippo[npippo]=float(parts[i])
npippo=npippo+1
c_cvs.append(pippo)
else:
do_us.append(False)
has_h=False
for i in range (0,nparts):
if str(parts[i])=="HILLS_FILE":
lh=i
has_h=True
nhfiles=nhfiles+1
if nhfiles>1:
print ("ERROR, HILLS_FILE must be specidied just one time on a single line")
sys.exit()
if has_h:
do_hills_bias=True
if has_us:
print ("ERROR, reading HILLS_FILE is not compatible with US")
sys.exit()
has_hills.append(True)
hfile.append(str(parts[lh+1]))
has_a=False
nact=0
has_bf=False
for i in range (0,nparts):
if str(parts[i])=="BF":
lbf=i
nbffields=nbffields+1
has_bf=True
if has_bf==True and nbffields>1:
print ("ERROR, BF (bias factor) specified more than once on a single line")
sys.exit()
if has_bf:
bias_factor=float(parts[lbf+1])
if bias_factor<=0:
print ("ERROR, please select a positive (and non zero) bias factor")
sys.exit()
gammaf.append((bias_factor-1.0)/bias_factor)
else:
gammaf.append(1.0)
for i in range (0,nparts):
if str(parts[i])=="HILLS_CVS-CLS":
la=i
has_a=True
nafields=nafields+1
if has_a==False or nafields>1:
print ("ERROR, HILLS_CVS-CLS not specified or specidied more than once on a single line")
sys.exit()
for i in range (la+1,nparts):
if str(parts[i])=="COLVAR_FILE" or str(parts[i])=="HILLS_FILE" or str(parts[i])=="BF":
break
nact=nact+1
nactive.append(nact)
pippo=np.zeros((nactive[ncolvars]),dtype=np.int64)
npippo=0
for i in range(la+1,nparts):
if str(parts[i])=="COLVAR_FILE" or str(parts[i])=="HILLS_FILE" or str(parts[i])=="BF":
break
pippo[npippo]=int(parts[i])-1
npippo=npippo+1
a_cvs.append(pippo)
else:
has_hills.append(False)
hfile.append('none')
if has_h==False and has_us==False:
nactive.append(int(0))
a_cvs.append(int(-2))
ncolvars=ncolvars+1
if str(parts[0])=="CV-CL":
if ndim==0:
whichcv=[int(parts[1])-1]
lowbound=[float(parts[2])]
upbound=[float(parts[3])]
if float(parts[2])>=float(parts[3]):
print ("ERROR: lower boundary must be smaller than upper boudary ")
sys.exit()
npointsv=[int(parts[4])]
if len(parts)>5:
if str(parts[5])=="PERIODIC":
periodic=[1]
if len(parts)>6:
if str(parts[6])=="LABEL":
if str(parts[7]) in cvdict:
print ("ERROR: CV LABEL",str(parts[7]),"was already used for another CV")
sys.exit()
cvdict[str(parts[7])]=ndim
else:
print ("Unrecognized character:",str(parts[5]))
sys.exit()
else:
periodic=[0]
if str(parts[5])=="LABEL":
if str(parts[6]) in cvdict:
print ("ERROR: CV LABEL",str(parts[6]),"was already used for another CV")
sys.exit()
cvdict[str(parts[6])]=ndim
else:
print ("Unrecognized character:",str(parts[5]))
sys.exit()
else:
periodic=[0]
if ndim>0:
whichcv.append(int(parts[1])-1)
lowbound.append(float(parts[2]))
upbound.append(float(parts[3]))
if float(parts[2])>=float(parts[3]):
print ("ERROR: lower boundary must be smaller than upper boudary ")
sys.exit()
npointsv.append(int(parts[4]))
if len(parts)>5:
if str(parts[5])=="PERIODIC":
periodic.append(1)
if len(parts)>6:
if str(parts[6])=="LABEL":
if str(parts[7]) in cvdict:
print ("ERROR: CV LABEL",str(parts[7]),"was already used for another CV")
sys.exit()
cvdict[str(parts[7])]=ndim
else:
print ("Unrecognized character:",str(parts[5]))
sys.exit()
else:
periodic.append(0)
if str(parts[5])=="LABEL":
if str(parts[6]) in cvdict:
print ("ERROR: CV LABEL",str(parts[6]),"was already used for another CV")
sys.exit()
cvdict[str(parts[6])]=ndim
else:
print ("Unrecognized character:",str(parts[5]))
sys.exit()
else:
periodic.append(0)
ndim=ndim+1
if str(parts[0])=="READ_BIAS_GRAD_TRJ" or str(parts[0])=="READ_BIAS_FORCE_TRJ":
if colvarbias_column>0:
print ("ERROR: you are reading the biasing forces two times; from a file (through READ_BIAS_GRAD_TRJ)")
print ("and also from the COLVAR_FILE (through -colvarbias_column). Select just the pertinent option. ")
sys.exit()
if ngfiles==0:
if str(parts[0])=="READ_BIAS_FORCE_TRJ":
use_force=[-1.0]
else:
use_force=[1.0]
gfile=[str(parts[1])]
read_gfile=True
do_hills_bias=False
do_umbrella_bias=False
if ngfiles>0:
if str(parts[0])=="READ_BIAS_FORCE_TRJ":
use_force.append(-1.0)
else:
use_force.append(1.0)
gfile.append(str(parts[1]))
if nparts>2:
if str(parts[2])=="CV_COMP":
if nparts>3:
pluto=np.zeros((nparts-3),dtype=np.int8)
if ndim==0:
print ("ERROR: to read specific components of the biasing force (through CV_COMP) GRID information must be provided before")
sys.exit()
else:
pluto="ALL"
print ("NO SPECIFIC CV components specified though CV_COMP: assuming the trajectory FORCE/GRAD file includes all components")
for i in range (3,nparts):
if str(parts[i]) in cvdict:
pluto[i-3]=cvdict[str(parts[i])]
else:
tmpwhichcv=np.array(whichcv)
tmppluto=np.where(tmpwhichcv==int(parts[i])-1)
pluto[i-3]=np.int8(tmppluto[0])
else:
print ("NO SPECIFIC CV components specified though CV_COMP: assuming the trajectory FORCE/GRAD file includes all components")
pluto="ALL"
if ngfiles==0:
usecomp=[pluto]
if ngfiles>0:
usecomp.append(pluto)
ngfiles=ngfiles+1
if str(parts[0])=="READ_EPOINTS":
if nefiles==0:
efile=[str(parts[1])]
read_efile=True
calc_epoints=False
if nefiles>0:
efile.append(str(parts[1]))
nefiles=nefiles+1
if str(parts[0])=="READ_GRAD_PMF":
if nffiles==0:
ffile=[str(parts[1])]
read_ffile=True
read_efile=False
do_hills_bias=False
do_umbrella_bias=False
calc_force_eff=False
calc_epoints=False
if nffiles>0:
ffile.append(str(parts[1]))
pluto=np.zeros((ndim),dtype=np.int8)
if nparts>2:
if str(parts[2])=="REMOVE_COMP":
if len(pluto)==0:
print ("ERROR: to use REMOVE_COMP GRID information must be provided before reading the free energy gradients in the input file")
sys.exit()
for i in range (3,nparts):
pluto[np.int8(parts[i])-1]=1
if nffiles==0:
rcvcomp=[pluto]
if nffiles>0:
rcvcomp.append(pluto)
nffiles=nffiles+1
print ("Input read")
if str(units)=="kj":
kb=0.00831446261815324
elif str(units)=="kcal":
kb=0.0019858775
elif kb<0 and do_just_eff_points==False and do_just_hills_bias==False and read_ffile==False and do_force==True:
print ("ERROR: please specify either the units (-units) or the value of the Boltzmann factor (-kb option)")
sys.exit()
# Below it checks which options have been selected to proceed with different calculations
if colvarbias_column>0:
read_gfile=True
do_hills_bias=False
do_umbrella_bias=False
if do_gefilter:
print ("ERROR: filtering according to colvar energy match is not compatible with reading applied forces from colvar file (as those forces are expected to be exact)")
sys.exit()
if do_just_eff_points:
calc_epoints=True
do_hills_bias=False
do_umbrella_bias=False
read_gfile=False
read_efile=False
read_ffile=False
calc_force_eff=False
print ("Requested just derivation of effective points (e.g. GRID on which mean forces will be evaluated) and nothing else")
if do_just_hills_bias:
calc_epoints=False
do_hills_bias=True
do_umbrella_bias=False
read_gfile=False
read_efile=False
read_ffile=False
calc_force_eff=False
print ("Requested just derivation applied forces from HILLS files for each simulation frame and nothing else")
if ncolvars==0:
calc_epoints=False
calc_force_eff=False
if do_force==False:
calc_force_eff=False
if do_hills_bias:
do_internalf=False
do_umbrella_bias=False
if do_umbrella_bias:
do_internalf=False
do_hills_bias=False
internalf=1.0
if do_internalf:
internalf=0.0
print ("The force for each frame read from file (through READ_BIAS_GRAD_TRJ or -colvarbias_column) is the ")
print ("internal force: FCAM not applied, mean forces are calculated as the local average of the internal forces")
if colvarbias_column<=1:
print ("ERROR: both total and applied force must be provided to evaluate the internal force; -colvarbias_column must be larger than 1")
sys.exit()
if read_gfile==False:
if do_hills_bias==False and do_umbrella_bias==False:
if calc_force_eff:
print ("NOTE: no option for reading applied forces from files or calculating them (through HILLS files")
print (" or harmonic umbrella) was selected: mean forces will be calculated assuming UNBIASED SAMPLING.")
if ndim==0:
print ("ERROR: number of variables is zero, please provide some to continue")
sys.exit()
if calc_force_eff and temp<0:
print ("ERROR: temperature for calculating forces not provided or negative value")
sys.exit()
if read_gfile:
if colvarbias_column<=0:
for k in range (0,ngfiles):
if str(usecomp[k])!="ALL":
if do_gefilter:
print ("ERROR: gaussian energy filter is not supported when FORCE/GRAD components to read are specified")
sys.exit()
if ngfiles!=1:
try:
assert(ngfiles == ncolvars)
except AssertionError:
raise FCAM_Error("Please provide a unique gradient file or a gradient file for each colvar. Note that in either case this must be consistent with the ORDERED set of colvar files provided")
if do_hills_bias or do_umbrella_bias:
if print_bias:
with open(bias_grad_file, 'w') as f:
f.write("# Time, grad, Gaussenergy, numhill, next_restart, previous_restart, replica \n")
if calc_epoints:
with open(eff_points_file, 'w') as f:
f.write("# numeff, colvar, npoints \n")
if do_label:
if writelabelscoord==False:
with open(labelfile, 'w') as f:
f.write("# time, label, ntraj \n")
else:
with open(labelfile, 'w') as f:
f.write("# time, colvar, label, ntraj \n")
has_hills=np.array(has_hills)
do_us=np.array(do_us)
nactive=np.array(nactive)
upbound=np.array(upbound)
lowbound=np.array(lowbound)
npointsv=np.array(npointsv)
periodic=np.array(periodic,dtype=np.int8)
box=upbound-lowbound
width=box/npointsv
if calc_force_eff:
with open(force_points_file, 'w') as f:
f.write("# %s \n" % ndim)
for j in range (0,ndim):
f.write("# %s " % lowbound[j])
f.write(" %s " % (width[j]))
f.write(" %s " % (npointsv[j]))
f.write(" %s \n" % periodic[j])
with open(force_points_file1, 'w') as f:
f.write("# %s \n" % ndim)
for j in range (0,ndim):
f.write("# %s " % lowbound[j])
f.write(" %s " % (width[j]))
f.write(" %s " % (npointsv[j]))
f.write(" %s \n" % periodic[j])
with open(force_points_file2, 'w') as f:
f.write("# %s \n" % ndim)
for j in range (0,ndim):
f.write("# %s " % lowbound[j])
f.write(" %s " % (width[j]))
f.write(" %s " % (npointsv[j]))
f.write(" %s \n" % periodic[j])
if read_ffile:
with open(force_points_file_comb, 'w') as f:
f.write("# %s \n" % ndim)
for j in range (0,ndim):
f.write("# %s " % lowbound[j])
f.write(" %s " % (width[j]))
f.write(" %s " % (npointsv[j]))
f.write(" %s \n" % periodic[j])
cunique=np.unique(cfile)
hunique=np.unique(hfile)
vunique=np.unique(whichcv)
try:
assert(len(cunique)==len(cfile))
except AssertionError:
try:
assert(cunique[0]=="none")
except AssertionError:
raise FCAM_Error("Same COLVAR file introduced multiple times in the input")
try:
assert(len(hunique)==len(hfile))
except AssertionError:
try:
assert(hunique[0]=="none")
except AssertionError:
raise FCAM_Error("Same HILLS file introduced multiple times in the input")
try:
assert(len(vunique)==len(whichcv))
except AssertionError:
raise FCAM_Error("Same CV introduced multiple times in the input")
#if len(vunique)!=len(whichcv):
# print ("ERROR: same CV introduced multiple times in the input")
# sys.exit()
iactive=np.zeros((ncolvars,ndim),dtype=np.int8)
iactive[:,:]=-1
allfound=True
for i in range (0,ncolvars):
if nactive[i]>ndim:
print ("ERROR: number of HILLS_CVS-CLS or US_CVS-CLS larger than total number of CVS")
sys.exit()
for j in range (0,nactive[i]):
if nactive[i]>0:
if a_cvs[i][j]>=0:
hasit=False
for k in range (0,ndim):
if a_cvs[i][j]==whichcv[k]:
hasit=True
iactive[i,j]=k
if hasit==False:
allfound=False
if allfound==False:
print ("ERROR: HILLS_CVS-CLS or US_CVS-CLS must be part of the CVS used for CV-CL")
sys.exit()
# read the hills files
for i in range (0,ncolvars):
if has_hills[i]:
if i==0:
hillsarray=[np.loadtxt(hfile[i])]
nhills=[len(hillsarray[i])]
else:
hillsarray.append(np.loadtxt(hfile[i]))
nhills.append(len(hillsarray[i]))
else:
if i==0:
hillsarray=[0]
nhills=[0]
else:
hillsarray.append(0)
nhills.append(0)
# read the colvar file
for i in range (0,ncolvars):
if i==0:
colvarsarray=[np.loadtxt(cfile[i])]
npoints=[len(colvarsarray[i])]
gradv=[np.zeros((npoints[i],ndim))]
else:
colvarsarray.append(np.loadtxt(cfile[i]))
npoints.append(len(colvarsarray[i]))
gradv.append(np.zeros((npoints[i],ndim)))
if do_gefilter:
for i in range (0,ncolvars):
if i==0:
if colgener<0:
colgenerread=[np.ma.size(colvarsarray[i],axis=1)-2]
else:
colgenerread=[colgener-1]
else:
if colgener<0:
colgenerread.append(np.ma.size(colvarsarray[i],axis=1)-2)
else:
colgenerread.append(colgener-1)
# Below there are a set of routines to calculate the number of effective points on which forces will be calculated.
# The fastest and default routune is fast_calc_eff_points, which provides points on a regular grid.
# Note that effective points that are not explored (far from sampled ones) are excluded.
# TO DO: might be useful to do fast a routine for a non regular grid, e.g. using Daura or similar clustering scheme, that
# better captures the space topology.
# This routine calculates effective points based on spherical cut-off (upperbound-lowerbound/number of points in each direction).
# Doesn't give memory issues or overflow but it's slow in high dimensionality.
@jit(nopython=True)
def calc_eff_points(numpoints, inputarray, npointsins):
diffc=np.zeros((numpoints,ndim))
distance=np.zeros((numpoints))
numinbin=np.zeros((numpoints),dtype=np.int64)
neffp=1
ceff=np.zeros((numpoints,ndim))
ceff[0,:]=inputarray[0,:]
numinbin[0]=npointsins[0]
totperiodic=np.sum(periodic[0:ndim])
for i in range(1,numpoints):
diffc[0:neffp,:]=ceff[0:neffp,:]-inputarray[i,:]
if totperiodic>0:
diffc[0:neffp,:]=diffc[0:neffp,:]/box[0:ndim]
diffc[0:neffp,:]=diffc[0:neffp,:]-np.rint(diffc[0:neffp,:])*periodic[0:ndim]
diffc[0:neffp,:]=diffc[0:neffp,:]*box[0:ndim]
diffc[0:neffp,:]=diffc[0:neffp,:]/width[0:ndim]
diffc[0:neffp,:]=diffc[0:neffp,:]*diffc[0:neffp,:]
distance[0:neffp]=np.sum(diffc[0:neffp,:],axis=1)
whichbin=np.argmin(distance[0:neffp])
mindistance=distance[whichbin]
if mindistance>1:
ceff[neffp,:]=inputarray[i,:]
whichbin=neffp
neffp=neffp+1
numinbin[whichbin]=numinbin[whichbin]+npointsins[i]
return ceff[0:neffp], neffp, numinbin[0:neffp]
# This routine calculates effective points on a regular grid. Does not give memory issues or overflow
# but is slow in high dimensionality.
@jit(nopython=True)
def calc_eff_points_bin(numepoints, effparray, npointsins):
colvarsbineff=np.zeros((numepoints, ndim))
nbins=1
mywidth=width
diffc=np.zeros((numepoints,ndim))
distance=np.zeros((numepoints))
diffbin=np.zeros(ndim)
colvarbin=np.zeros(ndim)
numinbin=np.zeros((numepoints),dtype=np.int64)
totperiodic=np.sum(periodic[0:ndim])
diffc[0,:]=effparray[0,:]-lowbound
bingrid=np.floor(diffc[0,:]/mywidth)
if totperiodic>0:
tmpdiffc=mywidth*(0.5+bingrid)-0.5*box
tmpdiffc=tmpdiffc/box[0:ndim]
tmpdiffc=tmpdiffc-np.rint(tmpdiffc)*periodic[0:ndim]
tmpdiffc=tmpdiffc*box[0:ndim]
bingrid=np.rint(((tmpdiffc+0.5*box)/mywidth)-0.5)
colvarbin=lowbound+mywidth*(0.5+bingrid)
colvarsbineff[0,:]=colvarbin
numinbin[0]=npointsins[0]
for i in range(1,numepoints):
diffc[i,:]=effparray[i,:]-lowbound
bingrid=np.floor(diffc[i,:]/mywidth)
if totperiodic>0:
tmpdiffc=mywidth*(0.5+bingrid)-0.5*box
tmpdiffc=tmpdiffc/box[0:ndim]
tmpdiffc=tmpdiffc-np.rint(tmpdiffc)*periodic[0:ndim]
tmpdiffc=tmpdiffc*box[0:ndim]
bingrid=np.rint(((tmpdiffc+0.5*box)/mywidth)-0.5)
colvarbin=lowbound+mywidth*(0.5+bingrid)
diffc[0:nbins,:]=colvarsbineff[0:nbins,:]-colvarbin[:]
if totperiodic>0:
diffc[0:nbins,:]=diffc[0:nbins,:]/box[0:ndim]
diffc[0:nbins,:]=diffc[0:nbins,:]-np.rint(diffc[0:nbins,:])*periodic[0:ndim]
diffc[0:nbins,:]=diffc[0:nbins,:]*box[0:ndim]
diffc[0:nbins,:]=diffc[0:nbins,:]/mywidth[0:ndim]
diffc[0:nbins,:]=diffc[0:nbins,:]*diffc[0:nbins,:]
distance[0:nbins]=np.sum(diffc[0:nbins,:],axis=1)
whichbin=np.argmin(distance[0:nbins])
mindistance=distance[whichbin]
if mindistance>0.5:
colvarsbineff[nbins,:]=colvarbin
whichbin=nbins
nbins=nbins+1
numinbin[whichbin]=numinbin[whichbin]+npointsins[i]
return colvarsbineff, nbins, numinbin[0:nbins]
# This routine calculates effective points on a regular grid. It is based on setting a bin identity number given by
# the sum of the bin grid vectors (integer associated to each bin in each component) scaled by specific factors
# for each component. In this manner, effective bins are associated unique identity numbers.
# The routine is very fast and can be used up to large dimensionality (around 10). Might give overflow at dimensionality beyond 10.
# It is fully tested and reliable.
#
def fast_calc_eff_points(numepoints, effparray, npointsins):
diffc=np.zeros((numepoints,ndim))
myshift=np.ones((ndim),dtype=np.int64)
mywidth=width
newnpointsv=npointsv+1
newlowbound=lowbound