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 package ffx.potential.parsers;
39
40 import static ffx.utilities.StringUtils.parseAtomRange;
41 import static java.lang.String.format;
42
43 import ffx.potential.MolecularAssembly;
44 import ffx.potential.Utilities.FileType;
45 import ffx.potential.bonded.Atom;
46 import ffx.potential.bonded.Bond;
47 import ffx.potential.bonded.Polymer;
48 import ffx.potential.nonbonded.CoordRestraint;
49 import ffx.potential.parameters.ForceField;
50 import java.io.File;
51 import java.util.ArrayList;
52 import java.util.Arrays;
53 import java.util.HashSet;
54 import java.util.List;
55 import java.util.OptionalDouble;
56 import java.util.Set;
57 import java.util.Vector;
58 import java.util.logging.Level;
59 import java.util.logging.Logger;
60 import java.util.regex.Pattern;
61 import java.util.stream.Collectors;
62 import java.util.stream.IntStream;
63 import java.util.stream.Stream;
64 import org.apache.commons.configuration2.CompositeConfiguration;
65
66
67
68
69
70
71
72 public abstract class SystemFilter {
73
74 protected static final Pattern lambdaPattern = Pattern.compile("Lambda: +([01]\\.\\d+)");
75 private static final Logger logger = Logger.getLogger(SystemFilter.class.getName());
76 private static Versioning vers = Versioning.TINKER;
77 private static int absoluteCounter = 0;
78
79 protected final boolean dieOnMissingAtom;
80
81 protected final boolean standardizeAtomNames;
82
83
84
85
86 protected final boolean vdwH;
87
88 protected List<Atom> atomList = null;
89
90 protected List<Bond> bondList = null;
91
92
93
94
95 protected List<MolecularAssembly> systems = new Vector<>();
96
97 protected List<File> files;
98
99 protected FileType fileType = FileType.UNK;
100
101 protected CompositeConfiguration properties;
102
103 protected ForceField forceField;
104
105 protected boolean fileRead = false;
106
107
108
109
110 MolecularAssembly activeMolecularAssembly;
111
112 File currentFile = null;
113
114 private List<CoordRestraint> coordRestraints;
115
116
117
118
119
120
121
122 private SystemFilter(ForceField forceField, CompositeConfiguration properties) {
123 this.forceField = forceField;
124 this.properties = properties;
125 if (properties != null) {
126 vdwH = properties.getBoolean("vdwHydrogens", false);
127 dieOnMissingAtom = properties.getBoolean("trajectory-dieOnMissing", false);
128 standardizeAtomNames = properties.getBoolean("standardizeAtomNames", true);
129 } else {
130 vdwH = false;
131 dieOnMissingAtom = false;
132 standardizeAtomNames = true;
133 }
134 }
135
136
137
138
139
140
141
142
143
144
145 public SystemFilter(List<File> files, MolecularAssembly molecularAssembly, ForceField forceField,
146 CompositeConfiguration properties) {
147 this(forceField, properties);
148 this.files = files;
149 if (files != null) {
150 this.currentFile = files.get(0);
151 }
152 this.activeMolecularAssembly = molecularAssembly;
153 }
154
155
156
157
158
159
160
161
162
163
164 public SystemFilter(File file, MolecularAssembly molecularAssembly, ForceField forceField,
165 CompositeConfiguration properties) {
166 this(forceField, properties);
167 files = new ArrayList<>();
168 if (file != null) {
169 files.add(file);
170 }
171 this.currentFile = file;
172 this.activeMolecularAssembly = molecularAssembly;
173 }
174
175
176
177
178
179
180
181
182
183
184 public SystemFilter(File file, List<MolecularAssembly> molecularAssemblies, ForceField forceField,
185 CompositeConfiguration properties) {
186 this(forceField, properties);
187 files = new ArrayList<>();
188 if (file != null) {
189 files.add(file);
190 }
191 this.currentFile = file;
192 this.systems = new ArrayList<>(molecularAssemblies);
193 this.activeMolecularAssembly = systems.get(0);
194 }
195
196
197
198
199
200
201
202 public static File previousVersion(File file) {
203 if (file == null) {
204 return null;
205 }
206 String fileName = file.getAbsolutePath();
207 int dot = file.getAbsolutePath().lastIndexOf(".");
208 int under = file.getAbsolutePath().lastIndexOf("_");
209 File newFile = file;
210 if (under > dot) {
211 String name = fileName.substring(0, under);
212 newFile = new File(name);
213 }
214 File baseFile = newFile;
215 File previousFile = null;
216 int i = 1;
217 while (newFile.exists()) {
218 i = i + 1;
219 previousFile = newFile;
220 newFile = baseFile;
221 int thousand = i / 1000;
222 int hundred = (i - 1000 * thousand) / 100;
223 int tens = (i - 1000 * thousand - 100 * hundred) / 10;
224 int ones = i - 1000 * thousand - 100 * hundred - 10 * tens;
225 StringBuilder newFileString = new StringBuilder(baseFile.getAbsolutePath());
226 if (thousand != 0) {
227 newFileString.append('_').append(thousand).append(hundred).append(tens).append(ones);
228 } else if (hundred != 0) {
229 newFileString.append('_').append(hundred).append(tens).append(ones);
230 } else if (tens != 0) {
231 newFileString.append('_').append(tens).append(ones);
232 } else {
233 newFileString.append('_').append(ones);
234 }
235 newFile = new File(newFileString.toString());
236 }
237 return previousFile;
238 }
239
240
241
242
243
244
245 public static void setVersioning(Versioning vers) {
246 SystemFilter.vers = vers;
247 }
248
249
250
251
252
253
254
255 public static File version(File file) {
256 if (vers == Versioning.TINKER) {
257 return versionTinker(file);
258 } else {
259 if (vers == Versioning.PREFIX_ABSOLUTE || vers == Versioning.POSTFIX_ABSOLUTE) {
260 return versionAbsolute(file, (vers == Versioning.PREFIX_ABSOLUTE));
261 } else {
262 return version(file, (vers == Versioning.PREFIX));
263 }
264 }
265 }
266
267 private static Set<Atom> parseAtomicRanges(MolecularAssembly mola, String[] toks, int tokOffset) {
268 return parseAtomicRanges(mola, Arrays.copyOfRange(toks, tokOffset, toks.length));
269 }
270
271 private static Set<Atom> parseAtomicRanges(MolecularAssembly mola, String[] toks) {
272 Atom[] atoms = mola.getAtomArray();
273 return Arrays.stream(toks).parallel().flatMap((String tok) -> {
274 if (tok.equalsIgnoreCase("HEAVY")) {
275 return Arrays.stream(mola.getChains())
276 .flatMap((Polymer poly) -> poly.getAtomList().stream().filter(Atom::isHeavy));
277 } else if (tok.matches("^[0-9]+$")) {
278 return Stream.of(atoms[Integer.parseInt(tok) - 1]);
279 } else if (tok.matches("^[0-9]+-[0-9]+")) {
280 String[] subtoks = tok.split("-");
281 int first = Integer.parseInt(subtoks[0]) - 1;
282 int last = Integer.parseInt(subtoks[1]) - 1;
283 return IntStream.rangeClosed(first, last).mapToObj((int i) -> atoms[i]);
284 } else {
285 return Arrays.stream(atoms).filter((Atom a) -> a.getName().equals(tok));
286 }
287 }).collect(Collectors.toSet());
288 }
289
290 private static File version(File file, boolean prefix) {
291 if (file == null || !(file.exists())) {
292 return file;
293 }
294 String fn = file.getAbsolutePath();
295 int dot = fn.lastIndexOf(".");
296 int under = fn.lastIndexOf("_");
297 if (dot < 0) {
298 fn += ".unk";
299 dot = fn.lastIndexOf(".");
300 }
301 if (under < 0) {
302 under = dot;
303 }
304 String name = (prefix) ? fn.substring(0, under) : fn.substring(0, dot);
305 String extension = (prefix) ? fn.substring(dot + 1) : fn.substring(dot + 1, under);
306 int number = 0;
307 String newFn = (prefix) ? format("%s_%d.%s", name, number, extension)
308 : format("%s.%s_%d", name, extension, number);
309 if (prefix && under < dot) {
310 try {
311 number = Integer.parseInt(fn.substring(under + 1, dot));
312 } catch (NumberFormatException ex) {
313
314 name = fn.substring(0, dot);
315 number++;
316 newFn = format("%s_%d.%s", name, number, extension);
317 }
318 } else if (!prefix && under > dot) {
319 try {
320 number = Integer.parseInt(fn.substring(under + 1));
321 number++;
322 } catch (NumberFormatException ex) {
323
324 }
325 }
326 File newFile = new File(newFn);
327 while (newFile.exists()) {
328 ++number;
329 newFn = (prefix) ? format("%s_%d.%s", name, number, extension)
330 : format("%s.%s_%d", name, extension, number);
331 newFile = new File(newFn);
332 }
333 return newFile;
334 }
335
336 private static synchronized File versionAbsolute(File file, boolean prefix) {
337 if (file == null || !(file.exists())) {
338 return file;
339 }
340 String fn = file.getAbsolutePath();
341 int dot = fn.lastIndexOf(".");
342 int under = fn.lastIndexOf("_");
343 if (dot < 0) {
344 fn += ".unk";
345 dot = fn.lastIndexOf(".");
346 }
347 if (under < 0) {
348 under = dot;
349 }
350 String name = (prefix) ? fn.substring(0, under) : fn.substring(0, dot);
351 String extension = (prefix) ? fn.substring(dot + 1) : fn.substring(dot + 1, under);
352 String newFn = (prefix) ? format("%s_%d.%s", name, absoluteCounter, extension)
353 : format("%s.%s_%d", name, extension, absoluteCounter);
354 File newFile = new File(newFn);
355 while (newFile.exists()) {
356 absoluteCounter++;
357 newFn = (prefix) ? format("%s_%d.%s", name, absoluteCounter, extension)
358 : format("%s.%s_%d", name, extension, absoluteCounter);
359 newFile = new File(newFn);
360 }
361 return newFile;
362 }
363
364
365
366
367
368
369
370 private static File versionTinker(File file) {
371 if (file == null) {
372 return null;
373 }
374 if (!file.exists()) {
375 return file;
376 }
377 String fileName = file.getAbsolutePath();
378 int dot = file.getAbsolutePath().lastIndexOf(".");
379 int under = file.getAbsolutePath().lastIndexOf("_");
380 File newFile = file;
381 if (under > dot) {
382 String name = fileName.substring(0, under);
383 newFile = new File(name);
384 }
385 File oldFile = newFile;
386 int i = 1;
387 while (newFile.exists()) {
388 i = i + 1;
389 String newFileString = format("%s_%d", oldFile.getAbsolutePath(), i);
390 newFile = new File(newFileString);
391 }
392 return newFile;
393 }
394
395
396
397
398
399
400
401 public void applyAtomProperties() {
402
403
404
405
406
407 Atom[] molaAtoms = activeMolecularAssembly.getAtomArray();
408 int nmolaAtoms = molaAtoms.length;
409 String[] nouseKeys = properties.getStringArray("nouse");
410 for (String nouseKey : nouseKeys) {
411 String[] toks = nouseKey.split("\\s+");
412 for (String tok : toks) {
413 try {
414 List<Integer> nouseRange = parseAtomRange("nouse", tok, nmolaAtoms);
415 for (int j : nouseRange) {
416 molaAtoms[j].setUse(false);
417 }
418 } catch (IllegalArgumentException ex) {
419 boolean atomFound = false;
420 for (Atom atom : molaAtoms) {
421 if (atom.getName().equalsIgnoreCase(tok)) {
422 atomFound = true;
423 atom.setUse(false);
424 }
425 }
426 if (atomFound) {
427 logger.info(format(" Setting atoms with name %s to not be used", tok));
428 } else {
429 logger.log(Level.INFO, ex.getLocalizedMessage());
430 }
431 }
432 }
433 }
434
435 if (properties.containsKey("active")) {
436 for (Atom atom : molaAtoms) {
437 atom.setActive(false);
438 }
439 String[] activeKeys = properties.getStringArray("active");
440 for (String inactiveKey : activeKeys) {
441 try {
442 List<Integer> inactiveRange = parseAtomRange("inactive", inactiveKey, nmolaAtoms);
443 for (int i : inactiveRange) {
444 molaAtoms[i].setActive(false);
445 }
446 } catch (IllegalArgumentException ex) {
447 logger.log(Level.INFO, ex.getLocalizedMessage());
448 }
449 }
450 } else if (properties.containsKey("inactive")) {
451 for (Atom atom : molaAtoms) {
452 atom.setActive(true);
453 }
454 String[] inactiveKeys = properties.getStringArray("inactive");
455 for (String inactiveKey : inactiveKeys) {
456 try {
457 List<Integer> inactiveRange = parseAtomRange("inactive", inactiveKey, nmolaAtoms);
458 for (int i : inactiveRange) {
459 molaAtoms[i].setActive(false);
460 }
461 } catch (IllegalArgumentException ex) {
462 logger.log(Level.INFO, ex.getLocalizedMessage());
463 }
464 }
465 }
466
467 coordRestraints = new ArrayList<>();
468 String[] cRestraintStrings = properties.getStringArray("restraint");
469 for (String coordRestraint : cRestraintStrings) {
470 String[] toks = coordRestraint.split("\\s+");
471 double forceconst;
472 try {
473 forceconst = Double.parseDouble(toks[0]);
474 } catch (NumberFormatException ex) {
475 logger.log(Level.INFO,
476 " First argument to coordinate restraint must be a positive force constant; discarding coordinate restraint.");
477 continue;
478 }
479 if (forceconst < 0) {
480 logger.log(Level.INFO,
481 " Force constants must be positive. Discarding coordinate restraint.");
482 continue;
483 }
484 logger.info(format(
485 " Adding lambda-disabled coordinate restraint " + "with force constant %10.4f kcal/mol/A",
486 forceconst));
487 Set<Atom> restraintAtoms = parseAtomicRanges(activeMolecularAssembly, toks, 1);
488 if (!(restraintAtoms == null || restraintAtoms.isEmpty())) {
489 Atom[] ats = restraintAtoms.toArray(new Atom[0]);
490 coordRestraints.add(new CoordRestraint(ats, forceField, false, forceconst));
491 } else {
492 logger.warning(format(" Empty or unparseable restraint argument %s", coordRestraint));
493 }
494 }
495
496 String[] lamRestraintStrings = properties.getStringArray("lamrestraint");
497 for (String coordRestraint : lamRestraintStrings) {
498 String[] toks = coordRestraint.split("\\s+");
499 double forceconst = Double.parseDouble(toks[0]);
500 logger.info(format(
501 " Adding lambda-enabled coordinate restraint " + "with force constant %10.4f kcal/mol/A",
502 forceconst));
503 Set<Atom> restraintAtoms = new HashSet<>();
504
505 for (int i = 1; i < toks.length; i++) {
506 try {
507 List<Integer> restrRange = parseAtomRange("restraint", toks[i], nmolaAtoms);
508 for (int j : restrRange) {
509 restraintAtoms.add(molaAtoms[j]);
510 }
511 } catch (IllegalArgumentException ex) {
512 boolean atomFound = false;
513 for (Atom atom : molaAtoms) {
514 if (atom.getName().equalsIgnoreCase(toks[i])) {
515 atomFound = true;
516 restraintAtoms.add(atom);
517 }
518 }
519 if (atomFound) {
520 logger.info(format(" Added atoms with name %s to restraint", toks[i]));
521 } else {
522 logger.log(Level.INFO, format(
523 " Restraint input %s " + "could not be parsed as a numerical range or "
524 + "an atom type present in assembly", toks[i]));
525 }
526 }
527 }
528 if (!restraintAtoms.isEmpty()) {
529 Atom[] ats = restraintAtoms.toArray(new Atom[0]);
530 coordRestraints.add(new CoordRestraint(ats, forceField, true, forceconst));
531 } else {
532 logger.warning(format(" Empty or unparseable restraint argument %s", coordRestraint));
533 }
534 }
535
536 String[] xyzRestStrings = properties.getStringArray("xyzRestraint");
537
538
539
540 List<Atom> restraintAts = new ArrayList<>();
541 List<double[]> coords = new ArrayList<>();
542 double lastForceConst = 0;
543 boolean lastUseLam = false;
544
545 for (String xR : xyzRestStrings) {
546 String[] toks = xR.split("\\s+");
547 int nToks = toks.length;
548 if (nToks != 6) {
549 logger.info(
550 " XYZ restraint rejected: must have force constant, lambda boolean (true/false), 3 coordinates, and an atom number");
551 logger.info(
552 " For a coordinate restraint centered on original coordinates, use restraint or lamrestraint keys.");
553 logger.info(format(" Rejected restraint string: %s", xR));
554 } else {
555 try {
556 double forceConst = Double.parseDouble(toks[0]);
557 boolean useLambda = Boolean.parseBoolean(toks[1]);
558
559 if (forceConst != lastForceConst || useLambda != lastUseLam) {
560 int nAts = restraintAts.size();
561 if (nAts != 0) {
562 double[][] restXYZ = new double[3][nAts];
563 Atom[] atArr = restraintAts.toArray(new Atom[nAts]);
564 for (int i = 0; i < 3; i++) {
565 for (int j = 0; j < nAts; j++) {
566 restXYZ[i][j] = coords.get(j)[i];
567 }
568 }
569 CoordRestraint thePin = new CoordRestraint(atArr, forceField, lastUseLam,
570 lastForceConst);
571 thePin.setCoordinatePin(restXYZ);
572 thePin.setIgnoreHydrogen(false);
573 coordRestraints.add(thePin);
574 }
575 restraintAts = new ArrayList<>();
576 coords = new ArrayList<>();
577 lastForceConst = forceConst;
578 lastUseLam = useLambda;
579 }
580
581 double[] atXYZ = new double[3];
582 for (int i = 0; i < 3; i++) {
583 atXYZ[i] = Double.parseDouble(toks[i + 2]);
584 }
585 int atNum = Integer.parseInt(toks[5]) - 1;
586 restraintAts.add(molaAtoms[atNum]);
587 coords.add(atXYZ);
588 } catch (Exception ex) {
589 logger.info(format(" Exception parsing xyzRestraint %s: %s", xR, ex));
590 }
591 }
592 }
593
594 int nAts = restraintAts.size();
595 if (nAts != 0) {
596 double[][] restXYZ = new double[3][nAts];
597 Atom[] atArr = restraintAts.toArray(new Atom[nAts]);
598 for (int i = 0; i < 3; i++) {
599 for (int j = 0; j < nAts; j++) {
600 restXYZ[i][j] = coords.get(j)[i];
601 }
602 }
603 CoordRestraint thePin = new CoordRestraint(atArr, forceField, lastUseLam, lastForceConst);
604 thePin.setCoordinatePin(restXYZ);
605 thePin.setIgnoreHydrogen(false);
606 coordRestraints.add(thePin);
607 }
608
609 String[] noElStrings = properties.getStringArray("noElectro");
610 for (String noE : noElStrings) {
611 String[] toks = noE.split("\\s+");
612 for (String tok : toks) {
613 try {
614 List<Integer> noERange = parseAtomRange("noElectro", tok, nmolaAtoms);
615 for (int i : noERange) {
616 molaAtoms[i].setElectrostatics(false);
617 }
618 } catch (IllegalArgumentException ex) {
619 boolean atomFound = false;
620 for (Atom atom : molaAtoms) {
621 if (atom.getName().equalsIgnoreCase(tok)) {
622 atomFound = true;
623 atom.setElectrostatics(false);
624 }
625 }
626 if (atomFound) {
627 logger.info(format(" Disabled electrostatics for atoms with name %s", tok));
628 } else {
629 logger.log(Level.INFO, format(
630 " No electrostatics input %s could not be parsed as a numerical "
631 + "range or atom type present in assembly", tok));
632 }
633 }
634 }
635 }
636 }
637
638
639
640
641
642 public abstract void closeReader();
643
644 public int countNumModels() {
645 return -1;
646 }
647
648
649
650
651
652
653 public boolean fileRead() {
654 return fileRead;
655 }
656
657
658
659
660
661
662 public MolecularAssembly getActiveMolecularSystem() {
663 return activeMolecularAssembly;
664 }
665
666
667
668
669
670
671 public List<Atom> getAtomList() {
672 return atomList;
673 }
674
675
676
677
678
679
680 public List<CoordRestraint> getCoordRestraints() {
681 if (!coordRestraints.isEmpty()) {
682 return new ArrayList<>(coordRestraints);
683 } else {
684 return null;
685 }
686 }
687
688
689
690
691
692
693 public File getFile() {
694 return currentFile;
695 }
696
697
698
699
700
701
702 public void setFile(File file) {
703 this.currentFile = file;
704 files = new ArrayList<>();
705 if (file != null) {
706 files.add(file);
707 }
708 }
709
710
711
712
713
714
715 public List<File> getFiles() {
716 return files;
717 }
718
719
720
721
722
723
724 public void setFiles(List<File> files) {
725 this.files = files;
726 if (files != null && !files.isEmpty()) {
727 this.currentFile = files.get(0);
728 } else {
729 this.currentFile = null;
730 }
731 }
732
733
734
735
736
737
738 public OptionalDouble getLastReadLambda() {
739 return OptionalDouble.empty();
740 }
741
742
743
744
745
746
747 public MolecularAssembly[] getMolecularAssemblyArray() {
748 if (systems.size() > 0) {
749 return systems.toArray(new MolecularAssembly[0]);
750 } else {
751 return new MolecularAssembly[] {activeMolecularAssembly};
752 }
753 }
754
755
756
757
758
759
760 public String[] getRemarkLines() {
761 return new String[0];
762 }
763
764
765
766
767
768
769 public int getSnapshot() {
770 return -1;
771 }
772
773
774
775
776
777
778 public FileType getType() {
779 return fileType;
780 }
781
782
783
784
785
786
787 public void setType(FileType fileType) {
788 this.fileType = fileType;
789 }
790
791
792
793
794
795
796 public abstract boolean readFile();
797
798
799
800
801
802
803 public abstract boolean readNext();
804
805
806
807
808
809
810
811 public abstract boolean readNext(boolean resetPosition);
812
813
814
815
816
817
818
819
820 public abstract boolean readNext(boolean resetPosition, boolean print);
821
822
823
824
825
826
827
828
829
830 public abstract boolean readNext(boolean resetPosition, boolean print, boolean parse);
831
832
833
834
835
836
837 public void setForceField(ForceField forceField) {
838 this.forceField = forceField;
839 }
840
841
842
843
844
845
846
847 public void setProperties(CompositeConfiguration properties) {
848 this.properties = properties;
849 }
850
851
852
853
854
855
856
857
858
859
860
861
862 public boolean writeFile(File saveFile, boolean append) {
863 return writeFile(saveFile, append, null);
864 }
865
866
867
868
869
870
871
872
873
874
875
876
877
878 public abstract boolean writeFile(File saveFile, boolean append, String[] extraLines);
879
880
881
882
883
884
885 protected void setFileRead(boolean fileRead) {
886 this.fileRead = fileRead;
887 }
888
889
890
891
892
893
894 void setMolecularSystem(MolecularAssembly molecularAssembly) {
895 activeMolecularAssembly = molecularAssembly;
896 }
897
898 public enum Versioning {
899 TINKER, PREFIX, POSTFIX, PREFIX_ABSOLUTE, POSTFIX_ABSOLUTE, NONE
900 }
901 }