View Javadoc
1   // ******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2024.
6   //
7   // This file is part of Force Field X.
8   //
9   // Force Field X is free software; you can redistribute it and/or modify it
10  // under the terms of the GNU General Public License version 3 as published by
11  // the Free Software Foundation.
12  //
13  // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16  // details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA 02111-1307 USA
21  //
22  // Linking this library statically or dynamically with other modules is making a
23  // combined work based on this library. Thus, the terms and conditions of the
24  // GNU General Public License cover the whole combination.
25  //
26  // As a special exception, the copyright holders of this library give you
27  // permission to link this library with independent modules to produce an
28  // executable, regardless of the license terms of these independent modules, and
29  // to copy and distribute the resulting executable under terms of your choice,
30  // provided that you also meet, for each linked independent module, the terms
31  // and conditions of the license of that module. An independent module is a
32  // module which is not derived from or based on this library. If you modify this
33  // library, you may extend this exception to your version of the library, but
34  // you are not obligated to do so. If you do not wish to do so, delete this
35  // exception statement from your version.
36  //
37  // ******************************************************************************
38  package ffx.ui;
39  
40  import ffx.potential.bonded.Atom;
41  import ffx.ui.commands.FFXClient;
42  import ffx.ui.commands.SimulationFilter;
43  import ffx.ui.commands.SimulationUpdate;
44  import ffx.utilities.Keyword;
45  import java.awt.event.ActionEvent;
46  import java.awt.event.ActionListener;
47  import java.io.File;
48  import java.net.InetSocketAddress;
49  import java.util.List;
50  import javax.swing.Timer;
51  
52  /**
53   * This SimulationLoader class oversees loading information from an executing FFX instance program
54   * this instance of Force Field X.
55   *
56   * @author Michael J. Schnieders
57   */
58  public class SimulationLoader implements ActionListener {
59    // The client monitors a socket based connection to an executing TINKER
60    // program.
61  
62    private FFXClient client;
63    private SimulationFilter simulationFilter;
64    private InetSocketAddress address;
65    // If the TINKER program was launched from the GUI,
66    // the job Thread will be alive until the program exits.
67    private Thread job = null;
68    // Once the simulation is finished, this flag will be true.
69    private boolean finished = false;
70    private MainPanel mainPanel;
71    private FFXSystem system;
72    private SimulationUpdate ffxUpdate = null;
73    private boolean firstUpdate = true;
74    private Timer timer;
75    private double time = 0.0;
76    private int step = 0;
77  
78    // Constructor
79  
80    /**
81     * Constructor for SimulationLoader.
82     *
83     * @param s a {@link ffx.ui.FFXSystem} object.
84     * @param j a {@link java.lang.Thread} object.
85     * @param f a {@link ffx.ui.MainPanel} object.
86     * @param a a {@link java.net.InetSocketAddress} object.
87     */
88    public SimulationLoader(FFXSystem s, Thread j, MainPanel f, InetSocketAddress a) {
89      system = s;
90      job = j;
91      mainPanel = f;
92      address = a;
93      if (address == null) {
94        finished = true;
95      }
96    }
97  
98    /** {@inheritDoc} */
99    @Override
100   public void actionPerformed(ActionEvent e) {
101     // Check if we're connected to a TINKER Server.
102     if (!connect()) {
103       return;
104     }
105     // Check if we need to initialize the Simulation System.
106     if (system == null) {
107       ffx.ui.commands.SimulationDefinition sys = client.getSystem();
108       if (sys != null) {
109         if (simulationFilter == null) {
110           if (system == null) {
111             system =
112                 new FFXSystem(new File("Simulation"), "Simulation", Keyword.loadProperties(null));
113           }
114           simulationFilter = new SimulationFilter(sys, system);
115           UIFileOpener openFile = new UIFileOpener(simulationFilter, mainPanel);
116           // The reader thread contains a SimulationFilter instance that will read a
117           // simulation into the FFX.
118           Thread reader = new Thread(openFile);
119           reader.start();
120         } else if (simulationFilter.fileRead()) {
121           system = (FFXSystem) simulationFilter.getActiveMolecularSystem();
122           simulationFilter = null;
123         }
124       }
125     } // A Simulation System exists, attempt to Update it.
126     else {
127       if (ffxUpdate == null || ffxUpdate.read) {
128         ffxUpdate = client.getUpdate();
129       }
130       if (ffxUpdate != null
131           && !ffxUpdate.read
132           && !mainPanel.getGraphics3D().isSceneRendering()) {
133         update();
134       }
135     }
136   }
137 
138   /**
139    * isConnected
140    *
141    * @return a boolean.
142    */
143   public boolean isConnected() {
144     return client != null && client.isConnected();
145   }
146 
147   /** release */
148   public void release() {
149     finished = true;
150     if (timer != null) {
151       timer.stop();
152     }
153     if (client != null) {
154       client.release();
155     }
156     mainPanel.getMainMenu().setConnect(true);
157   }
158 
159   /**
160    * connect
161    *
162    * @return a boolean.
163    */
164   boolean connect() {
165     if (isFinished()) {
166       return false;
167     }
168     if (isConnected()) {
169       return true;
170     }
171     // Create a timer to regularly wake up this SimulationLoader.
172     int delay = 10;
173     if (timer == null) {
174       timer = new Timer(delay, this);
175       timer.setCoalesce(true);
176       timer.setDelay(10);
177       timer.start();
178     }
179     // Create the FFXClient to monitor messages to/from TINKER.
180     if (client == null) {
181       client = new FFXClient(address);
182     }
183     // Try to connect.
184     client.connect();
185     // If connected, change to our "steady-state" timer delay.
186     if (client.isConnected()) {
187       timer.setDelay(delay);
188       return true;
189     }
190     // The FFXClient and the Timer are set up, but a TINKER simulation
191     // has not responded yet. This connect method will be called again
192     // through "actionPerformed" when
193     // the timer wakes up.
194     return false;
195   }
196 
197   /**
198    * getFSystem
199    *
200    * @return a {@link ffx.ui.FFXSystem} object.
201    */
202   FFXSystem getFSystem() {
203     return system;
204   }
205 
206   // Release the simulation
207 
208   /**
209    * isFinished
210    *
211    * @return a boolean.
212    */
213   boolean isFinished() {
214     if (client != null && client.isClosed()) {
215       finished = true;
216     }
217     if (job != null && !job.isAlive()) {
218       finished = true;
219     }
220     if (finished) {
221       if (timer != null) {
222         timer.stop();
223       }
224       update();
225       release();
226     }
227     return finished;
228   }
229 
230   private void update() {
231     if (system.isStale()) {
232       return;
233     }
234     if (ffxUpdate == null || ffxUpdate.read) {
235       return;
236     }
237     // Sanity check - FFX and TINKER should agree on the number of atoms.
238     List<Atom> atoms = system.getAtomList();
239     int n = atoms.size();
240     if (ffxUpdate.numatoms != n) {
241       finished = true;
242       return;
243     }
244     // This is either an MD Run.
245     if (ffxUpdate.type == ffx.ui.commands.SimulationUpdate.SIMULATION) {
246       if (ffxUpdate.time == time) {
247         ffxUpdate.read = true;
248         return;
249       }
250       time = ffxUpdate.time;
251     } else if (ffxUpdate.type == ffx.ui.commands.SimulationUpdate.OPTIMIZATION) {
252       if (ffxUpdate.step == step) {
253         ffxUpdate.read = true;
254         return;
255       }
256       step = ffxUpdate.step;
257     }
258     // Reset the Maximum Magnitude Values, such that they will be consistent
259     // with this frame of the simulation after the update.
260     double[] d = new double[3];
261     for (Atom a : atoms) {
262       int index = a.getIndex() - 1;
263       d[0] = ffxUpdate.coordinates[0][index];
264       d[1] = ffxUpdate.coordinates[1][index];
265       d[2] = ffxUpdate.coordinates[2][index];
266       a.moveTo(d);
267     }
268     if (firstUpdate) {
269       system.center();
270       firstUpdate = false;
271     }
272     mainPanel.getGraphics3D().updateScene(system, true, false, null, false, null);
273     mainPanel.getHierarchy().updateStatus();
274     mainPanel.getHierarchy().repaint();
275     ffxUpdate.read = true;
276     ffxUpdate = client.getUpdate();
277   }
278 }