/* 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.exe; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Observable; import java.util.Observer; import mpi.MPI; import mpi.Request; import es.uvigo.darwin.jmodeltest.ApplicationOptions; import es.uvigo.darwin.jmodeltest.ModelTest; import es.uvigo.darwin.jmodeltest.exception.InternalException; import es.uvigo.darwin.jmodeltest.io.TextOutputStream; import es.uvigo.darwin.jmodeltest.model.Model; import es.uvigo.darwin.jmodeltest.observer.ProgressInfo; /** * RunPhymlMPJ.java * * Description: Makes phyml calculate likelihood scores for competing models * @author Diego Darriba, University of Vigo / University of A Coruna, Spain * ddarriba@udc.es * @author David Posada, University of Vigo, Spain * dposada@uvigo.es | darwin.uvigo.es * @version 2.0.2 (Feb 2012) */ public class RunPhymlMPJ extends RunPhyml { private List<Model> myModels; // Synchronization package variables. // Thread safe under current operation. Keep in mind. volatile Model rootModel = null; volatile boolean rootModelRequest = false; public RunPhymlMPJ(Observer progress, ApplicationOptions options, Model[] models) { super(progress, options, models); // this.deleteObserver(progress); myModels = new ArrayList<Model>(); } public void distribute() { List<Model> modelList = Arrays.asList(models); Distributor distributor = new Distributor(modelList, this, ModelTest.MPJ_ME, ModelTest.MPJ_SIZE); distributor.addObserver(progress); Thread distributorThread = new Thread(distributor); distributorThread.start(); request(); for (Model model : ModelTest.getCandidateModels()) { model.update(modelList.get(modelList.indexOf(model))); } notifyObservers(ProgressInfo.OPTIMIZATION_COMPLETED_OK, models.length, null, null); } public void request() { List<PhymlSingleModel> phymlEstimatorList = new ArrayList<PhymlSingleModel>(); Model[] lastComputedModel = new Model[1]; while (true) { // send request to root Model[] modelToReceive = null; Model model = null; if (ModelTest.MPJ_ME > 0) { Request modelRequest = MPI.COMM_WORLD.Isend(lastComputedModel, 0, 1, MPI.OBJECT, 0, Distributor.TAG_SEND_REQUEST); // prepare reception modelToReceive = new Model[1]; // wait for request modelRequest.Wait(); // receive model Request modelReceive = MPI.COMM_WORLD.Irecv(modelToReceive, 0, 1, MPI.OBJECT, 0, Distributor.TAG_SEND_MODEL); modelReceive.Wait(); model = modelToReceive[0]; } else { // This strategy is an easy way to avoid the problem of // thread-safety in MPJ-Express. It works correctly, but // it also causes to introduce coupling between this class // and Distributor having to define two volatile attributes: // rootModelRequest and rootModel. rootModelRequest = true; while (rootModelRequest) { try { Thread.sleep(200); } catch (InterruptedException e) { throw new InternalException( "Thread interrupted"); } } model = rootModel; } if (model == null) break; else { // compute myModels.add(model); PhymlSingleModel runenv = new PhymlSingleModel(model, 0, false, false, options); runenv.addObserver(this); if (!runenv.compute()) throw new InternalException("Optimization error"); phymlEstimatorList.add(runenv); lastComputedModel[0] = runenv.getModel(); } } // endTime = System.currentTimeMillis(); } protected Object doPhyml() { return null; } // doPhyml public void execute() { if (ModelTest.MPJ_ME == 0) { printSettings(ModelTest.getMainConsole()); // TODO: Send topology to each processor // estimate a NJ-JC tree if needed if (options.fixedTopology) { notifyObservers(ProgressInfo.BASE_TREE_INIT, 0, models[0], null); PhymlSingleModel jcModel = new PhymlSingleModel(models[0], 0, true, false, options); jcModel.run(); // create JCtree file TextOutputStream JCtreeFile = new TextOutputStream(options .getTreeFile().getAbsolutePath(), false); JCtreeFile.print(models[0].getTreeString() + "\n"); JCtreeFile.close(); options.setUserTree(models[0].getTreeString()); notifyObservers(ProgressInfo.BASE_TREE_COMPUTED, 0, models[0], null); } // compute likelihood scores for all models System.out.println("computing likelihood scores for " + models.length + " models with Phyml " + PHYML_VERSION); } // sincronize ApplicationOptions from root ApplicationOptions[] optionsBCast = new ApplicationOptions[1]; optionsBCast[0] = options; MPI.COMM_WORLD.Bcast(optionsBCast, 0, 1, MPI.OBJECT, 0); this.options = optionsBCast[0]; ApplicationOptions.setInstance(this.options); if (ModelTest.MPJ_ME == 0) { notifyObservers(ProgressInfo.OPTIMIZATION_INIT, 0, models[0], null); distribute(); } else { try { this.options.buildWorkFiles(); } catch (IOException e) { e.printStackTrace(); } request(); } } @Override public void update(Observable o, Object arg) { // Ignore runtime messages // setChanged(); // notifyObservers(arg); } } // class RunPhyml