Coverage Report

Created: 2025-11-11 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openbabel/src/formats/cacaoformat.cpp
Line
Count
Source
1
/**********************************************************************
2
Copyright (C) 1998-2001 by OpenEye Scientific Software, Inc.
3
Some portions Copyright (C) 2001-2006 by Geoffrey R. Hutchison
4
Some portions Copyright (C) 2004 by Chris Morley
5
6
This program is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation version 2 of the License.
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 General Public License for more details.
14
***********************************************************************/
15
#include <openbabel/babelconfig.h>
16
17
#include <openbabel/obmolecformat.h>
18
#include <openbabel/mol.h>
19
#include <openbabel/atom.h>
20
#include <openbabel/elements.h>
21
#include <openbabel/generic.h>
22
#include <openbabel/internalcoord.h>
23
#include <openbabel/math/matrix3x3.h>
24
#include <cstdlib>
25
26
using namespace std;
27
namespace OpenBabel
28
{
29
30
  class CacaoFormat : public OBMoleculeFormat
31
  {
32
  public:
33
    //Register this format type ID
34
    CacaoFormat()
35
6
    {
36
6
      OBConversion::RegisterFormat("caccrt",this);
37
6
    }
38
39
    const char* Description() override  // required
40
0
    {
41
0
      return
42
0
        "Cacao Cartesian format\n"
43
0
        "Read Options e.g. -as\n"
44
0
        "  s  Output single bonds only\n"
45
0
        "  b  Disable bonding entirely\n\n";
46
0
    }
47
48
    const char* SpecificationURL() override
49
0
    { return "http://www.chembio.uoguelph.ca/oakley/310/cacao/cacao.htm"; }  // optional
50
51
    //Flags() can return be any the following combined by | or be omitted if none apply
52
    // NOTREADABLE  READONEONLY  NOTWRITABLE  WRITEONEONLY
53
    unsigned int Flags() override
54
10
    {
55
10
      return READONEONLY | WRITEONEONLY;
56
10
    }
57
58
    ////////////////////////////////////////////////////
59
    /// The "API" interface functions
60
    bool ReadMolecule(OBBase* pOb, OBConversion* pConv) override;
61
    bool WriteMolecule(OBBase* pOb, OBConversion* pConv) override;
62
63
    static void SetHilderbrandt(OBMol&,vector<OBInternalCoord*>&);
64
  };
65
66
  //Make an instance of the format class
67
  CacaoFormat theCacaoFormat;
68
69
  /////////////////////////////////////////////////////////////////
70
  bool CacaoFormat::ReadMolecule(OBBase* pOb, OBConversion* pConv)
71
1
  {
72
73
1
    OBMol* pmol = pOb->CastAndClear<OBMol>();
74
1
    if (pmol == nullptr)
75
0
      return false;
76
77
    //Define some references so we can use the old parameter names
78
1
    istream &ifs = *pConv->GetInStream();
79
1
    OBMol &mol = *pmol;
80
1
    mol.SetTitle( pConv->GetTitle()); //default title is the filename
81
82
1
    char buffer[BUFF_SIZE];
83
1
    int natoms;
84
1
    double A,B,C,Alpha,Beta,Gamma;
85
1
    matrix3x3 m;
86
87
1
    ifs.getline(buffer,BUFF_SIZE);
88
1
    mol.SetTitle(buffer);
89
1
    ifs.getline(buffer,BUFF_SIZE);
90
1
    sscanf(buffer,"%d",&natoms);
91
92
161
    while (ifs.getline(buffer,BUFF_SIZE) &&!EQn(buffer,"CELL",4))
93
160
      ;
94
95
1
    if (!EQn(buffer,"CELL",4))
96
1
      return(false);
97
0
    vector<string> vs;
98
0
    tokenize(vs,buffer," \n\t,");
99
0
    if (vs.size() != 7)
100
0
      return(false);
101
102
    //parse cell values
103
0
    A = atof((char*)vs[1].c_str());
104
0
    B = atof((char*)vs[2].c_str());
105
0
    C = atof((char*)vs[3].c_str());
106
0
    Alpha = atof((char*)vs[4].c_str());
107
0
    Beta  = atof((char*)vs[5].c_str());
108
0
    Gamma = atof((char*)vs[6].c_str());
109
110
0
    OBUnitCell *uc = new OBUnitCell;
111
0
    uc->SetData(A, B, C, Alpha, Beta, Gamma);
112
0
    uc->SetOrigin(fileformatInput);
113
0
    mol.SetData(uc);
114
115
0
    int i;
116
0
    double x,y,z;
117
0
    OBAtom *atom;
118
0
    vector3 v;
119
120
0
    mol.BeginModify();
121
122
0
    for (i = 1; i <= natoms;i++)
123
0
      {
124
0
        if (!ifs.getline(buffer,BUFF_SIZE))
125
0
          return(false);
126
0
        tokenize(vs,buffer," \n\t,");
127
0
        if (vs.size() < 4)
128
0
          return(false);
129
0
        atom = mol.NewAtom();
130
131
0
        x = atof((char*)vs[1].c_str());
132
0
        y = atof((char*)vs[2].c_str());
133
0
        z = atof((char*)vs[3].c_str());
134
0
        v.Set(x,y,z);
135
0
        v = uc->FractionalToCartesian(v);
136
137
0
        atom->SetAtomicNum(OBElements::GetAtomicNum(vs[0].c_str()));
138
0
        atom->SetVector(v);
139
0
      }
140
141
0
    if (!pConv->IsOption("b",OBConversion::INOPTIONS))
142
0
      mol.ConnectTheDots();
143
0
    if (!pConv->IsOption("s",OBConversion::INOPTIONS) && !pConv->IsOption("b",OBConversion::INOPTIONS))
144
0
      mol.PerceiveBondOrders();
145
146
    // clean out remaining blank lines
147
0
    std::streampos ipos;
148
0
    do
149
0
    {
150
0
      ipos = ifs.tellg();
151
0
      ifs.getline(buffer,BUFF_SIZE);
152
0
    }
153
0
    while(strlen(buffer) == 0 && !ifs.eof() );
154
0
    ifs.seekg(ipos);
155
156
0
    mol.EndModify();
157
0
    return(true);
158
0
  }
159
160
  ////////////////////////////////////////////////////////////////
161
162
  bool CacaoFormat::WriteMolecule(OBBase* pOb, OBConversion* pConv)
163
0
  {
164
0
    OBMol* pmol = dynamic_cast<OBMol*>(pOb);
165
0
    if (pmol == nullptr)
166
0
      return false;
167
168
    //Define some references so we can use the old parameter names
169
0
    ostream &ofs = *pConv->GetOutStream();
170
0
    OBMol &mol = *pmol;
171
172
0
    OBAtom *atom;
173
0
    char buffer[BUFF_SIZE];
174
0
    vector<OBAtom*>::iterator i;
175
176
0
    snprintf(buffer, BUFF_SIZE, "%s\n",mol.GetTitle());
177
0
    ofs << buffer;
178
0
    snprintf(buffer, BUFF_SIZE, "%3d   DIST  0  0  0\n",mol.NumAtoms());
179
0
    ofs << buffer;
180
181
0
    if (!mol.HasData(OBGenericDataType::UnitCell))
182
0
      ofs << "CELL 1.,1.,1.,90.,90.,90.\n";
183
0
    else
184
0
      {
185
0
        OBUnitCell *uc = (OBUnitCell*)mol.GetData(OBGenericDataType::UnitCell);
186
0
        snprintf(buffer, BUFF_SIZE, "CELL %f,%f,%f,%f,%f,%f\n",
187
0
                 uc->GetA(), uc->GetB(), uc->GetC(),
188
0
                 uc->GetAlpha(), uc->GetBeta(), uc->GetGamma());
189
0
        ofs << buffer;
190
0
      }
191
192
0
    for (atom = mol.BeginAtom(i);atom;atom = mol.NextAtom(i))
193
0
      {
194
0
        snprintf(buffer,BUFF_SIZE,"%2s %7.4f, %7.4f, %7.4f\n",
195
0
                 OBElements::GetSymbol(atom->GetAtomicNum()),
196
0
                 atom->x(),
197
0
                 atom->y(),
198
0
                 atom->z());
199
0
        ofs << buffer;
200
0
      }
201
202
0
    return(true);
203
0
  }
204
205
  //! \todo Make this method bulletproof. Currently it causes segfaults sometimes
206
  void CacaoFormat::SetHilderbrandt(OBMol &mol,vector<OBInternalCoord*> &vit)
207
0
  {
208
    // Roundtrip testing shows that some atoms are NULL
209
    //  which causes segfaults when dereferencing later
210
    //   (e.g. in the last "segment" of this routine)
211
0
    double sum,r;
212
213
0
    OBAtom dummy1,dummy2;
214
0
    dummy1.SetVector(0.0,0.0,1.0);
215
0
    dummy2.SetVector(1.0,0.0,0.0);
216
217
0
    OBAtom *atom,*a1,*a2,*ref;
218
0
    vector<OBAtom*>::iterator ai;
219
220
0
    vit.push_back(nullptr);
221
0
    for (atom = mol.BeginAtom(ai);atom;atom = mol.NextAtom(ai))
222
0
      vit.push_back(new OBInternalCoord (atom));
223
224
0
    vit[1]->_a = &dummy1;
225
0
    vit[1]->_b = &dummy2;
226
0
    if (vit.size() > 2) {
227
0
      vit[2]->_b = &dummy1;
228
0
      vit[2]->_c = &dummy2;
229
0
      if  (vit.size() > 3) {
230
0
        vit[3]->_c = &dummy1;
231
0
      }
232
0
    }
233
234
0
    unsigned int i,j;
235
0
    for (i = 2;i <= mol.NumAtoms();i++)
236
0
      {
237
0
        ref = nullptr;
238
0
        a1 = mol.GetAtom(i);
239
0
        sum = 100.0;
240
0
        for (j = 1;j < i;j++)
241
0
          {
242
0
            a2 = mol.GetAtom(j);
243
0
            r = (a1->GetVector()-a2->GetVector()).length_2();
244
0
            if ((r < sum) && (vit[j]->_a != a2) && (vit[j]->_b != a2))
245
0
              {
246
0
                sum = r;
247
0
                ref = a2;
248
0
              }
249
0
          }
250
0
        vit[i]->_a = ref;
251
0
      }
252
253
0
    for (i = 3;i <= mol.NumAtoms();i++)
254
0
      vit[i]->_b = vit[vit[i]->_a->GetIdx()]->_a;
255
256
0
    for (i = 4;i <= mol.NumAtoms();i++)
257
0
      {
258
0
        if (vit[i]->_b && vit[i]->_b->GetIdx())
259
0
          vit[i]->_c = vit[vit[i]->_b->GetIdx()]->_b;
260
0
        else
261
0
          vit[i]->_c = &dummy1;
262
0
      }
263
264
0
    OBAtom *a,*b,*c;
265
0
    vector3 v1,v2;
266
0
    for (i = 2;i <= mol.NumAtoms();i++)
267
0
      {
268
0
        atom = mol.GetAtom(i);
269
0
        a = vit[i]->_a;
270
0
        b = vit[i]->_b;
271
0
        c = vit[i]->_c;
272
        // TODO: fix explicit calculations for PBC?
273
0
        v1 = atom->GetVector() - a->GetVector();
274
0
        v2 = b->GetVector() - a->GetVector();
275
0
        vit[i]->_ang = vectorAngle(v1,v2);
276
0
        vit[i]->_tor = CalcTorsionAngle(atom->GetVector(),
277
0
                                        a->GetVector(),
278
0
                                        b->GetVector(),
279
0
                                        c->GetVector());
280
0
        vit[i]->_dst = (vit[i]->_a->GetVector() - atom->GetVector()).length();
281
0
      }
282
283
0
  }
284
  //***************************************************************
285
  class CacaoInternalFormat : public OBMoleculeFormat
286
  {
287
  public:
288
    //Register this format type ID
289
    CacaoInternalFormat()
290
6
    {
291
6
      OBConversion::RegisterFormat("cacint",this);
292
6
    }
293
294
    const char* Description() override  // required
295
0
    {
296
0
      return
297
0
        "Cacao Internal format\n"
298
0
        "No comments yet\n";
299
0
    }
300
301
0
    const char* SpecificationURL() override {
302
0
      return "http://www.chembio.uoguelph.ca/oakley/310/cacao/cacao.htm";
303
0
    } //optional
304
305
    //Flags() can return be any the following combined by | or be omitted if none apply
306
    // NOTREADABLE  READONEONLY  NOTWRITABLE  WRITEONEONLY
307
    unsigned int Flags() override
308
6
    {
309
6
      return NOTREADABLE | WRITEONEONLY;
310
6
    }
311
312
    ////////////////////////////////////////////////////
313
    /// The "API" interface functions
314
    bool WriteMolecule(OBBase* pOb, OBConversion* pConv) override;
315
  };
316
317
  //Make an instance of the format class
318
  CacaoInternalFormat theCacaoInternalFormat;
319
320
  bool CacaoInternalFormat::WriteMolecule(OBBase* pOb, OBConversion* pConv)
321
0
  {
322
0
    OBMol* pmol = dynamic_cast<OBMol*>(pOb);
323
0
    if (pmol == nullptr)
324
0
      return false;
325
326
    //Define some references so we can use the old parameter names
327
0
    ostream &ofs = *pConv->GetOutStream();
328
0
    OBMol &mol = *pmol;
329
330
0
    unsigned int i;
331
0
    vector3 v;
332
0
    char tmptype[16],buffer[BUFF_SIZE];
333
334
0
    if (mol.Empty())
335
0
      return(false);
336
337
    //translate first atom to origin
338
0
    v = mol.GetAtom(1)->GetVector();
339
0
    v *= -1.0;
340
0
    mol.Translate(v);
341
342
0
    vector<OBInternalCoord*> vit;
343
0
    CacaoFormat::SetHilderbrandt(mol,vit);
344
0
    strncpy(tmptype,OBElements::GetSymbol(mol.GetAtom(1)->GetAtomicNum()), sizeof(tmptype));
345
0
    tmptype[sizeof(tmptype) - 1] = '\0';
346
347
0
    ofs << " # TITLE\n";
348
0
    snprintf(buffer, BUFF_SIZE, "%3d  0DIST  0  0  0\n",mol.NumAtoms());
349
0
    ofs << "  EL\n";
350
0
    snprintf(buffer, BUFF_SIZE, "0.,0.,0., %s\n",tmptype);
351
0
    ofs << buffer;
352
0
    for (i = 2; i <= mol.NumAtoms(); i++)
353
0
      {
354
0
        strncpy(tmptype,OBElements::GetSymbol(mol.GetAtom(i)->GetAtomicNum()), sizeof(tmptype));
355
0
        tmptype[sizeof(tmptype) - 1] = '\0';
356
357
0
        if (vit[i]->_tor < 0.0)
358
0
          vit[i]->_tor += 360.0;
359
0
        snprintf(buffer, BUFF_SIZE, "%2d,%d,%2s%7.3f,%7.3f,%7.3f",
360
0
                 vit[i]->_a->GetIdx(),i,tmptype,
361
0
                 vit[i]->_dst,
362
0
                 vit[i]->_ang,
363
0
                 vit[i]->_tor);
364
0
        ofs << buffer << endl;
365
0
      }
366
367
0
    vector<OBInternalCoord*>::iterator j;
368
0
    for (j = vit.begin();j != vit.end();++j)
369
0
      if (*j)
370
0
        {
371
0
          delete *j;
372
0
          *j = nullptr;
373
0
        }
374
375
0
    return(true);
376
0
  }
377
378
} //namespace OpenBabel