/* Copyright (C) 2011 Diego Darriba, David Posada This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package es.uvigo.darwin.jmodeltest; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; import java.io.PushbackReader; import java.io.Serializable; import java.io.StringReader; import java.util.Vector; import pal.alignment.Alignment; import pal.datatype.DataType; import pal.tree.TreeParseException; import es.uvigo.darwin.jmodeltest.exception.AlignmentParseException; import es.uvigo.darwin.jmodeltest.io.AlignmentReader; import es.uvigo.darwin.jmodeltest.io.TextOutputStream; import es.uvigo.darwin.jmodeltest.model.Model; import es.uvigo.darwin.jmodeltest.model.ModelConstants; import es.uvigo.darwin.jmodeltest.selection.InformationCriterion; import es.uvigo.darwin.jmodeltest.utilities.Utilities; /** * This class gathers the parameters of a single execution of jModelTest 2. It * is a singleton class. * * @author Diego Darriba * */ public class ApplicationOptions implements Serializable { private static final long serialVersionUID = -3961572952922591321L; private static final int AMBIGUOUS_DATATYPE_STATE = 4; private static final int DEFAULT_SEED = 12345; /** Tree topology search algorithms */ public static enum TreeSearch { NNI, SPR, BEST }; private static ApplicationOptions instance; private static Vector<String> testOrder; private File inputDataFile, inputTreeFile; private File alignmentFile, treeFile; private File ckpFile; private File logFile; private Alignment alignment; private boolean isAmbiguous; private boolean forceCheckULnL = false; private double unconstrainedLnL = 0.0d; private int numPatterns = 0; // Newick user tree private String userTree; /* * Number of threads for shared memory execution or static thread scheduling */ private int numberOfThreads = Runtime.getRuntime().availableProcessors(); /* * Number of threads for dynamic thread scheduling */ private File machinesFile; public boolean threadScheduling = false; public boolean doAIC = false; public boolean doAICc = false; public boolean doBIC = false; public boolean doDT = false; public boolean doDLRT = false; public boolean doHLRT = false; // whether to include the parameter base frequencies public boolean doF = false; public boolean doI = false; // whether to include the parameter pinv public boolean doG = false; // whether to include gamma public int numGammaCat = 4; public boolean backwardHLRTSelection = false; public double confidenceLevelHLRT = 0.01; public boolean writePAUPblock = false; public boolean doImportances = false; public boolean doModelAveraging = false; public boolean doAveragedPhylogeny = false; public double confidenceInterval = 1.0; // by default include all models // for specific simulations public boolean doingSimulations = false; public String simulationsName = ""; // whether to use the same user fixed topology for all calculations public boolean userTopologyExists = false; // whether to use the same BIONJ-JC fixed tree for all calculations public boolean fixedTopology = false; // whether to optimize the BIONJ-model tree by ML public boolean optimizeMLTopology = true; public TreeSearch treeSearchOperations = TreeSearch.BEST; // Threshold for the guided search mode. A QST == 0.0 means no model // but the GTR one is optimized. A high QST means the whole set of // set of models will be optimized. private double guidedSearchThreshold = 0.0d; private boolean doClusteringSearch = false; private int heuristicInformationCriterion = InformationCriterion.IC_BIC; private int numSites; private int numTaxa; private int numBranches; private int numInvariableSites; private int numModels; private int rngSeed = DEFAULT_SEED; private String executionName; public String consensusType = "50% majority rule"; // consensus type // for model // averaged // phylogeny public boolean countBLasParameters = true; // whether to count branch // lengths as parameters private int substTypeCode = 0; // number of substitution types to // consider private ApplicationOptions() { } public void createCkpFile() { if (ckpFile != null && ckpFile.exists()) return; try { if (executionName == null) { executionName = Utilities.getCurrentTime("yyyyMMddHHmmss"); } if (ModelTestConfiguration.isCkpEnabled() && getInputFile() != null) { ckpFile = new File(ModelTestConfiguration.getLogDir() + File.separator + getInputFile().getName() + "." + executionName + ".ckp"); } else { ckpFile = File.createTempFile("jmodeltest", ".ckp"); ckpFile.deleteOnExit(); } } catch (IOException e) { System.err.println("Error creating checkpointing file"); } } public void createLogFile() { try { if (executionName == null) { executionName = Utilities.getCurrentTime("yyyyMMddHHmmss"); } if (ModelTestConfiguration.isPhymlLogEnabled() && getInputFile() != null) { logFile = new File(ModelTestConfiguration.getLogDir() + File.separator + getInputFile().getName() + ".phyml." + executionName + ".log"); if (!logFile.canWrite()) { logFile = null; } } if (logFile == null) { logFile = File.createTempFile("jmodeltest-phyml", ".log"); logFile.deleteOnExit(); } } catch (IOException e) { System.err.println("Error creating PhyML log file"); } } public void deleteLogFile() { if (!ModelTestConfiguration.isPhymlLogEnabled()) { logFile.delete(); } } // This method copies the input files into the scratch. // It is important to speed up the I/O in distributed memory. public void buildWorkFiles() throws IOException { File workDataFile = getAlignmentFile(); if (!workDataFile.exists()) { workDataFile.createNewFile(); workDataFile.deleteOnExit(); } try { String alnStr = ModelTestService.readAlignment(inputDataFile, alignmentFile); PushbackReader pr = new PushbackReader( new StringReader(alnStr)); setAlignment( AlignmentReader.createAlignment( new PrintWriter(System.err), pr, true)); } catch (AlignmentParseException e) { e.printStackTrace(); } if (userTree != null) { File workTreeFile = getTreeFile(); if (!workTreeFile.exists()) { workTreeFile.createNewFile(); workTreeFile.deleteOnExit(); } TextOutputStream out = new TextOutputStream(getTreeFile() .getAbsolutePath()); out.print(getUserTree()); out.close(); } } public static ApplicationOptions getInstance() { if (instance == null) { instance = new ApplicationOptions(); } return instance; } /**************************** * setCandidateModels *********************** * Build the set of candidate * models * * ************************************************************************/ public void setCandidateModels() { boolean includeModel; // fill in list of models ModelTest.setCandidateModels(new Model[getNumModels()]); if (substTypeCode < 4) { int j = 0; for (int i = 0; i < ModelTest.MAX_NUM_MODELS; i++) { includeModel = true; if (ModelConstants.substType[i] > substTypeCode) includeModel = false; if (!doF && !ModelConstants.equalBaseFrequencies[i]) // is F includeModel = false; if (!doI && (ModelConstants.rateVariation[i] == 1 || ModelConstants.rateVariation[i] == 3)) includeModel = false; if (!doG && (ModelConstants.rateVariation[i] == 2 || ModelConstants.rateVariation[i] == 3)) includeModel = false; if (includeModel) { loadModelConstraints(ModelTest.getCandidateModel(j), j, i); j++; } } } else { int j = 0; for (int ratesCount = 1; ratesCount <= 6; ratesCount++) { for (String partition : ModelConstants.fullModelSet .get(ratesCount)) { boolean baseFrequencies; int rateVariation; for (int k = 0; k < 8; k++) { baseFrequencies = (k / 4 == 1); rateVariation = (k % 4); includeModel = true; if (!doF && baseFrequencies) includeModel = false; if (!doI && (rateVariation == 1 || rateVariation == 3)) includeModel = false; if (!doG && (rateVariation == 2 || rateVariation == 3)) includeModel = false; if (includeModel) { loadModelConstraints( ModelTest.getCandidateModel(j), j, partition, baseFrequencies, rateVariation, ratesCount); j++; } } } } } if (ModelTest.buildGUI || ModelTest.testingOrder == null) { // set set of hypotheses for hLRTs in default order testOrder = new Vector<String>(); // we need to reinitialize the hypotheses list in case it existed // before.. testOrder.removeAllElements(); if (doF) testOrder.add("freq"); switch (substTypeCode) { case 0: testOrder.add("titv"); testOrder.add("2ti4tv"); break; case 1: testOrder.add("titv"); testOrder.add("2ti"); testOrder.add("2tv"); break; case 2: case 3: case 4: testOrder.add("titv"); testOrder.add("2ti"); testOrder.add("2tv"); testOrder.add("4tv"); } if (doI) testOrder.add("pinv"); if (doG) testOrder.add("gamma"); // ModelTest.testingOrder.removeAllElements(); ModelTest.testingOrder = testOrder; } } /************************ * loadModelConstraints ************************ * Loads initial definitions * of models * * * ***********************************************************************/ public void loadModelConstraints(Model currentModel, int order, int modelNo) { int modelParameters, BL; boolean pF, pT, pV, pR, pI, pG; // initialize pT = pV = pR = pI = pG = false; pF = !ModelConstants.equalBaseFrequencies[modelNo]; if (ModelConstants.numTransitions[modelNo] == 1 && ModelConstants.numTransversions[modelNo] == 1) { pT = true; } else if (ModelConstants.numTransitions[modelNo] > 1 || ModelConstants.numTransversions[modelNo] > 1) { pR = true; } /* * find out first wich models in phyml give titv if * (Model.numTransitions[modelNo] == 2 || * Model.numTransversions[modelNo] == 2) { pV = true; } */ switch (ModelConstants.rateVariation[modelNo]) { case 1: pI = true; break; case 2: pG = true; break; case 3: pI = true; pG = true; break; } if (countBLasParameters) BL = getNumBranches(); else BL = 0; if (optimizeMLTopology) modelParameters = ModelConstants.freeParameters[modelNo] + BL + 1; else modelParameters = ModelConstants.freeParameters[modelNo] + BL; ModelTest.getCandidateModels()[order] = new Model(order + 1, ModelConstants.modelName[modelNo], ModelConstants.modelCode[modelNo], modelParameters, pF, pT, pV, pR, pI, pG, ModelConstants.numTransitions[modelNo], ModelConstants.numTransversions[modelNo]); if (ModelTest.getLoadedModels() != null && ModelTest.getLoadedModels().length > 0) { for (Model model : ModelTest.getLoadedModels()) { if (model.getName().equals(ModelTest.getCandidateModels()[order].getName()) && model.getLnL() > 0.0) { /* load from checkpoint */ Model otherModel = ModelTest.getCandidateModels()[order]; otherModel.setLnL(model.getLnL()); otherModel.setLnLIgnoringGaps(model.getLnLIgnoringGaps()); otherModel.setShape(model.getShape()); otherModel.setfA(model.getfA()); otherModel.setfC(model.getfC()); otherModel.setfG(model.getfG()); otherModel.setfT(model.getfT()); otherModel.setRa(model.getRa()); otherModel.setRb(model.getRb()); otherModel.setRc(model.getRc()); otherModel.setRd(model.getRd()); otherModel.setRe(model.getRe()); otherModel.setRf(model.getRf()); otherModel.setPinv(model.getPinv()); otherModel.setKappa(model.getKappa()); otherModel.setTitv(model.getTitv()); try { otherModel.setTreeString(model.getTreeString()); } catch (TreeParseException e) { e.printStackTrace(); } model.setLnL(0.0); break; } } } } public void loadModelConstraints(Model currentModel, int order, String partition, boolean baseFrequencies, int rateVariation, int ratesCount) { int modelParameters, BL, nTi, nTv; boolean pF, pT, pV, pR, pI, pG; pT = pV = pR = pI = pG = false; pF = baseFrequencies; switch (rateVariation) { case 1: pI = true; break; case 2: pG = true; break; case 3: pI = true; pG = true; break; } if (countBLasParameters) BL = getNumBranches(); else BL = 0; modelParameters = (ratesCount - 1) + (pF ? 3 : 0) + (pI ? 1 : 0) + (pG ? 1 : 0) + BL + (optimizeMLTopology ? 1 : 0); String modelName; if (ModelConstants.codeList.contains(partition)) { int index = ModelConstants.codeList.indexOf(partition); if (pF) index += 4; if (pI) index += 1; if (pG) index += 2; modelName = ModelConstants.modelName[index]; nTi = ModelConstants.numTransitions[index]; nTv = ModelConstants.numTransversions[index]; if (nTi == 1 && nTv == 1) { pT = true; } else if (nTi > 1 || nTv > 1) { pR = true; } } else { modelName = partition + (pI ? "+I" : "") + (pG ? "+G" : "") + (pF ? "+F" : ""); nTi = 0;// ModelConstants.getNumberOfTransitions(partition); nTv = 0;// ModelConstants.getNumberOfTransversions(partition); pT = false; pR = true; } ModelTest.getCandidateModels()[order] = new Model(order + 1, modelName, partition, modelParameters, pF, pT, pV, pR, pI, pG, 0, 0); if (ModelTest.getLoadedModels() != null && ModelTest.getLoadedModels().length > 0) { for (Model model : ModelTest.getLoadedModels()) { if (model.getName().equals(ModelTest.getCandidateModels()[order].getName()) && model.getLnL() > 0.0) { /* load from checkpoint */ Model otherModel = ModelTest.getCandidateModels()[order]; otherModel.setLnL(model.getLnL()); otherModel.setLnLIgnoringGaps(model.getLnLIgnoringGaps()); otherModel.setShape(model.getShape()); otherModel.setfA(model.getfA()); otherModel.setfC(model.getfC()); otherModel.setfG(model.getfG()); otherModel.setfT(model.getfT()); otherModel.setRa(model.getRa()); otherModel.setRb(model.getRb()); otherModel.setRc(model.getRc()); otherModel.setRd(model.getRd()); otherModel.setRe(model.getRe()); otherModel.setRf(model.getRf()); otherModel.setPinv(model.getPinv()); otherModel.setKappa(model.getKappa()); otherModel.setTitv(model.getTitv()); try { otherModel.setTreeString(model.getTreeString()); } catch (TreeParseException e) { e.printStackTrace(); } model.setLnL(0.0); break; } } } } public File getInputFile() { return inputDataFile; } public File getInputTreeFile() { return inputTreeFile; } public void setInputFile(File file) { this.inputDataFile = file; } public void setInputTreeFile(File file) { this.inputTreeFile = file; } public File getAlignmentFile() { if (alignmentFile == null) { try { alignmentFile = File.createTempFile("jmodeltest", ".phy"); alignmentFile.deleteOnExit(); } catch (IOException e) { alignmentFile = inputDataFile; } } return alignmentFile; } public Alignment getAlignment() { return alignment; } public void setAlignment(Alignment alignment) { this.alignment = alignment; setNumTaxa(alignment.getSequenceCount()); setNumSites(alignment.getSiteCount()); setNumBranches(2 * numTaxa - 3); setNumInvariableSites(Utilities.calculateInvariableSites(alignment)); DataType dt = alignment.getDataType(); // check ambiguity isAmbiguous = false; if (alignment.getDataType().isAmbiguous()) { isAmbiguous = true; } else { for (int i = 0; i < numTaxa; i++) { String seq = alignment.getAlignedSequenceString(i); if (seq.indexOf(Alignment.GAP) >= 0) { isAmbiguous = true; break; } for (int j = 0; j < seq.length(); j++) { if (dt.getState(seq.charAt(j)) == AMBIGUOUS_DATATYPE_STATE) { isAmbiguous = true; break; } } } } } public boolean isForceCheckULnL() { return forceCheckULnL; } public void setForceCheckULnL(boolean forceCheckULnL) { this.forceCheckULnL = forceCheckULnL; } public double getUnconstrainedLnL() { return unconstrainedLnL; } public void setUnconstrainedLnL(double unconstrainedLikelihood) { this.unconstrainedLnL = unconstrainedLikelihood; } public int getNumPatterns() { return numPatterns; } public void setNumPatterns(int numPatterns) { this.numPatterns = numPatterns; } public boolean isAmbiguous() { return isAmbiguous; } public String getExecutionName() { return executionName; } public void setExecutionName(String executionName) { this.executionName = executionName; } public File getTreeFile() { if (treeFile == null) { try { treeFile = File.createTempFile("jmodeltest", ".tree"); treeFile.deleteOnExit(); } catch (IOException e) { treeFile = inputTreeFile; } } return treeFile; } public File getCkpFile() { return ckpFile; } public File getLogFile() { return logFile; } public String getUserTree() { return userTree; } public void setUserTree(String tree) { this.userTree = tree; } /** * @param substTypeCode * the substTypeCode to set */ public void setSubstTypeCode(int substTypeCode) { this.substTypeCode = substTypeCode; this.doClusteringSearch = (substTypeCode == 4); } /** * @return the substTypeCode */ public int getSubstTypeCode() { return substTypeCode; } public double getSampleSize() { return numSites; } public static void setInstance(ApplicationOptions newInstance) { File alignmentFile = instance.alignmentFile; File treeFile = instance.treeFile; instance = newInstance; instance.alignmentFile = alignmentFile; instance.treeFile = treeFile; } public int getNumberOfThreads() { return numberOfThreads; } public void setNumberOfThreads(int numberOfThreads) { this.numberOfThreads = numberOfThreads; } public boolean isGuidedSearch() { return guidedSearchThreshold > 1e-6; } public double getGuidedSearchThreshold() { return guidedSearchThreshold; } public void setGuidedSearchThreshold(double guidedSearchThreshold) { this.guidedSearchThreshold = guidedSearchThreshold; } public void setMachinesFile(File machinesFile) throws FileNotFoundException { if (ModelTest.HOSTS_TABLE.containsKey(ModelTest.getHostname())) { setNumberOfThreads( ModelTest.HOSTS_TABLE.get(ModelTest.getHostname())); } else { System.err.println(""); System.err.println("WARNING: Machines File format is wrong."); System.err.println(" This host: " + ModelTest.getHostname() + " does not exist"); System.err.println(" Using a single thread"); System.err.println(""); this.numberOfThreads = 1; } } public File getMachinesFile() { return machinesFile; } public boolean isClusteringSearch() { return doClusteringSearch; } public void setClusteringSearch(boolean doClusteringSearch) { this.doClusteringSearch = doClusteringSearch; } public int getHeuristicInformationCriterion() { return heuristicInformationCriterion; } public void setHeuristicInformationCriterion( int heuristicInformationCriterion) { this.heuristicInformationCriterion = heuristicInformationCriterion; } public int getNumSites() { return numSites; } public void setNumSites(int numSites) { this.numSites = numSites; } public int getNumTaxa() { return numTaxa; } public void setNumTaxa(int numTaxa) { this.numTaxa = numTaxa; } public int getNumBranches() { return numBranches; } public void setNumBranches(int numBranches) { this.numBranches = numBranches; } public int getNumInvariableSites() { return numInvariableSites; } public void setNumInvariableSites(int numInvariableSites) { this.numInvariableSites = numInvariableSites; } public int getNumModels() { return numModels; } public void setNumModels(int numModels) { this.numModels = numModels; } public int getRngSeed() { return rngSeed; } public void setRngSeed(int rngSeed) { this.rngSeed = rngSeed; } }