1 // ******************************************************************************
2 //
3 // Title: Force Field X.
4 // Description: Force Field X - Software for Molecular Biophysics.
5 // Copyright: Copyright (c) Michael J. Schnieders 2001-2025.
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 }