$darkmode
DENOPTIM
RCOSocketServerClient.java
Go to the documentation of this file.
1/*
2 * DENOPTIM
3 * Copyright (C) 2025 Marco Foscato <marco.foscato@uib.no>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as published
7 * by the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19package denoptim.integration.rcoserver;
20
21import java.io.BufferedReader;
22import java.io.IOException;
23import java.io.InputStream;
24import java.io.InputStreamReader;
25import java.io.OutputStream;
26import java.io.PrintWriter;
27import java.net.Socket;
28import java.util.ArrayList;
29import java.util.List;
30import java.util.Set;
31import java.util.logging.Level;
32import java.util.logging.Logger;
33
34import javax.vecmath.Point3d;
35
36import com.google.gson.Gson;
37import com.google.gson.GsonBuilder;
38import com.google.gson.JsonArray;
39import com.google.gson.JsonElement;
40import com.google.gson.JsonObject;
41import com.google.gson.JsonSyntaxException;
42
43import denoptim.exception.DENOPTIMException;
44import denoptim.graph.rings.RingClosingAttractor;
45import denoptim.molecularmodeling.ChemicalObjectModel;
46import denoptim.molecularmodeling.zmatrix.ZMatrix;
47import denoptim.molecularmodeling.zmatrix.ZMatrixAtom;
48import denoptim.utils.ObjectPair;
49
50
60{
64 private static RCOSocketServerClient instance = null;
65
69 private final int version = 1;
70
75 private String hostname;
76
80 private Integer port;
81
85 private Gson jsonConverter = new GsonBuilder().create();
86
87//------------------------------------------------------------------------------
88
92 private RCOSocketServerClient(String hostname, Integer port) {
93 this.hostname = hostname;
94 this.port = port;
95 }
96
97//------------------------------------------------------------------------------
98
105 public static synchronized RCOSocketServerClient getInstance(String hostname,
106 Integer port)
107 {
108 if (instance == null) {
110 }
111 return instance;
112 }
113
114//------------------------------------------------------------------------------
115
124 {
125 if (instance == null) {
126 throw new IllegalStateException(
127 "RCOSocketServerClient has not been initialized. "
128 + "Call getInstance(hostname, port) first.");
129 }
130 return instance;
131 }
132
133//------------------------------------------------------------------------------
134
139 public void setHostname(String hostname)
140 {
141 this.hostname = hostname;
142 }
143
144//------------------------------------------------------------------------------
145
150 public void setPort(Integer port) {
151 this.port = port;
152 }
153
154//------------------------------------------------------------------------------
155
160 public String getHostname() {
161 return hostname;
162 }
163
164//------------------------------------------------------------------------------
165
170 public Integer getPort() {
171 return port;
172 }
173
174//------------------------------------------------------------------------------
175
188 Logger logger)
189 throws IOException, JsonSyntaxException, DENOPTIMException
190 {
191 runConformationalOptimization(chemObj, null, logger);
192 }
193
194//------------------------------------------------------------------------------
195
209 Set<ObjectPair> rcaCombination, Logger logger)
210 throws IOException, JsonSyntaxException, DENOPTIMException
211 {
212 if (chemObj.getNumberRotatableBonds() == 0)
213 {
214 logger.log(Level.FINE, "No rotatable bond: skipping "
215 + " conformational search.");
216 return;
217 }
218
219 List<int[]> rcpTerms = new ArrayList<int[]>();
220 if (rcaCombination!=null && rcaCombination.size()>0)
221 {
222 for (ObjectPair op : rcaCombination)
223 {
224 RingClosingAttractor rcaA = (RingClosingAttractor) op.getFirst();
225 RingClosingAttractor rcaB = (RingClosingAttractor) op.getSecond();
226 int iZMatRcaA = chemObj.getZMatIdxOfRCA(rcaA);
227 int iZMatRcaB = chemObj.getZMatIdxOfRCA(rcaB);
228 int iZMatSrcA = chemObj.getZMatIdxOfRCASrc(rcaA);
229 int iZMatSrcB = chemObj.getZMatIdxOfRCASrc(rcaB);
230 rcpTerms.add(new int[] {iZMatRcaA, iZMatSrcB});
231 rcpTerms.add(new int[] {iZMatRcaB, iZMatSrcA});
232 }
233 }
234
235 List<int[]> rotatableBonds = new ArrayList<int[]>();
236 for(ObjectPair bondedAtms : chemObj.getRotatableBonds())
237 {
238 int t1 = ((Integer)bondedAtms.getFirst()).intValue();
239 int t2 = ((Integer)bondedAtms.getSecond()).intValue();
240 rotatableBonds.add(new int[] {t1, t2});
241 }
242
243 List<int[]> allBonds = new ArrayList<int[]>();
244 for(int[] bondedAtmIds : chemObj.getZMatrix().getBondData())
245 {
246 int t1 = bondedAtmIds[0];
247 int t2 = bondedAtmIds[1];
248 allBonds.add(new int[] {t1, t2});
249 }
250
251 String requestAsJSONString = formulateRequest(chemObj.getZMatrix(),
252 rcpTerms, rotatableBonds, allBonds);
253
254 //This might be useful for debugging to get the actual request placed to the server
255 logger.log(Level.FINE, "Request to the socket server: " + requestAsJSONString);
256 //TinkerUtils.writeTinkerINT("/tmp/zmat.int", chemObj.getZMatrix());
257
258 JsonObject answer = sendRequest(requestAsJSONString);
259 for (String requiredMember : new String[] {"Cartesian_coordinates", "zmatrix"})
260 {
261 if (!answer.has(requiredMember))
262 {
263 throw new Error("Socket server replied without "
264 + "including '" + requiredMember + "' member. "
265 + "Aborting! " + answer.toString());
266 }
267 }
268
269 // Update local molecular representation with output from the conf. search
270 JsonArray cartesianCoordinates = answer.get(
271 "Cartesian_coordinates").getAsJsonArray();
272 List<Point3d> newCoordinates = new ArrayList<>();
273 for (int i = 0; i < cartesianCoordinates.size(); i++)
274 {
275 JsonArray element = cartesianCoordinates.get(i).getAsJsonArray();
276 newCoordinates.add(new Point3d(
277 element.get(0).getAsDouble(),
278 element.get(1).getAsDouble(),
279 element.get(2).getAsDouble()));
280 }
281 chemObj.updateXYZ(newCoordinates);
282
283 JsonArray zmatrixArrayAsJson = answer.get("zmatrix").getAsJsonArray();
284 for (int i = 0; i < zmatrixArrayAsJson.size(); i++)
285 {
286 JsonElement element = zmatrixArrayAsJson.get(i);
287 JsonObject zmatrixObject = element.getAsJsonObject();
288 ZMatrixAtom zatm = chemObj.getZMatrix().getAtom(i);
289 // NB: no change is expected on the reference atoms used to define
290 // the internal coordinates.
291 if (i>0)
292 {
293 zatm.setBondLength(
294 zmatrixObject.get("bond_length").getAsDouble());
295 if (i>1)
296 {
297 zatm.setAngleValue(
298 zmatrixObject.get("angle").getAsDouble());
299 if (i>3)
300 {
301 zatm.setAngle2Value(
302 zmatrixObject.get("dihedral").getAsDouble());
303 zatm.setChiralFlag(
304 zmatrixObject.get("chirality").getAsInt());
305 }
306 }
307 }
308 }
309 }
310
311//------------------------------------------------------------------------------
312
327 public String formulateRequest(ZMatrix zmat, List<int[]> rcpTerms,
328 List<int[]> rotatableBonds, List<int[]> allBonds) throws IOException
329 {
330 JsonObject jsonObj = new JsonObject();
331 jsonObj.add("zmatrix", getZMatrixAsJsonArray(zmat));
332 jsonObj.add("rcp_terms", convertIntArrayListToJsonArray(rcpTerms));
333 jsonObj.add("rotatable_bonds", convertIntArrayListToJsonArray(rotatableBonds));
334 jsonObj.add("bonds_data", convertIntArrayListToJsonArray(allBonds));
335 jsonObj.addProperty("version", version);
336
337 return jsonConverter.toJson(jsonObj);
338 }
339
340//------------------------------------------------------------------------------
341
348 public static JsonArray getZMatrixAsJsonArray(ZMatrix zmat)
349 {
350 JsonArray zmatrixArray = new JsonArray();
351 for(int i = 0; i<zmat.getAtomCount(); i++)
352 {
353 ZMatrixAtom atom = zmat.getAtom(i);
354 JsonObject atomObj = new JsonObject();
355 atomObj.addProperty("id", atom.getId()+1);
356 atomObj.addProperty("element", atom.getSymbol());
357 if (zmat.getBondRefAtomIndex(i) > -1)
358 {
359 atomObj.addProperty("bond_ref", zmat.getBondRefAtomIndex(i)+1);
360 }
361 if (zmat.getBondLength(i) != null)
362 {
363 atomObj.addProperty("bond_length", zmat.getBondLength(i));
364 }
365 if (zmat.getAngleRefAtomIndex(i) > -1)
366 {
367 atomObj.addProperty("angle_ref", zmat.getAngleRefAtomIndex(i)+1);
368 }
369 if (zmat.getAngleValue(i) != null)
370 {
371 atomObj.addProperty("angle", zmat.getAngleValue(i));
372 }
373 if (zmat.getAngle2RefAtomIndex(i) > -1)
374 {
375 atomObj.addProperty("dihedral_ref", zmat.getAngle2RefAtomIndex(i)+1);
376 }
377 if (zmat.getAngle2Value(i) != null)
378 {
379 atomObj.addProperty("dihedral", zmat.getAngle2Value(i));
380 }
381 if (zmat.getChiralFlag(i) != null)
382 {
383 atomObj.addProperty("chirality", zmat.getChiralFlag(i));
384 }
385 zmatrixArray.add(atomObj);
386 }
387 return zmatrixArray;
388 }
389
390//------------------------------------------------------------------------------
391
398 private static JsonArray convertIntArrayListToJsonArray(List<int[]> intArrayList)
399 {
400 JsonArray jsonArray = new JsonArray();
401 for (int[] intArray : intArrayList)
402 {
403 JsonArray innerArray = new JsonArray();
404 for (int value : intArray)
405 {
406 innerArray.add(value+1);
407 }
408 jsonArray.add(innerArray);
409 }
410 return jsonArray;
411 }
412
413//------------------------------------------------------------------------------
414
422 public JsonObject sendRequest(String requestAsJSONString)
423 throws IOException, JsonSyntaxException
424 {
425 Socket socket;
426 try
427 {
428 socket = new Socket(hostname, port);
429 } catch (IOException e1)
430 {
431 throw new IllegalArgumentException("Could not connect to socket",e1);
432 }
433
434 Runtime.getRuntime().addShutdownHook(new Thread(){public void run(){
435 try {
436 socket.close();
437 } catch (IOException e) { /* failed */ }
438 }});
439
440 PrintWriter writerToSocket;
441 try
442 {
443 OutputStream outputSocket = socket.getOutputStream();
444 writerToSocket = new PrintWriter(outputSocket, true);
445 } catch (IOException e1)
446 {
447 try
448 {
449 socket.close();
450 } catch (IOException e)
451 {
452 e.printStackTrace();
453 }
454 throw new IllegalArgumentException("Could not connect to socket",e1);
455 }
456
457 BufferedReader readerFromSocket;
458 try
459 {
460 InputStream inputFromSocket = socket.getInputStream();
461 readerFromSocket = new BufferedReader(
462 new InputStreamReader(inputFromSocket));
463 } catch (IOException e1)
464 {
465 try
466 {
467 socket.close();
468 } catch (IOException e)
469 {
470 e.printStackTrace();
471 }
472 throw new IllegalArgumentException("Could not read from socket",e1);
473 }
474
475 // Here we send the request to the socket
476 writerToSocket.println(requestAsJSONString);
477 try
478 {
479 socket.shutdownOutput();
480 } catch (IOException e1)
481 {
482 try
483 {
484 socket.close();
485 } catch (IOException e)
486 {
487 // At this point the socket is probably closed already...
488 }
489 throw new IllegalStateException("Could not half-close socket from "
490 + this.getClass().getName(),e1);
491 }
492
493 // Read and process the answer from the socket
494 JsonObject answer = null;
495 try {
496 answer = jsonConverter.fromJson(readerFromSocket.readLine(),
497 JsonObject.class);
498 if (!(answer.has("STATUS")))
499 {
500
501 throw new Error("Socket server replied without "
502 + "including " + "STATUS" + " member. "
503 + "Something is badly wrong: aborting! " + answer.toString());
504 }
505 if (!answer.get("STATUS").getAsString().equals("SUCCESS"))
506 {
507 throw new Error("Socket server replied but with STATUS="
508 + answer.get("STATUS") + ". "
509 + "Something is badly wrong: aborting! " + answer.toString());
510 }
511 } catch (JsonSyntaxException e) {
512 throw new Error("Socket server replied with invalid JSON: " + e.getMessage());
513 } catch (IOException e) {
514 throw new Error("Error reading from socket: " + e.getMessage());
515 }
516
517 try
518 {
519 socket.close();
520 } catch (IOException e)
521 {
522 e.printStackTrace();
523 }
524 return answer;
525 }
526
527//------------------------------------------------------------------------------
528
529}
The RingClosingAttractor represent the available valence/connection that allows to close a ring.
Sends the request to produce a socket server running the RingClosingMM service.
static synchronized RCOSocketServerClient getInstance(String hostname, Integer port)
Gets the singleton instance of RCOSocketServerClient.
static JsonArray convertIntArrayListToJsonArray(List< int[]> intArrayList)
Converts a List of 0-based int arrays to a JsonArray containing 1-based ints.
void setHostname(String hostname)
Sets the hostname for the socket server connection.
String hostname
The name of the host or ID address used to communicate with the socket server.
JsonObject sendRequest(String requestAsJSONString)
Sends the given request to the socket server and waits for the answer, which is then processed and re...
static RCOSocketServerClient instance
Singleton instance.
Integer getPort()
Gets the currently configured port.
String getHostname()
Gets the currently configured hostname.
Integer port
The identifier of the port used to communicate with the socket server.
Gson jsonConverter
Converter to and from JSON/Java objects.
void setPort(Integer port)
Sets the port for the socket server connection.
void runConformationalOptimization(ChemicalObjectModel chemObj, Logger logger)
Runs a conformational optimization using the services provided by the socket server configured for th...
static JsonArray getZMatrixAsJsonArray(ZMatrix zmat)
Gets a JsonArray representation of the Z-matrix.
static RCOSocketServerClient getInstance()
Gets the singleton instance of RCOSocketServerClient if it has been initialized.
void runConformationalOptimization(ChemicalObjectModel chemObj, Set< ObjectPair > rcaCombination, Logger logger)
Runs a conformational optimization using the services provided by the socket server configured for th...
String formulateRequest(ZMatrix zmat, List< int[]> rcpTerms, List< int[]> rotatableBonds, List< int[]> allBonds)
Formulates the request to be sent to the socket server.
RCOSocketServerClient(String hostname, Integer port)
Private constructor for singleton pattern.
Collector of molecular information, related to a single chemical object, that is deployed within the ...
Representation of an atom in the ZMatrix.
Definition: ZMatrixAtom.java:9
String getSymbol()
Get the symbol of the atom.
void setBondLength(Double bondLength)
Set the bond length.
void setChiralFlag(Integer chiralFlag)
Set the chiral flag.
void setAngleValue(Double angleValue)
Set the angle value.
void setAngle2Value(Double angle2Value)
Set the angle2 value.
Representation of an atom container's geometry with internal coordinates.
Definition: ZMatrix.java:27
int getAtomCount()
Get the number of atoms in the ZMatrix.
Definition: ZMatrix.java:92
Integer getChiralFlag(int index)
Get the chiral flag for the atom at the given index.
Definition: ZMatrix.java:375
int getBondRefAtomIndex(int index)
Get the index of the bond reference atom for the atom at the given index.
Definition: ZMatrix.java:270
Double getBondLength(int index)
Get the bond length for the atom at the given index.
Definition: ZMatrix.java:339
Double getAngle2Value(int index)
Get the angle2 angle for the atom at the given index.
Definition: ZMatrix.java:363
Double getAngleValue(int index)
Get the bond angle for the atom at the given index.
Definition: ZMatrix.java:351
ZMatrixAtom getAtom(int index)
Get the atom at the given index.
Definition: ZMatrix.java:223
int getAngle2RefAtomIndex(int index)
Get the index of the second angle reference atom for the atom at the given index.
Definition: ZMatrix.java:325
int getAngleRefAtomIndex(int index)
Get the index of the angle reference atom for the atom at the given index.
Definition: ZMatrix.java:297
This class is the equivalent of the Pair data structure used in C++ Although AbstractMap....
Definition: ObjectPair.java:30