Coverage Report

Created: 2025-10-10 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openbabel/src/math/spacegroup.cpp
Line
Count
Source
1
/**********************************************************************
2
spacegroup.cpp - Handle Space Groups.
3
4
Copyright (C) 2007-2011 by Jean Bréfort
5
6
This file is part of the Open Babel project.
7
For more information, see <http://openbabel.org/>
8
9
This program is free software; you can redistribute it and/or
10
modify it under the terms of the GNU General Public License as
11
published by the Free Software Foundation; either version 2 of the
12
License, or (at your option) any later version.
13
14
This program is distributed in the hope that it will be useful,
15
but WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
GNU General Public License for more details.
18
***********************************************************************/
19
#include <openbabel/babelconfig.h>
20
21
#include <openbabel/math/spacegroup.h>
22
#include <openbabel/data.h>
23
#include <openbabel/obutil.h>
24
#include <iostream>
25
#include <map>
26
#include <set>
27
#include <vector>
28
#include <locale>
29
30
#include <cstdarg>
31
#include <cstdlib>
32
33
#include "spacegroups.h"
34
35
using namespace std;
36
37
namespace OpenBabel
38
{
39
  /** Function to remove whitespaces from a string, returning
40
   * a new string
41
   */
42
0
  std::string RemoveWhiteSpaceUnderscore(const string &in){
43
0
    std::string out=in;
44
0
    for(std::string::iterator pos=out.begin();pos!=out.end();){
45
0
      if( ((char)(*pos)==' ') ||((char)(*pos)=='_'))  pos=out.erase(pos);
46
0
      else ++pos;
47
0
    }
48
0
    return out;
49
0
  }
50
51
  class SpaceGroups: public OBGlobalDataBase
52
  {
53
  public:
54
    SpaceGroups();
55
    ~SpaceGroups() override;
56
57
    void ParseLine(const char*) override;
58
0
    size_t GetSize() override { return sgs.size(); }
59
0
    bool Inited() { return _init;}
60
61
    map<string, const SpaceGroup*> sgbn;
62
    vector< list<const SpaceGroup*> > sgbi;
63
    set<SpaceGroup*> sgs;
64
  };
65
66
  static SpaceGroups _SpaceGroups;
67
68
  SpaceGroups::SpaceGroups()
69
2
  {
70
2
    sgbi.assign(230, list<const SpaceGroup*>());
71
2
    _dir = BABEL_DATADIR;
72
2
    _envvar = "BABEL_DATADIR";
73
2
    _filename = "space-groups.txt";
74
2
    _subdir = "data";
75
2
    _dataptr = SpaceGroupsData;
76
2
  }
77
78
  SpaceGroups::~SpaceGroups()
79
0
  {
80
0
    set<SpaceGroup*>::iterator i, end = sgs.end();
81
0
    for (i = sgs.begin(); i != end; ++i)
82
0
      delete (*i);
83
0
  }
84
85
  enum
86
    {
87
      SPACE_GROUP_ID,
88
      SPACE_GROUP_HALL,
89
      SPACE_GROUP_HM,
90
      SPACE_GROUP_TRANSFORM
91
    };
92
93
  void SpaceGroups::ParseLine(const char* line)
94
0
  {
95
0
    static SpaceGroup *group = nullptr;
96
0
    static int step = SPACE_GROUP_ID;
97
0
    static string HMs;
98
0
    switch (step)
99
0
      {
100
0
      case SPACE_GROUP_ID:
101
0
        group = new SpaceGroup();
102
0
        group->SetId(atoi (line));
103
0
        step++;
104
0
        break;
105
0
      case SPACE_GROUP_HALL:
106
0
        group->SetHallName(line);
107
0
        step++;
108
0
        break;
109
0
      case SPACE_GROUP_HM:
110
0
        {
111
0
          string linestr = std::string(line);
112
0
          std::string::size_type idx = linestr.find(',');
113
0
          if (idx != std::string::npos)
114
0
            {
115
0
              std::string alt = linestr.substr(0, idx);
116
0
              if (alt.length() > 0 && _SpaceGroups.sgbn[alt] == nullptr)
117
0
                _SpaceGroups.sgbn[alt] = group;
118
0
              std::string stripped_HM=RemoveWhiteSpaceUnderscore(alt);
119
0
              if (stripped_HM.length() > 0 && _SpaceGroups.sgbn[stripped_HM] == nullptr)
120
0
                _SpaceGroups.sgbn[stripped_HM] = group;
121
0
              group->SetHMName(linestr.substr(idx+1, std::string::npos).c_str());
122
0
            }
123
0
          else
124
0
            group->SetHMName(line);
125
0
          step++;
126
0
          break;
127
0
        }
128
0
      case SPACE_GROUP_TRANSFORM:
129
0
        if (strlen(line) == 0)
130
0
          {
131
0
            step = SPACE_GROUP_ID;
132
0
            if (HMs.length() > 0)
133
0
              group->RegisterSpaceGroup(1, HMs.c_str());
134
0
            else
135
0
              group->RegisterSpaceGroup();
136
0
            group = nullptr;
137
0
            HMs.clear();
138
0
          }
139
0
        else
140
0
          group->AddTransform(line);
141
0
        break;
142
0
      }
143
0
  }
144
145
  SpaceGroup::SpaceGroup():
146
102
    HEXAGONAL_ORIGIN(10), m_HM(""),m_Hall(""),m_id(0),m_OriginAlternative(0)
147
102
  {
148
102
  }
149
150
  SpaceGroup::~SpaceGroup()
151
102
  {
152
102
    list<transform3d*>::iterator i, end = m_transforms.end();
153
102
    for (i = m_transforms.begin(); i != end; ++i)
154
0
      delete *i;
155
102
  }
156
157
  void SpaceGroup::SetHMName(const char *name_in)
158
0
  {
159
0
    string name = std::string(name_in);
160
0
    std::string::size_type idx = name.find(':');
161
0
    if (idx != std::string::npos)
162
0
      {
163
0
        std::string origin = name.substr(idx + 1, std::string::npos);
164
0
        if (origin == "H")
165
0
          {
166
0
            m_OriginAlternative = HEXAGONAL_ORIGIN;
167
0
          } else {
168
0
            m_OriginAlternative = atoi (origin.c_str());
169
0
          }
170
0
      }
171
0
    m_HM = name;
172
0
  }
173
174
  /*!
175
   */
176
  void SpaceGroup::AddTransform(const string &s)
177
0
  {
178
0
    matrix3x3 m;
179
0
    vector3 v;
180
0
    locale cLocale("C");
181
182
0
    if (s.find(',') != string::npos)
183
0
      {
184
0
        string s1 = RemoveWhiteSpaceUnderscore(s);
185
0
        istringstream iss(s1);
186
0
        iss.imbue(cLocale);
187
188
0
        string row;
189
0
        int i;
190
0
        size_t j;
191
0
        bool neg;
192
0
        double *t;
193
0
        for (i = 0; i < 3; i++)
194
0
          {
195
0
            getline(iss, row, ',');
196
0
            j = 0;
197
0
            neg = false;
198
0
            while (j < row.length())
199
0
              {
200
0
                switch (row[j])
201
0
                  {
202
0
                  case '0':
203
0
                  case '.': // anticipating something like 0.5 or .3333
204
0
                    {
205
0
                      char *end;
206
0
                      switch (i)
207
0
                        {
208
0
                        case 0:
209
0
                          t = &v.x();
210
0
                          break;
211
0
                        case 1:
212
0
                          t = &v.y();
213
0
                          break;
214
0
                        case 2:
215
0
                          t = &v.z();
216
0
                          break;
217
0
                        }
218
0
                      *t = strtod(row.c_str() + j, &end);
219
0
                      j = end - row.c_str() - 1;
220
0
                      if (neg)
221
0
                        *t = - *t;
222
0
                      break;
223
0
                    }
224
0
                  case '1':
225
0
                  case '2':
226
0
                  case '3':
227
0
                  case '4':
228
0
                  case '5':
229
0
                  case '6':
230
0
                  case '7':
231
0
                  case '8':
232
0
                  case '9':
233
0
                    if (j+2 < row.length() && row[j+1] == '/')
234
0
                      {
235
0
                        double *t = nullptr;
236
0
                        switch (i)
237
0
                          {
238
0
                          case 0:
239
0
                            t = &v.x();
240
0
                            break;
241
0
                          case 1:
242
0
                            t = &v.y();
243
0
                            break;
244
0
                          case 2:
245
0
                            t = &v.z();
246
0
                            break;
247
0
                          }
248
0
                        *t = ((double) (row[j] - '0')) / (row[j+2] - '0');
249
0
                        if (neg)
250
0
                          *t = - *t;
251
252
0
                        j +=2;
253
0
                      }
254
0
                    break;
255
0
                  case '-':
256
0
                    neg = true;
257
0
                    break;
258
0
                  case '+':
259
0
                    neg = false;
260
0
                    break;
261
0
                  case 'X':
262
0
                  case 'x':
263
0
                    m(i, 0) = (neg)? -1.: 1.;
264
0
                  break;
265
0
                  case 'Y':
266
0
                  case 'y':
267
0
                    m(i, 1) = (neg)? -1.: 1.;
268
0
                  break;
269
0
                  case 'Z':
270
0
                  case 'z':
271
0
                    m(i, 2) = (neg)? -1.: 1.;
272
0
                  break;
273
0
                  }
274
0
                j++;
275
0
              }
276
0
          }
277
0
      }
278
0
    else if (s.find(' ') != string::npos)
279
0
      {
280
0
        istringstream iss(s);
281
0
        iss.imbue(cLocale);
282
        /* supposing the string is a list of at least 12 float values. If there are
283
           16, the last four are 0., 0., 0. and 1. and are not needed */
284
0
        iss >> m(0,0) >> m(0,1) >> m(0,2) >> v.x();
285
0
        iss >> m(1,0) >> m(1,1) >> m(1,2) >> v.y();
286
0
        iss >> m(2,0) >> m(2,1) >> m(2,2) >> v.z();
287
0
      }
288
0
    if (v.x() < 0)
289
0
      v.x() += 1.;
290
0
    else if (v.x() >= 1.)
291
0
      v.x() -= 1.;
292
0
    if (v.y() < 0)
293
0
      v.y() += 1.;
294
0
    else if (v.y() >= 1.)
295
0
      v.y() -= 1.;
296
0
    if (v.z() < 0)
297
0
      v.z() += 1.;
298
0
    else if (v.z() >= 1.)
299
0
      v.z() -= 1.;
300
301
    // only push_back unique transformations
302
0
    transform3dIterator i, iend = m_transforms.end();
303
0
    transform3d* candidate = new transform3d (m, v);
304
0
    bool transform_exists = false;
305
306
0
    for (i = m_transforms.begin(); i!= iend; i++)
307
0
    {
308
0
      if (candidate->DescribeAsString() == (*i)->DescribeAsString())
309
0
      {
310
0
        transform_exists = true;
311
0
        break;
312
0
      }
313
0
    }
314
315
0
    if (transform_exists){
316
0
      delete candidate;
317
0
    }else{
318
0
      m_transforms.push_back (candidate);
319
0
    }
320
0
  }
321
322
  /*!
323
   */
324
  list<vector3> SpaceGroup::Transform(const vector3 &v) const
325
0
  {
326
0
    static double prec = 2e-5;
327
0
    list<vector3> res;
328
0
    transform3dIterator i, iend = m_transforms.end();
329
0
    for (i = m_transforms.begin(); i!= iend; i++)
330
0
      {
331
0
        vector3 t;
332
0
        t = *(*i) * v;
333
0
        if (t.x() < 0.)
334
0
          t.x() += 1.;
335
0
        if (t.x() >= 1.)
336
0
          t.x() -= 1.;
337
0
        if (t.y() < 0.)
338
0
          t.y() += 1.;
339
0
        if (t.y() >= 1.)
340
0
          t.y() -= 1.;
341
0
        if (t.z() < 0.)
342
0
          t.z() += 1.;
343
0
        if (t.z() >= 1.)
344
0
          t.z() -= 1.;
345
0
        list<vector3>::iterator j, jend = res.end();
346
0
        bool duplicate = false;
347
0
        for (j = res.begin(); j != jend; ++j)
348
0
          if (fabs(t.x() - (*j).x()) < prec &&
349
0
              fabs(t.y() - (*j).y()) < prec &&
350
0
              fabs(t.z() - (*j).z()) < prec)
351
0
            {
352
0
              duplicate = true;
353
0
              break;
354
0
            }
355
0
        if (!duplicate)
356
0
          res.push_back (t);
357
0
      }
358
0
    return res;
359
0
  }
360
361
  /*!
362
   */
363
  transform3d const * SpaceGroup::BeginTransform(transform3dIterator &i) const
364
0
  {
365
0
    i = m_transforms.begin ();
366
0
    return (i == m_transforms.end())? static_cast<transform3d*>(nullptr): *i++;
367
0
  }
368
369
  /*!
370
   */
371
  transform3d const * SpaceGroup::NextTransform(transform3dIterator &i) const
372
0
  {
373
0
    return (i == m_transforms.end())? static_cast<transform3d*>(nullptr): *i++;
374
0
  }
375
376
  /*!
377
   */
378
  const SpaceGroup * SpaceGroup::GetSpaceGroup (char const *name)
379
0
  {
380
0
    return GetSpaceGroup(std::string(name)); // let's only use one method
381
0
  }
382
383
  /*!
384
   */
385
  const SpaceGroup * SpaceGroup::GetSpaceGroup (const string &name_in)
386
0
  {
387
0
    if (!_SpaceGroups.Inited())
388
0
      _SpaceGroups.Init();
389
390
    // This needs to be more forgiving
391
    // First, try it without removing the white space
392
0
    const SpaceGroup *match = _SpaceGroups.sgbn.find(name_in) != _SpaceGroups.sgbn.end() ? _SpaceGroups.sgbn[name_in] : nullptr;
393
0
    if (match) return match;
394
395
    // If a match wasn't found, remove the white space and try again
396
0
    string name = RemoveWhiteSpaceUnderscore(name_in);
397
0
    match = _SpaceGroups.sgbn.find(name) != _SpaceGroups.sgbn.end() ? _SpaceGroups.sgbn[name] : nullptr;
398
399
0
    if (!match) {
400
      // Try another search, e.g. Fm-3m instead of Fm3m
401
0
      string search = name;
402
0
      bool hasMirror = (name.find('m') != string::npos || name.find('d') != string::npos || name.find('n') != string::npos || name.find('c') != string::npos);
403
0
      if (name.find('4') != string::npos && hasMirror && name.find('-') == string::npos) {
404
0
        search.insert(name.find('4'), "-");
405
0
      } else if (name.find('3') != string::npos && hasMirror && name.find('-') == string::npos) {
406
0
        search.insert(name.find('3'), "-");
407
0
      } else if (name.find('6') != string::npos && hasMirror && name.find('-') == string::npos) {
408
0
        search.insert(name.find('6'), "-");
409
0
      }
410
411
0
      match = _SpaceGroups.sgbn.find(search) != _SpaceGroups.sgbn.end() ? _SpaceGroups.sgbn[search] : nullptr;
412
0
    }
413
414
0
    return (match);
415
0
  }
416
417
  /*!
418
   */
419
  const SpaceGroup * SpaceGroup::GetSpaceGroup (unsigned id)
420
0
  {
421
0
    if (!_SpaceGroups.Inited())
422
0
      _SpaceGroups.Init();
423
0
    return (id > 0 && id <= 230)? _SpaceGroups.sgbi[id - 1].front() : nullptr;
424
0
  }
425
426
  /*!
427
   */
428
  void SpaceGroup::RegisterSpaceGroup (int nb, ...)
429
0
  {
430
0
    _SpaceGroups.sgs.insert(this);
431
0
    if (m_id > 0 && m_id <= 230)
432
0
      _SpaceGroups.sgbi[m_id - 1].push_back(this);
433
0
    if (m_HM.length() > 0)
434
0
    {
435
0
        if (m_OriginAlternative != 0)
436
0
      {
437
0
            char a = '0' + m_OriginAlternative;
438
0
            std::string nm = m_HM + ':' + a;
439
0
            if (_SpaceGroups.sgbn[nm] == nullptr)
440
0
              _SpaceGroups.sgbn[nm] = this;
441
            // Also use the symbol stripped from whitespaces as key
442
0
            std::string stripped_HM=RemoveWhiteSpaceUnderscore(nm);
443
0
            if (stripped_HM.length() > 0 && _SpaceGroups.sgbn[nm] == nullptr)
444
0
              _SpaceGroups.sgbn[nm] = this;
445
0
      }
446
0
        if ((m_OriginAlternative & 1) == 0 && _SpaceGroups.sgbn[m_HM] == nullptr)
447
0
          _SpaceGroups.sgbn[m_HM] = this;
448
0
    }
449
    // Also use the HM symbol stripped from whitespaces as key
450
0
    std::string stripped_HM=RemoveWhiteSpaceUnderscore(m_HM);
451
0
    if (stripped_HM.length() > 0 && _SpaceGroups.sgbn[stripped_HM] == nullptr)
452
0
      _SpaceGroups.sgbn[stripped_HM] = this;
453
0
    if (m_Hall.length() > 0 && _SpaceGroups.sgbn[m_Hall] == nullptr)
454
0
      _SpaceGroups.sgbn[m_Hall] = this;
455
0
    if (nb == 0)
456
0
      return;
457
0
    va_list args;
458
0
    va_start(args, nb);
459
0
    string name;
460
0
    for (int i = 0; i < nb; i++)
461
0
      {
462
0
        name=va_arg(args, const char *);
463
0
        if (name.length() > 0 && _SpaceGroups.sgbn[name] == nullptr)
464
0
          _SpaceGroups.sgbn[name] = this;
465
0
      }
466
0
    va_end(args);
467
0
  }
468
469
  /*!
470
   */
471
  bool SpaceGroup::operator ==(const SpaceGroup &sg) const
472
0
  {
473
0
    if (m_transforms.size() != sg.m_transforms.size())
474
0
      return false;
475
0
    set<string> s0, s1;
476
0
    list<transform3d*>::const_iterator i, iend;
477
0
    iend = m_transforms.end();
478
0
    for (i = m_transforms.begin(); i != iend; ++i)
479
0
      s0.insert((*i)->DescribeAsString());
480
0
    iend = sg.m_transforms.end();
481
0
    for (i = sg.m_transforms.begin(); i != iend; ++i)
482
0
      s1.insert((*i)->DescribeAsString());
483
0
    if (s0.size() != s1.size())
484
0
      return false;
485
0
    set<string>::iterator j, jend = s0.end();
486
0
    for (j = s0.begin(); j != jend; ++j)
487
0
      if (s1.find(*j) == s1.end())
488
0
        return false;
489
0
    return true;
490
0
  }
491
492
  /*!
493
   */
494
  bool SpaceGroup::IsValid() const
495
0
  {
496
0
    if (!m_transforms.size())
497
0
      return false;
498
0
    list<transform3d*>::const_iterator i, iend = m_transforms.end();
499
0
    map <string, transform3d*>T;
500
0
    for (i = m_transforms.begin(); i != iend; ++i)
501
0
      {
502
0
        if (T.find((*i)->DescribeAsString()) != T.end())
503
0
          {
504
0
            cerr << "Duplicated transform: " << (*i)->DescribeAsString() << endl;
505
0
            return false;
506
0
          }
507
0
        T[(*i)->DescribeAsString()] = *i;
508
0
      }
509
    // calculate all products and check if they are in the group
510
0
    map <string, transform3d*>::iterator j, k, end = T.end();
511
0
    string s;
512
0
    bool has_inverse;
513
0
    for (j = T.begin(); j != end; ++j)
514
0
      {
515
0
        has_inverse = false;
516
0
        for (k = T.begin(); k != end; ++k)
517
0
          {
518
0
            s = (*(*j).second * *(*k).second).DescribeAsString();
519
0
            if (T.find(s) == end)
520
0
              {
521
0
                cerr << "Invalid transform: " << (*j).first << " * " << (*k).first << " = " << s << endl;
522
0
                return false;
523
0
              }
524
0
            if (!has_inverse && s == "x,y,z")
525
0
              has_inverse = true;
526
0
          }
527
0
        if (!has_inverse)
528
0
          {
529
0
            cerr << "Transform with no inverse: " << (*j).first << endl;
530
0
            return false;
531
0
          }
532
0
      }
533
0
    return true;
534
0
  }
535
536
  /*!
537
   */
538
  const SpaceGroup * SpaceGroup::Find (SpaceGroup* group)
539
0
  {
540
0
    const SpaceGroup *found = nullptr;
541
0
    if (group->m_Hall.length() > 0 && _SpaceGroups.sgbn.find(group->m_Hall)!=_SpaceGroups.sgbn.end())
542
0
      {
543
0
        found = _SpaceGroups.sgbn[group->m_Hall];
544
0
        if (!found)
545
0
          obErrorLog.ThrowError(__FUNCTION__, "Unknown space group (Hall symbol:"+group->m_Hall+") error, please file a bug report.", obError);
546
0
        if (group->m_transforms.size() && *found  != *group)
547
0
          {
548
0
            unsigned id = group->GetId();
549
0
            if (id != 3 && id != 68) // these groups have duplicates
550
0
              {
551
0
                obErrorLog.ThrowError(__FUNCTION__, "Space group error (Hall symbol and list of transforms do not match), please file a bug report.", obWarning);
552
0
                return found;
553
0
              }
554
0
          }
555
0
        else
556
        /* even if there is an error (this should not occur) return the found group, since
557
           Hall names are secure */
558
0
          return found;
559
0
      }
560
    // Identify from the HM symbol, after removing all whitespaces or underscore (which are valid separators in
561
    // old CIF files)
562
0
    std::string stripped_hm=RemoveWhiteSpaceUnderscore(group->m_HM);
563
0
    if (stripped_hm.length() > 0 &&
564
0
        _SpaceGroups.sgbn.find(stripped_hm)!=_SpaceGroups.sgbn.end() &&
565
0
        (found = _SpaceGroups.sgbn[stripped_hm]))
566
0
      {
567
0
        if (*found == *group){
568
0
          found = _SpaceGroups.sgbn[found->GetHallName()];
569
0
          return found;
570
0
        }
571
0
        if (group->m_transforms.size())
572
0
          {// If transforms (symmetry operations) are listed, make sure they match the tabulated ones
573
0
            list<const SpaceGroup*>::const_iterator i, end = _SpaceGroups.sgbi[found->m_id - 1].end();
574
0
            for (i = _SpaceGroups.sgbi[found->m_id - 1].begin(); i!= end; ++i)
575
0
              if ((**i) == *group)
576
0
                return *i;
577
0
            obErrorLog.ThrowError(__FUNCTION__, "Unknown space group error (H-M symbol:"+group->m_HM+"), cannot match the list of transforms, please file a bug report.", obError);
578
0
            return nullptr;
579
0
          }
580
0
        else if (group->m_transforms.size() == 0)
581
0
          {// No transforms (symmetry operations) are listed, warn if HM symbol can match several spacegroups
582
0
            int n = 0;
583
0
            list<const SpaceGroup*>::const_iterator i, end = _SpaceGroups.sgbi[group->m_id].end();
584
0
            for (i = _SpaceGroups.sgbi[group->m_id].begin(); i!= end; ++i)
585
0
              if (RemoveWhiteSpaceUnderscore((*i)->m_HM) == stripped_hm)
586
0
                n++;
587
0
            if (n > 1)
588
0
              obErrorLog.ThrowError(__FUNCTION__, "Ambiguous space group: HM symbol corresponds to several space groups.", obWarning);
589
0
            return found;
590
0
          }
591
        /* even if there is an error (this should not occur) return the found group, since
592
           Hall names are secure */
593
0
      }
594
0
    else if (group->m_id > 0 && group->m_id <= 230)
595
0
      {
596
0
        if (group->m_transforms.size())
597
0
          {
598
0
            list<const SpaceGroup*>::const_iterator i, end = _SpaceGroups.sgbi[group->m_id - 1].end();
599
0
            for (i = _SpaceGroups.sgbi[group->m_id - 1].begin(); i!= end; ++i)
600
0
              if ((**i) == *group)
601
0
                return *i;
602
0
          }
603
0
        else if (group->m_transforms.size() == 0)
604
0
          {
605
0
            if (_SpaceGroups.sgbi[group->m_id - 1].size() > 1)
606
0
              obErrorLog.ThrowError(__FUNCTION__, "Ambiguous space group: sg number corresponds to several space groups.", obWarning);
607
0
            return _SpaceGroups.sgbi[group->m_id - 1].front();
608
0
          }
609
0
      }
610
    // If we are there, we need to make a hard search through the whole collection
611
0
    if (!group->IsValid())
612
0
      {
613
0
        obErrorLog.ThrowError(__FUNCTION__, "Unknown space group (HM:"+group->m_HM+",Hall:"+group->m_Hall
614
0
                                           +") with incomplete or wrong definition.", obWarning);
615
0
        return nullptr;
616
0
      }
617
0
    set<SpaceGroup*>::iterator i, end = _SpaceGroups.sgs.end();
618
0
    for (i = _SpaceGroups.sgs.begin(); i != end; ++i)
619
0
      if (**i == *group)
620
0
        return *i;
621
0
    obErrorLog.ThrowError(__FUNCTION__, "Unknown space group error, please file a bug report.", obWarning);
622
0
    return nullptr;
623
0
  }
624
}
625
626
//! \file spacegroup.cpp
627
//! \brief Handle Crystallographic Space Groups