/*
* ModelPanel.java
*
* Copyright (C) 2002-2006 Alexei Drummond and Andrew Rambaut
*
* 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.oldbeauti;
import dr.app.gui.components.RealNumberField;
import dr.evolution.datatype.DataType;
import jam.framework.Exportable;
import jam.panels.OptionsPanel;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
/**
* @author Andrew Rambaut
* @author Alexei Drummond
* @version $Id: ModelPanel.java,v 1.17 2006/09/05 13:29:34 rambaut Exp $
*/
public class ModelPanel extends OptionsPanel implements Exportable {
/**
*
*/
private static final long serialVersionUID = 2778103564318492601L;
JComboBox nucSubstCombo = new JComboBox(new String[] {"HKY", "GTR"});
JComboBox aaSubstCombo = new JComboBox(new String[] {"Blosum62", "Dayhoff", "JTT", "mtREV", "cpREV", "WAG"});
JComboBox binarySubstCombo = new JComboBox(new String[] {"Simple", "Covarion"});
JComboBox frequencyCombo =new JComboBox(new String[] {"Estimated", "Empirical", "All equal"});
JComboBox heteroCombo = new JComboBox(new String[] {"None", "Gamma", "Invariant Sites", "Gamma + Invariant Sites"});
JComboBox gammaCatCombo = new JComboBox(new String[] {"4", "5", "6", "7", "8", "9", "10"});
JLabel gammaCatLabel;
JComboBox codingCombo = new JComboBox(new String[] {
"Off",
"2 partitions: codon positions (1 + 2), 3",
"3 partitions: codon positions 1, 2, 3"});
JCheckBox substUnlinkCheck = new JCheckBox("Unlink substitution model across codon positions");
JCheckBox heteroUnlinkCheck = new JCheckBox("Unlink rate heterogeneity model across codon positions");
JCheckBox freqsUnlinkCheck = new JCheckBox("Unlink base frequencies across codon positions");
JButton setSRD06Button;
JCheckBox fixedSubstitutionRateCheck = new JCheckBox("Fix mean substitution rate:");
JLabel substitutionRateLabel = new JLabel("Mean substitution rate:");
RealNumberField substitutionRateField = new RealNumberField(Double.MIN_VALUE, Double.POSITIVE_INFINITY);
JComboBox clockModelCombo = new JComboBox(new String[] {
"Strict Clock",
"Random Local Clock",
"Relaxed Clock: Uncorrelated Lognormal",
"Relaxed Clock: Uncorrelated Exponential" } );
BeautiFrame frame = null;
boolean warningShown = false;
boolean hasSetFixedSubstitutionRate = false;
boolean settingOptions = false;
boolean hasAlignment = false;
int dataType = DataType.NUCLEOTIDES;
public ModelPanel(BeautiFrame parent) {
super(12, 18);
this.frame = parent;
setOpaque(false);
setupComponent(substUnlinkCheck);
substUnlinkCheck.setEnabled(false);
substUnlinkCheck.setToolTipText("" +
"<html>Gives each codon position partition different<br>" +
"substitution model parameters.</html>");
setupComponent(heteroUnlinkCheck);
heteroUnlinkCheck.setEnabled(false);
heteroUnlinkCheck.setToolTipText("<html>Gives each codon position partition different<br>rate heterogeneity model parameters.</html>");
setupComponent(freqsUnlinkCheck);
freqsUnlinkCheck.setEnabled(false);
freqsUnlinkCheck.setToolTipText("<html>Gives each codon position partition different<br>nucleotide frequency parameters.</html>");
java.awt.event.ItemListener listener = new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent ev) {
frame.modelChanged();
}
};
setupComponent(nucSubstCombo);
nucSubstCombo.addItemListener(listener);
nucSubstCombo.setToolTipText("<html>Select the type of nucleotide substitution model.</html>");
setupComponent(aaSubstCombo);
aaSubstCombo.addItemListener(listener);
aaSubstCombo.setToolTipText("<html>Select the type of amino acid substitution model.</html>");
setupComponent(binarySubstCombo);
binarySubstCombo.addItemListener(listener);
binarySubstCombo.setToolTipText("<html>Select the type of binay substitution model.</html>");
setupComponent(frequencyCombo);
frequencyCombo.addItemListener(listener);
frequencyCombo.setToolTipText("<html>Select the policy for determining the base frequencies.</html>");
setupComponent(heteroCombo);
heteroCombo.setToolTipText("<html>Select the type of site-specific rate<br>heterogeneity model.</html>");
heteroCombo.addItemListener(
new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent ev) {
frame.modelChanged();
if (heteroCombo.getSelectedIndex() == 1 || heteroCombo.getSelectedIndex() == 3) {
gammaCatLabel.setEnabled(true);
gammaCatCombo.setEnabled(true);
} else {
gammaCatLabel.setEnabled(false);
gammaCatCombo.setEnabled(false);
}
}
}
);
setupComponent(gammaCatCombo);
gammaCatCombo.setToolTipText("<html>Select the number of categories to use for<br>the discrete gamma rate heterogeneity model.</html>");
gammaCatCombo.addItemListener(listener);
setupComponent(codingCombo);
codingCombo.setToolTipText("<html>Select how to partition the codon positions.</html>");
codingCombo.addItemListener(
new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent ev) {
frame.modelChanged();
if (codingCombo.getSelectedIndex() != 0) { // codon position partitioning
substUnlinkCheck.setEnabled(true);
heteroUnlinkCheck.setEnabled(true);
freqsUnlinkCheck.setEnabled(true);
} else {
substUnlinkCheck.setEnabled(false);
substUnlinkCheck.setSelected(false);
heteroUnlinkCheck.setEnabled(false);
heteroUnlinkCheck.setSelected(false);
freqsUnlinkCheck.setEnabled(false);
freqsUnlinkCheck.setSelected(false);
}
}
}
);
substUnlinkCheck.addItemListener(listener);
heteroUnlinkCheck.addItemListener(listener);
freqsUnlinkCheck.addItemListener(listener);
setSRD06Button = new JButton(setSRD06Action);
setupComponent(setSRD06Button);
setSRD06Button.setToolTipText("<html>Sets the SRD06 model as described in<br>" +
"Shapiro, Rambaut & Drummond (2006) <i>MBE</i> <b>23</b>: 7-9.</html>");
setupComponent(fixedSubstitutionRateCheck);
fixedSubstitutionRateCheck.setToolTipText(
"<html>Select this option to fix the substitution rate<br>" +
"rather than try to infer it. If this option is<br>" +
"turned off then either the sequences should have<br>" +
"dates or the tree should have sufficient calibration<br>" +
"informations specified as priors.</html>");
fixedSubstitutionRateCheck.addItemListener(
new java.awt.event.ItemListener() {
public void itemStateChanged(java.awt.event.ItemEvent ev) {
boolean fixed = fixedSubstitutionRateCheck.isSelected();
substitutionRateLabel.setEnabled(fixed);
substitutionRateField.setEnabled(fixed);
hasSetFixedSubstitutionRate = true;
frame.modelChanged();
}
}
);
setupComponent(substitutionRateField);
substitutionRateField.addKeyListener(new java.awt.event.KeyAdapter() {
public void keyTyped(java.awt.event.KeyEvent ev) {
frame.mcmcChanged();
}});
substitutionRateField.setToolTipText("<html>Enter the substitution rate here.</html>");
setupComponent(clockModelCombo);
clockModelCombo.setToolTipText("<html>Select either a strict molecular clock or<br>or a relaxed clock model.</html>");
clockModelCombo.addItemListener(listener);
setupPanel();
}
private void setupComponent(JComponent comp) {
comp.setOpaque(false);
//comp.setFont(UIManager.getFont("SmallSystemFont"));
//comp.putClientProperty("JComponent.sizeVariant", "small");
if (comp instanceof JButton) {
comp.putClientProperty("JButton.buttonType", "roundRect");
}
if (comp instanceof JComboBox) {
comp.putClientProperty("JComboBox.isSquare", Boolean.TRUE);
}
}
private void setupPanel() {
removeAll();
if (hasAlignment) {
switch (dataType){
case DataType.NUCLEOTIDES:
addComponentWithLabel("Substitution Model:", nucSubstCombo, true);
addComponentWithLabel("Base frequencies:", frequencyCombo);
addComponentWithLabel("Site Heterogeneity Model:", heteroCombo);
gammaCatLabel = addComponentWithLabel("Number of Gamma Categories:", gammaCatCombo);
addSeparator();
JPanel panel = new JPanel(new BorderLayout(6,6));
panel.setOpaque(false);
panel.add(codingCombo, BorderLayout.CENTER);
panel.add(setSRD06Button, BorderLayout.EAST);
addComponentWithLabel("Partition into codon positions:", panel);
panel = new JPanel();
panel.setOpaque(false);
panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
panel.setBorder(BorderFactory.createTitledBorder("Link/Unlink parameters:"));
panel.add(substUnlinkCheck);
panel.add(heteroUnlinkCheck);
panel.add(freqsUnlinkCheck);
addComponent(panel);
break;
case DataType.AMINO_ACIDS:
addComponentWithLabel("Substitution Model:", aaSubstCombo);
addComponentWithLabel("Site Heterogeneity Model:", heteroCombo);
gammaCatLabel = addComponentWithLabel("Number of Gamma Categories:", gammaCatCombo);
break;
case DataType.TWO_STATES:
case DataType.COVARION:
addComponentWithLabel("Substitution Model:", binarySubstCombo);
addComponentWithLabel("Site Heterogeneity Model:", heteroCombo);
gammaCatLabel = addComponentWithLabel("Number of Gamma Categories:", gammaCatCombo);
break;
default:
throw new IllegalArgumentException("Unknown data type");
}
addSeparator();
//addComponent(fixedSubstitutionRateCheck);
substitutionRateField.setColumns(10);
addComponents(fixedSubstitutionRateCheck, substitutionRateField);
addSeparator();
}
addComponentWithLabel("Molecular Clock Model:", clockModelCombo);
validate();
repaint();
}
private void setSRD06Model() {
nucSubstCombo.setSelectedIndex(0);
heteroCombo.setSelectedIndex(1);
codingCombo.setSelectedIndex(1);
substUnlinkCheck.setSelected(true);
heteroUnlinkCheck.setSelected(true);
}
public void setOptions(BeautiOptions options) {
settingOptions = true;
if (options.alignment != null) {
hasAlignment = true;
dataType=options.dataType;
switch(dataType){
case DataType.NUCLEOTIDES:
if (options.nucSubstitutionModel == BeautiOptions.GTR) {
nucSubstCombo.setSelectedIndex(1);
} else {
nucSubstCombo.setSelectedIndex(0);
}
frequencyCombo.setSelectedIndex(options.frequencyPolicy);
break;
case DataType.AMINO_ACIDS:
aaSubstCombo.setSelectedIndex(options.aaSubstitutionModel);
break;
case DataType.TWO_STATES:
case DataType.COVARION:
binarySubstCombo.setSelectedIndex(options.binarySubstitutionModel);
break;
default:
throw new IllegalArgumentException("Unknown data type");
}
} else {
hasAlignment = false;
}
if (options.gammaHetero && !options.invarHetero) {
heteroCombo.setSelectedIndex(1);
} else if (!options.gammaHetero && options.invarHetero) {
heteroCombo.setSelectedIndex(2);
} else if (options.gammaHetero && options.invarHetero) {
heteroCombo.setSelectedIndex(3);
} else {
heteroCombo.setSelectedIndex(0);
}
gammaCatCombo.setSelectedIndex(options.gammaCategories - 4);
if (options.codonHeteroPattern == null) {
codingCombo.setSelectedIndex(0);
} else if (options.codonHeteroPattern.equals("112")) {
codingCombo.setSelectedIndex(1);
} else {
codingCombo.setSelectedIndex(2);
}
substUnlinkCheck.setSelected(options.unlinkedSubstitutionModel);
heteroUnlinkCheck.setSelected(options.unlinkedHeterogeneityModel);
freqsUnlinkCheck.setSelected(options.unlinkedFrequencyModel);
hasSetFixedSubstitutionRate = options.hasSetFixedSubstitutionRate;
if (!hasSetFixedSubstitutionRate) {
if (options.maximumTipHeight > 0.0) {
options.meanSubstitutionRate = 0.001;
options.fixedSubstitutionRate = false;
} else {
options.meanSubstitutionRate = 1.0;
options.fixedSubstitutionRate = true;
}
}
fixedSubstitutionRateCheck.setSelected(options.fixedSubstitutionRate);
substitutionRateField.setValue(options.meanSubstitutionRate);
substitutionRateField.setEnabled(options.fixedSubstitutionRate);
switch (options.clockModel) {
case BeautiOptions.STRICT_CLOCK:
clockModelCombo.setSelectedIndex(0); break;
case BeautiOptions.RANDOM_LOCAL_CLOCK:
clockModelCombo.setSelectedIndex(1); break;
case BeautiOptions.UNCORRELATED_LOGNORMAL:
clockModelCombo.setSelectedIndex(2); break;
case BeautiOptions.UNCORRELATED_EXPONENTIAL:
clockModelCombo.setSelectedIndex(3); break;
default:
throw new IllegalArgumentException("Unknown option for clock model");
}
setupPanel();
settingOptions = false;
validate();
repaint();
}
public void getOptions(BeautiOptions options) {
// This prevents options be overwritten due to listeners calling
// this function (indirectly through modelChanged()) whilst in the
// middle of the setOptions() method.
if (settingOptions) return;
if (nucSubstCombo.getSelectedIndex() == 1) {
options.nucSubstitutionModel = BeautiOptions.GTR;
} else {
options.nucSubstitutionModel = BeautiOptions.HKY;
}
options.aaSubstitutionModel = aaSubstCombo.getSelectedIndex();
options.binarySubstitutionModel = binarySubstCombo.getSelectedIndex();
options.frequencyPolicy = frequencyCombo.getSelectedIndex();
options.gammaHetero = heteroCombo.getSelectedIndex() == 1 || heteroCombo.getSelectedIndex() == 3;
options.invarHetero = heteroCombo.getSelectedIndex() == 2 || heteroCombo.getSelectedIndex() == 3;
options.gammaCategories = gammaCatCombo.getSelectedIndex() + 4;
if (codingCombo.getSelectedIndex() == 0) {
options.codonHeteroPattern = null;
} else if (codingCombo.getSelectedIndex() == 1) {
options.codonHeteroPattern = "112";
} else {
options.codonHeteroPattern = "123";
}
options.unlinkedSubstitutionModel = substUnlinkCheck.isSelected();
options.unlinkedHeterogeneityModel = heteroUnlinkCheck.isSelected();
options.unlinkedFrequencyModel = freqsUnlinkCheck.isSelected();
options.hasSetFixedSubstitutionRate = hasSetFixedSubstitutionRate;
options.fixedSubstitutionRate = fixedSubstitutionRateCheck.isSelected();
options.meanSubstitutionRate = substitutionRateField.getValue();
boolean fixed = fixedSubstitutionRateCheck.isSelected();
if (!warningShown && !fixed && options.maximumTipHeight == 0.0) {
JOptionPane.showMessageDialog(frame,
"You have chosen to sample substitution rates but all \n"+
"the sequences have the same date. In order for this to \n"+
"work, a strong prior is required on the substitution\n"+
"rate or the root of the tree.",
"Warning",
JOptionPane.WARNING_MESSAGE);
warningShown = true;
}
switch (clockModelCombo.getSelectedIndex()) {
case 0:
options.clockModel = BeautiOptions.STRICT_CLOCK; break;
case 1:
options.clockModel = BeautiOptions.RANDOM_LOCAL_CLOCK; break;
case 2:
options.clockModel = BeautiOptions.UNCORRELATED_LOGNORMAL; break;
case 3:
options.clockModel = BeautiOptions.UNCORRELATED_EXPONENTIAL; break;
default:
throw new IllegalArgumentException("Unknown option for clock model");
}
}
public JComponent getExportableComponent() {
return this;
}
private Action setSRD06Action = new AbstractAction("Use SRD06 Model") {
public void actionPerformed(ActionEvent actionEvent) {
setSRD06Model();
}
};
}