View Javadoc
1   //******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2025.
6   //
7   // This file is part of Force Field X.
8   //
9   // Force Field X is free software; you can redistribute it and/or modify it
10  // under the terms of the GNU General Public License version 3 as published by
11  // the Free Software Foundation.
12  //
13  // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16  // details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA 02111-1307 USA
21  //
22  // Linking this library statically or dynamically with other modules is making a
23  // combined work based on this library. Thus, the terms and conditions of the
24  // GNU General Public License cover the whole combination.
25  //
26  // As a special exception, the copyright holders of this library give you
27  // permission to link this library with independent modules to produce an
28  // executable, regardless of the license terms of these independent modules, and
29  // to copy and distribute the resulting executable under terms of your choice,
30  // provided that you also meet, for each linked independent module, the terms
31  // and conditions of the license of that module. An independent module is a
32  // module which is not derived from or based on this library. If you modify this
33  // library, you may extend this exception to your version of the library, but
34  // you are not obligated to do so. If you do not wish to do so, delete this
35  // exception statement from your version.
36  //
37  //******************************************************************************
38  package ffx.potential.commands;
39  
40  import ffx.potential.bonded.Atom;
41  import ffx.potential.bonded.Bond;
42  import ffx.potential.bonded.Molecule;
43  import ffx.potential.cli.PotentialCommand;
44  import ffx.potential.parameters.BioType;
45  import ffx.utilities.FFXBinding;
46  import picocli.CommandLine.Command;
47  import picocli.CommandLine.Option;
48  import picocli.CommandLine.Parameters;
49  
50  import java.io.File;
51  import java.util.ArrayList;
52  import java.util.List;
53  
54  import static org.apache.commons.io.FilenameUtils.getName;
55  import static org.apache.commons.io.FilenameUtils.removeExtension;
56  
57  /**
58   * Print out Biotype records for the atoms in an XYZ file.
59   *
60   * Usage:
61   *   ffxc Biotype <filename>
62   */
63  @Command(name = "Biotype", description = " Print out Biotype records for the atoms in an XYZ file.")
64  public class Biotype extends PotentialCommand {
65  
66    @Option(names = {"--name", "--moleculeName"}, paramLabel = "MOL", defaultValue = "MOL",
67        description = "The molecule name to use for the Biotype records.")
68    private String molName;
69  
70    @Option(names = {"-a", "--useAtomNames"}, paramLabel = "false", defaultValue = "false",
71        description = "Use the atom names in the XYZ file.")
72    private boolean useAtomNames;
73  
74    @Option(names = {"-c", "--writeCONECT"}, paramLabel = "false", defaultValue = "false",
75        description = "Write out CONECT records to append to the PDB file.")
76    private boolean writeCONNECT;
77  
78    @Option(names = {"-w", "--writePDB"}, paramLabel = "false", defaultValue = "false",
79        description = "Write out a PDB file with the updated atom and molecule names.")
80    private boolean writePDB;
81  
82    @Parameters(arity = "1", paramLabel = "files",
83        description = "An XYZ coordinate file.")
84    private String filename = null;
85  
86    /** List of created Biotype records. */
87    private List<BioType> bioTypes = null;
88  
89    public Biotype() {
90      super();
91    }
92  
93    public Biotype(FFXBinding binding) {
94      super(binding);
95    }
96  
97    public Biotype(String[] args) {
98      super(args);
99    }
100 
101   /** Get the list of created Biotype records. */
102   public List<BioType> getBioTypes() {
103     return bioTypes;
104   }
105 
106   @Override
107   public Biotype run() {
108     // Init the context and bind variables.
109     if (!init()) {
110       return this;
111     }
112 
113     // Load the MolecularAssembly.
114     activeAssembly = getActiveAssembly(filename);
115     if (activeAssembly == null) {
116       logger.info(helpString());
117       return this;
118     }
119 
120     // Set the filename.
121     filename = activeAssembly.getFile().getAbsolutePath();
122 
123     logger.info("\n Running Biotype on " + filename);
124 
125     Molecule[] molecules = activeAssembly.getMoleculeArray();
126     Atom[] atoms = activeAssembly.getAtomArray();
127 
128     if (writeCONNECT) {
129       for (Atom a : atoms) {
130         // PDB CONECT format columns reference (7-31 are bond target serials)
131         StringBuilder sb = new StringBuilder(String.format("CONECT%5s", Integer.toString(a.getXyzIndex())));
132         List<Bond> bonds = a.getBonds();
133         for (Bond b : bonds) {
134           sb.append(String.format("%5s", Integer.toString(b.get1_2(a).getXyzIndex())));
135         }
136         logger.info(sb.toString());
137       }
138     }
139 
140     if (molecules.length > 1) {
141       logger.info(" Biotype is intended for a system with one molecule.");
142       return this;
143     }
144 
145     // Update the molecule name.
146     molecules[0].setName(molName);
147 
148     // Create a List of biotype entries.
149     bioTypes = new ArrayList<>();
150 
151     // Update atom names if requested.
152     if (!useAtomNames) {
153       Atom.ElementSymbol[] symbols = Atom.ElementSymbol.values();
154       int[] elementCounts = new int[symbols.length + 1]; // atomic numbers start at 1
155       for (Atom atom : atoms) {
156         int element = atom.getAtomicNumber();
157         int n = elementCounts[element];
158         String name = symbols[element - 1].name().toUpperCase() + n;
159         atom.setName(name);
160         elementCounts[element] = n + 1;
161       }
162     }
163 
164     int index = 1;
165     for (Atom atom : atoms) {
166       // Update the residue/molecule name.
167       atom.setResName(molName);
168 
169       // Collect bond neighbor atom names.
170       List<Bond> bonds = atom.getBonds();
171       String[] bondString = null;
172       if (bonds != null) {
173         bondString = new String[bonds.size()];
174         int i = 0;
175         for (Bond bond : bonds) {
176           bondString[i++] = bond.get1_2(atom).getName();
177         }
178       }
179 
180       // Create a Biotype entry and log it.
181       BioType biotype = new BioType(index++, atom.getName(), molName,
182           atom.getAtomType().type, bondString);
183 
184       bioTypes.add(biotype);
185       logger.info(biotype.toString());
186     }
187 
188     // Optionally save a PDB with updated atom/molecule names.
189     String dirString = getBaseDirString(filename);
190     String name = getName(filename);
191     name = removeExtension(name);
192 
193     if (writePDB) {
194       File pdbFile = new File(dirString + name + ".pdb");
195       logger.info("\n Saving PDB file: " + pdbFile);
196       potentialFunctions.saveAsPDB(activeAssembly, pdbFile);
197     }
198 
199     // Return the bioTypes via the Binding for compatibility with callers.
200     binding.setVariable("bioTypes", bioTypes);
201 
202     return this;
203   }
204 }