1 /**********************************************************************
2 Copyright (C) 2007 by Daniel Mansfield
3 Some portions Copyright (C) 2004-2006 by Chris Morley
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation version 2 of the License.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13 ***********************************************************************/
14 
15 /*
16  * File extension module for CaRIne's ASCII Crystal (ACR)
17  * By Daniel Mansfield
18  * 30th January 2007
19  */
20 
21 #include <openbabel/babelconfig.h>
22 
23 #include <openbabel/mol.h>
24 #include <openbabel/atom.h>
25 #include <openbabel/elements.h>
26 #include <openbabel/obmolecformat.h>
27 #include <stdio.h>
28 #include <cstdlib>
29 
30 using namespace std;
31 namespace OpenBabel
32 {
33 
34   class ACRFormat : public OBMoleculeFormat
35   {
36   public:
37     //Register this format type ID in the constructor
ACRFormat()38     ACRFormat()
39     {
40       OBConversion::RegisterFormat("acr", this, "chemical/x-acr");
41       //		OBConversion::RegisterOptionParam("f", this, 1);
42       //		OBConversion::RegisterOptionParam("n", this);
43       OBConversion::RegisterOptionParam("s", this, 0, OBConversion::INOPTIONS);
44 
45     }
46 
Description()47     virtual const char* Description() //required
48     {
49       return
50         "ACR format\n"
51         "CaRIne ASCII Crystal format (ACR)\n"
52         //      "Write Options e.g. -xf3 \n"
53         // "  f# Number of (fictional) levels \n"
54         //			"  n  Omit (virtual) title\n"
55         "Read Options e.g. -as\n"
56         "  s  Consider single bonds only\n";
57     };
58 
SpecificationURL()59     virtual const char* SpecificationURL()
60     {return "http://pros.orange.fr/carine.crystallography/books/31/carine_31_us.pdf";};
61 
GetMIMEType()62     virtual const char* GetMIMEType() { return "chemical/x-acr"; };
63 
64 
Flags()65 	  virtual unsigned int Flags()
66 	  {
67       return READONEONLY | NOTWRITABLE;
68 	  };
69 
SkipObjects(int n,OBConversion * pConv)70     virtual int SkipObjects(int n, OBConversion* pConv)
71     {
72       return 0;
73     };
74 
75     virtual bool ReadMolecule(OBBase* pOb, OBConversion* pConv);
76     //virtual bool WriteMolecule(OBBase* pOb, OBConversion* pConv);
77 
78   private:
79   };
80 
81   ACRFormat theACRFormat;
82 
ReadMolecule(OBBase * pOb,OBConversion * pConv)83   bool ACRFormat::ReadMolecule(OBBase* pOb, OBConversion* pConv)
84   {
85     OBMol* pmol = pOb->CastAndClear<OBMol>();
86     if (pmol == nullptr)
87       return false;
88 
89     istream& ifs = *pConv->GetInStream();
90 
91     pmol->BeginModify();
92 
93     /** Parse the input stream and use the OpenBabel API to populate the OBMol **/
94     char buf[BUFF_SIZE];
95     unsigned int atoms, bonds, tmp;
96     float scale, dtmp;
97     bool atom_input = false, bond_input = false;
98     string type;
99     //int from, to;
100     double X,Y,Z;
101     vector<string> vs;
102 
103     // read in one at a time
104     /* WARNING: Atom id starts from zero in Carine; not so in openbabel.
105      * Solution: Let Open Babel to set them. */
106 
107     while (true) {
108       ifs.getline(buf, BUFF_SIZE);
109       if (ifs.eof()) {
110         break;
111       }
112 
113       if (sscanf(buf, "General Scale=%f\n", &dtmp)) {
114         scale = dtmp;
115         continue;
116       } else if (sscanf(buf, "Number of Atoms in Crystal=%d\n", &tmp)) {
117         atoms = tmp;
118         atom_input = true;
119 
120         // read table column names
121         ifs.getline(buf, BUFF_SIZE);
122         continue;
123       } else if (sscanf(buf, "Number of Links in Crystal=%d\n", &tmp)) {
124         atom_input = false;
125         bond_input = true;
126         bonds = tmp;
127 
128         // read table column names
129         ifs.getline(buf, BUFF_SIZE);
130         continue;
131       } else if ( '#' == buf[0] || '\r' == buf[0] || '\n' == buf[0] ) {
132         // between sections, in both windows and unix.
133         continue;
134       }
135       tokenize(vs, buf, " \t\r\n");
136 
137       if (atom_input) {
138 	if (vs.size() < 9) return false; // timvdm 18/06/2008
139         type = vs[1];
140         X = atof((char*)vs[6].c_str())/scale;
141         Y = atof((char*)vs[7].c_str())/scale;
142         Z = atof((char*)vs[8].c_str())/scale;
143 
144         OBAtom* a = pmol->NewAtom();
145         if (*(type.c_str()) != '*')
146           a->SetAtomicNum(OBElements::GetAtomicNum(type.c_str()));
147         a->SetVector(X,Y,Z);
148 
149       } else if (bond_input) {
150 	if (vs.size() < 2) return false; // timvdm 18/06/2008
151         // add to pmol
152         if (!pmol->AddBond(atoi((char*)vs[0].c_str()) + 1, atoi((char*)vs[1].c_str()) + 1,
153                            1 /* bond order not specified in Carine, use PerceiveBondOrder later */))
154           {
155             obErrorLog.ThrowError(__FUNCTION__, "addition of bond between " + vs[0] + " and " + vs[1] + " failed", obError);
156             return false;
157           }
158       }
159     }
160 
161     /* got sanity? */
162     if ( pmol->NumBonds() != bonds ) {
163       // then we read a different number of bonds than those promised.
164       obErrorLog.ThrowError(__FUNCTION__, "Number of bonds read does not match the number promised", obError);
165       return false;
166     } else if ( pmol->NumAtoms() != atoms ) {
167       obErrorLog.ThrowError(__FUNCTION__, "Number of atoms read does not match the number promised", obError);
168       return false;
169     }
170 
171     pmol->PerceiveBondOrders();
172 
173     pmol->EndModify();
174 
175     return true;
176   }
177 
178 } //namespace OpenBabel
179 
180