/*
* LogGenerator.java
*
* Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
*
* This file is part of BEAST.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership and licensing.
*
* BEAST is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* BEAST 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with BEAST; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
package dr.app.beauti.generator;
import dr.app.beauti.components.ComponentFactory;
import dr.app.beauti.options.*;
import dr.app.beauti.types.TreePriorType;
import dr.app.beauti.util.XMLWriter;
import dr.evolution.datatype.DataType;
import dr.evolution.util.Taxa;
import dr.evomodel.branchratemodel.BranchRateModel;
import dr.evomodel.tree.TreeModel;
import dr.evomodelxml.branchratemodel.*;
import dr.oldevomodelxml.clock.ACLikelihoodParser;
import dr.evomodelxml.coalescent.CoalescentLikelihoodParser;
import dr.evomodelxml.coalescent.GMRFSkyrideLikelihoodParser;
import dr.evomodelxml.speciation.*;
import dr.evomodelxml.tree.TMRCAStatisticParser;
import dr.evomodelxml.tree.TreeLoggerParser;
import dr.evomodelxml.tree.TreeModelParser;
import dr.inference.model.ParameterParser;
import dr.inferencexml.distribution.MixedDistributionLikelihoodParser;
import dr.inferencexml.loggers.ColumnsParser;
import dr.inferencexml.loggers.LoggerParser;
import dr.inferencexml.model.CompoundLikelihoodParser;
import dr.util.Attribute;
import dr.xml.XMLParser;
import java.util.ArrayList;
import java.util.List;
/**
* @author Alexei Drummond
* @author Andrew Rambaut
* @author Walter Xie
*/
public class LogGenerator extends Generator {
private final static String TREE_FILE_LOG = "treeFileLog";
private final static String SUB_TREE_FILE_LOG = "substTreeFileLog";
public LogGenerator(BeautiOptions options, ComponentFactory[] components) {
super(options, components);
}
/**
* write log to screen
*
* @param writer XMLWriter
* @param clockModelGenerator ClockModelGenerator
*/
public void writeLogToScreen(XMLWriter writer, ClockModelGenerator clockModelGenerator,
SubstitutionModelGenerator substitutionModelGenerator) {
writer.writeComment("write log to screen");
writer.writeOpenTag(LoggerParser.LOG,
new Attribute[]{
new Attribute.Default<String>(XMLParser.ID, "screenLog"),
new Attribute.Default<String>(LoggerParser.LOG_EVERY, options.echoEvery + "")
});
if (options.hasData()) {
writer.writeOpenTag(ColumnsParser.COLUMN,
new Attribute[]{
new Attribute.Default<String>(ColumnsParser.LABEL, "Posterior"),
new Attribute.Default<String>(ColumnsParser.DECIMAL_PLACES, "4"),
new Attribute.Default<String>(ColumnsParser.WIDTH, "12")
}
);
writer.writeIDref(CompoundLikelihoodParser.POSTERIOR, "posterior");
writer.writeCloseTag(ColumnsParser.COLUMN);
}
writer.writeOpenTag(ColumnsParser.COLUMN,
new Attribute[]{
new Attribute.Default<String>(ColumnsParser.LABEL, "Prior"),
new Attribute.Default<String>(ColumnsParser.DECIMAL_PLACES, "4"),
new Attribute.Default<String>(ColumnsParser.WIDTH, "12")
}
);
writer.writeIDref(CompoundLikelihoodParser.PRIOR, "prior");
writer.writeCloseTag(ColumnsParser.COLUMN);
if (options.hasData()) {
writer.writeOpenTag(ColumnsParser.COLUMN,
new Attribute[]{
new Attribute.Default<String>(ColumnsParser.LABEL, "Likelihood"),
new Attribute.Default<String>(ColumnsParser.DECIMAL_PLACES, "4"),
new Attribute.Default<String>(ColumnsParser.WIDTH, "12")
}
);
writer.writeIDref(CompoundLikelihoodParser.LIKELIHOOD, "likelihood");
writer.writeCloseTag(ColumnsParser.COLUMN);
}
if (options.useStarBEAST) { // species
writer.writeOpenTag(ColumnsParser.COLUMN,
new Attribute[]{
new Attribute.Default<String>(ColumnsParser.LABEL, "PopMean"),
new Attribute.Default<String>(ColumnsParser.DECIMAL_PLACES, "4"),
new Attribute.Default<String>(ColumnsParser.WIDTH, "12")
}
);
writer.writeIDref(ParameterParser.PARAMETER, TraitData.TRAIT_SPECIES + "." + options.starBEASTOptions.POP_MEAN);
writer.writeCloseTag(ColumnsParser.COLUMN);
}
for (PartitionTreeModel model : options.getPartitionTreeModels()) {
writer.writeOpenTag(ColumnsParser.COLUMN,
new Attribute[]{
// new Attribute.Default<String>(ColumnsParser.LABEL, model.getPrefix() + TreeModelParser.ROOT_HEIGHT),
// Switching to use 'rootAge' in screen log (an absolute date if tip dates are used)
(options.getPartitionTreeModels().size() > 1 ?
new Attribute.Default<String>(ColumnsParser.LABEL, model.getPrefix() + ".rootAge"):
new Attribute.Default<String>(ColumnsParser.LABEL, "rootAge")
),
new Attribute.Default<String>(ColumnsParser.SIGNIFICANT_FIGURES, "6"),
new Attribute.Default<String>(ColumnsParser.WIDTH, "12")
}
);
// writer.writeIDref(ParameterParser.PARAMETER, model.getPrefix() + TreeModel.TREE_MODEL + "." + TreeModelParser.ROOT_HEIGHT);
// Switching to use 'rootAge' in screen log (an absolute date if tip dates are used)
writer.writeIDref(TMRCAStatisticParser.TMRCA_STATISTIC, model.getPrefix() + TreeModel.TREE_MODEL + ".rootAge");
writer.writeCloseTag(ColumnsParser.COLUMN);
}
for (PartitionClockModel model : options.getPartitionClockModels()) {
if (!model.getClockRateParameter().isFixed()) {
writer.writeOpenTag(ColumnsParser.COLUMN,
new Attribute[]{
new Attribute.Default<String>(ColumnsParser.LABEL, clockModelGenerator.getClockRateString(model)),
new Attribute.Default<String>(ColumnsParser.SIGNIFICANT_FIGURES, "6"),
new Attribute.Default<String>(ColumnsParser.WIDTH, "12")
}
);
clockModelGenerator.writeAllClockRateRefs(model, writer);
writer.writeCloseTag(ColumnsParser.COLUMN);
}
}
for (PartitionSubstitutionModel model : options.getPartitionSubstitutionModels()) {
if (model.getDataType().getType() == DataType.MICRO_SAT)
substitutionModelGenerator.writeMicrosatSubstModelParameterRef(model, writer);
}
generateInsertionPoint(ComponentGenerator.InsertionPoint.IN_SCREEN_LOG, writer);
writer.writeCloseTag(LoggerParser.LOG);
generateInsertionPoint(ComponentGenerator.InsertionPoint.AFTER_SCREEN_LOG, writer);
}
/**
* write log to file
*
* @param writer XMLWriter
* @param treePriorGenerator TreePriorGenerator
* @param clockModelGenerator ClockModelGenerator
* @param substitutionModelGenerator SubstitutionModelGenerator
* @param treeLikelihoodGenerator TreeLikelihoodGenerator
*/
public void writeLogToFile(XMLWriter writer,
TreePriorGenerator treePriorGenerator,
ClockModelGenerator clockModelGenerator,
SubstitutionModelGenerator substitutionModelGenerator,
TreeLikelihoodGenerator treeLikelihoodGenerator) {
writer.writeComment("write log to file");
if (options.logFileName == null) {
options.logFileName = options.fileNameStem + ".log";
}
writer.writeOpenTag(LoggerParser.LOG,
new Attribute[]{
new Attribute.Default<String>(XMLParser.ID, "fileLog"),
new Attribute.Default<String>(LoggerParser.LOG_EVERY, options.logEvery + ""),
new Attribute.Default<String>(LoggerParser.FILE_NAME, options.logFileName),
new Attribute.Default<Boolean>(LoggerParser.ALLOW_OVERWRITE_LOG, options.allowOverwriteLog)
});
if (options.hasData()) {
writer.writeIDref(CompoundLikelihoodParser.POSTERIOR, "posterior");
}
writer.writeIDref(CompoundLikelihoodParser.PRIOR, "prior");
if (options.hasData()) {
writer.writeIDref(CompoundLikelihoodParser.LIKELIHOOD, "likelihood");
}
if (options.useStarBEAST) { // species
// coalescent prior
writer.writeIDref(MultiSpeciesCoalescentParser.SPECIES_COALESCENT, TraitData.TRAIT_SPECIES + "." + COALESCENT);
// prior on population sizes
// if (options.speciesTreePrior == TreePriorType.SPECIES_YULE) {
writer.writeIDref(MixedDistributionLikelihoodParser.DISTRIBUTION_LIKELIHOOD, SPOPS);
// } else {
// writer.writeIDref(SpeciesTreeBMPrior.STPRIOR, STP);
// }
// prior on species tree
writer.writeIDref(SpeciationLikelihoodParser.SPECIATION_LIKELIHOOD, SPECIATION_LIKE);
writer.writeIDref(ParameterParser.PARAMETER, TraitData.TRAIT_SPECIES + "." + options.starBEASTOptions.POP_MEAN);
writer.writeIDref(ParameterParser.PARAMETER, SpeciesTreeModelParser.SPECIES_TREE + "." + SPLIT_POPS);
if (options.getPartitionTreePriors().get(0).getNodeHeightPrior() == TreePriorType.SPECIES_BIRTH_DEATH) {
writer.writeIDref(ParameterParser.PARAMETER, TraitData.TRAIT_SPECIES + "." + BirthDeathModelParser.MEAN_GROWTH_RATE_PARAM_NAME);
writer.writeIDref(ParameterParser.PARAMETER, TraitData.TRAIT_SPECIES + "." + BirthDeathModelParser.RELATIVE_DEATH_RATE_PARAM_NAME);
} else if (options.getPartitionTreePriors().get(0).getNodeHeightPrior() == TreePriorType.SPECIES_YULE ||
options.getPartitionTreePriors().get(0).getNodeHeightPrior() == TreePriorType.SPECIES_YULE_CALIBRATION) {
writer.writeIDref(ParameterParser.PARAMETER, TraitData.TRAIT_SPECIES + "." + YuleModelParser.YULE + "." + YuleModelParser.BIRTH_RATE);
} else {
throw new IllegalArgumentException("Get wrong species tree prior using *BEAST : " + options.getPartitionTreePriors().get(0).getNodeHeightPrior().toString());
}
//Species Tree: tmrcaStatistic
writer.writeIDref(TMRCAStatisticParser.TMRCA_STATISTIC, SpeciesTreeModelParser.SPECIES_TREE + "." + TreeModelParser.ROOT_HEIGHT);
}
for (PartitionTreeModel model : options.getPartitionTreeModels()) {
writer.writeIDref(ParameterParser.PARAMETER, model.getPrefix() + TreeModel.TREE_MODEL + "." + TreeModelParser.ROOT_HEIGHT);
}
// for convenience, log root age statistic - gives the absolute age of the root given the tip dates.
// @todo check for redundancy with rootHeight - if no tip dates or given as heights (time before present)
for (PartitionTreeModel model : options.getPartitionTreeModels()) {
if (model.hasTipCalibrations()) {
writer.writeIDref(TMRCAStatisticParser.TMRCA_STATISTIC, model.getPrefix() + TreeModel.TREE_MODEL + ".rootAge");
}
}
if (options.useStarBEAST) {
for (Taxa taxa : options.speciesSets) {
// make tmrca(tree.name) eay to read in log for Tracer
writer.writeIDref(TMRCAStatisticParser.TMRCA_STATISTIC, "tmrca(" + taxa.getId() + ")");
}
} else {
for (Taxa taxa : options.taxonSets) {
// make tmrca(tree.name) eay to read in log for Tracer
PartitionTreeModel treeModel = options.taxonSetsTreeModel.get(taxa);
writer.writeIDref(TMRCAStatisticParser.TMRCA_STATISTIC, "tmrca(" + treeModel.getPrefix() + taxa.getId() + ")");
}
}
for (PartitionTreePrior prior : options.getPartitionTreePriors()) {
treePriorGenerator.writeParameterLog(prior, writer);
}
for (PartitionSubstitutionModel model : options.getPartitionSubstitutionModels()) {
substitutionModelGenerator.writeLog(model, writer);
}
for (PartitionClockModel model : options.getPartitionClockModels()) {
clockModelGenerator.writeLog(model, writer);
}
for (PartitionClockModel model : options.getPartitionClockModels()) {
clockModelGenerator.writeLogStatistic(model, writer);
}
generateInsertionPoint(ComponentGenerator.InsertionPoint.IN_FILE_LOG_PARAMETERS, writer);
treeLikelihoodGenerator.writeTreeLikelihoodReferences(writer);
clockModelGenerator.writeClockLikelihoodReferences(writer);
generateInsertionPoint(ComponentGenerator.InsertionPoint.IN_FILE_LOG_LIKELIHOODS, writer);
// coalescentLikelihood
for (PartitionTreeModel model : options.getPartitionTreeModels()) {
PartitionTreePrior prior = model.getPartitionTreePrior();
treePriorGenerator.writePriorLikelihoodReferenceLog(prior, model, writer);
writer.writeText("");
}
for (PartitionTreePrior prior : options.getPartitionTreePriors()) {
if (prior.getNodeHeightPrior() == TreePriorType.EXTENDED_SKYLINE) {
writer.writeIDref(CoalescentLikelihoodParser.COALESCENT_LIKELIHOOD, prior.getPrefix() + COALESCENT); // only 1 coalescent
} else if (prior.getNodeHeightPrior() == TreePriorType.SKYGRID) {
writer.writeIDref(GMRFSkyrideLikelihoodParser.SKYGRID_LIKELIHOOD, prior.getPrefix() + "skygrid");
}
}
writer.writeCloseTag(LoggerParser.LOG);
generateInsertionPoint(ComponentGenerator.InsertionPoint.AFTER_FILE_LOG, writer);
}
public void writeDemographicLogToFile(XMLWriter writer,
TreePriorGenerator treePriorGenerator,
ClockModelGenerator clockModelGenerator,
SubstitutionModelGenerator substitutionModelGenerator,
TreeLikelihoodGenerator treeLikelihoodGenerator) {
writer.writeComment("demographic log file");
if (options.demographicLogFileName == null) {
options.demographicLogFileName = options.fileNameStem + ".demo.log";
}
String header = "Demographic Model: " + options.demographicModelName;
writer.writeOpenTag(LoggerParser.LOG,
new Attribute[]{
new Attribute.Default<String>(XMLParser.ID, "fileLog"),
new Attribute.Default<String>(LoggerParser.HEADER, header + ""),
new Attribute.Default<String>(LoggerParser.LOG_EVERY, options.logEvery + ""),
new Attribute.Default<String>(LoggerParser.FILE_NAME, options.logFileName),
new Attribute.Default<Boolean>(LoggerParser.ALLOW_OVERWRITE_LOG, options.allowOverwriteLog)
});
if (options.hasData()) {
writer.writeIDref(CompoundLikelihoodParser.POSTERIOR, "posterior");
}
writer.writeIDref(CompoundLikelihoodParser.PRIOR, "prior");
for (PartitionTreeModel model : options.getPartitionTreeModels()) {
writer.writeIDref(ParameterParser.PARAMETER, model.getPrefix() + TreeModel.TREE_MODEL + "." + TreeModelParser.ROOT_HEIGHT);
}
if (options.useStarBEAST) {
for (Taxa taxa : options.speciesSets) {
// make tmrca(tree.name) eay to read in log for Tracer
writer.writeIDref(TMRCAStatisticParser.TMRCA_STATISTIC, "tmrca(" + taxa.getId() + ")");
}
} else {
for (Taxa taxa : options.taxonSets) {
// make tmrca(tree.name) eay to read in log for Tracer
PartitionTreeModel treeModel = options.taxonSetsTreeModel.get(taxa);
writer.writeIDref(TMRCAStatisticParser.TMRCA_STATISTIC, "tmrca(" + treeModel.getPrefix() + taxa.getId() + ")");
}
}
// if ( options.shareSameTreePrior ) { // Share Same Tree Prior
// treePriorGenerator.setModelPrefix("");
// treePriorGenerator.writeParameterLog(options.activedSameTreePrior, writer);
// } else { // no species
for (PartitionTreePrior prior : options.getPartitionTreePriors()) {
// treePriorGenerator.setModelPrefix(prior.getPrefix()); // priorName.treeModel
treePriorGenerator.writeParameterLog(prior, writer);
}
// }
for (PartitionSubstitutionModel model : options.getPartitionSubstitutionModels()) {
substitutionModelGenerator.writeLog(model, writer);
}
for (PartitionClockModel model : options.getPartitionClockModels()) {
// if (model.getRateTypeOption() == FixRateType.FIXED_MEAN) {
// writer.writeIDref(ParameterParser.PARAMETER, model.getName());
// if (model.getClockType() == ClockType.UNCORRELATED) {
// switch (model.getClockDistributionType()) {
// case LOGNORMAL:
// writer.writeIDref(ParameterParser.PARAMETER, model.getPrefix() + ClockType.UCLD_STDEV);
// break;
// case GAMMA:
// throw new UnsupportedOperationException("Uncorrelated gamma model not implemented yet");
//// break;
// case CAUCHY:
// throw new UnsupportedOperationException("Uncorrelated Cauchy model not implemented yet");
//// break;
// case EXPONENTIAL:
// // nothing required
// break;
// }
// }
// }
clockModelGenerator.writeLog(model, writer);
}
for (PartitionClockModel model : options.getPartitionClockModels()) {
clockModelGenerator.writeLogStatistic(model, writer);
}
generateInsertionPoint(ComponentGenerator.InsertionPoint.IN_FILE_LOG_PARAMETERS, writer);
treeLikelihoodGenerator.writeTreeLikelihoodReferences(writer);
clockModelGenerator.writeClockLikelihoodReferences(writer);
generateInsertionPoint(ComponentGenerator.InsertionPoint.IN_FILE_LOG_LIKELIHOODS, writer);
// coalescentLikelihood
for (PartitionTreeModel model : options.getPartitionTreeModels()) {
PartitionTreePrior prior = model.getPartitionTreePrior();
treePriorGenerator.writePriorLikelihoodReferenceLog(prior, model, writer);
writer.writeText("");
}
for (PartitionTreePrior prior : options.getPartitionTreePriors()) {
if (prior.getNodeHeightPrior() == TreePriorType.EXTENDED_SKYLINE) {
writer.writeIDref(CoalescentLikelihoodParser.COALESCENT_LIKELIHOOD, prior.getPrefix() + COALESCENT); // only 1 coalescent
} else if (prior.getNodeHeightPrior() == TreePriorType.SKYGRID) {
writer.writeIDref(GMRFSkyrideLikelihoodParser.SKYGRID_LIKELIHOOD, prior.getPrefix() + "skygrid");
}
}
writer.writeCloseTag(LoggerParser.LOG);
generateInsertionPoint(ComponentGenerator.InsertionPoint.AFTER_FILE_LOG, writer);
}
/**
* write tree log to file
*
* @param writer XMLWriter
*/
public void writeTreeLogToFile(XMLWriter writer) {
writer.writeComment("write tree log to file");
if (options.useStarBEAST) { // species
// species tree log
writer.writeOpenTag(TreeLoggerParser.LOG_TREE,
new Attribute[]{
new Attribute.Default<String>(XMLParser.ID, TraitData.TRAIT_SPECIES + "." + TREE_FILE_LOG), // speciesTreeFileLog
new Attribute.Default<String>(TreeLoggerParser.LOG_EVERY, options.logEvery + ""),
new Attribute.Default<String>(TreeLoggerParser.NEXUS_FORMAT, "true"),
new Attribute.Default<String>(TreeLoggerParser.FILE_NAME, options.fileNameStem + "." + options.starBEASTOptions.SPECIES_TREE_FILE_NAME),
new Attribute.Default<String>(TreeLoggerParser.SORT_TRANSLATION_TABLE, "true")
});
writer.writeIDref(SpeciesTreeModelParser.SPECIES_TREE, SP_TREE);
if (options.hasData()) {
// we have data...
writer.writeIDref("posterior", "posterior");
}
writer.writeCloseTag(TreeLoggerParser.LOG_TREE);
}
// gene tree log
//TODO make code consistent to MCMCPanel
for (PartitionTreeModel tree : options.getPartitionTreeModels()) {
String treeFileName;
if (options.substTreeLog) {
treeFileName = options.fileNameStem + "." + tree.getPrefix() + "(time)." + STARBEASTOptions.TREE_FILE_NAME;
} else {
treeFileName = options.fileNameStem + "." + tree.getPrefix() + STARBEASTOptions.TREE_FILE_NAME; // stem.partitionName.tree
}
if (options.treeFileName.get(0).endsWith(".txt")) {
treeFileName += ".txt";
}
List<Attribute> attributes = new ArrayList<Attribute>();
attributes.add(new Attribute.Default<String>(XMLParser.ID, tree.getPrefix() + TREE_FILE_LOG)); // partionName.treeFileLog
attributes.add(new Attribute.Default<String>(TreeLoggerParser.LOG_EVERY, options.logEvery + ""));
attributes.add(new Attribute.Default<String>(TreeLoggerParser.NEXUS_FORMAT, "true"));
attributes.add(new Attribute.Default<String>(TreeLoggerParser.FILE_NAME, treeFileName));
attributes.add(new Attribute.Default<String>(TreeLoggerParser.SORT_TRANSLATION_TABLE, "true"));
//if (options.clockModelOptions.getRateOptionClockModel() == FixRateType.RElATIVE_TO && tree.containsUncorrelatedRelaxClock()) { //TODO: Sibon's discretized branch length stuff
// double aveFixedRate = options.clockModelOptions.getSelectedRate(options.getPartitionClockModels());
// attributes.add(new Attribute.Default<String>(TreeLoggerParser.NORMALISE_MEAN_RATE_TO, Double.toString(aveFixedRate)));
//}
// generate <logTree>
writer.writeOpenTag(TreeLoggerParser.LOG_TREE, attributes);
// writer.writeOpenTag(TreeLoggerParser.LOG_TREE,
// new Attribute[]{
// new Attribute.Default<String>(XMLParser.ID, tree.getPrefix() + TREE_FILE_LOG), // partionName.treeFileLog
// new Attribute.Default<String>(TreeLoggerParser.LOG_EVERY, options.logEvery + ""),
// new Attribute.Default<String>(TreeLoggerParser.NEXUS_FORMAT, "true"),
// new Attribute.Default<String>(TreeLoggerParser.FILE_NAME, treeFileName),
// new Attribute.Default<String>(TreeLoggerParser.SORT_TRANSLATION_TABLE, "true")
// });
writer.writeIDref(TreeModel.TREE_MODEL, tree.getPrefix() + TreeModel.TREE_MODEL);
writeTreeTraits(writer, tree);
if (options.hasData()) {
// we have data...
writer.writeIDref("posterior", "posterior");
}
generateInsertionPoint(ComponentGenerator.InsertionPoint.IN_TREES_LOG, tree, writer);
writer.writeCloseTag(TreeLoggerParser.LOG_TREE);
} // end For loop
if (options.substTreeLog) {
if (options.useStarBEAST) { // species
//TODO: species sub tree
}
// gene tree
for (PartitionTreeModel tree : options.getPartitionTreeModels()) {
// write tree log to file
writer.writeOpenTag(TreeLoggerParser.LOG_TREE,
new Attribute[]{
new Attribute.Default<String>(XMLParser.ID, tree.getPrefix() + SUB_TREE_FILE_LOG),
new Attribute.Default<String>(TreeLoggerParser.LOG_EVERY, options.logEvery + ""),
new Attribute.Default<String>(TreeLoggerParser.NEXUS_FORMAT, "true"),
new Attribute.Default<String>(TreeLoggerParser.FILE_NAME, options.fileNameStem + "." + tree.getPrefix() +
"(subst)." + STARBEASTOptions.TREE_FILE_NAME),
new Attribute.Default<String>(TreeLoggerParser.BRANCH_LENGTHS, TreeLoggerParser.SUBSTITUTIONS)
});
writer.writeIDref(TreeModel.TREE_MODEL, tree.getPrefix() + TreeModel.TREE_MODEL);
PartitionClockModel model = options.getPartitionClockModels(options.getDataPartitions(tree)).get(0);
String tag = "";
String id = "";
switch (model.getClockType()) {
case STRICT_CLOCK:
tag = StrictClockBranchRatesParser.STRICT_CLOCK_BRANCH_RATES;
id = model.getPrefix() + BranchRateModel.BRANCH_RATES;
break;
case UNCORRELATED:
tag = model.isContinuousQuantile() ?
ContinuousBranchRatesParser.CONTINUOUS_BRANCH_RATES :
DiscretizedBranchRatesParser.DISCRETIZED_BRANCH_RATES;
id = model.getPrefix() + BranchRateModel.BRANCH_RATES;
break;
case RANDOM_LOCAL_CLOCK:
tag = RandomLocalClockModelParser.LOCAL_BRANCH_RATES;
id = model.getPrefix() + BranchRateModel.BRANCH_RATES;
break;
case FIXED_LOCAL_CLOCK:
tag = LocalClockModelParser.LOCAL_CLOCK_MODEL;
id = model.getPrefix() + BranchRateModel.BRANCH_RATES;
break;
case AUTOCORRELATED:
tag = ACLikelihoodParser.AC_LIKELIHOOD;
id = options.noDuplicatedPrefix(model.getPrefix(), tree.getPrefix()) + BranchRateModel.BRANCH_RATES;
break;
default:
throw new IllegalArgumentException("Unknown clock model");
}
writer.writeIDref(tag, id);
writeTreeTrait(writer, tag, id, BranchRateModel.RATE, model.getPrefix() + BranchRateModel.RATE);
writer.writeCloseTag(TreeLoggerParser.LOG_TREE);
}
}
generateInsertionPoint(ComponentGenerator.InsertionPoint.AFTER_TREES_LOG, writer);
}
private void writeTreeTraits(XMLWriter writer, PartitionTreeModel tree) {
for (PartitionClockModel model : options.getPartitionClockModels(options.getDataPartitions(tree))) {
switch (model.getClockType()) {
case STRICT_CLOCK:
writeTreeTrait(writer, StrictClockBranchRatesParser.STRICT_CLOCK_BRANCH_RATES,
model.getPrefix() + BranchRateModel.BRANCH_RATES,
BranchRateModel.RATE, model.getPrefix() + BranchRateModel.RATE);
break;
case UNCORRELATED:
writeTreeTrait(writer, model.isContinuousQuantile() ?
ContinuousBranchRatesParser.CONTINUOUS_BRANCH_RATES :
DiscretizedBranchRatesParser.DISCRETIZED_BRANCH_RATES,
options.noDuplicatedPrefix(model.getPrefix(), tree.getPrefix()) + BranchRateModel.BRANCH_RATES,
BranchRateModel.RATE, model.getPrefix() + BranchRateModel.RATE);
break;
case RANDOM_LOCAL_CLOCK:
writeTreeTrait(writer, RandomLocalClockModelParser.LOCAL_BRANCH_RATES,
model.getPrefix() + BranchRateModel.BRANCH_RATES,
BranchRateModel.RATE, model.getPrefix() + BranchRateModel.RATE);
break;
case FIXED_LOCAL_CLOCK:
writeTreeTrait(writer, LocalClockModelParser.LOCAL_CLOCK_MODEL,
options.noDuplicatedPrefix(model.getPrefix(), tree.getPrefix()) + BranchRateModel.BRANCH_RATES,
BranchRateModel.RATE, model.getPrefix() + BranchRateModel.RATE);
break;
case AUTOCORRELATED:
writer.writeIDref(ACLikelihoodParser.AC_LIKELIHOOD,
options.noDuplicatedPrefix(model.getPrefix(), tree.getPrefix()) + BranchRateModel.BRANCH_RATES);
writeTreeTrait(writer, ACLikelihoodParser.AC_LIKELIHOOD,
options.noDuplicatedPrefix(model.getPrefix(), tree.getPrefix()) + BranchRateModel.BRANCH_RATES,
BranchRateModel.RATE, model.getPrefix() + BranchRateModel.RATE);
break;
default:
throw new IllegalArgumentException("Unknown clock model");
}
}
}
private void writeTreeTrait(XMLWriter writer, String treeTraitTag, String treeTraitID, String traitName, String traitTag) {
writer.writeOpenTag(TreeLoggerParser.TREE_TRAIT,
new Attribute[]{
new Attribute.Default<String>(TreeLoggerParser.NAME, traitName),
new Attribute.Default<String>(TreeLoggerParser.TAG, traitTag)
});
writer.writeIDref(treeTraitTag, treeTraitID);
writer.writeCloseTag(TreeLoggerParser.TREE_TRAIT);
}
}