@Override
 public void makeDirty() {
   super.makeDirty();
   updateSiteModel = true;
   updateSubstitutionModel = true;
   updateRestrictedNodePartials = true;
 }
  /** Stores the additional state other than model components */
  protected void storeState() {
    partialBufferHelper.storeState();
    substitutionModelDelegate.storeState();

    if (useScaleFactors || useAutoScaling) { // Only store when actually used
      scaleBufferHelper.storeState();
      System.arraycopy(
          scaleBufferIndices, 0, storedScaleBufferIndices, 0, scaleBufferIndices.length);
      //            storedRescalingCount = rescalingCount;
    }

    super.storeState();
  }
  /** Restore the additional stored state */
  protected void restoreState() {
    updateSiteModel =
        true; // this is required to upload the categoryRates to BEAGLE after the restore

    partialBufferHelper.restoreState();
    substitutionModelDelegate.restoreState();

    if (useScaleFactors || useAutoScaling) {
      scaleBufferHelper.restoreState();
      int[] tmp = storedScaleBufferIndices;
      storedScaleBufferIndices = scaleBufferIndices;
      scaleBufferIndices = tmp;
      //            rescalingCount = storedRescalingCount;
    }

    updateRestrictedNodePartials = true;

    super.restoreState();
  }
  /** Handles model changed events from the submodels. */
  protected void handleModelChangedEvent(Model model, Object object, int index) {

    fireModelChanged();

    if (model == treeModel) {
      if (object instanceof TreeModel.TreeChangedEvent) {

        if (((TreeModel.TreeChangedEvent) object).isNodeChanged()) {
          // If a node event occurs the node and its two child nodes
          // are flagged for updating (this will result in everything
          // above being updated as well. Node events occur when a node
          // is added to a branch, removed from a branch or its height or
          // rate changes.
          updateNodeAndChildren(((TreeModel.TreeChangedEvent) object).getNode());
          updateRestrictedNodePartials = true;

        } else if (((TreeModel.TreeChangedEvent) object).isTreeChanged()) {
          // Full tree events result in a complete updating of the tree likelihood
          // This event type is now used for EmpiricalTreeDistributions.
          //                    System.err.println("Full tree update event - these events currently
          // aren't used\n" +
          //                            "so either this is in error or a new feature is using them
          // so remove this message.");
          updateAllNodes();
          updateRestrictedNodePartials = true;
        } else {
          // Other event types are ignored (probably trait changes).
          // System.err.println("Another tree event has occured (possibly a trait change).");
        }
      }

    } else if (model == branchRateModel) {
      if (index == -1) {
        if (COUNT_TOTAL_OPERATIONS) totalRateUpdateAllCount++;
        updateAllNodes();
      } else {
        if (COUNT_TOTAL_OPERATIONS) totalRateUpdateSingleCount++;
        updateNode(treeModel.getNode(index));
      }

    } else if (model == branchModel) {
      //            if (index == -1) {
      //                updateSubstitutionModel = true;
      //                updateAllNodes();
      //            } else {
      //                updateNode(treeModel.getNode(index));
      //            }

      makeDirty();

    } else if (model == siteModel) {

      updateSiteModel = true;
      updateAllNodes();

    } else if (model == tipStatesModel) {
      if (object instanceof Taxon) {
        for (int i = 0; i < treeModel.getNodeCount(); i++)
          if (treeModel.getNodeTaxon(treeModel.getNode(i)) != null
              && treeModel
                  .getNodeTaxon(treeModel.getNode(i))
                  .getId()
                  .equalsIgnoreCase(((Taxon) object).getId())) updateNode(treeModel.getNode(i));
      } else updateAllNodes();
    } else {

      throw new RuntimeException("Unknown componentChangedEvent");
    }

    super.handleModelChangedEvent(model, object, index);
  }