1 |
duarte |
323 |
package tinker; |
2 |
duarte |
322 |
|
3 |
|
|
import java.io.BufferedReader; |
4 |
|
|
import java.io.File; |
5 |
|
|
import java.io.FileInputStream; |
6 |
|
|
import java.io.FileNotFoundException; |
7 |
|
|
import java.io.FileOutputStream; |
8 |
|
|
import java.io.IOException; |
9 |
|
|
import java.io.InputStreamReader; |
10 |
|
|
import java.io.PrintWriter; |
11 |
|
|
import java.nio.channels.FileChannel; |
12 |
|
|
import java.util.Formatter; |
13 |
|
|
import java.util.regex.Matcher; |
14 |
|
|
import java.util.regex.Pattern; |
15 |
|
|
|
16 |
|
|
public class TinkerRunner { |
17 |
|
|
|
18 |
|
|
private static final String PROTEIN_PROG = "protein"; |
19 |
|
|
private static final String DISTGEOM_PROG = "distgeom"; |
20 |
|
|
private static final String PDBXYZ_PROG = "pdbxyz"; |
21 |
|
|
private static final String XYZPDB_PROG = "xyzpdb"; |
22 |
|
|
private static final String CYCLISE_PROTEIN_STR = "N"; |
23 |
|
|
private static final String DGEOM_PARAMS = "Y N Y Y N N A"; |
24 |
|
|
private static final String TINKER_ERROR_STR = " TINKER is Unable to Continue"; |
25 |
|
|
|
26 |
|
|
private String tinkerBinDir; |
27 |
|
|
|
28 |
|
|
private String forceFieldFileName; |
29 |
|
|
|
30 |
|
|
private String proteinProg; |
31 |
|
|
private String distgeomProg; |
32 |
|
|
private String pdbxyzProg; |
33 |
|
|
private String xyzpdbProg; |
34 |
|
|
|
35 |
|
|
private File logFile; |
36 |
|
|
private PrintWriter log; |
37 |
|
|
|
38 |
|
|
// arrays for storing distgeom output data |
39 |
|
|
private int[] numUpperBoundViol; |
40 |
|
|
private int[] numLowerBoundViol; |
41 |
|
|
private double[] maxUpperBoundViol; |
42 |
|
|
private double[] maxLowerBoundViol; |
43 |
|
|
private double[] rmsBoundViol; |
44 |
|
|
private int[] numUpperViol; |
45 |
|
|
private int[] numLowerViol; |
46 |
|
|
private double[] maxUpperViol; |
47 |
|
|
private double[] maxLowerViol; |
48 |
|
|
private double[] rmsRestViol; |
49 |
|
|
|
50 |
|
|
|
51 |
|
|
/** |
52 |
|
|
* Constructs a TinkerRunner object by passing initial parameters |
53 |
|
|
* @param tinkerBinDir The directory where the tinker executables are |
54 |
|
|
* @param forceFieldFileName The force field file |
55 |
|
|
* @param logFile File where all tinker output will be logged to |
56 |
|
|
* @throws FileNotFoundException If logFile can't be written |
57 |
|
|
*/ |
58 |
|
|
public TinkerRunner(String tinkerBinDir, String forceFieldFileName, File logFile) throws FileNotFoundException { |
59 |
|
|
this.tinkerBinDir = tinkerBinDir; |
60 |
|
|
this.forceFieldFileName = forceFieldFileName; |
61 |
|
|
this.proteinProg = new File(this.tinkerBinDir,PROTEIN_PROG).getAbsolutePath(); |
62 |
|
|
this.distgeomProg = new File(this.tinkerBinDir,DISTGEOM_PROG).getAbsolutePath(); |
63 |
|
|
this.pdbxyzProg = new File(this.tinkerBinDir,PDBXYZ_PROG).getAbsolutePath(); |
64 |
|
|
this.xyzpdbProg = new File(this.tinkerBinDir,XYZPDB_PROG).getAbsolutePath(); |
65 |
|
|
|
66 |
|
|
this.logFile = logFile; |
67 |
|
|
this.log = new PrintWriter(new FileOutputStream(logFile)); |
68 |
|
|
} |
69 |
|
|
|
70 |
|
|
/** |
71 |
|
|
* To get the expected File that a tinker program will output given an input file and an extension for the output files |
72 |
|
|
* The directory where the input file is will be scanned to see if it contains files of the form basename.ext, basename.ext_2, basename.ext_3 etc. |
73 |
|
|
* @param file |
74 |
|
|
* @param ext |
75 |
|
|
* @return |
76 |
|
|
*/ |
77 |
|
|
private File getTinkerOutputFileName(File file, String ext){ |
78 |
|
|
String basename = file.getName(); |
79 |
|
|
basename = basename.substring(0, basename.lastIndexOf(".")); |
80 |
|
|
String dirname = file.getParent(); |
81 |
|
|
|
82 |
|
|
String tinkerOutFileName = basename + "." + ext; |
83 |
|
|
|
84 |
|
|
if (new File(dirname,tinkerOutFileName).exists()) { |
85 |
|
|
int i = 2; |
86 |
|
|
tinkerOutFileName = basename + "." + ext + "_" + i; |
87 |
|
|
while (new File(dirname,tinkerOutFileName).exists()) { |
88 |
|
|
i++; |
89 |
|
|
tinkerOutFileName = basename + "." + ext + "_" + i; |
90 |
|
|
} |
91 |
|
|
} |
92 |
|
|
return new File(dirname,tinkerOutFileName); |
93 |
|
|
} |
94 |
|
|
|
95 |
|
|
/** |
96 |
|
|
* Runs tinker's protein program to generate an elongated protein structure given a sequence |
97 |
|
|
* @param sequence |
98 |
|
|
* @param outPath The directory where output files will be written |
99 |
|
|
* @param outBasename The base name for the output files |
100 |
|
|
* @throws IOException |
101 |
|
|
* @throws TinkerError |
102 |
|
|
*/ |
103 |
|
|
public void runProtein(String sequence, String outPath, String outBasename) throws IOException, TinkerError { |
104 |
|
|
boolean tinkerError = false; // to store the exit state of the tinker program |
105 |
|
|
|
106 |
|
|
if (!new File(outPath).exists()) { |
107 |
|
|
throw new FileNotFoundException("Specified directory "+outPath+" does not exist"); |
108 |
|
|
} |
109 |
|
|
File tinkerxyzout = getTinkerOutputFileName(new File(outPath,outBasename+".xyz"),"xyz"); |
110 |
|
|
File tinkerintout = getTinkerOutputFileName(new File(outPath,outBasename+".int"),"int"); |
111 |
|
|
tinkerintout.deleteOnExit(); |
112 |
|
|
File tinkerseqout = getTinkerOutputFileName(new File(outPath,outBasename+".seq"),"seq"); |
113 |
|
|
|
114 |
|
|
// running protein program in outPath dir (so that output files are written to outPath) |
115 |
|
|
Process protProc = Runtime.getRuntime().exec(proteinProg, null, new File(outPath)); |
116 |
|
|
// piping input |
117 |
|
|
PrintWriter protInput = new PrintWriter(protProc.getOutputStream()); |
118 |
|
|
protInput.println(outBasename); |
119 |
|
|
protInput.println("Unfolded chain created by tinker's protein program"); |
120 |
|
|
protInput.println(forceFieldFileName); |
121 |
|
|
for (int i=0;i<sequence.length();i++) { |
122 |
|
|
// we've got to use 3 letter code for CYS, otherwise tinker takes the default CYX (which means cystein with disulfide bridge) |
123 |
|
|
if (sequence.charAt(i)=='C') { |
124 |
|
|
protInput.println("CYS"); |
125 |
|
|
} else { |
126 |
|
|
protInput.println(sequence.charAt(i)); |
127 |
|
|
} |
128 |
|
|
} |
129 |
|
|
protInput.println(); |
130 |
|
|
protInput.println(CYCLISE_PROTEIN_STR); |
131 |
|
|
protInput.close(); |
132 |
|
|
|
133 |
|
|
// logging output |
134 |
|
|
BufferedReader protOutput = new BufferedReader(new InputStreamReader(protProc.getInputStream())); |
135 |
|
|
String line; |
136 |
|
|
while((line = protOutput.readLine()) != null) { |
137 |
|
|
log.println(line); |
138 |
|
|
if (line.startsWith(TINKER_ERROR_STR)) { |
139 |
|
|
tinkerError = true; |
140 |
|
|
} |
141 |
|
|
} |
142 |
|
|
|
143 |
|
|
tinkerxyzout.renameTo(new File(outPath,outBasename+".xyz")); |
144 |
|
|
tinkerseqout.renameTo(new File(outPath,outBasename+".seq")); |
145 |
|
|
|
146 |
|
|
if (tinkerError) { |
147 |
|
|
log.close(); |
148 |
|
|
throw new TinkerError("Tinker error, revise log file "+logFile.getAbsolutePath()); |
149 |
|
|
} |
150 |
|
|
} |
151 |
|
|
|
152 |
|
|
/** |
153 |
|
|
* Runs tinker's distgeom program capturing output with restrain violation statistics into member variable arrays |
154 |
|
|
* that can be retrieved using the getters: getMaxLowerBoundViol, getMaxUpperBoundViol, getMaxLowerViol etc... |
155 |
duarte |
338 |
* Two files are needed as input for distgeom: an xyz file and a key file, the latter is not passed but instead implicitely |
156 |
|
|
* defined by xyzFile: must be in same directory and must have same basename with extension .key |
157 |
duarte |
322 |
* @param xyzFile |
158 |
|
|
* @param outPath Directory where output files will be written |
159 |
|
|
* @param outBasename Base name of the output files |
160 |
|
|
* @param n Number of models that we want distgeom to produce |
161 |
|
|
* @throws TinkerError If an error seen in tinker's output |
162 |
|
|
* @throws IOException |
163 |
|
|
*/ |
164 |
stehr |
343 |
public void runDistgeom(File xyzFile, String outPath, String outBasename, int n) throws TinkerError, IOException, InterruptedException { |
165 |
duarte |
322 |
boolean tinkerError = false; // to store the exit state of the tinker program |
166 |
|
|
if (!new File(outPath).exists()) { |
167 |
|
|
throw new FileNotFoundException("Specified directory "+outPath+" does not exist"); |
168 |
|
|
} |
169 |
|
|
if (!xyzFile.exists()){ |
170 |
|
|
throw new FileNotFoundException("Specified xyz file "+xyzFile.getAbsolutePath()+" does not exist"); |
171 |
|
|
} |
172 |
|
|
String basename = xyzFile.getName(); |
173 |
|
|
basename = basename.substring(0, basename.lastIndexOf(".")); |
174 |
|
|
File keyFile = new File(xyzFile.getParent(),basename+".key"); |
175 |
|
|
if (! keyFile.exists()) { |
176 |
|
|
throw new FileNotFoundException("Key file "+keyFile.getAbsolutePath()+" not present in input directory "+xyzFile.getParent()); |
177 |
|
|
} |
178 |
|
|
// getting names of tinker output files |
179 |
|
|
File[] tinkerout = new File[n+1]; |
180 |
|
|
for (int i=1;i<=n;i++) { |
181 |
|
|
String ext = new Formatter().format("%03d", i).toString(); |
182 |
|
|
tinkerout[i] = getTinkerOutputFileName(xyzFile, ext); |
183 |
|
|
} |
184 |
|
|
// initialising arrays were we store captured output data |
185 |
|
|
numUpperBoundViol = new int[n+1]; |
186 |
|
|
numLowerBoundViol = new int[n+1]; |
187 |
|
|
maxUpperBoundViol = new double[n+1]; |
188 |
|
|
maxLowerBoundViol = new double[n+1]; |
189 |
|
|
rmsBoundViol = new double[n+1]; |
190 |
|
|
numUpperViol = new int[n+1]; |
191 |
|
|
numLowerViol = new int[n+1]; |
192 |
|
|
maxUpperViol = new double[n+1]; |
193 |
|
|
maxLowerViol = new double[n+1]; |
194 |
|
|
rmsRestViol = new double[n+1]; |
195 |
|
|
// running distgeom program |
196 |
stehr |
342 |
String cmdLine = distgeomProg+" "+xyzFile.getAbsolutePath()+" "+n+" "+DGEOM_PARAMS; |
197 |
|
|
Process dgeomProc = Runtime.getRuntime().exec(cmdLine); |
198 |
duarte |
322 |
// logging and capturing output |
199 |
|
|
BufferedReader dgeomOutput = new BufferedReader(new InputStreamReader(dgeomProc.getInputStream())); |
200 |
|
|
String line; |
201 |
|
|
int i=1; |
202 |
|
|
while((line = dgeomOutput.readLine()) != null) { |
203 |
|
|
log.println(line); |
204 |
|
|
if (line.startsWith(TINKER_ERROR_STR)) { |
205 |
|
|
tinkerError = true; |
206 |
|
|
} |
207 |
|
|
Pattern p = Pattern.compile("^ Num Upper Bound Violations :\\s+(\\d+)"); |
208 |
|
|
Matcher m = p.matcher(line); |
209 |
|
|
if (m.find()) { |
210 |
|
|
numUpperBoundViol[i]=Integer.parseInt(m.group(1)); |
211 |
|
|
} |
212 |
|
|
p = Pattern.compile("^ Num Lower Bound Violations :\\s+(\\d+)"); |
213 |
|
|
m = p.matcher(line); |
214 |
|
|
if (m.find()) { |
215 |
|
|
numLowerBoundViol[i]=Integer.parseInt(m.group(1)); |
216 |
|
|
} |
217 |
|
|
p = Pattern.compile("^ Max Upper Bound Violation :\\s+(\\d+\\.\\d\\d\\d\\d)"); |
218 |
|
|
m = p.matcher(line); |
219 |
|
|
if (m.find()) { |
220 |
|
|
maxUpperBoundViol[i]=Double.parseDouble(m.group(1)); |
221 |
|
|
} |
222 |
|
|
p = Pattern.compile("^ Max Lower Bound Violation :\\s+(\\d+\\.\\d\\d\\d\\d)"); |
223 |
|
|
m = p.matcher(line); |
224 |
|
|
if (m.find()) { |
225 |
|
|
maxLowerBoundViol[i]=Double.parseDouble(m.group(1)); |
226 |
|
|
} |
227 |
|
|
p = Pattern.compile("^ RMS Deviation from Bounds :\\s+(\\d+\\.\\d\\d\\d\\d)"); |
228 |
|
|
m = p.matcher(line); |
229 |
|
|
if (m.find()) { |
230 |
|
|
rmsBoundViol[i]=Double.parseDouble(m.group(1)); |
231 |
|
|
} |
232 |
|
|
p = Pattern.compile("^ Num Upper Restraint Violations :\\s+(\\d+)"); |
233 |
|
|
m = p.matcher(line); |
234 |
|
|
if (m.find()) { |
235 |
|
|
numUpperViol[i]=Integer.parseInt(m.group(1)); |
236 |
|
|
} |
237 |
|
|
p = Pattern.compile("^ Num Lower Restraint Violations :\\s+(\\d+)"); |
238 |
|
|
m = p.matcher(line); |
239 |
|
|
if (m.find()) { |
240 |
|
|
numLowerViol[i]=Integer.parseInt(m.group(1)); |
241 |
|
|
} |
242 |
|
|
p = Pattern.compile("^ Max Upper Restraint Violation :\\s+(\\d+\\.\\d\\d\\d\\d)"); |
243 |
|
|
m = p.matcher(line); |
244 |
|
|
if (m.find()) { |
245 |
|
|
maxUpperViol[i]=Double.parseDouble(m.group(1)); |
246 |
|
|
} |
247 |
|
|
p = Pattern.compile("^ Max Lower Restraint Violation :\\s+(\\d+\\.\\d\\d\\d\\d)"); |
248 |
|
|
m = p.matcher(line); |
249 |
|
|
if (m.find()) { |
250 |
|
|
maxLowerViol[i]=Double.parseDouble(m.group(1)); |
251 |
|
|
} |
252 |
|
|
p = Pattern.compile("^ RMS Restraint Dist Violation :\\s+(\\d+\\.\\d\\d\\d\\d)"); |
253 |
|
|
m = p.matcher(line); |
254 |
|
|
if (m.find()) { |
255 |
|
|
rmsRestViol[i]=Double.parseDouble(m.group(1)); |
256 |
duarte |
341 |
System.out.println("Done model "+i+". Violations: "+numUpperViol[i]+" upper, "+numLowerViol[i]+" lower"); |
257 |
duarte |
322 |
i++; |
258 |
duarte |
341 |
|
259 |
duarte |
322 |
} |
260 |
|
|
} |
261 |
|
|
//renaming files to our chosen outBasename+ext |
262 |
|
|
for (i=1;i<=n;i++) { |
263 |
|
|
String ext = new Formatter().format("%03d", i).toString(); |
264 |
|
|
tinkerout[i].renameTo(new File(outPath,outBasename+"."+ext)); |
265 |
|
|
} |
266 |
|
|
// throwing exception if error string was caught in output |
267 |
|
|
if (tinkerError) { |
268 |
|
|
log.close(); |
269 |
|
|
throw new TinkerError("Tinker error, revise log file "+logFile.getAbsolutePath()); |
270 |
|
|
} |
271 |
stehr |
343 |
int exitValue = dgeomProc.waitFor(); |
272 |
duarte |
341 |
// throwing exception if exit state is 137: happens in Linux when another instance of distgeom is running in same machine, the OS kills it with exit state 137 |
273 |
stehr |
343 |
if (exitValue==137) { |
274 |
duarte |
341 |
log.close(); |
275 |
|
|
throw new TinkerError("Distgeom was killed by OS, probably another instance of distgeom is running in this computer"); |
276 |
|
|
} |
277 |
stehr |
343 |
if (exitValue==139) { |
278 |
stehr |
342 |
log.close(); |
279 |
|
|
throw new TinkerError("Distgeom was killed with exit code 139. Not enough memory."); |
280 |
|
|
} |
281 |
duarte |
322 |
|
282 |
|
|
} |
283 |
|
|
|
284 |
|
|
/** |
285 |
|
|
* Runs tinker's xyzpdb program to convert a given xyzFile (needing also a seqFile) to a pdbFile |
286 |
|
|
* @param xyzFile |
287 |
|
|
* @param seqFile |
288 |
|
|
* @param pdbFile |
289 |
|
|
* @throws IOException |
290 |
|
|
* @throws TinkerError If an error seen in tinker's output |
291 |
|
|
*/ |
292 |
|
|
public void runXyzpdb(File xyzFile, File seqFile, File pdbFile) throws IOException, TinkerError { |
293 |
|
|
boolean tinkerError = false; // to store the exit state of the tinker program |
294 |
|
|
if (!xyzFile.exists()){ |
295 |
|
|
throw new FileNotFoundException("Specified xyz file "+xyzFile.getAbsolutePath()+" does not exist"); |
296 |
|
|
} |
297 |
|
|
if (!seqFile.exists()){ |
298 |
|
|
throw new FileNotFoundException("Specified seq file "+seqFile.getAbsolutePath()+" does not exist"); |
299 |
|
|
} |
300 |
|
|
|
301 |
|
|
String basename = xyzFile.getName(); |
302 |
|
|
basename = basename.substring(0, basename.lastIndexOf(".")); |
303 |
|
|
File tmpSeqFile = new File(seqFile.getParent(),basename+".seq"); |
304 |
|
|
|
305 |
|
|
// if seqFile doesn't follow the naming convention (basename of xyzFile+seq extension) that tinker expects, we copy it to tmpSeqFile (which has right name) |
306 |
|
|
if (!tmpSeqFile.equals(seqFile)) { |
307 |
|
|
FileChannel srcChannel = new FileInputStream(seqFile).getChannel(); |
308 |
|
|
FileChannel dstChannel = new FileOutputStream(tmpSeqFile).getChannel(); |
309 |
|
|
dstChannel.transferFrom(srcChannel, 0, srcChannel.size()); |
310 |
|
|
srcChannel.close(); |
311 |
|
|
dstChannel.close(); |
312 |
|
|
// if we copied then that means tmpSeqFile is different from seqFile and thus we want to delete the tmp file on exit |
313 |
|
|
tmpSeqFile.deleteOnExit(); |
314 |
|
|
} |
315 |
|
|
|
316 |
|
|
File tinkerpdbout = getTinkerOutputFileName(xyzFile, "pdb"); |
317 |
|
|
|
318 |
|
|
// running tinker's xyzpdb |
319 |
|
|
// beware: it takes as a silent input the seq file seqFile (or tmpSeqFile if the input seqFile didn't have the right name) |
320 |
|
|
Process xyzpdbProc = Runtime.getRuntime().exec(xyzpdbProg+" "+xyzFile.getAbsolutePath()+" "+forceFieldFileName); |
321 |
|
|
|
322 |
|
|
// logging output |
323 |
|
|
BufferedReader xyzpdbOutput = new BufferedReader(new InputStreamReader(xyzpdbProc.getInputStream())); |
324 |
|
|
String line; |
325 |
|
|
while((line = xyzpdbOutput.readLine()) != null) { |
326 |
|
|
log.println(line); |
327 |
|
|
if (line.startsWith(TINKER_ERROR_STR)) { |
328 |
|
|
tinkerError = true; |
329 |
|
|
} |
330 |
|
|
} |
331 |
|
|
|
332 |
|
|
tinkerpdbout.renameTo(pdbFile); |
333 |
|
|
|
334 |
|
|
if (tinkerError) { |
335 |
|
|
log.close(); |
336 |
|
|
throw new TinkerError("Tinker error, revise log file "+logFile.getAbsolutePath()); |
337 |
|
|
} |
338 |
|
|
} |
339 |
|
|
|
340 |
|
|
/** |
341 |
|
|
* Runs tinker's pdbxyz program to convert a pdbFile to a xyzFile |
342 |
|
|
* @param pdbFile |
343 |
|
|
* @param xyzFile |
344 |
|
|
* @throws IOException |
345 |
|
|
* @throws TinkerError If an error seen in tinker's output |
346 |
|
|
*/ |
347 |
|
|
public void runPdbxyz(File pdbFile, File xyzFile) throws IOException, TinkerError{ |
348 |
|
|
boolean tinkerError = false; // to store the exit state of the tinker program |
349 |
|
|
if (!pdbFile.exists()){ |
350 |
|
|
throw new FileNotFoundException("Specified pdb file "+pdbFile.getAbsolutePath()+" does not exist"); |
351 |
|
|
} |
352 |
|
|
File tinkerxyzout = getTinkerOutputFileName(pdbFile, "xyz"); |
353 |
|
|
// running tinker's pdbxyz |
354 |
|
|
Process pdbxyzProc = Runtime.getRuntime().exec(pdbxyzProg+" "+pdbFile.getAbsolutePath()+" "+forceFieldFileName); |
355 |
|
|
|
356 |
|
|
// logging output |
357 |
|
|
BufferedReader pdbxyzOutput = new BufferedReader(new InputStreamReader(pdbxyzProc.getInputStream())); |
358 |
|
|
String line; |
359 |
|
|
while((line = pdbxyzOutput.readLine()) != null) { |
360 |
|
|
log.println(line); |
361 |
|
|
if (line.startsWith(TINKER_ERROR_STR)) { |
362 |
|
|
tinkerError = true; |
363 |
|
|
} |
364 |
|
|
} |
365 |
|
|
tinkerxyzout.renameTo(xyzFile); |
366 |
|
|
if (tinkerError) { |
367 |
|
|
log.close(); |
368 |
|
|
throw new TinkerError("Tinker error, revise log file "+logFile.getAbsolutePath()); |
369 |
|
|
} |
370 |
|
|
} |
371 |
|
|
|
372 |
|
|
/** |
373 |
|
|
* Closes log stream, must be called after no other tinker program will be run with this TinkerRunner object |
374 |
|
|
* (otherwise log is not flushed to file) |
375 |
|
|
*/ |
376 |
|
|
public void closeLog() { |
377 |
|
|
log.close(); |
378 |
|
|
} |
379 |
|
|
|
380 |
|
|
public double[] getMaxLowerBoundViol() { |
381 |
|
|
return maxLowerBoundViol; |
382 |
|
|
} |
383 |
|
|
|
384 |
|
|
public double[] getMaxLowerViol() { |
385 |
|
|
return maxLowerViol; |
386 |
|
|
} |
387 |
|
|
|
388 |
|
|
public double[] getMaxUpperBoundViol() { |
389 |
|
|
return maxUpperBoundViol; |
390 |
|
|
} |
391 |
|
|
|
392 |
|
|
public double[] getMaxUpperViol() { |
393 |
|
|
return maxUpperViol; |
394 |
|
|
} |
395 |
|
|
|
396 |
|
|
public int[] getNumLowerBoundViol() { |
397 |
|
|
return numLowerBoundViol; |
398 |
|
|
} |
399 |
|
|
|
400 |
|
|
public int[] getNumLowerViol() { |
401 |
|
|
return numLowerViol; |
402 |
|
|
} |
403 |
|
|
|
404 |
|
|
public int[] getNumUpperBoundViol() { |
405 |
|
|
return numUpperBoundViol; |
406 |
|
|
} |
407 |
|
|
|
408 |
|
|
public int[] getNumUpperViol() { |
409 |
|
|
return numUpperViol; |
410 |
|
|
} |
411 |
|
|
|
412 |
|
|
public double[] getRmsBoundViol() { |
413 |
|
|
return rmsBoundViol; |
414 |
|
|
} |
415 |
|
|
|
416 |
|
|
public double[] getRmsRestViol() { |
417 |
|
|
return rmsRestViol; |
418 |
|
|
} |
419 |
|
|
} |