Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

MSMSInterface.C

Go to the documentation of this file.
00001 /***************************************************************************
00002  *cr                                                                       
00003  *cr            (C) Copyright 1995-2019 The Board of Trustees of the           
00004  *cr                        University of Illinois                       
00005  *cr                         All Rights Reserved                        
00006  *cr                                                                   
00007  *cr Some of these lines may be copyright Michel Sanner or Scripps as
00008  *cr they come from example code of the MSMS distribution
00009  ***************************************************************************/
00010 
00011 /***************************************************************************
00012  * RCS INFORMATION:
00013  *
00014  *      $RCSfile: MSMSInterface.C,v $
00015  *      $Author: johns $        $Locker:  $             $State: Exp $
00016  *      $Revision: 1.57 $       $Date: 2020/07/08 04:34:46 $
00017  *
00018  ***************************************************************************
00019  * DESCRIPTION:
00020  *   Start the MSMS server and talk to it.  For more information about
00021  * MSMS, see 
00022  * http://www.scripps.edu/pub/olson-web/people/sanner/html/msms_home.html
00023  *
00024  ***************************************************************************/
00025 
00026 #include "utilities.h"
00027 #include "vmdsock.h"
00028 #include <stdio.h>
00029 #include <stdlib.h>
00030 #include <string.h>
00031 
00032 #if defined(ARCH_AIX4)
00033 #include <strings.h>
00034 #endif
00035 
00036 #if defined(__irix)
00037 #include <bstring.h>
00038 #endif
00039 
00040 #if defined(__hpux)
00041 #include <time.h>
00042 #endif
00043 
00044 #include "MSMSInterface.h"
00045 #include "Inform.h"
00046 
00047 #define MIN_PORT 1357
00048 #define MAX_PORT 9457
00049 
00050 // find port for connecting to MSMS; return port
00051 int MSMSInterface::find_free_port(void) {
00052 
00053   void *sock = vmdsock_create(); 
00054   if (!sock) return 0;
00055 
00056   int port = 0;
00057   // search for a free port
00058   for (int i=MIN_PORT; i<=MAX_PORT; i++) {
00059     if (vmdsock_bind(sock, i) == 0) {
00060       port = i;
00061       break;
00062     }
00063   }
00064   vmdsock_destroy(sock); 
00065 
00066   if (port == 0) {
00067     msgErr << "Could not find an available port between " << 
00068            MIN_PORT << " and " << MAX_PORT << "." << sendmsg;
00069   }
00070   return port; // return port number or 0 if none available
00071 }
00072 
00073 
00074 const char *MSMSInterface::server_name(void) {
00075   const char *msms = getenv("MSMSSERVER");
00076   if (!msms) {
00077 #ifdef _MSC_VER
00078     msms = "msms.exe";
00079 #else
00080     msms = "msms";
00081 #endif
00082   }
00083 
00084   return msms;
00085 }
00086 
00087 
00088 // returns 1
00089 int MSMSInterface::start_msms(int port) {
00090   const char *msms = server_name();
00091   char *s = new char[strlen(msms) + 100];
00092   sprintf(s, "%s -no_area -socketPort %d &", msms, port);
00093   msgInfo << "Starting MSMS with: '" << s << "'" << sendmsg;
00094   vmd_system(s);
00095   delete [] s;
00096   return 1;
00097 }
00098 
00099 
00100 
00101 // return 0 on failure, or vmdsock handle 
00102 void *MSMSInterface::conn_to_service_port(int port) {
00103   void *sock;
00104 
00105   sock = vmdsock_create();
00106   if (!sock) return NULL;
00107   if (vmdsock_connect(sock, "localhost", port)) {
00108     vmdsock_destroy(sock);
00109     sock = NULL;
00110   }
00111   return sock;
00112 }
00113 
00114 
00115 int MSMSInterface::compute_from_socket(float probe_radius, float density,
00116                                        int n, int *ids, float *xyzr, int *flgs,
00117                                        int /* component */) {
00118   if (xyzr == NULL) {
00119     err = BAD_RANGE;
00120     return err;
00121   }
00122 
00123   // Find a free port
00124   int port = find_free_port();
00125   if (port == 0) {
00126     err = NO_PORTS;
00127     return err;
00128   }
00129 
00130   // spawn an MSMS server pointed at that port
00131   start_msms(port);
00132 
00133   // try to connect up to 100 times with a linear backoff
00134   for (int loop=0; loop<100; loop++) {
00135     msms_server = conn_to_service_port(port);
00136     if (!msms_server) {
00137       if (loop!=0 && !(loop%50)) {
00138         msgInfo << "Waiting for MSMS server ..." << sendmsg;
00139       }
00140       vmd_msleep(loop);
00141     } else {
00142       break;
00143     }
00144   }
00145 
00146   if (msms_server == 0) {
00147     msgErr << "Could not connect to MSMS server.  " <<
00148            "Please check that the program '" << server_name() << 
00149            "' exists and is executable, or set the environment variable " <<
00150            "MSMSSERVER to point to the correct binary." << sendmsg;
00151     err = NO_CONNECTION; // (try to) kill server now?
00152     return err;
00153   }
00154 
00155   // from here on, error is set by close_server();
00156 
00157   // send info to server
00158   if (!call_msms(probe_radius, density, n, xyzr, flgs)) {
00159     return err;
00160   }
00161 
00162   // get data back
00163   int t;
00164   int surface_count = 0;
00165   do {
00166     t = msms_ended();
00167     //    printf("t is %d\n", t); fflush(stdout);
00168     switch (t) {
00169     case 5: get_triangulated_ses(surface_count++); break;
00170     case 1: close_server(COMPUTED); break;
00171     default: break;
00172     }
00173   } while (t!=1 && msms_server != 0);
00174 
00175   if (err != COMPUTED) {
00176     return err;
00177   }
00178 
00179   // map the atom ids as (and if) requested
00180   // atomids is an array of MSMS atoms indices which correspond to the
00181   // MSMS vertices. (one array element for each MSMS vertex)
00182   // ids is an array of VMD atom indices which correspond to the
00183   // MSMS atoms. (one array element for each MSMS atom)
00184   if (ids != NULL) {
00185     for (int i=0; i<atomids.num(); i++) {
00186       atomids[i] = ids[atomids[i]];
00187     }
00188   }
00189   return COMPUTED; // it all worked!
00190 }
00191 
00192 
00193 int MSMSInterface::check_for_input(int secs, int reps, int stage) {
00194   for (int count = 0; count < reps; count++) {
00195     int ret = vmdsock_selread(msms_server, secs);
00196     if (ret > 0) {  // got something
00197       return 1;  // return success
00198     }
00199 
00200     if (ret == 0) { 
00201       // select timeout reached
00202       msgInfo << "Waiting for data from MSMS(" << stage << ") ..." << sendmsg;
00203     } else {
00204       // select returned an error.
00205       msgErr << "Unknown error " << ret << "with MSMS interface!" << sendmsg;
00206       perror("Did you press Control-C? : ");
00207       break;
00208     }
00209   }
00210 
00211   vmdsock_destroy(msms_server); // never got anything, close the connection and
00212   return 0;                     // return failure (never got anything)
00213 }
00214 
00215 
00216 char *MSMSInterface::get_message(char *buffer) {
00217   int i,j;
00218   char *car;
00219 
00220   if (!check_for_input(1, 10, 1)) {
00221     return NULL;
00222   }
00223 
00224   for (i=0,car=buffer; car-buffer<255; car++) {
00225     j=vmdsock_read(msms_server, car, 1);
00226     if (j!=-1) {
00227       i++;
00228       if (*car=='\n') {*car='\0';break;}
00229     }
00230   }
00231 
00232   if (*car!='\0') 
00233     buffer[255]='\0';
00234 
00235   return(buffer);
00236 }
00237 
00238 
00239 int MSMSInterface::call_msms(float probe_radius, float density,
00240                              int n, float *xyzr, int *flgs) {
00241   int mask1 = 0; 
00242   int mask2 = 1;
00243   int  flag = 1;
00244 
00245   char buffer[256] = { 0 };
00246   if (!get_message(buffer)) {
00247     msgErr << "Couldn't send initialization to MSMS" << sendmsg;
00248     close_server(NO_INITIALIZATION);
00249     return 0; // return error
00250   }
00251 
00252   vmdsock_write(msms_server,(char *)&probe_radius, sizeof(float)); 
00253   vmdsock_write(msms_server,(char *)&density, sizeof(float)); 
00254   vmdsock_write(msms_server,(char *)&mask1, sizeof(int));
00255   vmdsock_write(msms_server,(char *)&mask2, sizeof(int));
00256   vmdsock_write(msms_server,(char *)&n, sizeof(int));
00257   for (int i=0; i<n; i++) {
00258     vmdsock_write(msms_server,(char *)(xyzr+4L*i) , 4L*sizeof(float));
00259 
00260     // If no flags passed, set a default
00261     if (flgs) {
00262       vmdsock_write(msms_server, (char *)(flgs+i), sizeof(int));
00263     } else {
00264       vmdsock_write(msms_server, (char *)&flag, sizeof(int));
00265     }
00266   }
00267 
00268   return 1; // return success
00269 }
00270 
00271 
00272 int MSMSInterface::msms_ended(void) {
00273   char buffer[256] = { 0 };
00274   int nread = 0;
00275   char c = '\0';
00276   int i;
00277   //int fl;
00278   //fl = fcntl(msms_server, F_GETFL, 0);         // get current setting
00279   //fcntl(msms_server, F_SETFL, O_NDELAY | fl);  // don't block
00280 
00281   if (!check_for_input(10, 12, 2)) { // two minutes (compute could be long)
00282     msgErr << "No information from MSMS.. giving up." << sendmsg;
00283     close_server(MSMS_DIED);
00284     return 1; // for a premature end
00285   }
00286 
00287   while (1) {
00288     // once I am getting data, I had better get it
00289     if (!check_for_input(1, 4, 3)) {
00290       msgErr << "No data from MSMS.. giving up." << sendmsg;
00291       close_server(MSMS_DIED);
00292       return 1; // for a premature end
00293     }
00294 
00295     i = vmdsock_read(msms_server, &c, 1);              // get a character
00296     if (i != -1 && i > 0) {
00297       buffer[nread] = c;
00298       nread++;
00299       if (c == '\n') {         // read new line, so end of header
00300         //fcntl(msms_server, F_SETFL, ~O_NDELAY & fl); // restore setting
00301         buffer[nread-1] = '\0';
00302         if (strcmp(buffer,"MSMS END")==0)       return(1);
00303         if (strcmp(buffer,"MSMS RS")==0)        return(2);
00304         if (strcmp(buffer,"MSMS CS")==0)        return(3);
00305         if (strcmp(buffer,"MSMS SEND DOTS")==0) return(4);
00306         if (strcmp(buffer,"MSMS RCF")==0)       return(5);
00307 
00308         msgErr << "Unknown MSMS message: " << buffer << sendmsg;
00309         return 1; // for a premature end
00310       }
00311     } else {
00312       // Why did I get a -1 or 0?  
00313       // Probably bacause the server crashed, or didn't flush its output
00314       // on exit?
00315       if (nread == 0)
00316         msgErr << "No data from MSMS.. giving up." << sendmsg;
00317 
00318       close_server(COMPUTED); // Should return MSMS_DIED, once error handling
00319                               // is fixed and we know msms is working right.
00320       return 1;
00321     }
00322   }
00323 }
00324 
00325 
00326 int MSMSInterface::get_blocking(char *str, int nbytes) {
00327   int i=0, to_be_read;
00328   char *cptr=str;
00329   
00330   // 30 seconds (3 seconds at a time)
00331   if (!check_for_input(3, 10, 4)) {
00332     msgErr << "Failed in MSMSInterface::get_blocking" << sendmsg;
00333     return -1; // return failure
00334   }; 
00335 
00336   for (to_be_read = nbytes; to_be_read > 0; cptr += i) {
00337     i = vmdsock_read(msms_server, cptr, to_be_read);
00338 
00339     if (i==-1) 
00340       return(nbytes - to_be_read);  // connection died?
00341 
00342     to_be_read -= i;
00343   }
00344 
00345   return nbytes; // return number of bytes read
00346 }
00347 
00348 
00349 
00350 void MSMSInterface::get_triangulated_ses(int component) {
00351   int    i, i1, i2, nf, ns;
00352   char  *buffer;
00353   float *bf;
00354   int   *bi, s1, s2, max;
00355 
00356   // get number of facets
00357   i1=get_blocking((char *)&nf, sizeof(int));
00358   if (i1 < 0) { close_server(MSMS_DIED); return; }
00359 
00360   // number of vertices
00361   i1=get_blocking((char *)&ns, sizeof(int));
00362   if (i1 < 0) { close_server(MSMS_DIED); return; }
00363 
00364   s1 = nf*5L*sizeof(int);
00365   s2 = ns*(6L*sizeof(float) + sizeof(int));
00366   
00367   max = (s2>s1) ? s2:s1;
00368   buffer = (char *)malloc(max*sizeof(char) + 1);
00369   if (buffer==NULL) {
00370     msgErr << "MSMS: allocation failed for buffer" << sendmsg;
00371     return;
00372   }
00373 
00374   // read in facet list
00375   i1 = get_blocking(buffer, s1*sizeof(char));
00376   if (i1 < 0) { close_server(MSMS_DIED); free(buffer); return; }
00377   bi = (int *)buffer;
00378   for (i=0;i<nf;i++) {
00379     MSMSFace face;
00380     face.vertex[0]=*bi++;
00381     face.vertex[1]=*bi++;
00382     face.vertex[2]=*bi++;
00383     face.surface_type=*bi++;
00384     face.anaface=*bi++;
00385     face.component = component;
00386     faces.append(face);
00387   }
00388 
00389   // read in vertex and normal lists
00390   i2 = get_blocking(buffer,s2*sizeof(char));
00391   if (i2 < 0) { close_server(MSMS_DIED); free(buffer); return; }
00392   bf = (float *)buffer;
00393   for (i=0; i<ns; i++) {
00394     MSMSCoord norm, coord;
00395     norm.x[0]=*bf++;
00396     norm.x[1]=*bf++;
00397     norm.x[2]=*bf++;
00398     coord.x[0]=*bf++;
00399     coord.x[1]=*bf++;
00400     coord.x[2]=*bf++;
00401     norms.append(norm);
00402     coords.append(coord);
00403   }
00404  
00405   // read in atomid mappings for each vertex 
00406   bi = (int *)bf;
00407   for (i=0;i<ns;i++) {
00408     atomids.append(*bi++);
00409   }
00410 
00411   free(buffer);
00412 }
00413 
00414 
00415 
00416 void MSMSInterface::close_server(int erno) {
00417   if (msms_server != 0) {
00418     err = erno;
00419     vmdsock_destroy(msms_server);
00420     msms_server = 0;
00421   }
00422 }
00423 
00424 void MSMSInterface::clear() {
00425   atomids.clear();
00426   faces.clear();
00427   coords.clear();
00428   norms.clear();
00429 }
00430 
00431 // mark a file for destruction when the object goes out of scope
00432 class VMDTempFile {
00433 private:
00434   const char *m_filename;
00435 public:
00436   VMDTempFile(const char *fname) {
00437     m_filename = stringdup(fname);
00438   }
00439   ~VMDTempFile() {
00440     vmd_delete_file(m_filename);
00441     delete [] m_filename; 
00442   }
00443 };
00444 
00445 
00446 int MSMSInterface::compute_from_file(float probe_radius, float density,
00447                                      int n, int *ids, float *xyzr, int *flgs,
00448                                      int /* component */) {
00449   const char *msmsbin = server_name();
00450 
00451   // generate output file name and try to create
00452   char *dirname = vmd_tempfile("");
00453   char *ofilename = new char[strlen(dirname) + 100];
00454   sprintf(ofilename, "%svmdmsms.u%d.%d", 
00455       dirname, vmd_getuid(), (int)(vmd_random() % 999));
00456   delete [] dirname;
00457   FILE *ofile = fopen(ofilename, "wt");
00458   if (!ofile) {
00459     delete [] ofilename;
00460     msgErr << "Failed to create MSMS atom radii input file" << sendmsg;
00461     return 0;  // failure
00462   }
00463 
00464   // MSMS-generated input files append .vert and .face to what you give
00465   // it, so might as well use the same name as the output file.
00466   char *facetfilename = new char[strlen(ofilename) + 6];
00467   char *vertfilename = new char[strlen(ofilename) + 6];
00468   sprintf(facetfilename, "%s.face", ofilename);
00469   sprintf(vertfilename, "%s.vert", ofilename);
00470 
00471   // temporary files we want to make sure to clean up
00472   VMDTempFile otemp(ofilename);
00473   VMDTempFile ftemp(facetfilename);
00474   VMDTempFile vtemp(vertfilename);
00475 
00476   //
00477   // write atom coordinates and radii to the file we send to MSMS 
00478   //
00479   for (int i=0; i<n; i++) {
00480     fprintf(ofile, "%f %f %f %f\n", 
00481             xyzr[4L*i], xyzr[4L*i+1], xyzr[4L*i+2], xyzr[4L*i+3]);
00482   }
00483   fclose(ofile);
00484 
00485   //
00486   // call MSMS to calculate the surface for the given atoms
00487   //
00488   {
00489     char *msmscmd = new char[2*strlen(ofilename) + strlen(msmsbin) + 100];
00490     sprintf(msmscmd, "\"%s\" -if %s -of %s -probe_radius %5.3f -density %5.3f -no_area -no_header", msmsbin, ofilename, ofilename, probe_radius, density);
00491     vmd_system(msmscmd);    
00492     delete [] msmscmd;
00493   }
00494   
00495   // read facets
00496   FILE *facetfile = fopen(facetfilename, "r");
00497   if (!facetfile) {
00498     msgErr << "Cannot read MSMS facet file: " << facetfilename << sendmsg;
00499     // Return cleanly, deleting temp files and so on. 
00500     return 0;  // failed
00501   }
00502   MSMSFace face;
00503   while (fscanf(facetfile, "%d %d %d %d %d",
00504         face.vertex+0, face.vertex+1, face.vertex+2, &face.surface_type,
00505         &face.anaface) == 5) {
00506     face.component = 0;  // XXX Unused by VMD, so why store?
00507     face.vertex[0]--;
00508     face.vertex[1]--;
00509     face.vertex[2]--;
00510     faces.append(face);
00511   }
00512   fclose(facetfile);
00513 
00514   // read verts
00515   FILE *vertfile = fopen(vertfilename, "r");
00516   if (!vertfile) {
00517     msgErr << "Cannot read MSMS vertex file: " << vertfilename << sendmsg;
00518     return 0;  // failed
00519   }
00520   MSMSCoord norm, coord;
00521   int atomid;  // 1-based atom index
00522   int l0fa;    // number of of the level 0 SES face
00523   int l;       // SES face level? (1/2/3)
00524   while (fscanf(vertfile, "%f %f %f %f %f %f %d %d %d",
00525         coord.x+0, coord.x+1, coord.x+2, 
00526         norm.x+0, norm.x+1, norm.x+2, 
00527         &l0fa, &atomid, &l) == 9) {
00528     norms.append(norm);
00529     coords.append(coord);
00530     atomids.append(atomid-1);
00531   }
00532   fclose(vertfile);
00533 
00534   if (ids) {
00535     for (int i=0; i<atomids.num(); i++) {
00536       atomids[i] = ids[atomids[i]];
00537     }
00538   }
00539   return 1; // success
00540 }
00541 

Generated on Tue Apr 23 04:23:27 2024 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002