$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.io.DenoptimIO;
46import denoptim.molecularmodeling.ChemicalObjectModel;
47import denoptim.molecularmodeling.zmatrix.ZMatrix;
48import denoptim.molecularmodeling.zmatrix.ZMatrixAtom;
49import denoptim.utils.ObjectPair;
50
51
61{
65 private static RCOSocketServerClient instance = null;
66
70 private final int version = 1;
71
76 private String hostname;
77
81 private Integer port;
82
86 private Gson jsonConverter = new GsonBuilder().create();
87
92 private String requestFileName = null;
93
94//------------------------------------------------------------------------------
95
99 private RCOSocketServerClient(String hostname, Integer port) {
100 this.hostname = hostname;
101 this.port = port;
102 }
103
104//------------------------------------------------------------------------------
105
111 this.requestFileName = requestFileName;
112 }
113
114//------------------------------------------------------------------------------
115
122 public static synchronized RCOSocketServerClient getInstance(String hostname,
123 Integer port)
124 {
125 if (instance == null) {
127 }
128 return instance;
129 }
130
131//------------------------------------------------------------------------------
132
141 {
142 if (instance == null) {
143 throw new IllegalStateException(
144 "RCOSocketServerClient has not been initialized. "
145 + "Call getInstance(hostname, port) first.");
146 }
147 return instance;
148 }
149
150//------------------------------------------------------------------------------
151
156 public void setHostname(String hostname)
157 {
158 this.hostname = hostname;
159 }
160
161//------------------------------------------------------------------------------
162
167 public void setPort(Integer port) {
168 this.port = port;
169 }
170
171//------------------------------------------------------------------------------
172
177 public String getHostname() {
178 return hostname;
179 }
180
181//------------------------------------------------------------------------------
182
187 public Integer getPort() {
188 return port;
189 }
190
191//------------------------------------------------------------------------------
192
205 Logger logger)
206 throws IOException, JsonSyntaxException, DENOPTIMException
207 {
208 runConformationalOptimization(chemObj, null, logger);
209 }
210
211//------------------------------------------------------------------------------
212
226 Set<ObjectPair> rcaCombination, Logger logger)
227 throws IOException, JsonSyntaxException, DENOPTIMException
228 {
229 if (chemObj.getNumberRotatableBonds() == 0)
230 {
231 logger.log(Level.FINE, "No rotatable bond: skipping "
232 + " conformational search.");
233 return;
234 }
235
236 List<int[]> rcpTerms = new ArrayList<int[]>();
237 if (rcaCombination!=null && rcaCombination.size()>0)
238 {
239 for (ObjectPair op : rcaCombination)
240 {
241 RingClosingAttractor rcaA = (RingClosingAttractor) op.getFirst();
242 RingClosingAttractor rcaB = (RingClosingAttractor) op.getSecond();
243 int iZMatRcaA = chemObj.getZMatIdxOfRCA(rcaA);
244 int iZMatRcaB = chemObj.getZMatIdxOfRCA(rcaB);
245 int iZMatSrcA = chemObj.getZMatIdxOfRCASrc(rcaA);
246 int iZMatSrcB = chemObj.getZMatIdxOfRCASrc(rcaB);
247 rcpTerms.add(new int[] {iZMatRcaA, iZMatSrcB});
248 rcpTerms.add(new int[] {iZMatRcaB, iZMatSrcA});
249 }
250 }
251
252 List<int[]> rotatableBonds = new ArrayList<int[]>();
253 for(ObjectPair bondedAtms : chemObj.getRotatableBonds())
254 {
255 int t1 = ((Integer)bondedAtms.getFirst()).intValue();
256 int t2 = ((Integer)bondedAtms.getSecond()).intValue();
257 rotatableBonds.add(new int[] {t1, t2});
258 }
259
260 List<int[]> allBonds = new ArrayList<int[]>();
261 for(int[] bondedAtmIds : chemObj.getZMatrix().getBondData())
262 {
263 int t1 = bondedAtmIds[0];
264 int t2 = bondedAtmIds[1];
265 allBonds.add(new int[] {t1, t2});
266 }
267
268 String requestAsJSONString = formulateRequest(chemObj.getZMatrix(),
269 rcpTerms, rotatableBonds, allBonds);
270
271 //This might be useful for debugging to get the actual request placed to the server
272 logger.log(Level.FINE, "Request to the socket server: " + requestAsJSONString);
273 if (requestFileName != null) {
275 requestAsJSONString+System.lineSeparator(), true);
276 }
277
278 JsonObject answer = sendRequest(requestAsJSONString);
279 for (String requiredMember : new String[] {"Cartesian_coordinates", "zmatrix"})
280 {
281 if (!answer.has(requiredMember))
282 {
283 throw new Error("Socket server replied without "
284 + "including '" + requiredMember + "' member. "
285 + "Aborting! " + answer.toString());
286 }
287 }
288
289 // Update local molecular representation with output from the conf. search
290 JsonArray cartesianCoordinates = answer.get(
291 "Cartesian_coordinates").getAsJsonArray();
292 List<Point3d> newCoordinates = new ArrayList<>();
293 for (int i = 0; i < cartesianCoordinates.size(); i++)
294 {
295 JsonArray element = cartesianCoordinates.get(i).getAsJsonArray();
296 newCoordinates.add(new Point3d(
297 element.get(0).getAsDouble(),
298 element.get(1).getAsDouble(),
299 element.get(2).getAsDouble()));
300 }
301 chemObj.updateXYZ(newCoordinates);
302
303 JsonArray zmatrixArrayAsJson = answer.get("zmatrix").getAsJsonArray();
304 for (int i = 0; i < zmatrixArrayAsJson.size(); i++)
305 {
306 JsonElement element = zmatrixArrayAsJson.get(i);
307 JsonObject zmatrixObject = element.getAsJsonObject();
308 ZMatrixAtom zatm = chemObj.getZMatrix().getAtom(i);
309 // NB: no change is expected on the reference atoms used to define
310 // the internal coordinates.
311 if (i>0)
312 {
313 zatm.setBondLength(
314 zmatrixObject.get("bond_length").getAsDouble());
315 if (i>1)
316 {
317 zatm.setAngleValue(
318 zmatrixObject.get("angle").getAsDouble());
319 if (i>3)
320 {
321 zatm.setAngle2Value(
322 zmatrixObject.get("dihedral").getAsDouble());
323 zatm.setChiralFlag(
324 zmatrixObject.get("chirality").getAsInt());
325 }
326 }
327 }
328 }
329 }
330
331//------------------------------------------------------------------------------
332
347 public String formulateRequest(ZMatrix zmat, List<int[]> rcpTerms,
348 List<int[]> rotatableBonds, List<int[]> allBonds) throws IOException
349 {
350 JsonObject jsonObj = new JsonObject();
351 jsonObj.add("zmatrix", getZMatrixAsJsonArray(zmat));
352 jsonObj.add("rcp_terms", convertIntArrayListToJsonArray(rcpTerms));
353 jsonObj.add("rotatable_bonds", convertIntArrayListToJsonArray(rotatableBonds));
354 jsonObj.add("bonds_data", convertIntArrayListToJsonArray(allBonds));
355 jsonObj.addProperty("version", version);
356
357 return jsonConverter.toJson(jsonObj);
358 }
359
360//------------------------------------------------------------------------------
361
368 public static JsonArray getZMatrixAsJsonArray(ZMatrix zmat)
369 {
370 JsonArray zmatrixArray = new JsonArray();
371 for(int i = 0; i<zmat.getAtomCount(); i++)
372 {
373 ZMatrixAtom atom = zmat.getAtom(i);
374 JsonObject atomObj = new JsonObject();
375 atomObj.addProperty("id", atom.getId()+1);
376 atomObj.addProperty("element", atom.getSymbol());
377 if (zmat.getBondRefAtomIndex(i) > -1)
378 {
379 atomObj.addProperty("bond_ref", zmat.getBondRefAtomIndex(i)+1);
380 }
381 if (zmat.getBondLength(i) != null)
382 {
383 atomObj.addProperty("bond_length", zmat.getBondLength(i));
384 }
385 if (zmat.getAngleRefAtomIndex(i) > -1)
386 {
387 atomObj.addProperty("angle_ref", zmat.getAngleRefAtomIndex(i)+1);
388 }
389 if (zmat.getAngleValue(i) != null)
390 {
391 atomObj.addProperty("angle", zmat.getAngleValue(i));
392 }
393 if (zmat.getAngle2RefAtomIndex(i) > -1)
394 {
395 atomObj.addProperty("dihedral_ref", zmat.getAngle2RefAtomIndex(i)+1);
396 }
397 if (zmat.getAngle2Value(i) != null)
398 {
399 atomObj.addProperty("dihedral", zmat.getAngle2Value(i));
400 }
401 if (zmat.getChiralFlag(i) != null)
402 {
403 atomObj.addProperty("chirality", zmat.getChiralFlag(i));
404 }
405 zmatrixArray.add(atomObj);
406 }
407 return zmatrixArray;
408 }
409
410//------------------------------------------------------------------------------
411
418 private static JsonArray convertIntArrayListToJsonArray(List<int[]> intArrayList)
419 {
420 JsonArray jsonArray = new JsonArray();
421 for (int[] intArray : intArrayList)
422 {
423 JsonArray innerArray = new JsonArray();
424 for (int value : intArray)
425 {
426 innerArray.add(value+1);
427 }
428 jsonArray.add(innerArray);
429 }
430 return jsonArray;
431 }
432
433//------------------------------------------------------------------------------
434
442 public JsonObject sendRequest(String requestAsJSONString)
443 throws IOException, JsonSyntaxException
444 {
445 Socket socket;
446 try
447 {
448 socket = new Socket(hostname, port);
449 } catch (IOException e1)
450 {
451 throw new IllegalArgumentException("Could not connect to socket",e1);
452 }
453
454 Runtime.getRuntime().addShutdownHook(new Thread(){public void run(){
455 try {
456 socket.close();
457 } catch (IOException e) { /* failed */ }
458 }});
459
460 PrintWriter writerToSocket;
461 try
462 {
463 OutputStream outputSocket = socket.getOutputStream();
464 writerToSocket = new PrintWriter(outputSocket, true);
465 } catch (IOException e1)
466 {
467 try
468 {
469 socket.close();
470 } catch (IOException e)
471 {
472 e.printStackTrace();
473 }
474 throw new IllegalArgumentException("Could not connect to socket",e1);
475 }
476
477 BufferedReader readerFromSocket;
478 try
479 {
480 InputStream inputFromSocket = socket.getInputStream();
481 readerFromSocket = new BufferedReader(
482 new InputStreamReader(inputFromSocket));
483 } catch (IOException e1)
484 {
485 try
486 {
487 socket.close();
488 } catch (IOException e)
489 {
490 e.printStackTrace();
491 }
492 throw new IllegalArgumentException("Could not read from socket",e1);
493 }
494
495 // Here we send the request to the socket
496 writerToSocket.println(requestAsJSONString);
497 try
498 {
499 socket.shutdownOutput();
500 } catch (IOException e1)
501 {
502 try
503 {
504 socket.close();
505 } catch (IOException e)
506 {
507 // At this point the socket is probably closed already...
508 }
509 throw new IllegalStateException("Could not half-close socket from "
510 + this.getClass().getName(),e1);
511 }
512
513 // Read and process the answer from the socket
514 JsonObject answer = null;
515 try {
516 answer = jsonConverter.fromJson(readerFromSocket.readLine(),
517 JsonObject.class);
518 if (!(answer.has("STATUS")))
519 {
520
521 throw new Error("Socket server replied without "
522 + "including " + "STATUS" + " member. "
523 + "Something is badly wrong: aborting! " + answer.toString());
524 }
525 if (!answer.get("STATUS").getAsString().equals("SUCCESS"))
526 {
527 throw new Error("Socket server replied but with STATUS="
528 + answer.get("STATUS") + ". "
529 + "Something is badly wrong: aborting! " + answer.toString());
530 }
531 } catch (JsonSyntaxException e) {
532 throw new Error("Socket server replied with invalid JSON: " + e.getMessage());
533 } catch (IOException e) {
534 throw new Error("Error reading from socket: " + e.getMessage());
535 }
536
537 try
538 {
539 socket.close();
540 } catch (IOException e)
541 {
542 e.printStackTrace();
543 }
544 return answer;
545 }
546
547//------------------------------------------------------------------------------
548
549}
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...
void setRecordRequestsFileName(String requestFileName)
Sets the pathname to file where to record requests sent to the server.
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.
String requestFileName
Pathname to file where to record requests sent to the server.
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.
Utility methods for input/output.
static void writeData(String fileName, String data, boolean append)
Write text-like data file.
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