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 ffx.crystal.SpaceGroup;
41 import ffx.crystal.SpaceGroupDefinitions;
42 import ffx.crystal.Crystal;
43 import ffx.crystal.LatticeSystem;
44 import ffx.potential.Utilities;
45 import ffx.potential.bonded.Atom;
46 import ffx.potential.bonded.Bond;
47 import ffx.potential.bonded.Angle;
48 import ffx.potential.bonded.MSNode;
49 import ffx.potential.bonded.MSGroup;
50 import ffx.potential.bonded.Molecule;
51 import ffx.potential.bonded.Polymer;
52 import ffx.potential.bonded.Residue;
53 import ffx.potential.MolecularAssembly;
54 import ffx.potential.parameters.ForceField;
55
56 import java.io.BufferedReader;
57 import java.io.BufferedWriter;
58 import java.io.File;
59 import java.io.FileWriter;
60 import java.io.IOException;
61 import java.nio.file.Path;
62 import java.nio.file.Paths;
63 import java.util.List;
64 import java.util.Arrays;
65 import java.util.ArrayList;
66 import java.util.Objects;
67 import java.util.logging.Level;
68 import java.util.logging.Logger;
69
70 import javax.vecmath.Point3d;
71
72 import org.apache.commons.configuration2.CompositeConfiguration;
73
74 import org.openscience.cdk.AtomContainer;
75 import org.openscience.cdk.config.AtomTypeFactory;
76 import org.openscience.cdk.graph.rebond.RebondTool;
77 import org.openscience.cdk.interfaces.IBond;
78 import org.openscience.cdk.interfaces.IAtom;
79 import org.openscience.cdk.interfaces.IAtomType;
80 import org.openscience.cdk.isomorphism.AtomMatcher;
81 import org.openscience.cdk.isomorphism.BondMatcher;
82 import org.openscience.cdk.isomorphism.VentoFoggia;
83 import org.openscience.cdk.isomorphism.Pattern;
84
85 import org.rcsb.cif.CifIO;
86 import org.rcsb.cif.model.Column;
87 import org.rcsb.cif.model.FloatColumn;
88 import org.rcsb.cif.schema.core.AtomSite;
89 import org.rcsb.cif.schema.core.CifCoreBlock;
90 import org.rcsb.cif.schema.core.CifCoreFile;
91 import org.rcsb.cif.schema.core.Chemical;
92 import org.rcsb.cif.schema.core.Symmetry;
93 import org.rcsb.cif.schema.core.Cell;
94 import org.rcsb.cif.schema.StandardSchemata;
95
96 import static ffx.crystal.SpaceGroupConversions.hrConversion;
97 import static ffx.numerics.math.DoubleMath.dihedralAngle;
98 import static ffx.numerics.math.DoubleMath.dist;
99 import static ffx.potential.bonded.BondedUtils.intxyz;
100
101 import static java.lang.String.format;
102
103 import static org.apache.commons.io.FilenameUtils.getExtension;
104 import static org.apache.commons.io.FilenameUtils.getFullPath;
105 import static org.apache.commons.io.FilenameUtils.getName;
106 import static org.apache.commons.io.FilenameUtils.removeExtension;
107 import static org.apache.commons.math3.util.FastMath.sqrt;
108 import static org.openscience.cdk.tools.periodictable.PeriodicTable.getCovalentRadius;
109 import static org.openscience.cdk.tools.periodictable.PeriodicTable.getSymbol;
110
111
112
113
114
115
116
117 public class CIFFilter extends SystemFilter {
118
119
120
121 private static final Logger logger = Logger.getLogger(CIFFilter.class.getName());
122
123
124
125 private double bondTolerance = 0.2;
126
127
128
129 private final BufferedReader bufferedReader = null;
130
131
132
133 private CifCoreFile cifFile;
134
135
136
137 private final List<String> createdFiles = new ArrayList<>();
138
139
140
141 private int snapShot;
142
143
144
145 private String sgName = null;
146
147
148
149 private int sgNum = -1;
150
151
152
153 private Crystal crystal;
154
155
156
157 private boolean fixLattice = false;
158
159
160
161 private boolean usePDB = false;
162
163
164
165 private int zPrime = -1;
166
167
168
169 public static final double MAX_COVALENT_RADIUS = 2.0;
170
171
172
173
174 public static final double MIN_BOND_DISTANCE = 0.5;
175
176
177
178
179
180
181
182
183
184 public CIFFilter(File file, MolecularAssembly molecularAssembly, ForceField forceField,
185 CompositeConfiguration properties, boolean saveCIF) {
186 super(file, molecularAssembly, forceField, properties);
187 this.fileType = Utilities.FileType.CIF;
188
189 if (!saveCIF) {
190 String dir = getFullPath(molecularAssembly.getFile().getAbsolutePath()) + File.separator;
191 String cifName = removeExtension(file.getName()) + ".cif";
192 Path path = Paths.get(dir + cifName);
193 try {
194 cifFile = CifIO.readFromPath(path).as(StandardSchemata.CIF_CORE);
195 } catch (Exception ex) {
196 logger.info(" Failed to create CIF file object.\n " + ex);
197 }
198 String ext = getExtension(file.getName());
199 currentFile = new File(removeExtension(currentFile.getAbsolutePath()) + ext);
200 if(ext.equalsIgnoreCase("pdb")){
201 usePDB = true;
202 }
203 }
204 }
205
206
207
208
209
210
211
212
213
214 public CIFFilter(File file, List<MolecularAssembly> molecularAssemblies, ForceField forceField,
215 CompositeConfiguration properties, boolean saveCIF) {
216 super(file, molecularAssemblies, forceField, properties);
217 this.fileType = Utilities.FileType.CIF;
218
219 if (!saveCIF) {
220 String dir =
221 getFullPath(molecularAssemblies.get(0).getFile().getAbsolutePath()) + File.separator;
222 String cifName = removeExtension(file.getName()) + ".cif";
223 Path path = Paths.get(dir + cifName);
224 try {
225 cifFile = CifIO.readFromPath(path).as(StandardSchemata.CIF_CORE);
226 } catch (Exception ex) {
227 logger.info(" Failed to create CIF file object.\n" + ex);
228 }
229 currentFile = new File(removeExtension(currentFile.getAbsolutePath()) + ".xyz");
230 }
231 }
232
233
234
235
236
237
238
239
240
241 public CIFFilter(List<File> files, MolecularAssembly molecularAssembly, ForceField forceField,
242 CompositeConfiguration properties, boolean saveCIF) {
243 super(files, molecularAssembly, forceField, properties);
244 this.fileType = Utilities.FileType.CIF;
245
246 if (!saveCIF) {
247 String dir = getFullPath(molecularAssembly.getFile().getAbsolutePath()) + File.separator;
248 String cifName = removeExtension(files.get(0).getName()) + ".cif";
249 Path path = Paths.get(dir + cifName);
250 try {
251 cifFile = CifIO.readFromPath(path).as(StandardSchemata.CIF_CORE);
252 } catch (Exception ex) {
253 logger.info(" Failed to create CIF file object.\n" + ex);
254 }
255 currentFile = new File(removeExtension(currentFile.getAbsolutePath()) + ".xyz");
256 }
257 }
258
259
260
261
262
263
264 public static int bondAtoms(Atom[] atoms, double bondTolerance) {
265 int bondCount = 0;
266 int nAtoms = atoms.length;
267 for (int i = 0; i < nAtoms; i++) {
268 Atom atom1 = atoms[i];
269 String atomIelement = getAtomElement(atom1);
270 double radiusI =
271 (getCovalentRadius(atomIelement) == null) ? 0 : getCovalentRadius(atomIelement);
272 if (radiusI == 0) {
273 logger.warning(format(" Atom %d (%s) element (%s) not determined.", i + 1, atom1.getName(),
274 atomIelement));
275 return -1;
276 }
277 double[] xyz = {atom1.getX(), atom1.getY(), atom1.getZ()};
278 for (int j = i + 1; j < nAtoms; j++) {
279 Atom atom2 = atoms[j];
280 String atomJelement = getAtomElement(atom2);
281 double radiusJ =
282 (getCovalentRadius(atomJelement) == null) ? 0 : getCovalentRadius(atomJelement);
283 if (radiusJ == 0) {
284 logger.warning(format(" Atom %d element (%s) not determined.", j + 1, atomJelement));
285 return -1;
286 }
287 double[] xyz2 = {atom2.getX(), atom2.getY(), atom2.getZ()};
288 double length = dist(xyz, xyz2);
289 double bondLength = radiusI + radiusJ + bondTolerance;
290 if (length < bondLength) {
291 bondCount++;
292 Bond bond = new Bond(atom1, atom2);
293 atom1.setBond(bond);
294 atom2.setBond(bond);
295 logger.finest(format(
296 " Bonded atom %d (%s) with atom %d (%s): bond length (%4.4f Å) < tolerance (%4.4f Å)",
297 i + 1, atomIelement, j + 1, atomJelement, length, bondLength));
298 }
299 }
300 }
301 return bondCount;
302 }
303
304
305 @Override
306 public void closeReader() {
307 if (bufferedReader != null) {
308 try {
309 bufferedReader.close();
310 } catch (IOException ex) {
311 logger.warning(format(" Exception in closing CIF filter: %s", ex));
312 }
313 }
314 }
315
316
317
318
319
320
321
322 public static void collectAtoms(Atom seed, ArrayList<Atom> atoms) {
323 logger.finest(format(" Atom: %s", seed.getName()));
324 atoms.add(seed);
325 for (Bond b : seed.getBonds()) {
326 Atom nextAtom = b.get1_2(seed);
327 if (!atoms.contains(nextAtom)) {
328 collectAtoms(nextAtom, atoms);
329 }
330 }
331 }
332
333
334
335
336
337
338
339
340 public int findMaxLessIndex(ArrayList<ArrayList<Atom>> xyzAtomLists, int currentList) {
341
342 int lessIndex = 0;
343 int minIndex = Integer.MAX_VALUE;
344 for (Atom atom : xyzAtomLists.get(currentList)) {
345 int temp = atom.getIndex();
346 if (temp < minIndex) {
347 minIndex = temp;
348 }
349 }
350 int xyzSize = xyzAtomLists.size();
351 for (int i = 0; i < xyzSize; i++) {
352 if (i != currentList) {
353 for (Atom atom : xyzAtomLists.get(i)) {
354 int temp = atom.getIndex();
355 if (temp < minIndex && temp > lessIndex) {
356 lessIndex = temp;
357 }
358 }
359 }
360 }
361 return lessIndex;
362 }
363
364
365
366
367
368
369
370 public static String getAtomElement(Atom atom) {
371 return getAtomElement(atom.getName());
372 }
373
374
375
376
377
378
379
380 private static String getAtomElement(String name) {
381 String value = "";
382 try {
383 value = name.replaceAll("[()]", "").replaceAll("_", "").replaceAll("-", "")
384 .replaceAll(" +", "").split("[0-9]")[0];
385 } catch (Exception e) {
386 logger.severe(" Error extracting atom element. Please ensure the CIF is formatted correctly.\n" + e);
387 }
388 return value;
389 }
390
391
392
393
394
395
396 public String[] getCreatedFileNames() {
397 String[] files = new String[createdFiles.size()];
398 return createdFiles.toArray(files);
399 }
400
401
402
403
404
405
406 private Atom[] parseBlock(CifCoreBlock block){
407
408 logger.info("\n Block ID: " + block.getBlockHeader());
409 Chemical chemical = block.getChemical();
410 Column<String[]> nameCommon = chemical.getNameCommon();
411 Column<String[]> nameSystematic = chemical.getNameSystematic();
412 int rowCount = nameCommon.getRowCount();
413 if (rowCount > 1) {
414 logger.info(" Chemical components");
415 for (int i = 0; i < rowCount; i++) {
416 logger.info(format(" %s", nameCommon.getColumnName()));
417 }
418 } else if (rowCount > 0) {
419 logger.info(format(" Chemical component: %s", nameCommon.getColumnName()));
420 }
421
422
423 Symmetry symmetry = block.getSymmetry();
424 if (sgNum == -1 && sgName == null || sgName.isEmpty()) {
425 if (symmetry.getIntTablesNumber().getRowCount() > 0) {
426 sgNum = symmetry.getIntTablesNumber().get(0);
427 logger.info(format(" CIF International Tables Number: %d", sgNum));
428 }
429 if (symmetry.getSpaceGroupNameH_M().getRowCount() > 0) {
430 sgName = symmetry.getSpaceGroupNameH_M().get(0);
431 logger.info(format(" CIF Hermann–Mauguin Space Group: %s", sgName));
432 } else if (block.getSpaceGroup().getNameH_mFull().getRowCount() > 0) {
433 sgName = block.getSpaceGroup().getNameH_mFull().get(0);
434 logger.info(format(" CIF Hermann–Mauguin Space Group: %s", sgName));
435 } else if (block.getSpaceGroup().getNameH_mAlt().getRowCount() > 0) {
436 sgName = block.getSpaceGroup().getNameH_mAlt().get(0);
437 logger.info(format(" CIF Hermann–Mauguin Space Group: %s", sgName));
438 }
439 } else {
440
441 if (sgNum != -1) {
442 logger.info(format(" Command line International Tables Number: %d", sgNum));
443 } else {
444 logger.info(format(" Command line space group name: %s", sgName));
445 }
446 }
447
448
449 SpaceGroup sg = null;
450 if (sgName != null) {
451 sg = SpaceGroupDefinitions.spaceGroupFactory(sgName);
452 }
453 if (sg == null) {
454 logger.finer(" Space group name not found. Attempting to use space group number.");
455 sg = SpaceGroupDefinitions.spaceGroupFactory(sgNum);
456 }
457
458
459 if (sg == null) {
460 logger.warning(" The space group could not be determined from the CIF file (using P1).");
461 sg = SpaceGroupDefinitions.spaceGroupFactory(1);
462 }
463
464
465 Cell cell = block.getCell();
466 if(cell.isDefined()) {
467 double a = cell.getLengthA().get(0);
468 double b = cell.getLengthB().get(0);
469 double c = cell.getLengthC().get(0);
470 double alpha = cell.getAngleAlpha().get(0);
471 double beta = cell.getAngleBeta().get(0);
472 double gamma = cell.getAngleGamma().get(0);
473 assert sg != null;
474 LatticeSystem latticeSystem = sg.latticeSystem;
475 double[] latticeParameters = {a, b, c, alpha, beta, gamma};
476 if (latticeSystem.validParameters(a, b, c, alpha, beta, gamma)) {
477 crystal = new Crystal(a, b, c, alpha, beta, gamma, sg.pdbName);
478 } else {
479
480 if (fixLattice) {
481 logger.info(
482 " Attempting to patch disagreement between lattice system and lattice parameters.");
483 boolean fixed = false;
484
485 if (latticeSystem == LatticeSystem.HEXAGONAL_LATTICE
486 && LatticeSystem.RHOMBOHEDRAL_LATTICE.validParameters(a, b, c, alpha, beta, gamma)) {
487 crystal = hrConversion(a, b, c, alpha, beta, gamma, sg);
488 latticeSystem = crystal.spaceGroup.latticeSystem;
489 if (latticeSystem.validParameters(crystal.a, crystal.b, crystal.c, crystal.alpha,
490 crystal.beta, crystal.gamma)) {
491 fixed = true;
492 }
493
494 } else if (latticeSystem == LatticeSystem.RHOMBOHEDRAL_LATTICE
495 && LatticeSystem.HEXAGONAL_LATTICE.validParameters(a, b, c, alpha, beta, gamma)) {
496 crystal = hrConversion(a, b, c, alpha, beta, gamma, sg);
497 latticeSystem = crystal.spaceGroup.latticeSystem;
498 if (latticeSystem.validParameters(crystal.a, crystal.b, crystal.c, crystal.alpha,
499 crystal.beta, crystal.gamma)) {
500 fixed = true;
501 }
502 }
503 if (!fixed) {
504 double[] newLatticeParameters = latticeSystem.fixParameters(a, b, c, alpha, beta, gamma);
505 if (newLatticeParameters == latticeParameters) {
506 logger.warning(" Conversion Failed: The proposed lattice parameters for " + sg.pdbName
507 + " do not satisfy the " + latticeSystem + ".");
508 return null;
509 } else {
510 a = newLatticeParameters[0];
511 b = newLatticeParameters[1];
512 c = newLatticeParameters[2];
513 alpha = newLatticeParameters[3];
514 beta = newLatticeParameters[4];
515 gamma = newLatticeParameters[5];
516 crystal = new Crystal(a, b, c, alpha, beta, gamma, sg.pdbName);
517 }
518 }
519 } else {
520 logger.warning(" Conversion Failed: The proposed lattice parameters for " + sg.pdbName
521 + " do not satisfy the " + latticeSystem + ".");
522 logger.info(" Use \"--fixLattice\" or \"--fl\" flag to attempt to fix automatically.");
523 return null;
524 }
525 }
526 activeMolecularAssembly.setName(block.getBlockHeader());
527 logger.info(" New Crystal: " + crystal);
528 activeMolecularAssembly.setCrystal(crystal);
529 }
530
531 AtomSite atomSite = block.getAtomSite();
532 Column<String[]> label = atomSite.getLabel();
533 Column<String[]> typeSymbol = atomSite.getTypeSymbol();
534 FloatColumn fractX = atomSite.getFractX();
535 FloatColumn fractY = atomSite.getFractY();
536 FloatColumn fractZ = atomSite.getFractZ();
537
538 FloatColumn cartX = atomSite.getCartnX();
539 FloatColumn cartY = atomSite.getCartnY();
540 FloatColumn cartZ = atomSite.getCartnZ();
541
542 int nAtoms = label.getRowCount();
543 if (nAtoms < 1) {
544 logger.warning(" CIF file did not contain coordinates.");
545 return null;
546 }
547 if (logger.isLoggable(Level.FINE)) {
548 logger.fine(format("\n Number of Atoms in CIF: %d", nAtoms));
549 }
550 Atom[] atoms = new Atom[nAtoms];
551
552
553 String resName = "CIF";
554 double bfactor = 1.0;
555 char altLoc = ' ';
556 char chain = 'A';
557
558 String[] symbols = new String[nAtoms];
559
560
561 for (int i = 0; i < nAtoms; i++) {
562 double occupancy = 1.0;
563 if(atomSite.getOccupancy().isDefined()){
564 occupancy = atomSite.getOccupancy().get(i);
565 }
566
567 if (typeSymbol.getRowCount() > 0) {
568 symbols[i] = typeSymbol.getStringData(i);
569 } else {
570 symbols[i] = getAtomElement(label.getStringData(i));
571 }
572 double x = (fractX.isDefined()) ? fractX.get(i) : cartX.get(i);
573 double y = (fractY.isDefined()) ? fractY.get(i) : cartY.get(i);
574 double z = (fractZ.isDefined()) ? fractZ.get(i) : cartZ.get(i);
575 double[] xyz = {x, y, z};
576 if (fractX.isDefined() && crystal != null) {
577 crystal.toCartesianCoordinates(xyz, xyz);
578 }
579 atoms[i] = new Atom(i + 1, label.getStringData(i), altLoc, xyz, resName, i, chain, occupancy,
580 bfactor, symbols[i]);
581 atoms[i].setHetero(true);
582 if (logger.isLoggable(Level.FINE)) {
583 logger.fine(format(
584 " Atom (%2d) Name: " + atoms[i].getName() + " Label: " + label.getStringData(i)
585 + " Symbol: " + symbols[i], i));
586 }
587 }
588 return atoms;
589 }
590
591
592
593
594
595
596 @Override
597 public boolean readFile() {
598
599 System.setProperty("mpoleterm", "false");
600 Atom[] inputAtoms = activeMolecularAssembly.getAtomArray();
601 int nInputAtoms = inputAtoms.length;
602
603 int numHydrogen = 0;
604 for (Atom atom : inputAtoms) {
605 if (atom.isHydrogen()) {
606 numHydrogen++;
607 }
608 }
609
610 List<MSNode> entitiesInput = activeMolecularAssembly.getNodeList();
611 int numEntitiesInput = entitiesInput.size();
612 if (logger.isLoggable(Level.FINE)) {
613 logger.fine(format(" Number of entities in input: %d", numEntitiesInput));
614 for (MSNode entity : entitiesInput) {
615 logger.fine(format(" Entity: " + entity.getName()));
616 int size = entity.getAtomList().size();
617 logger.fine(format(" Entity Size: %3d", size));
618 if (size > 0) {
619 logger.fine(format(" Entity First Atom: " + entity.getAtomList().get(0).toString()));
620 } else {
621 logger.warning(" Entity did not contain atoms.");
622 }
623 }
624 }
625
626 for (CifCoreBlock block : cifFile.getBlocks()) {
627 Atom[] atoms = parseBlock(block);
628 logger.info(crystal.toString());
629 if(atoms == null){
630 return false;
631 }
632 if(atoms.length == 0){
633 logger.warning(" Atoms could not be obtained from CIF.");
634 return false;
635 }
636 int nAtoms = atoms.length;
637
638
639 MolecularAssembly outputAssembly = new MolecularAssembly(block.getBlockHeader());
640 int zPrime;
641 if (this.zPrime > 0) {
642 zPrime = this.zPrime;
643 } else if (nAtoms % nInputAtoms == 0) {
644 zPrime = nAtoms / nInputAtoms;
645 } else if (nAtoms % (nInputAtoms - numHydrogen) == 0) {
646 zPrime = nAtoms / (nInputAtoms - numHydrogen);
647 } else {
648 zPrime = 1;
649 }
650
651 if (zPrime > 1) {
652 logger.info(format(" Detected more than one copy in asymmetric unit of CIF file (Z'=%d)."
653 + " -- attempting to separate.", zPrime));
654 }
655 ArrayList<ArrayList<Atom>> xyzatoms = new ArrayList<>();
656 int atomIndex = 1;
657 int shiftIndex = 0;
658 int[] moleculeNumbers = activeMolecularAssembly.getMoleculeNumbers();
659 if(logger.isLoggable(Level.FINER)) {
660 for (int i = 0; i < moleculeNumbers.length; i++) {
661 logger.finer(format(" Molecule Number (atom %2d): %2d", i, moleculeNumbers[i]));
662 }
663 }
664
665 for (int i = 0; i < numEntitiesInput; i++) {
666 MSNode mol = entitiesInput.get(i);
667 logger.info(format(" Size of Entity: %2d", mol.getAtomList().size()));
668 xyzatoms.add((ArrayList<Atom>) mol.getAtomList());
669
670 AtomContainer xyzCDKAtoms = new AtomContainer();
671
672 int numMolHydrogen = 0;
673 int atomCount = 0;
674 for (Atom atom : xyzatoms.get(i)) {
675 if (atom.isHydrogen()) {
676 numMolHydrogen++;
677 }
678 String atomName = getSymbol(atom.getAtomType().atomicNumber);
679 xyzCDKAtoms.addAtom(
680 new org.openscience.cdk.Atom(atomName, new Point3d(atom.getXYZ(null))));
681 atomCount++;
682 }
683 logger.info(format(" Number of atoms in XYZ CDK: %2d", atomCount));
684 int numInputMolAtoms = xyzatoms.get(i).size();
685 if (logger.isLoggable(Level.FINE)) {
686 logger.fine(format(" Current entity number of atoms: %d (%d + %dH)", numInputMolAtoms,
687 numInputMolAtoms - numMolHydrogen, numMolHydrogen));
688 }
689
690
691 List<Bond> bonds = mol.getBondList();
692 IBond.Order order = IBond.Order.SINGLE;
693 int xyzBonds = bonds.size();
694 if (xyzBonds == 0) {
695 logger.warning(" No bonds detected in input structure. Please check input.\n " +
696 "If correct, separate non-bonded entities into multiple CIFs.");
697 }
698 for (Bond xyzBond : bonds) {
699 int atom0Index = xyzBond.getAtom(0).getXyzIndex();
700 int atom1Index = xyzBond.getAtom(1).getXyzIndex();
701 if (logger.isLoggable(Level.FINER)) {
702 logger.finer(
703 format(" Bonded atom 1: %d, Bonded atom 2: %d", atom0Index,
704 atom1Index));
705 }
706 logger.fine(format(" Atom 0 index: %2d Atom 1 Index: %2d lessIndex: %2d Value 0: %2d Value 1: %2d",
707 atom0Index, atom1Index, shiftIndex, atom0Index - shiftIndex - 1, atom1Index - shiftIndex - 1));
708 xyzCDKAtoms.addBond(atom0Index - shiftIndex - 1,
709 atom1Index - shiftIndex - 1, order);
710 }
711
712
713 AtomTypeFactory factory = AtomTypeFactory.getInstance(
714 "org/openscience/cdk/config/data/jmol_atomtypes.txt", xyzCDKAtoms.getBuilder());
715
716
717 for (IAtom atom : xyzCDKAtoms.atoms()) {
718 setAtomTypes(factory, atom);
719 try {
720 factory.configure(atom);
721 } catch (Exception ex) {
722 logger.info(" Failed to configure atoms from CIF.\n" + ex + "\n" + Arrays.toString(
723 ex.getStackTrace()));
724 }
725 }
726
727 ArrayList<ArrayList<Integer>> zindices = new ArrayList<>();
728 int counter = 0;
729
730 int cifBonds = bondAtoms(atoms, bondTolerance);
731 if (logger.isLoggable(Level.FINE)) {
732 logger.fine(
733 format(" Created %d bonds between CIF atoms (%d in input).", cifBonds, xyzBonds));
734 }
735 List<Atom> atomPool = new ArrayList<>(Arrays.asList(atoms));
736
737 try {
738 while (!atomPool.isEmpty()) {
739 ArrayList<Atom> molecule = new ArrayList<>();
740 collectAtoms(atomPool.get(0), molecule);
741 if (logger.isLoggable(Level.FINER)) {
742 logger.finer(format(" Molecule (%d) Size: %d", counter, molecule.size()));
743 }
744 ArrayList<Integer> indices = new ArrayList<>();
745 while (!molecule.isEmpty()) {
746 Atom atom = molecule.remove(0);
747 indices.add(atom.getIndex());
748 atomPool.remove(atom);
749 }
750
751 if (logger.isLoggable(Level.FINER)) {
752 logger.finer(format(
753 " Molecule %d: %d atoms are ready and %d remain (%d atoms in input, %d atoms in CIF). ",
754 counter + 1, indices.size(), atomPool.size(), nInputAtoms, nAtoms));
755 }
756 zindices.add(indices);
757 counter++;
758 }
759 } catch (Exception e) {
760 logger.info(e + "\n" + Arrays.toString(e.getStackTrace()));
761 logger.warning(" Failed to separate copies within the asymmetric unit.");
762 return false;
763 }
764 zPrime = zindices.size();
765
766 AtomContainer[] cifCDKAtomsArr = new AtomContainer[zPrime];
767 AtomContainer cifCDKAtoms = new AtomContainer();
768 for (int j = 0; j < zPrime; j++) {
769 if (zPrime > 1) {
770 logger.info(format("\n Attempting entity %d of %d", j + 1, zPrime));
771 }
772 ArrayList<Integer> currentList = zindices.get(j);
773 int cifMolAtoms = currentList.size();
774 if (logger.isLoggable(Level.FINE)) {
775 logger.fine(format(" CIF atoms in current: %d", cifMolAtoms));
776 }
777
778 if (cifMolAtoms % numInputMolAtoms == 0
779 || cifMolAtoms % (numInputMolAtoms - numMolHydrogen) == 0) {
780 cifCDKAtomsArr[j] = new AtomContainer();
781 for (Integer integer : currentList) {
782 cifCDKAtomsArr[j].addAtom(new org.openscience.cdk.Atom(atoms[integer-1].getSegID(),
783 new Point3d(atoms[integer - 1].getXYZ(null))));
784 }
785 AtomContainer nullCDKAtoms = new AtomContainer();
786
787 for (IAtom atom : cifCDKAtomsArr[j].atoms()) {
788 if (atom.toString() == null) {
789 nullCDKAtoms.addAtom(atom);
790 }
791 }
792 cifCDKAtomsArr[j].remove(nullCDKAtoms);
793
794
795 factory = AtomTypeFactory.getInstance(
796 "org/openscience/cdk/config/data/jmol_atomtypes.txt",
797 cifCDKAtomsArr[j].getBuilder());
798 for (IAtom atom : cifCDKAtomsArr[j].atoms()) {
799 setAtomTypes(factory, atom);
800 try {
801 factory.configure(atom);
802 } catch (Exception ex) {
803 logger.info(" Failed to configure CIF atoms.\n" + ex + "\n" + Arrays.toString(
804 ex.getStackTrace()));
805 }
806 }
807
808 RebondTool rebonder = new RebondTool(MAX_COVALENT_RADIUS, MIN_BOND_DISTANCE,
809 bondTolerance);
810 try {
811 rebonder.rebond(cifCDKAtomsArr[j]);
812 } catch (Exception ex) {
813 logger.info(
814 "Failed to rebond CIF atoms.\n" + ex + "\n" + Arrays.toString(ex.getStackTrace()));
815 }
816
817 int cifMolBonds = cifCDKAtomsArr[j].getBondCount();
818 if (logger.isLoggable(Level.FINE)) {
819 logger.fine(format(" Number of CIF bonds: %d (%d in input)", cifMolBonds, xyzBonds));
820 }
821
822
823 if (cifMolBonds != 0 && cifMolBonds % xyzBonds == 0) {
824 Pattern pattern = VentoFoggia.findIdentical(
825 xyzCDKAtoms, AtomMatcher.forElement(), BondMatcher.forAny());
826 int[] p = pattern.match(cifCDKAtomsArr[j]);
827 int pLength = p.length;
828 if (p != null && pLength == numInputMolAtoms) {
829
830 for (int k = 0; k < pLength; k++) {
831 if (logger.isLoggable(Level.FINEST)) {
832 logger.finest(
833 format(" %d input %s -> CIF %s", k, xyzCDKAtoms.getAtom(k).getSymbol(),
834 cifCDKAtomsArr[j].getAtom(p[k]).getSymbol()));
835 }
836 Point3d point3d = cifCDKAtomsArr[j].getAtom(p[k]).getPoint3d();
837 xyzatoms.get(i).get(k).setXYZ(new double[]{point3d.x, point3d.y, point3d.z});
838 }
839 } else {
840 if (logger.isLoggable(Level.FINE)) {
841 logger.fine(
842 format(" Atoms from CIF (%d) and input (%d) structures don't match.", p.length,
843 nAtoms));
844 }
845 continue;
846 }
847 } else if ((xyzBonds - numMolHydrogen) == 0
848 || cifMolBonds % ((xyzBonds - numMolHydrogen)) == 0) {
849
850 if (logger.isLoggable(Level.FINE)) {
851 logger.info(" CIF may contain implicit hydrogen -- attempting to patch.");
852 }
853
854 Pattern pattern = VentoFoggia.findSubstructure(
855 cifCDKAtomsArr[j], AtomMatcher.forElement(), BondMatcher.forAny());
856 int[] p = pattern.match(xyzCDKAtoms);
857 int pLength = p.length;
858 if (p != null && pLength == numInputMolAtoms - numMolHydrogen) {
859
860 for (int k = 0; k < pLength; k++) {
861 Point3d point3d = cifCDKAtomsArr[j].getAtom(k).getPoint3d();
862 xyzatoms.get(i).get(p[k]).setXYZ(new double[]{point3d.x, point3d.y, point3d.z});
863 }
864
865 Atom lastKnownAtom1 = null;
866 int knownCount = 0;
867 for (Atom hydrogen : xyzatoms.get(i)) {
868 if (hydrogen.isHydrogen()) {
869 Bond bond0 = hydrogen.getBonds().get(0);
870 Atom atom1 = bond0.get1_2(hydrogen);
871 double angle0_2 = 0;
872 List<Angle> anglesList = hydrogen.getAngles();
873 Atom atom2 = null;
874 switch (anglesList.size()) {
875 case 0 ->
876
877
878 hydrogen.moveTo(new double[]{atom1.getX() - 0.6, atom1.getY() - 0.6,
879 atom1.getZ() - 0.6});
880 case 1 -> {
881
882
883 for (Angle angle : anglesList) {
884 atom2 = angle.get1_3(hydrogen);
885 if (atom2 != null) {
886 angle0_2 = angle.angleType.angle[0];
887 }
888 if (angle0_2 != 0) {
889
890 break;
891 }
892 }
893 assert atom2 != null;
894 List<Bond> bonds2 = atom2.getBonds();
895 Atom atom3 =
896 (bonds2.size() > 1 && atom1 == bonds2.get(0).get1_2(atom2)) ? bonds2.get(
897 1).get1_2(atom2) : bonds2.get(0).get1_2(atom2);
898 double diAng = dihedralAngle(hydrogen.getXYZ(null), atom1.getXYZ(null),
899 atom2.getXYZ(null), atom3.getXYZ(null));
900 if (atom1 != atom3) {
901 intxyz(hydrogen, atom1, bond0.bondType.distance, atom2, angle0_2, atom3,
902 Math.toDegrees(diAng), 0);
903 } else {
904
905
906 double[] coord = new double[]{atom2.getX(), atom2.getY(), atom3.getZ()};
907 double mag = sqrt(
908 coord[0] * coord[0] + coord[1] * coord[1] + coord[2] * coord[2]);
909 coord[0] /= mag;
910 coord[1] /= mag;
911 coord[2] /= mag;
912 hydrogen.moveTo(atom1.getX() - coord[0], atom1.getY() - coord[1],
913 atom1.getZ() - coord[2]);
914 }
915 }
916 default -> {
917
918 Atom atom2B = null;
919 double angle0_2B = 0;
920 Atom proposedAtom;
921 double proposedAngle = 0;
922 int chiral = 1;
923 for (Angle angle : anglesList) {
924 proposedAtom = angle.get1_3(hydrogen);
925 if (proposedAtom != null && !proposedAtom.isHydrogen()) {
926 proposedAngle = angle.angleType.angle[0];
927 }
928 if (proposedAngle != 0) {
929
930 if (angle0_2 != 0) {
931 atom2B = proposedAtom;
932 angle0_2B = proposedAngle;
933 } else {
934 atom2 = proposedAtom;
935 angle0_2 = proposedAngle;
936 }
937 proposedAngle = 0.0;
938 }
939 if (angle0_2 != 0 && angle0_2B != 0) {
940 break;
941 }
942 }
943 if (lastKnownAtom1 == null || lastKnownAtom1 != atom1) {
944 lastKnownAtom1 = atom1;
945 knownCount = 0;
946 } else {
947 knownCount++;
948 }
949 if (angle0_2B == 0.0) {
950
951 chiral = 0;
952 assert atom2 != null;
953 List<Bond> bonds2 = atom2.getBonds();
954 atom2B =
955 (atom1 == bonds2.get(0).get1_2(atom2)) ? bonds2.get(1).get1_2(atom2)
956 : bonds2.get(0).get1_2(atom2);
957
958 angle0_2B = 180.0 - 120.0 * knownCount;
959 } else if (anglesList.size() == 2) {
960
961 chiral = 3;
962 } else if (knownCount == 1) {
963
964 chiral = -1;
965 }
966
967 intxyz(hydrogen, atom1, bond0.bondType.distance, atom2, angle0_2, atom2B,
968 angle0_2B, chiral);
969 }
970 }
971 }
972 }
973 } else {
974 if (logger.isLoggable(Level.FINE)) {
975 logger.fine(" Could not match heavy atoms between CIF and input.");
976 }
977 if (p != null && logger.isLoggable(Level.FINE)) {
978 logger.fine(
979 format(" Matched %d atoms out of %d in CIF (%d in input)", pLength, nAtoms,
980 nInputAtoms - numMolHydrogen));
981 }
982 continue;
983 }
984 } else {
985 logger.info(format(" CIF (%d) and input ([%d+%dH=]%d) have a different number of bonds.",
986 cifMolBonds, xyzBonds - numMolHydrogen, numMolHydrogen, xyzBonds));
987 continue;
988 }
989 cifCDKAtoms.add(cifCDKAtomsArr[j]);
990 MSGroup molecule;
991 if (mol instanceof Polymer) {
992 molecule = new Polymer(((Polymer) mol).getChainID(), mol.getName(), true);
993 } else {
994 molecule = new Molecule(mol.getName());
995 }
996 List<Atom> atomList = new ArrayList<>();
997 for (Atom atom : xyzatoms.get(i)) {
998 if(logger.isLoggable(Level.FINER)) {
999 logger.finer(format(" Atom Residue: %s %2d", atom.getResidueName(), atom.getResidueNumber()));
1000 }
1001 Atom molAtom;
1002 if (molecule instanceof Polymer) {
1003 molAtom = new Atom(atomIndex++, atom.getName(), atom.getAltLoc(), atom.getXYZ(null),
1004 atom.getResidueName(), atom.getResidueNumber(), atom.getChainID(), atom.getOccupancy(),
1005 atom.getTempFactor(), atom.getSegID());
1006 molAtom.setAtomType(atom.getAtomType());
1007 } else {
1008 molAtom = new Atom(atomIndex++, atom.getName(), atom.getAtomType(), atom.getXYZ(null));
1009 if(atom.getResidueName() != null){
1010 molAtom.setResName(atom.getResidueName());
1011 }
1012 }
1013 atomList.add(molAtom);
1014 }
1015 List<Bond> bondList = mol.getBondList();
1016 for (Bond bond : bondList) {
1017 Atom a1 = bond.getAtom(0);
1018 Atom a2 = bond.getAtom(1);
1019 if(logger.isLoggable(Level.FINE)) {
1020 logger.fine(format(" Bonded atom 1: %d, Bonded atom 2: %d", a1.getXyzIndex(),
1021 a2.getXyzIndex()));
1022 }
1023 Atom newA1 = atomList.get(a1.getIndex() - shiftIndex - 1);
1024 Atom newA2 = atomList.get(a2.getIndex() - shiftIndex - 1);
1025 Bond bond2 = new Bond(newA1, newA2);
1026 bond2.setBondType(bond.getBondType());
1027 }
1028 Residue res = null;
1029 for (Atom atom : atomList) {
1030 if(molecule instanceof Polymer){
1031 if(res == null){
1032 res = new Residue(atom.getResidueName(), atom.getResidueNumber(), ((Polymer) mol).getResidue(atom.getResidueNumber()).getResidueType());
1033 } else if(res.getResidueNumber() != atom.getResidueNumber()){
1034 if(logger.isLoggable(Level.FINER)) {
1035 logger.finer(" Added Residue " + res.getResidueNumber() + " " + res.getName());
1036 }
1037 molecule.addMSNode(res);
1038 res = new Residue(atom.getResidueName(), atom.getResidueNumber(), ((Polymer) mol).getResidue(atom.getResidueNumber()).getResidueType());
1039 }
1040 res.addMSNode(atom);
1041 }else {
1042 molecule.addMSNode(atom);
1043 }
1044 }
1045 if(molecule instanceof Polymer && res != null){
1046 if(logger.isLoggable(Level.FINER)) {
1047 logger.finer(" Added Final Residue " + res.getResidueNumber() + " " + res.getName());
1048 }
1049 molecule.addMSNode(res);
1050 }
1051 outputAssembly.addMSNode(molecule);
1052 } else {
1053 if (logger.isLoggable(Level.INFO)) {
1054 logger.info(
1055 format(" Number of atoms in CIF (%d) molecule do not match input (%d + %dH = %d).",
1056 cifMolAtoms, nInputAtoms - numMolHydrogen, numMolHydrogen, nInputAtoms));
1057 }
1058 }
1059 }
1060 shiftIndex += mol.getAtomList().size();
1061 }
1062
1063 if (logger.isLoggable(Level.FINE)) {
1064 logger.fine(format("\n Output Assembly Atoms: %d", outputAssembly.getAtomList().size()));
1065 }
1066 outputAssembly.setPotential(activeMolecularAssembly.getPotentialEnergy());
1067 outputAssembly.setCrystal(crystal);
1068 outputAssembly.setForceField(activeMolecularAssembly.getForceField());
1069 outputAssembly.setFile(activeMolecularAssembly.getFile());
1070 outputAssembly.setName(activeMolecularAssembly.getName());
1071 setMolecularSystem(outputAssembly);
1072
1073 if (outputAssembly.getAtomList().isEmpty()) {
1074 logger.info(" Atom types could not be matched. File could not be written.");
1075 } else if (!writeOutputFile()) {
1076 logger.info(" Input File could not be written.");
1077 }
1078
1079 outputAssembly.destroy();
1080 sgNum = -1;
1081 sgName = null;
1082 }
1083 return true;
1084 }
1085
1086
1087
1088
1089
1090 @Override
1091 public boolean readNext() {
1092 return readNext(false, true);
1093 }
1094
1095
1096
1097
1098
1099 @Override
1100 public boolean readNext(boolean resetPosition) {
1101 return readNext(resetPosition, true);
1102 }
1103
1104
1105
1106
1107
1108 @Override
1109 public boolean readNext(boolean resetPosition, boolean print) {
1110 return readNext(resetPosition, print, true);
1111 }
1112
1113
1114
1115
1116
1117 @Override
1118 public boolean readNext(boolean resetPosition, boolean print, boolean parse) {
1119 List<CifCoreBlock> blocks = cifFile.getBlocks();
1120 CifCoreBlock currentBlock;
1121 if (!parse) {
1122 snapShot++;
1123 if (print) {
1124 logger.info(format(" Skipped Block: %d", snapShot));
1125 }
1126 return true;
1127 } else if (resetPosition) {
1128 currentBlock = blocks.get(0);
1129 snapShot = 0;
1130 } else if (++snapShot < blocks.size()) {
1131 currentBlock = blocks.get(snapShot);
1132 } else {
1133 if (print) {
1134 logger.info(" Reached end of available blocks in CIF file.");
1135 }
1136 return false;
1137 }
1138 if (print) {
1139 logger.info(" Current Block: " + currentBlock.getBlockHeader());
1140 }
1141 return true;
1142 }
1143
1144
1145
1146
1147
1148
1149
1150 public static void setAtomTypes(AtomTypeFactory factory, IAtom atom) {
1151 String atomTypeName = atom.getAtomTypeName();
1152 if (atomTypeName == null || atomTypeName.isEmpty()) {
1153 IAtomType[] types = factory.getAtomTypes(atom.getSymbol());
1154 if (types.length > 0) {
1155 IAtomType atomType = types[0];
1156 atom.setAtomTypeName(atomType.getAtomTypeName());
1157 } else {
1158 logger.info(" No atom type found for " + atom);
1159 }
1160 }
1161 }
1162
1163
1164
1165
1166
1167
1168 public void setBondTolerance(double bondTolerance) {
1169 this.bondTolerance = bondTolerance;
1170 }
1171
1172
1173
1174
1175
1176
1177 public void setFixLattice(boolean fixLattice) {
1178 this.fixLattice = fixLattice;
1179 }
1180
1181
1182
1183
1184
1185
1186 public void setSgName(String sgName) {
1187 this.sgName = sgName;
1188 }
1189
1190
1191
1192
1193
1194
1195 public void setSgNum(int sgNum) {
1196 this.sgNum = sgNum;
1197 }
1198
1199
1200
1201
1202
1203
1204 public void setZprime(int zPrime) {
1205 this.zPrime = zPrime;
1206 }
1207
1208
1209
1210
1211
1212
1213 public boolean writeFiles() {
1214 for (File file : files) {
1215 File saveFile = new File(removeExtension(file.getAbsolutePath()) + ".cif");
1216 if (!writeFile(saveFile, true, null)) {
1217 return false;
1218 }
1219 }
1220 return true;
1221 }
1222
1223
1224
1225
1226
1227
1228 @Override
1229 public boolean writeFile(File saveFile, boolean append) {
1230 return writeFile(saveFile, append, null);
1231 }
1232
1233
1234
1235
1236
1237
1238 @Override
1239 public boolean writeFile(File saveFile, boolean append, String[] extraLines) {
1240 try {
1241 if (!append) {
1242 SystemFilter.setVersioning(Versioning.PREFIX);
1243 saveFile = version(saveFile);
1244 }
1245 BufferedWriter bw = new BufferedWriter(new FileWriter(saveFile, append));
1246 if (extraLines != null) {
1247 for (String line : extraLines) {
1248 line = line.replaceAll("\n", " ");
1249 bw.write("### " + line + "\n");
1250 }
1251 }
1252 bw.write("\ndata_" + activeMolecularAssembly.getName());
1253 Crystal xtal = activeMolecularAssembly.getCrystal().getUnitCell();
1254 bw.write("\n_symmetry_cell_setting\t" + xtal.spaceGroup.latticeSystem.name().toLowerCase()
1255 .replaceAll("_lattice", ""));
1256 bw.write("\n_symmetry_space_group_name_H-M\t" + "'" + xtal.spaceGroup.shortName + "'");
1257 bw.write("\n_symmetry_Int_Tables_number\t" + xtal.spaceGroup.number);
1258 bw.write("\nloop_\n_symmetry_equiv_pos_site_id\n_symmetry_equiv_pos_as_xyz");
1259 int numSymOps = xtal.spaceGroup.getNumberOfSymOps();
1260 for (int i = 0; i < numSymOps; i++) {
1261 bw.write(format(
1262 "\n%d " + xtal.spaceGroup.getSymOp(i).toXYZString().toLowerCase().replaceAll(" +", ""),
1263 i));
1264 }
1265 bw.write(format("\n_cell_length_a\t%4.4f", xtal.a));
1266 bw.write(format("\n_cell_length_b\t%4.4f", xtal.b));
1267 bw.write(format("\n_cell_length_c\t%4.4f", xtal.c));
1268 bw.write(format("\n_cell_angle_alpha\t%4.4f", xtal.alpha));
1269 bw.write(format("\n_cell_angle_beta\t%4.4f", xtal.beta));
1270 bw.write(format("\n_cell_angle_gamma\t%4.4f", xtal.gamma));
1271 bw.write(format("\n_cell_volume\t%4.4f", xtal.volume));
1272 int numEntities = (zPrime < 1) ? activeMolecularAssembly.getMolecules().size() : zPrime;
1273 if (numEntities > 1) {
1274 if (zPrime < 1) {
1275 logger.info(format(" Molecules detected, guessing a Z' of %d. Set manually using --zp.",
1276 numEntities));
1277 }
1278 bw.write(format("\n_cell_formula_units_Z\t%3d", numEntities));
1279 }
1280 bw.write("\nloop_");
1281 bw.write("\n_atom_site_label");
1282 bw.write("\n_atom_site_type_symbol");
1283 bw.write("\n_atom_site_fract_x");
1284 bw.write("\n_atom_site_fract_y");
1285 bw.write("\n_atom_site_fract_z");
1286 Atom[] atoms = activeMolecularAssembly.getAtomArray();
1287 int count = 1;
1288 for (Atom atom : atoms) {
1289 String name = atom.getName();
1290 String symbol = getSymbol(atom.getAtomicNumber());
1291 if (Objects.equals(name, symbol)) {
1292 name += count++;
1293 }
1294 double[] xyzC = {atom.getX(), atom.getY(), atom.getZ()};
1295 double[] xyzF = new double[3];
1296 xtal.toFractionalCoordinates(xyzC, xyzF);
1297 bw.write(format("\n%-3s %2s %8.6f %8.6f %8.6f", name, symbol, xyzF[0], xyzF[1], xyzF[2]));
1298 }
1299 bw.write("\n#END\n");
1300 bw.close();
1301 logger.info(format("\n Wrote CIF file: %s \n", saveFile.getAbsolutePath()));
1302 } catch (Exception ex) {
1303 logger.info(format("\n Failed to write out CIF file: %s \n" + ex, saveFile.getAbsolutePath()));
1304 return false;
1305 }
1306 if (!createdFiles.contains(saveFile.getAbsolutePath())) {
1307 createdFiles.add(saveFile.getAbsolutePath());
1308 }
1309 return true;
1310 }
1311
1312
1313
1314
1315
1316
1317 private boolean writeOutputFile() {
1318 String dir = getFullPath(files.get(0).getAbsolutePath()) + File.separator;
1319 String fileName = removeExtension(getName(files.get(0).getAbsolutePath()));
1320 String spacegroup;
1321 if(activeMolecularAssembly.getCrystal() != null) {
1322 spacegroup = activeMolecularAssembly.getCrystal().getUnitCell().spaceGroup.shortName;
1323 }else{
1324 spacegroup = null;
1325 }
1326 List<MSNode> entities = activeMolecularAssembly.getAllBondedEntities();
1327
1328 File saveFile;
1329 if(usePDB){
1330
1331 if (entities.size() > 1) {
1332 fileName += "_z" + entities.size();
1333 }
1334 saveFile = new File(dir + fileName + ".pdb");
1335 } else if (cifFile.getBlocks().size() > 1) {
1336
1337 if (entities.size() > 1) {
1338 fileName += "_z" + entities.size();
1339 }
1340 fileName += "_" + spacegroup.replaceAll("\\/", "");
1341 saveFile = new File(dir + fileName + ".arc");
1342 } else {
1343
1344 saveFile = XYZFilter.version(new File(dir + fileName + ".xyz"));
1345 }
1346 ForceField ff = activeMolecularAssembly.getForceField();
1347 CompositeConfiguration properties = activeMolecularAssembly.getProperties();
1348 if(usePDB){
1349 PDBFilter pdbFilter = new PDBFilter(saveFile, activeMolecularAssembly, ff, properties);
1350 pdbFilter.writeFile(saveFile, true);
1351 }else {
1352 XYZFilter xyzFilter = new XYZFilter(saveFile, activeMolecularAssembly, ff, properties);
1353 xyzFilter.writeFile(saveFile, true);
1354 }
1355 logger.info("\n Saved output file: " + saveFile.getAbsolutePath());
1356 writePropertiesFile(dir, fileName, spacegroup, entities);
1357 if (!createdFiles.contains(saveFile.getAbsolutePath())) {
1358 createdFiles.add(saveFile.getAbsolutePath());
1359 }
1360 return true;
1361 }
1362
1363
1364
1365
1366
1367
1368 public boolean writeOutputFile(MolecularAssembly ma) {
1369 setMolecularSystem(ma);
1370 return writeOutputFile();
1371 }
1372
1373
1374
1375
1376
1377
1378
1379
1380 public void writePropertiesFile(String dir, String fileName, String spacegroup, List<MSNode> entities) {
1381
1382 File propertyFile = new File(dir + fileName + ".properties");
1383 if (!propertyFile.exists()) {
1384 try {
1385 FileWriter fw = new FileWriter(propertyFile, false);
1386 BufferedWriter bw = new BufferedWriter(fw);
1387 ForceField forceField = activeMolecularAssembly.getForceField();
1388 String parameters = forceField.getString("parameters", "none");
1389 if (parameters != null && !parameters.equalsIgnoreCase("none")) {
1390 bw.write(format("parameters %s\n", parameters));
1391 }
1392 String forceFieldProperty = forceField.getString("forcefield", "none");
1393 if (forceFieldProperty != null && !forceFieldProperty.equalsIgnoreCase("none")) {
1394 bw.write(format("forcefield %s\n", forceFieldProperty));
1395 }
1396 String patch = forceField.getString("patch", "none");
1397 if (patch != null && !patch.equalsIgnoreCase("none")) {
1398 bw.write(format("patch %s\n", patch));
1399 }
1400 if(spacegroup != null) {
1401 bw.write(format("spacegroup %s\n", spacegroup));
1402 }
1403 if (entities.size() > 1) {
1404 bw.write("intermolecular-softcore true\n");
1405 }
1406 logger.info("\n Saved properties file: " + propertyFile.getAbsolutePath() + "\n");
1407 bw.close();
1408 } catch (Exception ex) {
1409 logger.info("Failed to write files.\n" + ex);
1410 }
1411 } else {
1412 logger.info("\n Property file already exists: " + propertyFile.getAbsolutePath() + "\n");
1413 }
1414 }
1415 }