Coverage Report

Created: 2026-01-17 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openbabel/src/formats/lmpdatformat.cpp
Line
Count
Source
1
/**********************************************************************
2
Copyright (C) 2011 by Albert DeFusco
3
4
This program is free software; you can redistribute it and/or modify
5
it under the terms of the GNU General Public License as published by
6
the Free Software Foundation version 2 of the License.
7
8
This program is distributed in the hope that it will be useful,
9
but WITHOUT ANY WARRANTY; without even the implied warranty of
10
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
GNU General Public License for more details.
12
***********************************************************************/
13
14
#include <openbabel/babelconfig.h>
15
#include <openbabel/obmolecformat.h>
16
#include <openbabel/mol.h>
17
#include <openbabel/atom.h>
18
#include <openbabel/elements.h>
19
#include <openbabel/bond.h>
20
21
#include <openbabel/obiter.h>
22
23
#include <sstream>
24
#include <map>
25
#include <cstdlib>
26
27
using namespace std;
28
namespace OpenBabel
29
{
30
31
  class LMPDATFormat : public OBMoleculeFormat
32
  {
33
  public:
34
    //Register this format type ID
35
    LMPDATFormat()
36
6
    {
37
6
      OBConversion::RegisterFormat("lmpdat", this, "chemical/x-lmpdat");
38
6
      OBConversion::RegisterOptionParam("q", nullptr, 1, OBConversion::OUTOPTIONS);
39
6
      OBConversion::RegisterOptionParam("d", nullptr, 1, OBConversion::OUTOPTIONS);
40
6
    }
41
42
    const char* Description() override  // required
43
0
    {
44
0
      return
45
0
  "The LAMMPS data format\n\n"
46
47
0
  "LAMMPS is a classical molecular dynamics code, and an acronym for\n"
48
0
  "Large-scale Atomic/Molecular Massively Parallel Simulator.\n\n"
49
50
0
  "Write Options, e.g. -xq\n"
51
0
  "  q \"water-model\" Set atomic charges for water.\n"
52
0
  "    There are two options: SPC (default) or SPCE\n"
53
0
  "  d \"length\" Set the length of the boundary box around the molecule.\n"
54
0
  "    The default is to make a cube around the molecule\n"
55
0
  "    adding 50% to the most positive and negative\n"
56
0
  "    cartesian coordinate.\n"
57
0
  ;
58
0
    }
59
60
61
    const char* GetMIMEType() override
62
0
    { return "chemical/x-lmpdat"; }
63
64
    unsigned int Flags() override
65
11
    {
66
11
      return NOTREADABLE | WRITEONEONLY;
67
11
    }
68
69
    bool WriteMolecule(OBBase* pOb, OBConversion* pConv) override;
70
  };
71
  //***
72
73
  //Make an instance of the format class
74
  LMPDATFormat theLMPDATFormat;
75
76
  ////////////////////////////////////////////////////////////////
77
78
  bool LMPDATFormat::WriteMolecule(OBBase* pOb, OBConversion* pConv)
79
4
  {
80
4
    OBMol* pmol = dynamic_cast<OBMol*>(pOb);
81
4
    if (pmol == nullptr)
82
0
      return false;
83
84
    //Define some references so we can use the old parameter names
85
4
    ostream &ofs = *pConv->GetOutStream();
86
4
    OBMol &mol = *pmol;
87
4
    OBAtom *a;
88
4
    OBAtom *b;
89
4
    OBAtom *c;
90
4
    OBAtom *d;
91
92
4
    char buffer[BUFF_SIZE];
93
94
95
    //Very basic atom typing by element only
96
    //TODO: The maps should become smarts strings instead of element names
97
4
    double ThisMass;
98
4
    string ThisAtom;
99
4
    map<string, double> AtomMass;
100
4
    FOR_ATOMS_OF_MOL(atom, mol)
101
35.9k
    {
102
35.9k
     ThisMass=atom->GetAtomicMass();
103
35.9k
     ThisAtom=OBElements::GetSymbol(atom->GetAtomicNum());
104
35.9k
     AtomMass[ThisAtom] = ThisMass;
105
35.9k
    }
106
4
    map<string, int> AtomType;
107
4
    int AtomIndex=0;
108
4
    map<string, double>::iterator itr;
109
    //Set AtomType integer
110
15
    for(itr=AtomMass.begin();itr!=AtomMass.end();++itr) 
111
11
    {
112
11
      AtomIndex++;
113
11
      AtomType[itr->first] = AtomIndex;
114
11
    }
115
116
    //Determine unique bonds
117
4
    mol.ConnectTheDots();
118
4
    char BondString[BUFF_SIZE];
119
4
    map<string, int> BondType;
120
4
    FOR_BONDS_OF_MOL(bond,mol)
121
35.6k
    {
122
35.6k
      a=bond->GetBeginAtom();
123
35.6k
      b=bond->GetEndAtom();
124
125
      //To let the unordered map determine unique keys,
126
      //always put the heavier atom first
127
35.6k
      if(b->GetAtomicNum() > a->GetAtomicNum() )
128
14.5k
      {
129
14.5k
        snprintf(BondString,BUFF_SIZE,
130
14.5k
            "%2s:%2s",
131
14.5k
            OBElements::GetSymbol(b->GetAtomicNum()),
132
14.5k
            OBElements::GetSymbol(a->GetAtomicNum()));
133
14.5k
      }
134
21.1k
      else
135
21.1k
      {
136
21.1k
        snprintf(BondString,BUFF_SIZE,
137
21.1k
            "%2s:%2s",
138
21.1k
            OBElements::GetSymbol(a->GetAtomicNum()),
139
21.1k
            OBElements::GetSymbol(b->GetAtomicNum()));
140
21.1k
      }
141
35.6k
      BondType[BondString] = 0;
142
35.6k
    }
143
4
    map<string, int>::iterator intitr;
144
4
    int BondIndex=0;
145
    //Set the BondType integer
146
12
    for(intitr=BondType.begin();intitr!=BondType.end();++intitr)
147
8
    {
148
8
      BondIndex++;
149
8
      BondType[intitr->first] = BondIndex;
150
8
    }
151
152
    //Determine unique angles
153
4
    mol.FindAngles();
154
4
    int anglecount=0;
155
4
    char AngleString[BUFF_SIZE];
156
4
    map<string, int> AngleType;
157
4
    FOR_ANGLES_OF_MOL(angle,mol)
158
61.4k
    {
159
61.4k
      anglecount++;
160
61.4k
      a = mol.GetAtom((*angle)[0]+1);
161
61.4k
      b = mol.GetAtom((*angle)[1]+1);
162
61.4k
      c = mol.GetAtom((*angle)[2]+1);
163
      //Origanization trick:
164
      //1. The atom "a" is acutally the middle
165
      //2. Always write the heavy element first
166
61.4k
      if(b->GetAtomicNum() > c->GetAtomicNum() ) 
167
39.7k
      {
168
39.7k
        snprintf(AngleString,BUFF_SIZE,
169
39.7k
            "%2s:%2s:%2s",
170
39.7k
            OBElements::GetSymbol(b->GetAtomicNum()),
171
39.7k
            OBElements::GetSymbol(a->GetAtomicNum()),
172
39.7k
            OBElements::GetSymbol(c->GetAtomicNum()));
173
39.7k
      }
174
21.7k
      else
175
21.7k
      {
176
21.7k
        snprintf(AngleString,BUFF_SIZE,
177
21.7k
            "%2s:%2s:%2s",
178
21.7k
            OBElements::GetSymbol(c->GetAtomicNum()),
179
21.7k
            OBElements::GetSymbol(a->GetAtomicNum()),
180
21.7k
            OBElements::GetSymbol(b->GetAtomicNum()));
181
21.7k
      }
182
61.4k
      AngleType[AngleString]=0;
183
61.4k
    }
184
4
    int AngleIndex=0;
185
    //Set the AtomType integer
186
18
    for(intitr=AngleType.begin();intitr!=AngleType.end();++intitr)
187
14
    {
188
14
      AngleIndex++;
189
14
      AngleType[intitr->first] = AngleIndex;
190
14
    }
191
192
    //dihedrals
193
4
    mol.FindTorsions();
194
4
    int dihedralcount=0;
195
4
    char DihedralString[BUFF_SIZE];
196
4
    map<string, int>DihedralType;
197
4
    FOR_TORSIONS_OF_MOL(dihedral, mol)
198
84.4k
    {
199
84.4k
      dihedralcount++;
200
84.4k
      a = mol.GetAtom((*dihedral)[0]+1);
201
84.4k
      b = mol.GetAtom((*dihedral)[1]+1);
202
84.4k
      c = mol.GetAtom((*dihedral)[2]+1);
203
84.4k
      d = mol.GetAtom((*dihedral)[3]+1);
204
      //place the heavier element first
205
      //the same may have to be done with the inner two as well
206
84.4k
      if(a->GetAtomicNum() > d->GetAtomicNum() )
207
27.3k
      {
208
27.3k
        snprintf(DihedralString,BUFF_SIZE,
209
27.3k
            "%2s:%2s:%2s:%2s",
210
27.3k
            OBElements::GetSymbol(a->GetAtomicNum()),
211
27.3k
            OBElements::GetSymbol(b->GetAtomicNum()),
212
27.3k
            OBElements::GetSymbol(c->GetAtomicNum()),
213
27.3k
            OBElements::GetSymbol(d->GetAtomicNum()));
214
27.3k
      }
215
57.0k
      else
216
57.0k
      {
217
57.0k
        snprintf(DihedralString,BUFF_SIZE,
218
57.0k
            "%2s:%2s:%2s:%2s",
219
57.0k
            OBElements::GetSymbol(d->GetAtomicNum()),
220
57.0k
            OBElements::GetSymbol(b->GetAtomicNum()),
221
57.0k
            OBElements::GetSymbol(c->GetAtomicNum()),
222
57.0k
            OBElements::GetSymbol(a->GetAtomicNum()));
223
57.0k
      }
224
84.4k
      DihedralType[DihedralString]=0;
225
84.4k
    }
226
4
    int DihedralIndex=0;
227
    //Set DihedralType integer
228
28
    for(intitr=DihedralType.begin();intitr!=DihedralType.end();++intitr)
229
24
    {
230
24
      DihedralIndex++;
231
24
      DihedralType[intitr->first] = DihedralIndex;
232
24
    }
233
234
    //The box lengths
235
4
    vector3 vmin,vmax;
236
4
    vmax.Set(-10E10,-10E10,-10E10);
237
4
    vmin.Set( 10E10, 10E10, 10E10);
238
4
    FOR_ATOMS_OF_MOL(a,mol)
239
35.9k
    {
240
35.9k
        if (a->x() < vmin.x())
241
5
            vmin.SetX(a->x());
242
35.9k
        if (a->y() < vmin.y())
243
26
            vmin.SetY(a->y());
244
35.9k
        if (a->z() < vmin.z())
245
19
            vmin.SetZ(a->z());
246
247
35.9k
        if (a->x() > vmax.x())
248
10.9k
            vmax.SetX(a->x());
249
35.9k
        if (a->y() > vmax.y())
250
20
            vmax.SetY(a->y());
251
35.9k
        if (a->z() > vmax.z())
252
26
            vmax.SetZ(a->z());
253
35.9k
    }
254
255
    //For now, a simple cube may be the best way to go.
256
    //It may be necessary to set the boxlength to enforce
257
    //a density.
258
4
    const char *boxLn = pConv->IsOption("d",OBConversion::OUTOPTIONS);
259
4
    double xlo,xhi;
260
4
    char BoxString[BUFF_SIZE];
261
4
    if(boxLn)
262
0
    {
263
0
      xlo=-atof(boxLn);
264
0
      xhi=atof(boxLn);
265
0
    }
266
4
    else
267
4
    {
268
4
      double cmin=10e-10;
269
4
      double cmax=-10e10;
270
4
      if ( vmax.x() > cmax ) cmax=vmax.x();
271
4
      if ( vmax.y() > cmax ) cmax=vmax.y();
272
4
      if ( vmax.z() > cmax ) cmax=vmax.z();
273
4
      if ( vmin.x() < cmin ) cmin=vmin.x();
274
4
      if ( vmin.y() < cmin ) cmin=vmin.y();
275
4
      if ( vmin.z() < cmin ) cmin=vmin.z();
276
277
4
      double length=cmax-cmin;
278
4
      xlo = cmin -0.5;//- 0.01*length;
279
4
      xhi = cmax +0.5;//+ 0.01*length;
280
4
    }
281
4
    snprintf(BoxString,BUFF_SIZE,
282
4
        "%10.5f %10.5f xlo xhi\n%10.5f %10.5f ylo yhi\n%10.5f %10.5f zlo zhi\n",
283
4
        xlo,xhi,
284
4
        xlo,xhi,
285
4
        xlo,xhi);
286
    
287
288
    //%%%%%%%%%%%%%% Now writting the data file %%%%%%%%%%%%%
289
290
    //The LAMMPS header stars here
291
4
    ofs << "LAMMPS data file generated by OpenBabel" << endl;
292
4
    ofs << mol.NumAtoms() << " atoms"     << endl;
293
4
    ofs << mol.NumBonds() << " bonds"     << endl;
294
4
    ofs << anglecount     << " angles"    << endl; // no NumAngles()?
295
4
    ofs << dihedralcount  << " dihedrals" << endl;
296
4
    ofs << 0              << " impropers" << endl;
297
4
    ofs << AtomType.size()     << " atom types"     << endl;
298
4
    ofs << BondType.size()     << " bond types"     << endl;
299
4
    ofs << AngleType.size()    << " angle types"    << endl;
300
4
    ofs << DihedralType.size() << " dihedral types" << endl;
301
4
    ofs << 0                   << " improper types" << endl;
302
4
    ofs << BoxString << endl;
303
304
305
    //Write the atom types
306
4
    ofs << endl << endl << "Masses" << endl << endl;
307
15
    for(itr=AtomMass.begin();itr!=AtomMass.end();++itr) 
308
11
    {
309
11
      double mass=itr->second;
310
11
      ofs << AtomType[itr->first] << " " << mass << " # " << itr->first << endl;
311
11
    }
312
4
    ofs << endl;
313
314
315
316
    //Set atomic charges for atom_style=full
317
    //These are charges for the SPC water model
318
4
    const char *selectCharges = pConv->IsOption("q",OBConversion::OUTOPTIONS);
319
4
    map<string, double> AtomCharge;
320
15
    for(itr=AtomMass.begin();itr!=AtomMass.end();++itr) 
321
11
    {
322
11
      if(selectCharges)
323
0
      {
324
0
        if(strcmp(selectCharges,"spce")==0)
325
0
        {
326
0
          if(itr->second == 15.9994)
327
0
            AtomCharge[itr->first] = -0.8472;
328
0
          if(itr->second == 1.00794) 
329
0
            AtomCharge[itr->first] =  0.4236;
330
0
        }
331
0
        else if(strcmp(selectCharges,"spc")==0)
332
0
        {
333
0
          if(itr->second == 15.9994)
334
0
            AtomCharge[itr->first] = -0.820;
335
0
          if(itr->second == 1.00794) 
336
0
            AtomCharge[itr->first] =  0.410;
337
0
        }
338
0
      }
339
11
      else
340
11
      {
341
11
        if(itr->second == 15.9994)
342
2
          AtomCharge[itr->first] = -0.820;
343
11
        if(itr->second == 1.00794) 
344
1
          AtomCharge[itr->first] =  0.410;
345
11
      }
346
347
11
    }
348
349
350
351
    //Write atomic coordinates
352
4
    vector<OBMol>           mols;
353
4
    vector<OBMol>::iterator molitr;
354
4
    mols=mol.Separate();
355
4
    int atomcount=0;
356
4
    int molcount=0;
357
4
    ofs << endl;
358
4
    ofs << "Atoms" << endl << endl;
359
4
    snprintf(buffer,BUFF_SIZE,"#%3s %4s %4s %10s %10s %10s %10s\n",
360
4
        "idx","mol","type","charge","x","y","z");
361
    //ofs << buffer;
362
321
    for(molitr=mols.begin();molitr!=mols.end();++molitr)
363
317
    {
364
317
      molcount++;
365
317
      FOR_ATOMS_OF_MOL(atom,*molitr) 
366
35.9k
      {
367
35.9k
        int atomid=5;
368
35.9k
        double charge=0.5;
369
35.9k
        atomcount++;
370
35.9k
        ThisAtom=OBElements::GetSymbol(atom->GetAtomicNum());
371
35.9k
        snprintf(buffer,BUFF_SIZE,"%-4d %4d %4d %10.5f %10.5f %10.5f %10.5f # %3s\n",
372
35.9k
            atomcount,molcount,
373
35.9k
            AtomType[ThisAtom],
374
35.9k
            AtomCharge[ThisAtom],
375
35.9k
            atom->GetX(),
376
35.9k
            atom->GetY(),
377
35.9k
            atom->GetZ(),
378
35.9k
            ThisAtom.c_str());
379
35.9k
        ofs << buffer;
380
35.9k
      }
381
317
    } 
382
383
384
    //Write Bonds
385
4
    BondIndex=0;
386
4
    ofs << endl << endl;
387
4
    ofs << "Bonds" << endl;
388
4
    ofs << endl;
389
4
    snprintf(buffer,BUFF_SIZE,
390
4
        "#%3s %4s %4s %4s\n",
391
4
        "idx","type","atm1","atom2");
392
    //ofs << buffer;
393
4
    FOR_BONDS_OF_MOL(bond,mol)
394
35.6k
    {
395
35.6k
      BondIndex++;
396
35.6k
      a = bond->GetBeginAtom();
397
35.6k
      b = bond->GetEndAtom();
398
      //To let the unordered map determine unique keys,
399
      //always put the heavier atom first
400
35.6k
      if(b->GetAtomicNum() > a->GetAtomicNum() )
401
14.5k
      {
402
14.5k
        snprintf(BondString,BUFF_SIZE,
403
14.5k
            "%2s:%2s",
404
14.5k
            OBElements::GetSymbol(b->GetAtomicNum()),
405
14.5k
            OBElements::GetSymbol(a->GetAtomicNum()));
406
14.5k
        snprintf(buffer,BUFF_SIZE,
407
14.5k
            "%-4d %4d %4d %4d # %5s\n",
408
14.5k
            BondIndex,
409
14.5k
            BondType[BondString],
410
14.5k
            bond->GetEndAtomIdx(),
411
14.5k
            bond->GetBeginAtomIdx(),
412
14.5k
            BondString);
413
14.5k
      }
414
21.1k
      else
415
21.1k
      {
416
21.1k
        snprintf(BondString,BUFF_SIZE,
417
21.1k
            "%2s:%2s",
418
21.1k
            OBElements::GetSymbol(a->GetAtomicNum()),
419
21.1k
            OBElements::GetSymbol(b->GetAtomicNum()));
420
21.1k
        snprintf(buffer,BUFF_SIZE,
421
21.1k
            "%-4d %4d %4d %4d # %5s\n",
422
21.1k
            BondIndex,
423
21.1k
            BondType[BondString],
424
21.1k
            bond->GetBeginAtomIdx(),
425
21.1k
            bond->GetEndAtomIdx(),
426
21.1k
            BondString);
427
21.1k
      }
428
35.6k
      ofs << buffer;
429
35.6k
    }
430
4
    ofs << endl;
431
432
    //Write Angles
433
4
    AngleIndex=0;
434
4
    ofs << endl;
435
4
    ofs << "Angles" << endl;
436
4
    ofs << endl;
437
4
    snprintf(buffer,BUFF_SIZE,
438
4
        "#%3s %4s %4s %4s %4s\n",
439
4
        "idx","type","atm1","atm2","atm3");
440
    //ofs << buffer;
441
4
    FOR_ANGLES_OF_MOL(angle,mol)
442
61.4k
    {
443
61.4k
      AngleIndex++;
444
61.4k
      a = mol.GetAtom((*angle)[0]+1);
445
61.4k
      b = mol.GetAtom((*angle)[1]+1);
446
61.4k
      c = mol.GetAtom((*angle)[2]+1);
447
      //Origanization trick:
448
      //1. The atom "a" is acutally the middle
449
      //2. Always write the heavy element first
450
61.4k
      if(b->GetAtomicNum() > c->GetAtomicNum() ) 
451
39.7k
      {
452
39.7k
        snprintf(AngleString,BUFF_SIZE,
453
39.7k
            "%2s:%2s:%2s",
454
39.7k
            OBElements::GetSymbol(b->GetAtomicNum()),
455
39.7k
            OBElements::GetSymbol(a->GetAtomicNum()),
456
39.7k
            OBElements::GetSymbol(c->GetAtomicNum()));
457
39.7k
        snprintf(buffer,BUFF_SIZE,
458
39.7k
            "%-4d %4d %4d %4d %4d # %8s\n",
459
39.7k
            AngleIndex,
460
39.7k
            AngleType[AngleString],
461
39.7k
            b->GetIdx(),
462
39.7k
            a->GetIdx(),
463
39.7k
            c->GetIdx(),
464
39.7k
            AngleString);
465
39.7k
      }
466
21.7k
      else
467
21.7k
      {
468
21.7k
        snprintf(AngleString,BUFF_SIZE,
469
21.7k
            "%2s:%2s:%2s",
470
21.7k
            OBElements::GetSymbol(c->GetAtomicNum()),
471
21.7k
            OBElements::GetSymbol(a->GetAtomicNum()),
472
21.7k
            OBElements::GetSymbol(b->GetAtomicNum()));
473
21.7k
        snprintf(buffer,BUFF_SIZE,
474
21.7k
            "%-4d %4d %4d %4d %4d # %8s\n",
475
21.7k
            AngleIndex,
476
21.7k
            AngleType[AngleString],
477
21.7k
            c->GetIdx(),
478
21.7k
            a->GetIdx(),
479
21.7k
            b->GetIdx(),
480
21.7k
            AngleString);
481
21.7k
      }
482
61.4k
      ofs << buffer;
483
484
61.4k
    }
485
4
    ofs << endl;
486
487
    //Write Dihedrals
488
4
    if(dihedralcount>0)
489
1
    {
490
1
      ofs << endl;
491
1
      ofs << "Dihedrals" << endl;
492
1
      ofs << endl;
493
1
      snprintf(buffer,BUFF_SIZE,
494
1
          "#%3s %4s %4s %4s %4s %4s\n",
495
1
          "idx","type","atm1","atm2","atm3","atm4");
496
      //ofs << buffer;
497
1
      DihedralIndex=0;
498
1
      FOR_TORSIONS_OF_MOL(dihedral, mol)
499
84.4k
      {
500
84.4k
        DihedralIndex++;
501
84.4k
        a = mol.GetAtom((*dihedral)[0]+1);
502
84.4k
        b = mol.GetAtom((*dihedral)[1]+1);
503
84.4k
        c = mol.GetAtom((*dihedral)[2]+1);
504
84.4k
        d = mol.GetAtom((*dihedral)[3]+1);
505
        //place the heavier element first
506
        //the same may have to be done with the inner two as well
507
84.4k
        if(a->GetAtomicNum() > d->GetAtomicNum() )
508
27.3k
        {
509
27.3k
          snprintf(DihedralString,BUFF_SIZE,
510
27.3k
              "%2s:%2s:%2s:%2s",
511
27.3k
              OBElements::GetSymbol(a->GetAtomicNum()),
512
27.3k
              OBElements::GetSymbol(b->GetAtomicNum()),
513
27.3k
              OBElements::GetSymbol(c->GetAtomicNum()),
514
27.3k
              OBElements::GetSymbol(d->GetAtomicNum()));
515
27.3k
          snprintf(buffer,BUFF_SIZE,
516
27.3k
              "%-4d %4d %4d %4d %4d %4d # %11s\n",
517
27.3k
              DihedralIndex,
518
27.3k
              DihedralType[DihedralString],
519
27.3k
              a->GetIdx(),
520
27.3k
              b->GetIdx(),
521
27.3k
              c->GetIdx(),
522
27.3k
              d->GetIdx(),
523
27.3k
              DihedralString);
524
27.3k
        }
525
57.0k
        else
526
57.0k
        {
527
57.0k
          snprintf(DihedralString,BUFF_SIZE,
528
57.0k
              "%2s:%2s:%2s:%2s",
529
57.0k
              OBElements::GetSymbol(d->GetAtomicNum()),
530
57.0k
              OBElements::GetSymbol(b->GetAtomicNum()),
531
57.0k
              OBElements::GetSymbol(c->GetAtomicNum()),
532
57.0k
              OBElements::GetSymbol(a->GetAtomicNum()));
533
57.0k
          snprintf(buffer,BUFF_SIZE,
534
57.0k
              "%-4d %4d %4d %4d %4d %4d # %11s\n",
535
57.0k
              DihedralIndex,
536
57.0k
              DihedralType[DihedralString],
537
57.0k
              d->GetIdx(),
538
57.0k
              b->GetIdx(),
539
57.0k
              c->GetIdx(),
540
57.0k
              a->GetIdx(),
541
57.0k
              DihedralString);
542
57.0k
        }
543
84.4k
        ofs << buffer;
544
84.4k
      }
545
1
    }
546
547
548
4
    return(true);
549
4
  }
550
551
} //namespace OpenBabel