2
* International Chemical Identifier (InChI)
4
* Software version 1.04
7
* The InChI library and programs are free software developed under the
8
* auspices of the International Union of Pure and Applied Chemistry (IUPAC).
9
* Originally developed at NIST. Modifications and additions by IUPAC
10
* and the InChI Trust.
12
* IUPAC/InChI-Trust Licence for the International Chemical Identifier (InChI)
13
* Software version 1.0.
14
* Copyright (C) IUPAC and InChI Trust Limited
16
* This library is free software; you can redistribute it and/or modify it under the
17
* terms of the IUPAC/InChI Trust Licence for the International Chemical Identifier
18
* (InChI) Software version 1.0; either version 1.0 of the License, or
19
* (at your option) any later version.
21
* This library is distributed in the hope that it will be useful,
22
* but WITHOUT ANY WARRANTY; without even the implied warranty of
23
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
24
* See the IUPAC/InChI Trust Licence for the International Chemical Identifier (InChI)
25
* Software version 1.0 for more details.
27
* You should have received a copy of the IUPAC/InChI Trust Licence for the
28
* International Chemical Identifier (InChI) Software version 1.0 along with
29
* this library; if not, write to:
32
* c/o FIZ CHEMIE Berlin
45
/*#define CHECK_WIN32_VC_HEAP*/
48
#if ( READ_INCHI_STRING == 1 )
72
/******************************************************************************************************/
73
void CopyAt2St( inp_ATOM *at, inp_ATOM_STEREO * st, int num_atoms )
76
for ( i = 0; i < num_atoms; i ++ ) {
77
if ( at[i].p_parity ) {
78
memcpy( st[i].p_orig_at_num, at[i].p_orig_at_num, sizeof(st[0].p_orig_at_num) );
79
st[i].p_parity = at[i].p_parity;
81
if ( at[i].sb_parity[0] ) {
82
memcpy( st[i].sb_ord, at[i].sb_ord, sizeof(st[0].sb_ord) );
83
memcpy( st[i].sb_parity, at[i].sb_parity, sizeof(st[0].sb_parity) );
84
memcpy( st[i].sn_ord, at[i].sn_ord, sizeof(st[0].sn_ord) );
85
memcpy( st[i].sn_orig_at_num, at[i].sn_orig_at_num, sizeof(st[0].sn_orig_at_num) );
89
void CopySt2At( inp_ATOM *at, inp_ATOM_STEREO * st, int num_atoms )
95
for ( i = 0; i < num_atoms; i ++ ) {
96
if ( st[i].p_parity ) {
97
memcpy( at[i].p_orig_at_num, st[i].p_orig_at_num, sizeof(at[0].p_orig_at_num) );
98
at[i].p_parity = st[i].p_parity;
100
if ( st[i].sb_parity[0] ) {
101
memcpy( at[i].sb_ord, st[i].sb_ord, sizeof(st[0].sb_ord) );
102
memcpy( at[i].sb_parity, st[i].sb_parity, sizeof(at[0].sb_parity) );
103
memcpy( at[i].sn_ord, st[i].sn_ord, sizeof(at[0].sn_ord) );
104
memcpy( at[i].sn_orig_at_num, st[i].sn_orig_at_num, sizeof(at[0].sn_orig_at_num) );
109
/******************************************************************************************************/
110
int RestoreAtomConnectionsSetStereo( StrFromINChI *pStruct, int iComponent, int iAtNoOffset, INChI *pInChI, INChI *pInChIMobH)
113
inp_ATOM_STEREO * st = NULL;
114
int num_atoms, i, jv, jn, n_vertex, n_neigh, num_H, parity;
115
int nNumDeletedH=0, iDeletedH=0, idelH1, idelH2, ret = 0, len;
116
int num_stereo_bonds, num_stereo_centers, num_stereo_bonds2, num_stereo_centers2;
117
INChI_Stereo *pStereo = NULL, *pStereo2 = NULL;
118
AT_NUMB nCumulene[MAX_CUMULENE_LEN+2];
120
num_atoms = pInChI->nNumberOfAtoms;
121
if ( num_atoms <= 0 ) {
126
pStruct->at = at = (inp_ATOM *) inchi_calloc ( num_atoms, sizeof(pStruct->at[0]) );
131
pStruct->num_atoms = num_atoms;
133
pStruct->charge = pInChI->nTotalCharge;
134
/* elements, terminal atoms H */
135
for ( i = 0; i < num_atoms; i ++ ) {
136
at[i].el_number = pInChI->nAtom[i];
137
if ( GetElementFormulaFromAtNum(UCINT pInChI->nAtom[i], at[i].elname ) ) {
141
at[i].orig_at_number = iAtNoOffset + i+1;
142
at[i].orig_compt_at_numb = i + 1;
143
at[i].component = iComponent + 1;
144
num_H = pInChI->nNum_H[i];
145
/* --- pInChI->nNum_H_fixed[i] was added to pInChI->nNum_H[i] ---
146
if ( pInChI->nNum_H_fixed ) {
147
num_H += pInChI->nNum_H_fixed[i];
154
for ( i = 1, n_vertex = pInChI->nConnTable[0]-1; i < pInChI->lenConnTable; i ++ ) {
155
if ( (n_neigh = pInChI->nConnTable[i]-1) < n_vertex ) {
156
/* vertex - neighbor connection */
157
jv = at[n_vertex].valence ++;
158
at[n_vertex].neighbor[jv] = n_neigh;
159
at[n_vertex].bond_type[jv] = BOND_TYPE_SINGLE;
160
at[n_vertex].chem_bonds_valence += at[n_vertex].bond_type[jv];
161
/* neighbor - vertex connection */
162
jn = at[n_neigh].valence ++;
163
at[n_neigh].neighbor[jn] = n_vertex;
164
at[n_neigh].bond_type[jn] = BOND_TYPE_SINGLE;
165
at[n_neigh].chem_bonds_valence += at[n_neigh].bond_type[jn];
167
if ( (n_vertex = n_neigh) >= num_atoms ) {
174
if ( pInChI->IsotopicAtom && pInChI->nNumberOfIsotopicAtoms ) {
175
for ( i = 0; i < pInChI->nNumberOfIsotopicAtoms; i ++ ) {
176
n_vertex = pInChI->IsotopicAtom[i].nAtomNumber-1;
177
at[n_vertex].iso_atw_diff = (char)pInChI->IsotopicAtom[i].nIsoDifference;
178
at[n_vertex].num_iso_H[0] = (char)pInChI->IsotopicAtom[i].nNum_H;
179
at[n_vertex].num_iso_H[1] = (char)pInChI->IsotopicAtom[i].nNum_D;
180
at[n_vertex].num_iso_H[2] = (char)pInChI->IsotopicAtom[i].nNum_T;
182
pStruct->bIsotopic |= 1;
185
/* tautomeric groups */
186
if ( ret = GetTgroupInfoFromInChI( &pStruct->ti, at, NULL, pInChI ) ) {
190
/* coordinates: data from unused members: pInChI->IsotopicTGroup and InChI->nNumberOfIsotopicTGroups */
191
if ( pInChI->IsotopicTGroup && !pInChI->nNumberOfIsotopicTGroups ) {
192
pStruct->pXYZ = (XYZ_COORD *) pInChI->IsotopicTGroup;
193
pInChI->IsotopicTGroup = NULL;
196
if ( pInChI->StereoIsotopic &&
197
(pInChI->StereoIsotopic->nNumberOfStereoBonds +
198
pInChI->StereoIsotopic->nNumberOfStereoCenters) ) {
199
pStereo = pInChI->StereoIsotopic;
201
if ( pInChI->Stereo &&
202
(pInChI->Stereo->nNumberOfStereoBonds +
203
pInChI->Stereo->nNumberOfStereoCenters) ) {
204
pStereo = pInChI->Stereo;
208
/* stereo2: Mobile-H in addition to Fixed-H*/
210
if ( pInChIMobH && pInChIMobH->nNumberOfAtoms ) {
211
if ( pInChIMobH->StereoIsotopic &&
212
(pInChIMobH->StereoIsotopic->nNumberOfStereoBonds +
213
pInChIMobH->StereoIsotopic->nNumberOfStereoCenters) ) {
214
pStereo2 = pInChIMobH->StereoIsotopic;
216
if ( pInChIMobH->Stereo &&
217
(pInChIMobH->Stereo->nNumberOfStereoBonds +
218
pInChIMobH->Stereo->nNumberOfStereoCenters) ) {
219
pStereo2 = pInChIMobH->Stereo;
224
num_stereo_bonds = num_stereo_bonds2 = 0;
225
num_stereo_centers = num_stereo_centers2 = 0;
226
/* -- have already been done in the initialization --
230
if ( pStereo || pStereo2 ) {
231
/* count implicit H needed for parities and reallocate at[]; set at[n_vertex].at_type=1 for these atoms */
232
int len1 = pStereo? pStereo->nNumberOfStereoCenters : 0;
233
int len2 = pStereo2? pStereo2->nNumberOfStereoCenters : 0;
235
for ( i = i2 = 0; i < len1 || i2 < len2; ) {
236
if ( i < len1 && i2 < len2 ) {
237
diff = (int)pStereo->nNumber[i] - (int)pStereo2->nNumber[i2];
239
n_vertex = pStereo->nNumber[i]-1;
243
n_vertex = pStereo2->nNumber[i2]-1;
244
num_stereo_centers2 ++;
249
n_vertex = pStereo->nNumber[i]-1;
252
n_vertex = pStereo2->nNumber[i2]-1;
253
num_stereo_centers2 ++;
256
/* find whether it is an allene */
257
if ( at[n_vertex].valence == 2 &&
258
at[n_vertex].num_H == 0 &&
259
bCanAtomBeMiddleAllene(at[n_vertex].elname, 0, 0) &&
260
at[jv = at[n_vertex].neighbor[0]].valence + at[jv].num_H == 3 &&
261
bCanAtomBeTerminalAllene(at[jv].elname, 0, 0) &&
262
at[jn = at[n_vertex].neighbor[1]].valence + at[jn].num_H == 3 &&
263
bCanAtomBeTerminalAllene(at[jn].elname, 0, 0) ) {
265
if ( !at[jv].at_type && at[jv].num_H ) {
266
nNumDeletedH += at[jv].num_H;
267
at[jv].at_type ++; /* H should be added as an explicit H */
269
if ( !at[jn].at_type && at[jn].num_H ) {
270
nNumDeletedH += at[jn].num_H;
271
at[jn].at_type ++; /* H should be added as an explicit H */
274
/* stereogenic atom - sp3 */
275
if ( !at[n_vertex].at_type && at[n_vertex].num_H ) {
276
nNumDeletedH += at[n_vertex].num_H;
277
at[n_vertex].at_type ++; /* H should be added as an explicit H */
282
len1 = pStereo? pStereo->nNumberOfStereoBonds : 0;
283
len2 = pStereo2? pStereo2->nNumberOfStereoBonds : 0;
284
for ( i = i2 = 0; i < len1 || i2 < len2; ) {
285
if ( i < len1 && i2 < len2 ) {
286
diff = (int)pStereo->nBondAtom1[i] - (int)pStereo2->nBondAtom1[i2];
287
diff2 = (int)pStereo->nBondAtom2[i] - (int)pStereo2->nBondAtom2[i2];
288
if ( diff < 0 || diff == 0 && diff2 <= 0) {
289
n_vertex = pStereo->nBondAtom1[i]-1;
290
n_neigh = pStereo->nBondAtom2[i]-1;
292
i2 += !diff && !diff2;
294
n_vertex = pStereo2->nBondAtom1[i2]-1;
295
n_neigh = pStereo2->nBondAtom2[i2]-1;
296
num_stereo_bonds2 ++;
301
n_vertex = pStereo->nBondAtom1[i]-1;
302
n_neigh = pStereo->nBondAtom2[i]-1;
305
n_vertex = pStereo2->nBondAtom1[i2]-1;
306
n_neigh = pStereo2->nBondAtom2[i2]-1;
307
num_stereo_bonds2 ++;
310
if ( !is_in_the_list( at[n_vertex].neighbor, (AT_NUMB)n_neigh, at[n_vertex].valence ) ) {
311
/* must be a cumulene */
312
if ( !bFindCumuleneChain( at, (AT_NUMB)n_vertex, (AT_NUMB)n_neigh, nCumulene, MAX_CUMULENE_LEN+1 ) ) {
313
ret = RI_ERR_SYNTAX; /* not a cumulene */
317
if ( !at[n_vertex].at_type && at[n_vertex].num_H ) {
318
nNumDeletedH += at[n_vertex].num_H;
319
at[n_vertex].at_type ++; /* H should be added as an explicit H */
321
if ( !at[n_neigh].at_type && at[n_neigh].num_H ) {
322
nNumDeletedH += at[n_neigh].num_H;
323
at[n_neigh].at_type ++; /* H should be added as an explicit H */
327
if ( nNumDeletedH ) {
329
inp_ATOM *at2 = (inp_ATOM *)inchi_calloc( num_atoms + nNumDeletedH, sizeof(at2[0]) );
334
pStruct->num_deleted_H = nNumDeletedH;
335
memcpy( at2, at, num_atoms * sizeof(at2[0]) );
337
pStruct->at = at = at2;
338
/* fill out deleted H atom info */
339
for ( i = num_atoms; i < num_atoms + nNumDeletedH; i ++ ) {
340
strcpy( at[i].elname, "H" );
341
at[i].el_number = EL_NUMBER_H;
342
at[i].orig_at_number = iAtNoOffset + i+1;
343
at[i].orig_compt_at_numb = i + 1;
344
at[i].component = iComponent + 1;
346
/* connect deleted H */
347
for( i = 0; i < num_atoms; i ++ ) {
348
if ( at[i].at_type == 1 ) {
349
if ( 0 > (ret = AddExplicitDeletedH( at, i, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) {
359
/* mark stereo centers, they have already been connected the added explicit H, if any */
360
int bInvertedParity = (pStereo->nCompInv2Abs == -1);
361
for ( i = 0; i < pStereo->nNumberOfStereoCenters; i ++ ) {
362
n_vertex = pStereo->nNumber[i]-1;
363
parity = pStereo->t_parity[i];
364
if ( bInvertedParity ) {
365
parity = (parity == AB_PARITY_EVEN)? AB_PARITY_ODD : (parity == AB_PARITY_ODD)? AB_PARITY_EVEN : parity;
367
/* find whether it is allene */
368
if ( at[n_vertex].valence == 2 &&
369
at[n_vertex].num_H == 0 &&
370
bCanAtomBeMiddleAllene(at[n_vertex].elname, 0, 0) &&
371
/* allene has exactly 2 double bonds */
372
(jv = at[n_vertex].neighbor[0], at[jv].valence + at[jv].num_H == 3) &&
373
bCanAtomBeTerminalAllene(at[jv].elname, 0, 0) &&
374
(jn = at[n_vertex].neighbor[1], at[jn].valence + at[jn].num_H == 3) &&
375
bCanAtomBeTerminalAllene(at[jn].elname, 0, 0) ) {
376
/* allene: add explicit H if implicit H are present */
377
/* iDeletedH = current number of already added explicit H */
378
/* idelH1 = index in at[] of the explicit H added to atom jv */
379
if ( at[jv].num_H ) {
380
if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) {
384
/* index of the stereo atom neighbor */
385
idelH1 = at[jv].neighbor[at[jv].neighbor[0]==n_vertex];
387
if ( at[jn].num_H ) {
388
/* iDeletedH = current number of already added explicit H */
389
/* idelH2 = index of the explicit H added to atom jn */
390
if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) {
394
idelH2 = at[jn].neighbor[at[jn].neighbor[0]==n_vertex];
396
/* allene: set bond types to double */
398
if ( 0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jv, BOND_TYPE_DOUBLE ) ) ||
399
0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jn, BOND_TYPE_DOUBLE ) ) ) {
403
/* allene: make 0D parity */
404
ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, 2 );
409
/* stereogenic sp3 atom */
410
if ( at[n_vertex].num_H ) {
411
if ( 0 > (ret = AddExplicitDeletedH( at, n_vertex, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) {
415
ret = set_atom_0D_parity( at, st, num_atoms, nNumDeletedH, n_vertex, parity );
419
num_stereo_centers ++;
426
/* mark stereobonds */
427
for ( i = 0; i < pStereo->nNumberOfStereoBonds; i ++ ) {
428
jv = pStereo->nBondAtom1[i]-1;
429
jn = pStereo->nBondAtom2[i]-1;
430
parity = pStereo->b_parity[i];
431
if ( !is_in_the_list( at[jv].neighbor, (AT_NUMB)jn, at[jv].valence ) ) {
432
/* must be a cumulene */
433
if ( !bFindCumuleneChain( at, (AT_NUMB)jv, (AT_NUMB)jn, nCumulene, MAX_CUMULENE_LEN+1 ) ) {
434
return RI_ERR_SYNTAX; /* not a cumulene */
436
len = MAX_CUMULENE_LEN+1;
438
/* a regular double or alt bond */
441
len = 1; /* cumulene length is number of bonds, not number of atoms */
443
/* cumulene or double bond: add explicit H if implicit H are present */
444
if ( at[jv].num_H ) {
445
if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) {
449
/* double bond neighbor that has the smallest canonical number; it is either 0th or 1st */
450
idelH1 = at[jv].neighbor[at[jv].neighbor[0]==nCumulene[1]];
452
if ( at[jn].num_H ) {
453
if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) {
457
idelH2 = at[jn].neighbor[at[jn].neighbor[0]==nCumulene[len-1]];
459
if ( 0 > (ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, len )) ) {
465
/* allocate memory for Mobile-H-only stereo */
466
if ( num_stereo_centers2 + num_stereo_bonds2 ) {
467
if ( !(st = (inp_ATOM_STEREO *)inchi_calloc( num_atoms, sizeof(st[0])))) {
471
CopyAt2St( at, st, num_atoms );
474
if ( num_stereo_centers2 ) {
475
/* In case of Fixed-H */
476
/* mark additional Mobile-H stereo centers, they have already been connected the added explicit H, if any */
477
int bInvertedParity = (pStereo2->nCompInv2Abs == -1);
478
for ( i = 0; i < pStereo2->nNumberOfStereoCenters; i ++ ) {
479
n_vertex = pStereo2->nNumber[i]-1;
480
parity = pStereo2->t_parity[i];
481
if ( at[n_vertex].p_parity ) {
482
continue; /* the parity has already been set for Fixed-H */
484
if ( bInvertedParity ) {
485
parity = (parity == AB_PARITY_EVEN)? AB_PARITY_ODD : (parity == AB_PARITY_ODD)? AB_PARITY_EVEN : parity;
487
/* find whether it is allene */
488
if ( at[n_vertex].valence == 2 &&
489
at[n_vertex].num_H == 0 &&
490
bCanAtomBeMiddleAllene(at[n_vertex].elname, 0, 0) &&
491
/* allene has exactly 2 double bonds */
492
(jv = at[n_vertex].neighbor[0], at[jv].valence + at[jv].num_H == 3) &&
493
bCanAtomBeTerminalAllene(at[jv].elname, 0, 0) &&
494
(jn = at[n_vertex].neighbor[1], at[jn].valence + at[jn].num_H == 3) &&
495
bCanAtomBeTerminalAllene(at[jn].elname, 0, 0) ) {
496
/* allene: add explicit H if implicit H are present */
497
/* iDeletedH = current number of already added explicit H */
498
/* idelH1 = index in at[] of the explicit H added to atom jv */
499
if ( at[jv].num_H ) {
500
if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) {
504
/* index of the stereo atom neighbor */
505
idelH1 = at[jv].neighbor[at[jv].neighbor[0]==n_vertex];
507
if ( at[jn].num_H ) {
508
/* iDeletedH = current number of already added explicit H */
509
/* idelH2 = index of the explicit H added to atom jn */
510
if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) {
514
idelH2 = at[jn].neighbor[at[jn].neighbor[0]==n_vertex];
516
/* allene: set bond types to double */
518
if ( 0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jv, BOND_TYPE_DOUBLE ) ) ||
519
0 > (ret = set_bond_type( at, (AT_NUMB)n_vertex, (AT_NUMB)jn, BOND_TYPE_DOUBLE ) ) ) {
523
/* allene: make 0D parity */
524
ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, 2 );
529
/* stereogenic sp3 atom */
530
if ( at[n_vertex].num_H ) {
531
if ( 0 > (ret = AddExplicitDeletedH( at, n_vertex, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) {
535
ret = set_atom_0D_parity( at, st, num_atoms, nNumDeletedH, n_vertex, parity );
539
num_stereo_centers ++;
546
if ( num_stereo_bonds2 ) {
547
/* In case of Fixed-H */
548
/* mark additional Mobile-H stereobonds, they have already been connected the added explicit H, if any */
549
for ( i = 0; i < pStereo2->nNumberOfStereoBonds; i ++ ) {
550
jv = pStereo2->nBondAtom1[i]-1;
551
jn = pStereo2->nBondAtom2[i]-1;
552
parity = pStereo2->b_parity[i];
553
if ( !is_in_the_list( at[jv].neighbor, (AT_NUMB)jn, at[jv].valence ) ) {
554
/* must be a cumulene */
555
if ( !bFindCumuleneChain( at, (AT_NUMB)jv, (AT_NUMB)jn, nCumulene, MAX_CUMULENE_LEN+1 ) ) {
556
return RI_ERR_SYNTAX; /* not a cumulene */
558
len = MAX_CUMULENE_LEN+1;
560
/* a regular double or alt bond */
563
len = 1; /* cumulene length is number of bonds, not number of atoms */
565
/* cumulene or double bond: add explicit H if implicit H are present */
566
if ( at[jv].num_H ) {
567
if ( 0 > (ret = AddExplicitDeletedH( at, jv, num_atoms, &iDeletedH, &idelH1, nNumDeletedH, pStereo2 != NULL ))) {
571
/* double bond neighbor that has the smallest canonical number */
572
idelH1 = at[jv].neighbor[at[jv].neighbor[0]==nCumulene[1]];
574
if ( at[jn].num_H ) {
575
if ( 0 > (ret = AddExplicitDeletedH( at, jn, num_atoms, &iDeletedH, &idelH2, nNumDeletedH, pStereo2 != NULL ))) {
579
idelH2 = at[jn].neighbor[at[jn].neighbor[0]==nCumulene[len-1]];
581
if ( 0 > (ret = set_cumulene_0D_parity( at, st, num_atoms, idelH1, jv, jn, idelH2, parity, len )) ) {
594
/*************************************************************/
595
int SetStereoBondTypeFor0DParity( inp_ATOM *at, int i1, int m1 )
597
AT_NUMB nCumulene[MAX_CUMULENE_LEN+2];
598
int j, n1, n2, k1, m2, ret, nLenCumulene = 0, bond_type;
599
k1 = at[i1].sb_ord[m1];
601
nCumulene[nLenCumulene ++] = n1;
603
n2 = at[n1].neighbor[k1]; /* next atom */
604
nCumulene[nLenCumulene ++] = n2;
605
for (m2 = 0; m2 < MAX_NUM_STEREO_BONDS && at[n2].sb_parity[m2]; m2 ++ ) {
606
if ( n1 == at[n2].neighbor[(int)at[n2].sb_ord[m2]] ) {
607
/* found the endatom */
611
if ( at[n2].num_H || at[n2].valence != 2 || at[n2].endpoint ) {
612
break; /* not a middle cumulene */
614
k1 = (at[n2].neighbor[0] == n1);
616
} while ( at[n1].valence == 2 && !at[n1].num_H && nLenCumulene < MAX_CUMULENE_LEN+2 &&
617
bCanAtomBeMiddleAllene( at[n1].elname, at[n1].charge, at[n1].radical ) );
618
return RI_ERR_SYNTAX; /* failed */
621
if ( nLenCumulene == 2 ) {
622
bond_type = BOND_TYPE_STEREO; /* double bond or alternating bond */
624
bond_type = BOND_TYPE_DOUBLE; /* cumulene or allene */
627
for ( j = 1; j < nLenCumulene; j ++ ) {
628
/* if bond_type = BOND_TYPE_DOUBLE then increments at->cham_bonds_valence: */
629
/* at->cham_bonds_valence += BOND_TYPE_DOUBLE-BOND_TYPE_SINGLE */
630
if ( 0 > (ret = set_bond_type( at, (AT_NUMB)nCumulene[j-1], (AT_NUMB)nCumulene[j], bond_type ) ) ) {
631
return RI_ERR_PROGR; /* failed */
636
/******************************************************************************************************/
637
int SetStereoBondTypesFrom0DStereo( StrFromINChI *pStruct, INChI *pInChI)
639
INChI_Stereo *pStereo;
640
inp_ATOM *at = pStruct->at;
641
int num_atoms = pStruct->num_atoms;
642
int i, j, num_stereo_bonds, ret;
644
if ( pInChI->StereoIsotopic &&
645
(pInChI->StereoIsotopic->nNumberOfStereoBonds +
646
pInChI->StereoIsotopic->nNumberOfStereoCenters) ) {
647
pStereo = pInChI->StereoIsotopic;
649
if ( pInChI->Stereo &&
650
(pInChI->Stereo->nNumberOfStereoBonds +
651
pInChI->Stereo->nNumberOfStereoCenters) ) {
652
pStereo = pInChI->Stereo;
657
/************************ set bond types separately from stereo *******************/
659
num_stereo_bonds = 0;
660
for ( i = 0; i < num_atoms; i ++ ) {
661
/* set BOND_TYPE_DOUBLE in allenes and cumulenes */
662
/* set BOND_TYPE_STEREO in double bond stereo */
663
for ( j = 0; j < MAX_NUM_STEREO_BONDS && at[i].sb_parity[j]; j ++ ) {
665
if ( 0 > (ret = SetStereoBondTypeFor0DParity( at, i, j ) ) ) {
670
if ( num_stereo_bonds ) {
671
int num_bond_type_stereo;
672
int num_bond_type_altern;
674
/* replace adjacent BOND_TYPE_STEREO with BOND_TYPE_ALTERN */
675
for ( i = 0; i < num_atoms; i ++ ) {
676
num_bond_type_stereo = 0;
677
num_bond_type_altern = 0;
678
for ( j = 0; j < at[i].valence; j ++ ) {
679
num_bond_type_stereo += ( at[i].bond_type[j] == BOND_TYPE_STEREO );
680
num_bond_type_altern += ( at[i].bond_type[j] == BOND_TYPE_ALTERN );
682
if ( num_bond_type_stereo + num_bond_type_altern > 1 && num_bond_type_stereo ) {
683
for ( j = 0; j < at[i].valence; j ++ ) {
684
if ( at[i].bond_type[j] == BOND_TYPE_STEREO ) {
685
neigh = at[i].neighbor[j];
686
/* does not change at[i].chem_bond_valence in case of BOND_TYPE_ALTERN */
687
if ( 0 > (ret = set_bond_type( at, (AT_NUMB)i, neigh, BOND_TYPE_ALTERN ) ) ) {
693
/* at this point only isolated stereo bonds have type BOND_TYPE_STEREO */
695
/* increment at[i].chem_bonds_valence if at[i] has an altern. bond */
696
/* replace BOND_TYPE_STEREO with BOND_TYPE_DOUBLE and increment */
697
/* chem_bonds_valence of the adjacent atoms */
698
for ( i = 0; i < num_atoms; i ++ ) {
699
num_bond_type_stereo = 0;
700
num_bond_type_altern = 0;
701
for ( j = 0; j < at[i].valence; j ++ ) {
702
num_bond_type_stereo += ( at[i].bond_type[j] == BOND_TYPE_STEREO );
703
num_bond_type_altern += ( at[i].bond_type[j] == BOND_TYPE_ALTERN );
705
if ( !num_bond_type_stereo && num_bond_type_altern ) {
706
/* an atom has only BOND_TYPE_ALTERN => adjacent BOND_TYPE_ALTERN case */
707
at[i].chem_bonds_valence += 1;
709
if ( num_bond_type_stereo == 1 ) {
710
/* isolated BOND_TYPE_STEREO => replace with BOND_TYPE_DOUBLE */
711
for ( j = 0; j < at[i].valence; j ++ ) {
712
if ( at[i].bond_type[j] == BOND_TYPE_STEREO ) {
713
neigh = at[i].neighbor[j];
714
/* replacing BOND_TYPE_STEREO with BOND_TYPE_DOUBLE */
715
/* does not change at->chem_bonds_valence */
716
if ( 0 > (ret = set_bond_type( at, (AT_NUMB)i, neigh, BOND_TYPE_DOUBLE ) ) ) {
719
at[i].chem_bonds_valence ++;
720
at[(int)neigh].chem_bonds_valence ++;
724
if ( num_bond_type_stereo + num_bond_type_altern ) {
725
/* an atom still has both BOND_TYPE_STEREO and BOND_TYPE_ALTERN */
733
ret = 0; /* success */
738
/******************************************************************************************************/
739
int CopyBnsToAtom( StrFromINChI *pStruct, BN_STRUCT *pBNS, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int bAllowZeroBondOrder )
741
int i, j, atom_charge, left_charge, charge, ret = 0, v1, nMinorder;
742
int num_at = pStruct->num_atoms;
743
inp_ATOM *at = pStruct->at;
744
ICHICONST SRM *pSrm = pStruct->pSrm;
747
int chem_bonds_valence, bond_order;
749
atom_charge = left_charge = 0;
750
for ( i = 0; i < num_at; i ++ ) {
753
chem_bonds_valence = 0;
754
for ( j = 0; j < at[i].valence; j ++ ) {
755
pe = pBNS->edge + pv->iedge[j];
756
BondFlowMaxcapMinorder( at, pVA, pSrm, i, j, NULL, &nMinorder, NULL );
757
bond_order = pe->flow + nMinorder;
758
if ( !bAllowZeroBondOrder && !bond_order ) {
761
chem_bonds_valence += bond_order;
762
at[i].bond_type[j] = bond_order; /* BOND_MARK_HIGHLIGHT */
764
at[i].chem_bonds_valence = chem_bonds_valence;
765
/* charges (both may be present resulting in zero) */
766
at[i].charge = pVA[i].cInitCharge;
767
if ( pVA[i].nCMinusGroupEdge ) {
768
pe = pBNS->edge + pVA[i].nCMinusGroupEdge - 1;
769
if ( charge = pe->flow ) {
770
at[i].charge -= charge;
771
atom_charge -= charge;
774
if ( pVA[i].nCPlusGroupEdge ) {
775
pe = pBNS->edge + pVA[i].nCPlusGroupEdge - 1;
776
if ( charge = pe->cap - pe->flow ) {
777
at[i].charge += charge;
778
atom_charge += charge;
781
if ( pv->st_edge.cap > pv->st_edge.flow ) {
782
at[i].radical = RADICAL_SINGLET + (pv->st_edge.cap - pv->st_edge.flow);
785
/* find charge excess */
786
for ( i = num_at; i < pBNS->num_vertices; i ++ ) {
788
if ( charge = pv->st_edge.cap - pv->st_edge.flow ) {
789
if ( IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) {
790
left_charge -= charge;
792
if ( IS_BNS_VT_YVCONNECTOR(pv->type) ) {
793
left_charge += charge;
797
/* tautomeric H and (-) */
798
for ( i = 0; i < pBNS->num_t_groups; i ++ ) {
799
/* tautomeric groups are first non-atom vertices;
800
order of them is same as in pTCGroups->pTCG[] */
801
int num_H = pTCGroups->pTCG[i].tg_num_H;
802
int num_Minus = pTCGroups->pTCG[i].tg_num_Minus;
803
int bMinusFirst = (pTCGroups->pTCG[i].tg_RestoreFlags & TGRF_MINUS_FIRST);
805
Vertex vMinus = NO_VERTEX;
806
pv = pBNS->vert + num_at + i; /* t-group vertex */
807
if ( !(pv->type & BNS_VERT_TYPE_TGROUP) ) {
810
if ( pTCGroups->pTCG[i].tg_set_Minus > 0 && num_Minus > 0 ) {
811
vMinus = pTCGroups->pTCG[i].tg_set_Minus-1;
816
for ( j = 0; j < pv->num_adj_edges; j ++ ) {
817
pe = pBNS->edge + pv->iedge[j];
819
num_at_add = pe->flow;
820
if ( v1 == vMinus ) {
822
at[v1].charge = -1; /* no checking at[v1].charge == 0 for now ??? */
823
num_at_add --; /* no checking num_at_add > 0 for now ??? */
825
num_Minus ++; /* error ??? */
829
if ( num_at_add > 0 ) {
830
/* atom has tautomeric attachment; do not allow =N(-) */
831
if ( num_Minus && !at[v1].charge &&
832
at[v1].valence == at[v1].chem_bonds_valence ) {
837
if ( num_at_add > 0 ) {
838
at[v1].num_H += num_at_add;
843
at[v1].endpoint = i+1;
845
if ( (num_H+num_Minus != pv->st_edge.cap - pv->st_edge.flow) && (num_H || num_Minus || vMinus != NO_VERTEX) ) {
849
for ( j = pv->num_adj_edges-1; 0 <= j; j -- ) {
850
pe = pBNS->edge + pv->iedge[j];
852
num_at_add = pe->flow;
853
if ( v1 == vMinus ) {
855
at[v1].charge = -1; /* no checking at[v1].charge == 0 for now ??? */
856
num_at_add --; /* no checking num_at_add > 0 for now ??? */
858
num_Minus ++; /* error ??? */
862
if ( num_at_add > 0 ) {
863
/* atom has tautomeric attachment; do not allow =N(-) */
864
if ( num_Minus && !at[v1].charge &&
865
at[v1].valence == at[v1].chem_bonds_valence ) {
870
if ( num_at_add > 0 ) {
871
at[v1].num_H += num_at_add;
876
at[v1].endpoint = i+1;
878
if ( (num_H+num_Minus != pv->st_edge.cap - pv->st_edge.flow) && (num_H || num_Minus || vMinus != NO_VERTEX) ) {
886
/******************************************************************************************************/
887
int CheckBnsConsistency( StrFromINChI *pStruct, BN_STRUCT *pBNS, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int bNoRad )
890
#ifndef TARGET_API_LIB
891
#if ( bRELEASE_VERSION == 0 )
893
int i, j, atom_charge, left_charge, charge, excess_charge, ret = 0;
894
int v1, v2, flow, tot_st_flow, tot_st_cap, num_electrons, nNumMetalAtoms;
895
int num_at = pStruct->num_atoms;
896
inp_ATOM *at = pStruct->at;
900
int bDebugOutput = 0;
904
/* count electrons and metals */
905
num_electrons = -pTCGroups->total_charge;
907
for ( i = 0; i < pTCGroups->num_tgroups; i ++ ) {
908
num_electrons += pTCGroups->pTCG[i].tg_num_H;
910
for ( i = 0; i < num_at; i ++ ) {
911
num_electrons += at[i].el_number + at[i].num_H;
912
nNumMetalAtoms += pVA[i].cMetal;
914
/* create output string */
915
sprintf( s, "%d:%d%sM%02dv%da%de%db%d* ",
916
bNoRad, pTCGroups->iComponent+1, num_electrons%2?"O":"E", nNumMetalAtoms,
917
pBNS->num_vertices, num_at, pBNS->num_edges, pBNS->num_bonds );
920
tot_st_flow = tot_st_cap = 0;
921
atom_charge = left_charge = 0;
922
if ( pBNS->num_atoms != num_at ) {
923
fprintf( stdout, "\n%sNum. atoms discrepancy: %d(BNS) vs. %d(at) ", s, pBNS->num_atoms, num_at);
928
if ( bDebugOutput && bNoRad ) {
929
fprintf( stderr, "\n\n------begin------------------------------------------------\n" );
930
fprintf( stderr, "\n\fedge cap flow v1 v2\n\n" );
931
/* xxxx xxxx/xxxx xxxx/xxxx xxxx xxxx */
934
for ( i = 0; i < pBNS->num_edges; i ++ ) {
937
v2 = v1 ^ pe->neighbor12;
938
if ( pe->cap < pe->flow || pe->flow < 0 ) {
939
fprintf( stdout, "\n%sedge %d (%d-%d) has cap=%d flow=%d ", s, i, v1, v2, pe->cap, pe->flow );
943
if ( bDebugOutput && bNoRad ) {
944
/* xxxx xxxx/xxxx xxxx/xxxx xxxx xxxx */
945
fprintf( stderr, "%4d %4d/%-4d %4d/%-4d %4d %4d\n", i, pe->cap, pe->cap0, pe->flow, pe->flow0, v1, v2 );
953
if ( bDebugOutput && bNoRad ) {
954
fprintf( stderr, "\n\fvert st-cap st-flow type iedge : neigh\n\n" );
955
/* xxxx xxxx/xxxx xxxx/xxxx 0xXXX xxxx : xxx */
958
for ( i = 0; i < pBNS->num_vertices; i ++ ) {
961
if ( bDebugOutput && bNoRad ) {
962
/* xxxx xxxx/xxxx xxxx/xxxx 0xXXX xxxx : xxx */
967
case BNS_VERT_TYPE_ATOM:
968
sprintf( sAtom, "At %-2.2s", i < num_at? at[i].elname : "??" );
971
case BNS_VERT_TYPE_ATOM | BNS_VERT_TYPE_ENDPOINT:
992
case BNS_VT_C_POS_ALL:
995
case BNS_VT_C_NEG_ALL:
1001
case BNS_VERT_TYPE__AUX | BNS_VERT_TYPE_TEMP:
1004
case BNS_VERT_TYPE__AUX:
1007
case BNS_VERT_TYPE_TGROUP:
1014
fprintf( stderr, "%4d %4d/%-4d %4d/%-4d 0x%03X %5s",
1015
i, pv->st_edge.cap, pv->st_edge.cap0, pv->st_edge.flow, pv->st_edge.flow0,
1017
for ( j = 0; j < pv->num_adj_edges; j ++ ) {
1018
fprintf( stderr, " %2d", pv->iedge[j] );
1020
fprintf( stderr, ":" );
1021
for ( j = 0; j < pv->num_adj_edges; j ++ ) {
1022
pe = pBNS->edge + pv->iedge[j];
1023
fprintf( stderr, " %2d", pe->neighbor12 ^ i );
1025
fprintf( stderr, "\n" );
1028
tot_st_flow += pv->st_edge.flow;
1029
tot_st_cap += pv->st_edge.cap;
1030
if ( pv->num_adj_edges > pv->max_adj_edges ) {
1031
fprintf( stdout, "\n%s%s %d type 0x%X \"%s\" num_edges=%d > max=%d ", s,
1032
i < num_at? "atom":"vertex", i,
1033
pv->type, at[i].elname, pv->num_adj_edges, pv->max_adj_edges );
1037
/* charge on atoms */
1038
charge = pVA[i].cInitCharge;
1039
if ( pVA[i].nCMinusGroupEdge ) {
1040
pe = pBNS->edge + pVA[i].nCMinusGroupEdge - 1;
1041
if ( pe->flow > 0 ) {
1045
if ( pVA[i].nCPlusGroupEdge ) {
1046
pe = pBNS->edge + pVA[i].nCPlusGroupEdge - 1;
1047
if ( pe->cap > pe->flow ) {
1048
charge += pe->cap - pe->flow;
1051
if ( bNoRad && pv->st_edge.flow != pv->st_edge.cap ) {
1052
fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" unexpected st_cap=%d st_flow=%d ", s,
1053
i < num_at? "atom":"vertex", i,
1054
pv->type, at[i].elname, pv->st_edge.cap, pv->st_edge.flow );
1057
if ( bNoRad && charge && !strcmp(at[i].elname, "C") ) {
1058
/* ignore carbonyls */
1059
if ( i == 0 && num_at == 2 && !strcmp(at[1].elname, "O") &&
1060
!at[0].num_H && !at[1].num_H && !pTCGroups->total_charge) {
1061
; /* C(-)#O(+) structure */
1063
fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" charge=%d ", s,
1064
i < num_at? "atom":"vertex", i,
1065
pv->type, at[i].elname, charge );
1069
atom_charge += charge;
1071
if ( (charge = pv->st_edge.cap - pv->st_edge.flow) > 0 ) {
1073
if ( !bNoRad && IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) {
1074
left_charge -= charge;
1076
if ( !bNoRad && IS_BNS_VT_YVCONNECTOR(pv->type) ) {
1077
left_charge += charge;
1079
if ( !bNoRad && IS_BNS_VT_M_GR(pv->type) &&
1080
0 <= (j=pTCGroups->nGroup[TCG_MeFlower3]) &&
1081
i == pTCGroups->pTCG[j].nVertexNumber ) {
1082
; /* additional "radical" on metal flower */
1084
if ( !(pv->type & BNS_VERT_TYPE_TGROUP) || bNoRad ) {
1085
/* t-groups before running BFS should have st_cap > st_flow */
1086
fprintf( stdout, "\n%s%s %d: type 0x%X unexpected st_cap=%d st_flow=%d ", s,
1087
i < num_at? "atom":"vertex", i,
1088
pv->type, pv->st_edge.cap, pv->st_edge.flow);
1092
if ( pv->st_edge.cap < pv->st_edge.flow || pv->st_edge.flow < 0 ) {
1093
fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" st_cap=%d st_flow=%d ", s,
1094
i < num_at? "atom":"vertex", i,
1095
pv->type, i < num_at? at[i].elname:"", pv->st_edge.cap, pv->st_edge.flow );
1098
/* check edge_flow vs. st_flow consistency */
1099
for( j = 0, flow = 0; j < pv->num_adj_edges; j ++ ) {
1100
pe = pBNS->edge + pv->iedge[j];
1103
if ( flow != pv->st_edge.flow ) {
1104
fprintf( stdout, "\n%s%s %d: type 0x%X \"%s\" st_flow=%d edge_flow=%d ", s,
1105
i < num_at? "atom":"vertex", i,
1106
pv->type, i < num_at? at[i].elname:"", pv->st_edge.flow, flow );
1111
if ( bDebugOutput && bNoRad ) {
1112
fprintf( stderr, "\n------end--------------------------------------------------\n" );
1116
if ( num_electrons %= 2 ) {
1117
fprintf( stdout, "\n%d*Odd number of electrons (%d atoms) ", bNoRad, num_at );
1121
/* tautomeric groups charge */
1122
for ( i = 0, charge = 0; i < pTCGroups->num_tgroups; i ++ ) {
1123
charge -= pTCGroups->pTCG[i].tg_num_Minus;
1126
if ( charge != pTCGroups->tgroup_charge ) {
1127
fprintf( stdout, "\n%sCounted t-group charge=%d while %d was saved ", s,
1128
charge, pTCGroups->tgroup_charge);
1131
/* add other charges */
1132
charge += atom_charge + left_charge;
1133
excess_charge = pTCGroups->total_charge - pTCGroups->added_charge - pTCGroups->tgroup_charge;
1134
if ( charge != pTCGroups->total_charge && excess_charge != pTCGroups->total_charge - charge ) {
1135
fprintf( stdout, "\n%sCounted total charge=%d while %d was saved; excess charge=%d ", s,
1136
charge, pTCGroups->total_charge, excess_charge );
1139
if ( tot_st_cap != pBNS->tot_st_cap || tot_st_flow != pBNS->tot_st_flow ) {
1140
fprintf( stdout, "\n%sCounted/saved total st_flow=%d/%d st_cap=%d/%d ", s,
1141
tot_st_flow, pBNS->tot_st_flow, tot_st_cap, pBNS->tot_st_cap );
1145
fprintf( stdout, "\n" );
1152
/******************************************************************************************************/
1153
int AddExplicitDeletedH( inp_ATOM *at, int jv, int num_at, int *iDeletedH, int *iH, int nNumDeletedH, int bTwoStereo )
1155
inp_ATOM *cur_H, *cur_at = at+jv;
1156
int tot_num_iso_H = NUM_ISO_H(cur_at, 0);
1157
int num_H = cur_at->num_H;
1159
S_CHAR num_iso_H[NUM_H_ISOTOPES];
1162
if ( !at[jv].at_type ) {
1163
return RI_ERR_PROGR;
1166
if ( at[jv].at_type > 1 ) {
1167
/* explicit hydrogens have already been added; find them */
1168
for ( i = 0; i < *iDeletedH; i ++ ) {
1169
if ( at[num_at + i].neighbor[0] == jv ) {
1170
*iH = num_at + i; /* return the first found H, it has the smallest canonical pseudo rank */
1174
return RI_ERR_PROGR;
1176
/* add all explicit H disconnected from at[jv] in order H, 1H, D, T */
1177
*iH = *iDeletedH + num_at; /* num_H includes all H, both isotopic and normal */
1178
for ( i = 0; i < NUM_H_ISOTOPES; i ++ ) {
1179
num_iso_H[i] = at[jv].num_iso_H[i];
1181
for ( ; num_H && (*iDeletedH) < nNumDeletedH; (*iDeletedH) ++ ) {
1182
cur_H = at + num_at + (*iDeletedH); /* first available empty atom will be this explicit H */
1183
cur_H->neighbor[cur_H->valence] = jv; /* connect this new atom H to the real atom */
1184
cur_H->bond_type[cur_H->valence] = BOND_TYPE_SINGLE;
1186
if ( num_H > tot_num_iso_H ) {
1188
if ( num_H != tot_num_iso_H ) {
1189
/* may happen when Mobile-H stereo included in Fixed-H processing */
1193
return RI_ERR_SYNTAX; /* two identical H neighbors of a stereo atom/bond */
1197
while ( iso_H < NUM_H_ISOTOPES && !num_iso_H[iso_H] )
1199
if ( iso_H < NUM_H_ISOTOPES ) {
1200
cur_H->iso_atw_diff = iso_H + 1; /* isotopic shift + 1 */
1203
num_iso_H[iso_H] --;
1204
if ( num_iso_H[iso_H] ) {
1205
return RI_ERR_SYNTAX; /* two identical isotopic H neighbors of a stereo atom/bond */
1208
return RI_ERR_SYNTAX; /* not enough isotopic H */
1213
return RI_ERR_SYNTAX;
1215
at[jv].at_type ++; /* at[jv].at_type==2 => explicit hydrogens have already been added */
1216
return 0; /* success */
1218
/******************************************************************************************************/
1219
int bFindCumuleneChain( inp_ATOM *at, AT_NUMB i1, AT_NUMB i2, AT_NUMB nCumulene[], int nMaxLen )
1220
/* nMaxLen = number of bonds in cumulene = 3 = MAX_CUMULENE_LEN+1 */
1221
/* nCumulene[nMaxLen+1] will contain cumulene chain >i1=x=y=i2< in this order */
1223
int i, len, iat, nat;
1225
for ( i = 0; i < at[i1].valence; i ++ ) {
1227
iat = i1; /* current */
1228
nat = at[i1].neighbor[i]; /* next */
1229
if ( len+1 == nMaxLen ) {
1231
nCumulene[++len] = nat;
1232
return 1; /* success */
1234
continue; /* check next at[i1] neighbor */
1236
while ( at[nat].valence == 2 &&
1237
at[nat].num_H == 0 &&
1238
bCanAtomBeMiddleAllene(at[nat].elname, 0, 0) ) {
1239
nCumulene[++len] = nat;
1240
nat = at[nat].neighbor[at[nat].neighbor[0]==iat]; /* new next */
1241
if ( len+1 == nMaxLen ) {
1243
nCumulene[++len] = nat;
1244
return 1; /* success */
1246
break; /* check next at[i1] neighbor */
1248
iat = nCumulene[len]; /* new current */
1251
return 0; /* failed */
1253
/******************************************************************************************************/
1254
int set_bond_type( inp_ATOM *at, AT_NUMB i1, AT_NUMB i2, int bType )
1256
AT_NUMB *p1 = is_in_the_list( at[i1].neighbor, i2, at[i1].valence );
1257
AT_NUMB *p2 = is_in_the_list( at[i2].neighbor, i1, at[i2].valence );
1259
int j1 = p1 - at[i1].neighbor;
1260
int j2 = p2 - at[i2].neighbor;
1261
int bTypePrev = at[i1].bond_type[j1];
1262
at[i1].bond_type[j1] = bType;
1263
at[i2].bond_type[j2] = bType;
1264
if ( bTypePrev && bTypePrev <= BOND_TYPE_TRIPLE &&
1265
bType && bType <= BOND_TYPE_TRIPLE ) {
1266
at[i1].chem_bonds_valence += bType - bTypePrev;
1267
at[i2].chem_bonds_valence += bType - bTypePrev;
1271
return RI_ERR_SYNTAX;
1273
/******************************************************************************************************/
1274
int set_cumulene_0D_parity( inp_ATOM *at, inp_ATOM_STEREO *st, int num_at, int idelH1, int i1, int i2, int idelH2, int parity, int len )
1276
AT_NUMB nCumulene[MAX_CUMULENE_LEN+2];
1278
int m1, m2, parity1, parity2, sb_ord_m1, sb_ord_m2, k1, k2, num_neigh1, num_neigh2;
1279
/* the following types must exactly match types in inp_ATOM and inp_ATOM_STEREO */
1280
S_CHAR *sb_ord1, *sn_ord1, *sb_parity1;
1281
S_CHAR *sb_ord2, *sn_ord2, *sb_parity2;
1282
AT_NUMB *sn_orig_at_num1;
1283
AT_NUMB *sn_orig_at_num2;
1286
if ( !bFindCumuleneChain( at, (AT_NUMB)i1, (AT_NUMB)i2, nCumulene, len ) ) {
1287
return RI_ERR_SYNTAX; /* not an allene */
1289
/* stereo bond neighbors: index of a stereo bond in its end-atom adjacency lists */
1290
if ( (p1 = is_in_the_list( at[i1].neighbor, nCumulene[1], at[i1].valence )) &&
1291
(p2 = is_in_the_list( at[i2].neighbor, nCumulene[len-1], at[i2].valence )) ) {
1292
sb_ord_m1 = p1 - at[i1].neighbor; /* indes of stereobond in the atom's adjacency list */
1293
sb_ord_m2 = p2 - at[i2].neighbor;
1295
return RI_ERR_PROGR;
1297
num_neigh1 = at[i1].valence + at[i1].num_H;
1298
num_neigh2 = at[i2].valence + at[i2].num_H;
1300
if ( num_neigh1 < MIN_NUM_STEREO_BOND_NEIGH || num_neigh1 > MAX_NUM_STEREO_BOND_NEIGH ||
1301
num_neigh2 < MIN_NUM_STEREO_BOND_NEIGH || num_neigh2 > MAX_NUM_STEREO_BOND_NEIGH ) {
1302
return RI_ERR_SYNTAX;
1306
sb_ord1 = st? st[i1].sb_ord : at[i1].sb_ord;
1307
sb_ord2 = st? st[i2].sb_ord : at[i2].sb_ord;
1308
sb_parity1 = st? st[i1].sb_parity : at[i1].sb_parity;
1309
sb_parity2 = st? st[i2].sb_parity : at[i2].sb_parity;
1311
/* find the first unoccupied locations in the stereobond 0D descriptor lists; check whether the stereo has already been set */
1312
for( m1 = k1 = 0; m1 < MAX_NUM_STEREO_BONDS && sb_parity1[m1] && !(k1 = sb_ord1[m1] == sb_ord_m1); m1 ++ )
1314
for( m2 = k2 = 0; m2 < MAX_NUM_STEREO_BONDS && sb_parity2[m2] && !(k2 = sb_ord2[m2] == sb_ord_m2); m2 ++ )
1316
if ( m1 == MAX_NUM_STEREO_BONDS || m2 == MAX_NUM_STEREO_BONDS ) {
1317
return RI_ERR_SYNTAX;
1320
return 0; /* the stereo descriptor of this bond/allene/cumulene has already been set */
1323
return RI_ERR_SYNTAX; /* only half of a bond was set */
1326
sn_ord1 = st? st[i1].sn_ord : at[i1].sn_ord;
1327
sn_ord2 = st? st[i2].sn_ord : at[i2].sn_ord;
1328
sn_orig_at_num1 = st? st[i1].sn_orig_at_num : at[i1].sn_orig_at_num;
1329
sn_orig_at_num2 = st? st[i2].sn_orig_at_num : at[i2].sn_orig_at_num;
1331
/* stereo bond neighbors connection index */
1332
sb_ord1[m1] = sb_ord_m1;
1333
sb_ord2[m2] = sb_ord_m2;
1334
/* stereo bond end atom neighbors */
1335
sn_orig_at_num1[m1] = at[idelH1].orig_at_number;
1336
if ( idelH1 < num_at ) {
1337
if ( p1 = is_in_the_list( at[i1].neighbor, (AT_NUMB)idelH1, at[i1].valence ) ) {
1338
sn_ord1[m1] = p1 - at[i1].neighbor;
1340
return RI_ERR_PROGR;
1346
sn_orig_at_num2[m2] = at[idelH2].orig_at_number;
1347
if ( idelH2 < num_at ) {
1348
if ( p2 = is_in_the_list( at[i2].neighbor, (AT_NUMB)idelH2, at[i2].valence ) ) {
1349
sn_ord2[m2] = p2 - at[i2].neighbor;
1351
return RI_ERR_PROGR;
1356
if ( ATOM_PARITY_WELL_DEF(parity) ) {
1357
/* special case: 2 bonds to sb atom => inverse parity because */
1358
/* InChI parity refers to the lone pair as a neighbor */
1359
int num_inv = (num_neigh1 == MIN_NUM_STEREO_BOND_NEIGH) + (num_neigh2 == MIN_NUM_STEREO_BOND_NEIGH);
1360
if ( num_inv % 2 ) {
1361
parity = (parity == AB_PARITY_EVEN)? AB_PARITY_ODD : AB_PARITY_EVEN;
1363
parity1 = AB_PARITY_EVEN;
1364
parity2 = (parity == AB_PARITY_EVEN)? AB_PARITY_EVEN : AB_PARITY_ODD;
1366
parity1 = parity2 = parity;
1368
sb_parity1[m1] = parity1;
1369
sb_parity2[m2] = parity2;
1373
/******************************************************************************************************/
1374
int set_atom_0D_parity( inp_ATOM *at, inp_ATOM_STEREO *st, int num_at, int num_deleted_H, int i1, int parity )
1376
int m1=0, m2, i, j, tot_num_neigh;
1377
/* the following types must exactly match types in inp_ATOM and inp_ATOM_STEREO */
1378
/* Given parity from InChI, the order of stereo center neighbors is: */
1379
/* 1. The stereocenter itself if the total number of neighbors is 3 (not 4) */
1380
/* 2. Explicit H: non-isotopic, isotopic in order ofascending atomic mass */
1381
/* Explicit H have already been sorted in this order */
1382
/* 3. Normal neighboring atoms, atom numbers (=canonical numbers from InChI - 1) in ascending order */
1383
/* Normal neighboring atoms have already been sorted in this order */
1385
AT_NUMB *p_orig_at_num;
1387
if ( !st || !at[i1].p_parity ) {
1389
p_parity = st? &st[i1].p_parity : &at[i1].p_parity;
1390
p_orig_at_num = st? st[i1].p_orig_at_num : at[i1].p_orig_at_num;
1392
tot_num_neigh = at[i1].valence + at[i1].num_H;
1393
if ( tot_num_neigh == MAX_NUM_STEREO_ATOM_NEIGH-1 ) {
1394
/* only 3 neighbors: the atom itself is the first neighbor */
1395
p_orig_at_num[m1 ++] = at[i1].orig_at_number;
1397
if ( tot_num_neigh != MAX_NUM_STEREO_ATOM_NEIGH ) {
1398
return RI_ERR_PROGR; /* wrong number of members */
1400
m2 = m1 + (MAX_NUM_STEREO_ATOM_NEIGH - at[i1].valence);
1401
/* stereoneighbors: deleted explicit atoms H first, in order of increasing isotopic mass */
1402
if ( at[i1].num_H ) {
1403
for ( j = 0; m1 < m2 && j < num_deleted_H; j ++ ) {
1404
if ( at[j + num_at].neighbor[0] == i1 ) {
1405
p_orig_at_num[m1 ++] = at[j + num_at].orig_at_number;
1409
if ( m1 + at[i1].valence != MAX_NUM_STEREO_ATOM_NEIGH ) {
1410
return RI_ERR_PROGR; /* wrong number of members */
1412
/* stereoneighbors: other than explicit H atoms */
1413
for ( i = 0; i < at[i1].valence; i ++ ) {
1414
m2 = at[i1].neighbor[i];
1415
p_orig_at_num[m1 ++] = at[m2].orig_at_number;
1422
#if ( BNS_RAD_SEARCH == 1 )
1423
/******************************************************************************************************/
1424
int MoveRadToAtomsAddCharges( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
1425
inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups, int forbidden_mask )
1427
int nNumRad, ret = 0, ret2;
1428
int i, j, k, num_rad_not_atom, num_moved=0, num_candidates= 0, extra_charge=0, added_charge, delta;
1432
S_SHORT *pnRad = NULL, *pnDelta = NULL;
1433
CC_CAND *pCand = NULL;
1434
int cnBits, bAtomRadRemoved = 0;
1436
int num_at = pStruct->num_atoms;
1437
int num_deleted_H = pStruct->num_deleted_H;
1438
int len_at = num_at + num_deleted_H;
1440
for ( i = pBNS->num_atoms, num_rad_not_atom=0; i < pBNS->num_vertices; i ++ ) {
1441
num_rad_not_atom += pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow;
1443
if ( !num_rad_not_atom ) {
1446
/****************************************************/
1448
/* Move radicals from ChargeStruct to atoms */
1450
/****************************************************/
1452
/* allocate memory to keep track of moved radicals */
1453
pnRad = (S_SHORT *) inchi_malloc(pBNS->num_vertices * sizeof(pnRad[0]));
1454
pnDelta = (S_SHORT *)inchi_calloc(pBNS->num_atoms, sizeof(pnDelta[0]));
1455
if ( !pnRad || !pnDelta ) {
1459
for ( i = 0; i < pBNS->num_vertices; i ++ ) {
1460
pnRad[i] = pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow;
1463
/* remove radicals from atoms */
1464
for ( i = 0; i < pBNS->num_atoms; i ++ ) {
1465
pnDelta[i] = pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow;
1466
pBNS->vert[i].st_edge.cap -= pnDelta[i];
1467
bAtomRadRemoved += (0 != pnDelta[i]);
1469
ret = SetRadEndpoints( pBNS, pBD, RAD_SRCH_FROM_FICT );
1477
for ( i = 0; i < nNumRad; i ++ ) {
1478
pEdge = pBNS->edge + pBD->RadEdges[i];
1479
v1 = pEdge->neighbor1;
1480
v2 = pEdge->neighbor12 ^ v1;
1481
pBNS->vert[v1].st_edge.flow -= pEdge->flow;
1482
pBNS->vert[v2].st_edge.flow -= pEdge->flow;
1483
pBNS->tot_st_flow -= 2*pEdge->flow;
1485
pEdge->forbidden |= forbidden_mask;
1486
pBNS->edge_forbidden_mask |= forbidden_mask;
1488
ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
1494
RemoveRadEndpoints( pBNS, pBD, NULL );
1496
break; /* could not move more radicals */
1498
if ( bAtomRadRemoved ) {
1499
/* restore radicals to atoms */
1500
for ( i = 0; i < pBNS->num_atoms; i ++ ) {
1501
pBNS->vert[i].st_edge.cap += pnDelta[i];
1503
bAtomRadRemoved = 0;
1506
if ( bAtomRadRemoved ) {
1507
/* restore radicals to atoms */
1508
for ( i = 0; i < pBNS->num_atoms; i ++ ) {
1509
pBNS->vert[i].st_edge.cap += pnDelta[i];
1511
bAtomRadRemoved = 0;
1513
pBNS->edge_forbidden_mask &= ~forbidden_mask;
1516
/****************************************************/
1518
/* Fix the charges */
1520
/****************************************************/
1522
/* find reqired charge */
1524
for ( i = pBNS->num_atoms, pv=pBNS->vert+i; i < pBNS->num_vertices; i ++, pv++ ) {
1525
if ( delta = pv->st_edge.cap - pv->st_edge.flow ) {
1526
if ( IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) {
1527
extra_charge -= delta;
1529
if ( BNS_VERT_TYPE__AUX == pv->type ) {
1530
extra_charge += delta;
1537
if ( !extra_charge ) {
1540
/* find differences */
1542
for ( i = 0; i < pBNS->num_vertices; i ++ ) {
1543
pnRad[i] = (pBNS->vert[i].st_edge.cap - pBNS->vert[i].st_edge.flow) - pnRad[i];
1544
if ( pnRad[i] > 0 && i < pBNS->num_atoms && !pVA[i].nTautGroupEdge ) {
1549
if ( num_candidates > 0 ) {
1550
pCand = (CC_CAND *)inchi_calloc( num_candidates, sizeof(pCand[0]) );
1556
memcpy( at2, at, len_at*sizeof(at2[0]));
1558
ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
1565
for ( i = 0, j = 0; i < pBNS->num_vertices; i ++ ) {
1566
if ( pnRad[i] > 0 && i < pBNS->num_atoms && !pVA[i].nTautGroupEdge ) {
1568
pCand[j].num_bonds = at2[i].valence;
1569
pCand[j].chem_valence = at2[i].chem_bonds_valence;
1570
pCand[j].cMetal = pVA[i].cMetal;
1571
pCand[j].cNumBondsToMetal = pVA[i].cNumBondsToMetal;
1572
pCand[j].cNumValenceElectrons = pVA[i].cNumValenceElectrons;
1573
pCand[j].cPeriodicRowNumber = pVA[i].cPeriodicRowNumber;
1574
pCand[j].el_number = at2[i].el_number;
1575
cnBits = (pVA[i].cnListIndex > 0)? cnList[pVA[i].cnListIndex-1].bits : 0;
1576
while ( cnBits > 0 ) {
1577
pCand[j].cNumChargeStates ++;
1578
cnBits >>= cn_bits_shift;
1584
qsort( pCand, j, sizeof(pCand[0]), comp_cc_cand );
1588
for ( k = 0; k < j; k ++ ) {
1589
int rest_of_charge = extra_charge - added_charge;
1590
int charge_per_left_atom = (abs(rest_of_charge) + j-k - 1)/(j-k);
1591
int this_atom_add_charge = rest_of_charge > 0? charge_per_left_atom : -charge_per_left_atom;
1592
pVA[pCand[k].iat].cInitCharge += this_atom_add_charge;
1593
added_charge += this_atom_add_charge;
1594
if ( this_atom_add_charge ) {
1595
for ( i = pBNS->num_vertices-1, pv = pBNS->vert + i; this_atom_add_charge && pBNS->num_atoms <= i; i --, pv -- ) {
1596
if ( delta = pv->st_edge.cap - pv->st_edge.flow ) {
1597
if ( this_atom_add_charge < 0 && IS_BNS_VT_C_OR_CSUPER_GR(pv->type) ) {
1598
if ( delta + this_atom_add_charge > 0 ) {
1599
delta = -this_atom_add_charge;
1601
pv->st_edge.cap -= delta;
1602
pBNS->tot_st_cap -= delta;
1603
this_atom_add_charge += delta;
1605
if ( this_atom_add_charge > 0 && BNS_VERT_TYPE__AUX == pv->type ) {
1606
if ( delta > this_atom_add_charge ) {
1607
delta = this_atom_add_charge;
1609
pv->st_edge.cap -= delta;
1610
pBNS->tot_st_cap -= delta;
1611
this_atom_add_charge -= delta;
1621
inchi_free( pnRad );
1624
inchi_free( pnDelta );
1627
inchi_free( pCand );
1632
/**************************************************************************************************/
1633
typedef struct tagMobileHGroups {
1634
AT_NUMB group_number;
1635
AT_NUMB atom_number;
1636
AT_NUMB atom_type_pVA;
1640
/* S_CHAR el_type;*/
1641
S_CHAR endpoint_valence;
1643
S_CHAR bonds_valence;
1644
S_CHAR num_bonds_non_metal;
1645
S_CHAR bonds_valence_non_metal;
1649
typedef struct tagMobileGroupList {
1650
AT_NUMB group_number;
1653
/**************************************************************************************************/
1654
int AdjustTgroupsToForbiddenEdges2( BN_STRUCT *pBNS, inp_ATOM *at, VAL_AT *pVA,
1655
int num_atoms, int forbidden_mask )
1658
int centerpoint_type, neigh_type;
1660
int num_donors, num_acceptors, num_donor_endpoints, num_acceptor_endpoints;
1661
int neigh, tg_number, num_eql_mobile_gr, num_dif_mobile_gr, bond_type, has_mobile_H, has_mobile;
1662
int num_forbidden, ind_forbidden, forbidden, num_N, num_O, num_P, num_S, num_OSt;
1663
int val, delta_val, delta_met, num_bonds_non_metal, bonds_valence_non_metal;
1664
int num_bonds, bonds_valence;
1665
int inv_forbidden_mask = ~forbidden_mask;
1666
MOBILE_GR MobileGr[MAXVAL];
1668
MGROUPS MGroups[MAXVAL];
1669
int num_mgroups, num_diff_t_groups;
1670
BNS_EDGE *e, *e1, *e2, *ev, *ev1, *ev2;
1671
BNS_VERTEX *pv1, *pv2;
1673
/* search for possible centerpoints */
1674
for ( i = 0; i < num_atoms; i ++ ) {
1676
if ( at[i].chem_bonds_valence == at[i].valence || at[i].num_H ||
1677
at[i].endpoint || at[i].charge || at[i].radical ||
1678
!is_centerpoint_elem(at[i].el_number) ||
1679
!(centerpoint_type = get_pVA_atom_type( pVA, at, i, 0 )) ||
1680
2 > (delta_val = at[i].chem_bonds_valence - (val = get_el_valence(at[i].el_number, 0, 0))) ||
1681
2 > (delta_met = (bonds_valence_non_metal = nNoMetalBondsValence(at, i)) - val )
1686
num_donors = num_acceptors = num_donor_endpoints = num_acceptor_endpoints = 0;
1687
num_mgroups = num_endpoints = num_diff_t_groups = 0;
1688
has_mobile = has_mobile_H = num_eql_mobile_gr = num_dif_mobile_gr = tg_number = 0;
1691
num_N = num_O = num_P = num_S = num_OSt = 0;
1692
num_bonds_non_metal = nNoMetalNumBonds(at, i);
1693
bonds_valence = at[i].chem_bonds_valence;
1694
num_bonds = at[i].valence;
1696
for ( j = 0; j < at[i].valence; j ++ ) {
1697
/* collect neighbors info */
1698
neigh = at[i].neighbor[j];
1699
val = get_endpoint_valence( at[neigh].el_number );
1700
forbidden = pBNS->edge[(int)pBNS->vert[i].iedge[j]].forbidden;
1701
bond_type = (at[i].bond_type[j] & BOND_TYPE_MASK);
1702
neigh_type = get_pVA_atom_type( pVA, at, neigh, bond_type);
1703
if ( !forbidden && !at[neigh].endpoint ) {
1704
/* save forbidden bonds */
1705
if ( is_el_a_metal(at[neigh].el_number) ) {
1708
switch( bond_type ) {
1709
case BOND_TYPE_SINGLE:
1710
if ( !at[neigh].num_H && at[neigh].charge != -1 ) {
1711
continue; /* not a donor */
1714
case BOND_TYPE_DOUBLE:
1715
if ( !neigh_type ) {
1724
MobileGr[num_endpoints].atom_number = neigh;
1725
MobileGr[num_endpoints].ineigh = j;
1726
MobileGr[num_endpoints].bond_type = bond_type;
1727
MobileGr[num_endpoints].group_number = at[neigh].endpoint;
1728
MobileGr[num_endpoints].endpoint_valence = val;
1729
MobileGr[num_endpoints].forbidden = forbidden;
1730
MobileGr[num_endpoints].atom_type_pVA = neigh_type;
1731
MobileGr[num_endpoints].num_bonds = at[neigh].valence;
1732
MobileGr[num_endpoints].bonds_valence = at[neigh].chem_bonds_valence;
1733
MobileGr[num_endpoints].num_bonds_non_metal = nNoMetalNumBonds(at, neigh);
1734
MobileGr[num_endpoints].bonds_valence_non_metal = nNoMetalBondsValence( at, neigh );
1736
if ( forbidden & forbidden_mask ) {
1738
ind_forbidden = num_endpoints;
1740
num_O += 0 != (neigh_type & EL_TYPE_O) && at[neigh].valence == 1; /* ignore -O- */
1741
num_N += 0 != (neigh_type & EL_TYPE_N) &&
1742
!(at[neigh].valence == 3 && at[neigh].chem_bonds_valence == 3); /* ignore -N< */
1743
num_S += 0 != (neigh_type & EL_TYPE_S) && at[neigh].valence == 1; /* ignore -S- */
1744
num_P += 0 != (neigh_type & EL_TYPE_P) &&
1745
!(at[neigh].valence == 3 && at[neigh].chem_bonds_valence == 3); /* ignore -P< */
1746
num_OSt += 0 != (neigh_type & EL_TYPE_OSt);
1747
num_acceptors += (bond_type == BOND_TYPE_DOUBLE) && (neigh_type & EL_TYPE_PT);
1748
num_donors += (bond_type == BOND_TYPE_SINGLE) && (neigh_type & EL_TYPE_PT) &&
1749
(at[neigh].num_H || at[neigh].charge==-1 || at[neigh].endpoint);
1750
if ( at[neigh].endpoint ) {
1751
num_acceptor_endpoints += (bond_type == BOND_TYPE_DOUBLE);
1752
num_donor_endpoints += (bond_type == BOND_TYPE_SINGLE);
1754
tg_number = at[neigh].endpoint;
1755
num_eql_mobile_gr = 1;
1757
if ( tg_number == at[neigh].endpoint ) {
1758
num_eql_mobile_gr ++;
1760
num_dif_mobile_gr ++;
1763
if ( bond_type == BOND_TYPE_SINGLE && val ) {
1764
if ( at[neigh].endpoint ) {
1768
has_mobile_H |= (0 != at[neigh].num_H);
1769
has_mobile |= (0 != at[neigh].num_H) || (at[neigh].charge == -1);
1774
if ( at[neigh].endpoint || (neigh_type & EL_TYPE_PT) ) {
1775
for ( k = 0; k < num_mgroups; k ++ ) {
1776
if ( MGroups[k].group_number == at[neigh].endpoint ) {
1781
if ( k == num_mgroups ) {
1782
MGroups[k].group_number = at[neigh].endpoint;
1785
num_diff_t_groups += (0 != at[neigh].endpoint);
1789
if ( !num_acceptors || !num_donors || /* num_acceptors > 2 ||*/
1790
num_eql_mobile_gr == num_endpoints && !num_forbidden ||
1791
!tg_number && !has_mobile_H ) {
1792
continue; /* nothing to do */
1796
/***************** determine the case ************************/
1797
if ( 3 == num_bonds_non_metal &&
1798
4 == bonds_valence_non_metal &&
1799
(centerpoint_type == EL_TYPE_C) &&
1800
2 == num_O && 1 == num_N+num_S && num_OSt &&
1801
1 == num_forbidden && 3 == num_eql_mobile_gr ) {
1802
/********************************************************
1803
*** InChI Tech. Man., Table 5, case 1 ***
1804
********************************************************
1806
OH OH X = N, S, Se, Te
1808
e / / tg = Mobile-H vertex
1815
XH, O, and O belong to the same Mobile-H group.
1816
Fixed bond prevents the correct structure restoration:
1817
H cannot migrate from X to O because HX-N bond is fixed
1819
Move H from X to allow XH-C bond change
1820
(this unfixes the bond, see SetForbiddenEdges(...) )
1821
*********************************************************/
1822
int jXH = -1, jO1 = -1, jO2 = -1, n = 0;
1823
for ( j = 0; j < num_endpoints; j ++ ) {
1824
if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_S)) &&
1825
(MobileGr[j].forbidden == forbidden_mask) &&
1826
MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
1831
if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_O &&
1832
MobileGr[j].num_bonds_non_metal == 1 &&
1833
!MobileGr[j].forbidden ) {
1834
if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && jO1 < 0 ) {
1838
if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE && jO2 < 0 ) {
1848
e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jXH].ineigh];
1850
ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO1].ineigh];
1852
ev2 = pBNS->edge + pVA[MobileGr[jXH].atom_number].nTautGroupEdge - 1;
1854
if ( !ev1->flow || !ev2->flow ) {
1858
/* do not remove forbidden edge bit */
1862
pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --;
1863
pBNS->vert[ev2->neighbor12 ^ ev2->neighbor1].st_edge.flow --;
1864
pBNS->tot_st_flow -= 2;
1869
/*********************************************************************/
1870
if ( 3 == num_bonds_non_metal &&
1871
5 == bonds_valence_non_metal &&
1872
(centerpoint_type == EL_TYPE_N) &&
1873
2 == num_O && 1 == num_N+num_S &&
1874
1 == num_forbidden && 3 == num_eql_mobile_gr ) {
1875
/********************************************************
1876
*** InChI Tech. Man., Table 5, case 2 ***
1877
********************************************************
1879
O OH X = N, S, Se, Te
1881
e // / tg = Mobile-H vertex
1888
XH, O, and O belong to the same Mobile-H group.
1889
Fixed bond prevents the correct structure restoration:
1890
H cannot migrate from X to O because HX-N bond is fixed
1892
Move H from X to allow XH-N bond change
1893
(this unfixes the bond, see SetForbiddenEdges(...) )
1894
*********************************************************/
1895
int jXH = -1, jO1 = -1, jO2 = -1, n = 0;
1896
for ( j = 0; j < num_endpoints; j ++ ) {
1897
if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_S)) &&
1898
(MobileGr[j].forbidden == forbidden_mask) &&
1899
MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
1904
if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_O &&
1905
MobileGr[j].bond_type == BOND_TYPE_DOUBLE &&
1906
MobileGr[j].num_bonds_non_metal == 1 &&
1907
!MobileGr[j].forbidden ) {
1922
e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jXH].ineigh];
1924
ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO1].ineigh];
1926
ev2 = pBNS->edge + pVA[MobileGr[jXH].atom_number].nTautGroupEdge - 1;
1928
if ( !ev1->flow || !ev2->flow ) {
1931
/* do not remove forbidden edge bit */
1935
pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --; /* first =O vertex */
1936
pBNS->vert[ev2->neighbor12 ^ ev2->neighbor1].st_edge.flow --; /* taut group vertex tg */
1937
pBNS->tot_st_flow -= 2;
1942
/*********************************************************************/
1943
if ( 3 == num_bonds_non_metal &&
1944
5 == bonds_valence_non_metal &&
1945
(centerpoint_type & (EL_TYPE_N | EL_TYPE_P)) &&
1946
1 == num_O+num_S && 0 < num_N && 2 == (num_N + num_P) &&
1947
1 == num_forbidden && num_O+num_S+num_N == num_eql_mobile_gr ) {
1948
/********************************************************
1949
*** InChI Tech. Man., Table 5, case 4 ***
1950
********************************************************
1953
// // f = fixed bond
1954
e // ev2 // tg = Mobile-H vertex
1961
O, NH, and possibly X belong to the same Mobile-H group.
1962
Fixed bond prevents the correct structure restoration:
1963
H cannot migrate from NH to O because O=N bond is fixed
1965
Move H from NH to O to allow O=N bond change
1966
(this unfixes the bond, see fix_special_bonds(...) )
1967
*********************************************************/
1968
int jO = -1, jNH = -1, jX = -1, n = 0;
1969
for ( j = 0; j < num_endpoints; j ++ ) {
1970
if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) &&
1971
MobileGr[j].forbidden == forbidden_mask &&
1972
MobileGr[j].bond_type == BOND_TYPE_DOUBLE &&
1973
MobileGr[j].num_bonds_non_metal == 1 &&
1978
if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_P)) &&
1979
!MobileGr[j].forbidden ) {
1980
if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
1981
(MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N && jNH < 0 ) {
1985
if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE && jX < 0 ) {
1995
e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO].ineigh];
1997
ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jNH].ineigh];
1999
ev2 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jX].ineigh];
2004
/* do not remove forbidden edge bit */
2007
pBNS->vert[e->neighbor12 ^ i].st_edge.flow --;
2008
pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --;
2009
pBNS->tot_st_flow -= 2;
2014
/********* InChI Tech.Man. Table 5, case 6 **************/
2015
if ( 2 == delta_met && 4 == num_bonds_non_metal &&
2016
5 == bonds_valence_non_metal &&
2017
1 == num_forbidden && 1 < num_eql_mobile_gr &&
2018
!num_dif_mobile_gr &&
2019
(centerpoint_type & (EL_TYPE_N | EL_TYPE_P)) &&
2020
1 <= num_N && 2 <= num_N+num_O+num_S &&
2021
1 == num_acceptor_endpoints && 0 < num_donor_endpoints ) {
2022
int jN = -1, njFix = 0, jFix[4], n = 0;
2023
/* centerpoint is N, P, As, Sb
2032
X ZH X Z Z=N,O,S,Se,Te [terminal endpoint]
2035
Y---N===N--- Y---N---NH---
2040
int 1 tautomerism O==N--NH is allowed
2042
Problem: OH and =N- belong to a Mobile-H group, but
2043
forbidden edge e does not allow them to be
2044
tautomeric in the restored structure.
2048
1. Decrement flow in edge e
2049
2. Decrement st_edge flow in N and N connected by e
2050
3. Fix all single order bonds to not terminal tautomeric N around N(centerpoint)
2051
4. Run BNS to establist new flow distribution
2054
for ( j = 0; j < num_endpoints; j ++ ) {
2055
neigh = MobileGr[j].atom_number;
2056
if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE &&
2057
(MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N &&
2058
MobileGr[j].num_bonds_non_metal == 2 &&
2059
MobileGr[j].bonds_valence_non_metal == 3 &&
2060
at[neigh].endpoint &&
2061
!at[neigh].num_H && !at[neigh].charge &&
2062
!at[neigh].radical &&
2063
(MobileGr[j].forbidden & forbidden_mask) && jN < 0 ) {
2067
if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
2068
at[neigh].endpoint ) {
2069
if ( MobileGr[j].num_bonds > 1 ) {
2076
if ( jN < 0 || n < 2 || 1 + njFix == n ) {
2077
goto case_5_7; /* nothing to do */
2080
e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jN].ineigh]; /* fixed edge */
2085
pBNS->vert[i].st_edge.flow --;
2086
pBNS->vert[e->neighbor12 ^ i].st_edge.flow --;
2087
pBNS->tot_st_flow -= 2;
2089
for ( j = 0; j < njFix; j ++ ) {
2091
ev = pBNS->edge + pBNS->vert[i].iedge[(int)MobileGr[jFix[j]].ineigh];
2092
ev->forbidden |= forbidden_mask;
2098
/*********************************************************************/
2099
if ( 3 == num_bonds_non_metal &&
2100
4 == bonds_valence_non_metal &&
2101
(centerpoint_type == EL_TYPE_S) &&
2102
2 == num_O+num_S && 1 == num_OSt && 1 == num_N &&
2103
1 == num_forbidden && 3 == num_eql_mobile_gr &&
2104
MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) {
2105
/********************************************************
2106
*** InChI Tech. Man., Table 5, case 7 ***
2107
********************************************************
2111
e /ev2 f / tg = Mobile-H vertex
2112
HN---S --> N===S X = N or non-endpoint;
2118
O, NH, OH belong to the same Mobile-H group.
2119
Fixed bond prevents the correct structure restoration:
2120
H cannot migrate from NH to O because HN-S bond is fixed
2122
Move H from NH to =O to allow HN=S bond change by making a 2nd terminal -OH
2123
(this unfixes the bond, see fix_special_bonds(...) )
2124
*********************************************************/
2125
int jO = -1, jNH = -1, jOH = -1, n = 0;
2126
for ( j = 0; j < num_endpoints; j ++ ) {
2127
if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N &&
2128
MobileGr[j].forbidden == forbidden_mask &&
2129
MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
2130
MobileGr[j].num_bonds_non_metal <= 2 &&
2135
if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) &&
2136
!MobileGr[j].forbidden &&
2137
MobileGr[j].num_bonds_non_metal == 1 ) {
2138
if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE &&
2153
e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jNH].ineigh];
2155
ev1 = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jO].ineigh];
2157
ev2 = pBNS->edge + pVA[MobileGr[jNH].atom_number].nTautGroupEdge - 1;
2159
if ( !ev1->flow || !ev2->flow ) {
2163
/* do not remove forbidden edge bit */
2167
pBNS->vert[ev1->neighbor12 ^ i].st_edge.flow --; /* first =O vertex */
2168
pBNS->vert[ev2->neighbor12 ^ ev2->neighbor1].st_edge.flow --; /* taut group vertex tg */
2169
pBNS->tot_st_flow -= 2;
2174
/*********************************************************************/
2175
if ( 3 == num_bonds_non_metal &&
2176
4 == bonds_valence_non_metal &&
2177
(centerpoint_type == EL_TYPE_S) &&
2178
1 == num_O+num_S && !num_OSt && 1 <= num_N &&
2179
1 == num_forbidden &&
2180
num_O+num_S+num_N == num_eql_mobile_gr &&
2181
MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) {
2182
/********************************************************
2183
*** InChI Tech. Man., Table 5, case 9a ***
2184
********************************************************
2188
/ / tg = Mobile-H vertex
2189
HN---S --> N===S X = N or non-endpoint;
2190
|| \\ e \ -X is not -O(terminal)
2194
Problem: ================
2195
O, NH belong to the same Mobile-H group.
2196
Fixed bond prevents the correct structure restoration:
2197
H cannot migrate from NH to O because O=S bond is fixed
2199
Move H from NH to =O to allow O=S bond change by making a terminal -OH
2200
(this unfixes the bond, see fix_special_bonds(...) )
2201
*********************************************************/
2202
int jO = -1, jNH = -1, jX = -1, n = 0;
2203
for ( j = 0; j < num_endpoints; j ++ ) {
2204
if ( (MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) &&
2205
MobileGr[j].forbidden == forbidden_mask &&
2206
MobileGr[j].bond_type == BOND_TYPE_DOUBLE &&
2207
MobileGr[j].num_bonds_non_metal == 1 &&
2212
if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N &&
2213
!MobileGr[j].forbidden &&
2214
MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
2224
if ( jO < 0 || jNH < 0 ) {
2225
goto case_5_8b_to_9b;
2228
e = pBNS->edge + pBNS->vert[i].iedge[MobileGr[ind_forbidden].ineigh];
2230
goto case_5_8b_to_9b;
2233
pBNS->vert[e->neighbor1].st_edge.flow --;
2234
pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --;
2235
pBNS->tot_st_flow -= 2;
2239
case_5_8b_to_9b: /* #1 */
2240
/*********************************************************************/
2241
if ( 3 == num_bonds_non_metal &&
2242
4 == bonds_valence_non_metal &&
2243
(centerpoint_type == EL_TYPE_S) &&
2244
0 == num_O+num_S && 2 == num_N && 0 == num_P && !num_OSt &&
2245
1 == num_forbidden &&
2246
1 == num_eql_mobile_gr && 0 == num_dif_mobile_gr &&
2247
1 == num_donor_endpoints && 0 == num_acceptor_endpoints &&
2248
1 == num_donors && 1 == num_acceptors &&
2249
MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) {
2250
/********************************************************
2251
*** InChI Tech. Man., Table 5, case 8b->9b ***
2252
********************************************************
2253
---> O = O, S, Se, Te
2255
\ f/ \ f/ f = fixed bond
2256
\ ev / C=====Z \ / C-----ZH tg = Mobile-H vertex
2257
N===S | | N===S || || X = is N not an endpoint;
2258
not \ | | \ || || -X is not terminal -O,-S,-Se,-Te or
2259
an \ | e | \|| e || any N, P, As
2260
endpoint NH=====tg N------tg
2261
is an f N, N, X, fixed single
2262
endpoint =====================
2265
N is not a Mobile-H endpoint, NH is a Mobile-H endpoint
2266
Unfixed bond N==S prevents the correct structure restoration:
2267
H can migrate from NH to N because N=S bond is not fixed
2269
Move H from NH to =Z to make N=S bond fixed (Table 5, case 9)
2270
(this unfixes the bond, see fix_special_bonds(...) )
2271
*********************************************************/
2272
int jN = -1, jNH = -1, jX = -1, n = 0;
2273
for ( j = 0; j < num_endpoints; j ++ ) {
2274
if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N &&
2275
!(MobileGr[j].forbidden & forbidden_mask) ) {
2276
if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE &&
2277
!at[MobileGr[j].atom_number].endpoint &&
2282
if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
2283
MobileGr[j].num_bonds == 2 &&
2284
MobileGr[j].bonds_valence == 2 &&
2285
at[MobileGr[j].atom_number].endpoint &&
2291
if ( !((MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_P)) ||
2292
(MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) &&
2293
MobileGr[j].num_bonds > 1 ) &&
2294
(MobileGr[j].forbidden & forbidden_mask) &&
2295
MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
2302
goto case_5_8c_to_9c;
2305
e = pBNS->edge + pVA[MobileGr[jNH].atom_number].nTautGroupEdge - 1;
2307
goto case_5_8c_to_9c; /* should not happen ??? */
2310
pBNS->vert[e->neighbor1].st_edge.flow --;
2311
pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --;
2312
pBNS->tot_st_flow -= 2;
2313
e->forbidden |= forbidden_mask;
2317
case_5_8c_to_9c: /* #2 */
2318
/*********************************************************************/
2319
if ( 3 == num_bonds_non_metal &&
2320
4 == bonds_valence_non_metal &&
2321
(centerpoint_type == EL_TYPE_S) &&
2322
0 == num_O+num_S && 3 == num_N && 0 == num_P &&
2323
1 == num_forbidden &&
2324
3 == num_eql_mobile_gr && 0 == num_dif_mobile_gr &&
2325
2 == num_donor_endpoints && 1 == num_acceptor_endpoints &&
2326
2 == num_donors && 1 == num_acceptors &&
2327
MobileGr[ind_forbidden].bond_type == BOND_TYPE_SINGLE ) {
2328
/********************************************************
2329
*** InChI Tech. Man., Table 5, case 8c->9c ***
2330
********************************************************
2331
is an endpoint ---> O = O, S, Se, Te
2332
NH2(X) NH2(X) S = S, Se, Te
2333
\ / pv1 pv2 \ / f = fixed bond
2334
\ 1 / C-----ZH \ / C=====Z tg = Mobile-H vertex
2335
N===S || || N===S | | X = is N not an endpoint;
2336
is an \f||ev1 ||ev2 \f | | -X is not terminal -O,-S,-Se,-Te or
2337
endpoint \|| e || \ | e | any N, P, As
2338
2 N------tg NH=====tg C is a centerpoint of a t-group
2339
is an f N, N, X, fixed single
2340
endpoint =====================
2343
N is not a Mobile-H endpoint, NH is a Mobile-H endpoint
2344
Unfixed bond N==S prevents the correct structure restoration:
2345
H can migrate from NH to N because N=S bond is not fixed
2347
Move H from NH to =Z to make N=S bond fixed (Table 5, case 9)
2348
(this unfixes the bond, see fix_special_bonds(...) )
2349
*********************************************************/
2350
int jN1 = -1, jN2 = -1, jX = -1, n = 0;
2351
EdgeIndex ie, ie1, ie2;
2352
for ( j = 0; j < num_endpoints; j ++ ) {
2353
if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N &&
2354
!(MobileGr[j].forbidden & forbidden_mask) ) {
2355
if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE &&
2356
at[MobileGr[j].atom_number].endpoint &&
2361
if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
2362
MobileGr[j].num_bonds == 2 &&
2363
MobileGr[j].bonds_valence == 3 &&
2364
MobileGr[j].forbidden == forbidden_mask &&
2365
at[MobileGr[j].atom_number].endpoint &&
2370
if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
2371
MobileGr[j].num_bonds <= 2 &&
2372
MobileGr[j].bonds_valence <= 3 &&
2373
at[MobileGr[j].atom_number].endpoint &&
2381
goto case_5_9b_to_8b;
2384
e = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1;
2386
goto case_5_9b_to_8b; /* should not happen ??? */
2388
pv1 = pBNS->vert + e->neighbor1; /* must be jN2 */
2389
pv2 = pBNS->vert + (e->neighbor1 ^ e->neighbor12);
2390
ie = e - pBNS->edge;
2392
for ( j = 0; j < pv1->num_adj_edges; j ++ ) {
2393
ev1 = pBNS->edge + pv1->iedge[j];
2394
if ( ev1->flow && !ev1->forbidden ) {
2395
ie1 = ev1 - pBNS->edge;
2396
pv1 = pBNS->vert + (ev1->neighbor12 ^ (pv1 - pBNS->vert));
2400
for ( j = 0; j < pv2->num_adj_edges; j ++ ) {
2401
ev2 = pBNS->edge + pv2->iedge[j];
2402
if ( ev2->flow && !ev2->forbidden ) {
2403
ie2 = ev2 - pBNS->edge;
2404
pv2 = pBNS->vert + (ev2->neighbor12 ^ (pv2 - pBNS->vert));
2408
if ( ie1 < 0 || ie2 < 0 ) {
2409
goto case_5_9b_to_8b;
2412
e->forbidden |= forbidden_mask;
2415
pv1->st_edge.flow --;
2416
pv2->st_edge.flow --;
2417
pBNS->tot_st_flow -= 2;
2421
case_5_9b_to_8b: /* #3 */
2422
/*********************************************************************/
2423
if ( 3 == num_bonds_non_metal &&
2424
4 == bonds_valence_non_metal &&
2425
(centerpoint_type == EL_TYPE_S) &&
2426
0 == num_O+num_S && 2 == num_N && 0 == num_P &&
2427
1 == num_forbidden &&
2428
2 == num_eql_mobile_gr && 0 == num_dif_mobile_gr &&
2429
1 == num_donor_endpoints && 1 == num_acceptor_endpoints &&
2430
1 == num_donors && 1 == num_acceptors &&
2431
MobileGr[ind_forbidden].bond_type == BOND_TYPE_DOUBLE ) {
2432
/********************************************************
2433
*** InChI Tech. Man., Table 5, case 9b->8b ***
2434
********************************************************
2435
---> O = O, S, Se, Te
2436
X is an X S = S, Se, Te
2437
\ / endpoint \ / f = fixed bond
2438
\ 1ev / C-----ZH \ / C=====Z tg = Mobile-H vertex
2439
N===S || || N===S | | X = is N not an endpoint;
2440
is an f \ || || f \ | | -X is not terminal -O,-S,-Se,-Te or
2441
endpoint 2\|| e || \ | e | any N, P, As
2443
is an f N, N, X, fixed double
2444
endpoint =====================
2447
N1 and N2 are Mobile-H endpoints and belong to the same Mobile-H group.
2448
Fixed bond N1==S prevents the correct structure restoration:
2449
H cannot migrate ZH->N2->N1 because N1=S bond is fixed
2451
Move H from ZH to N2 to make N1=S bond unfixed and fix S-X bond (Table 5, case 8)
2452
(see fix_special_bonds(...) for details )
2453
*********************************************************/
2454
int jN1 = -1, jN2 = -1, jX = -1, n = 0;
2455
EdgeIndex ie, ie1, ie2;
2456
for ( j = 0; j < num_endpoints; j ++ ) {
2457
if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N ) {
2458
if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE &&
2459
at[MobileGr[j].atom_number].endpoint &&
2460
(MobileGr[j].forbidden == forbidden_mask) &&
2465
if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
2466
MobileGr[j].num_bonds == 2 &&
2467
MobileGr[j].bonds_valence == 3 &&
2468
at[MobileGr[j].atom_number].endpoint &&
2469
!(MobileGr[j].forbidden & forbidden_mask) &&
2475
if ( !((MobileGr[j].atom_type_pVA & (EL_TYPE_N | EL_TYPE_P)) ||
2476
(MobileGr[j].atom_type_pVA & (EL_TYPE_O | EL_TYPE_S)) &&
2477
MobileGr[j].num_bonds > 1 ) &&
2478
!(MobileGr[j].forbidden & forbidden_mask) &&
2479
MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
2485
if ( jN1 < 0 || jN2 < 0 ) {
2486
goto case_5_9c_to_8c;
2489
ev = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jN1].ineigh];
2490
e = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1;
2492
goto case_5_9c_to_8c; /* should not happen ??? */
2494
pv1 = pBNS->vert + e->neighbor1; /* must be jN2 */
2495
pv2 = pBNS->vert + (e->neighbor1 ^ e->neighbor12);
2496
ie = e - pBNS->edge;
2498
ev->forbidden &= inv_forbidden_mask;
2499
for ( j = 0; j < pv1->num_adj_edges; j ++ ) {
2500
ev1 = pBNS->edge + pv1->iedge[j];
2501
if ( ev1->flow && !ev1->forbidden ) {
2502
ie1 = ev1 - pBNS->edge;
2503
pv1 = pBNS->vert + (ev1->neighbor12 ^ (pv1 - pBNS->vert));
2507
for ( j = 0; j < pv2->num_adj_edges; j ++ ) {
2508
ev2 = pBNS->edge + pv2->iedge[j];
2509
if ( ev2->flow && !ev2->forbidden ) {
2510
ie2 = ev2 - pBNS->edge;
2511
pv2 = pBNS->vert + (ev2->neighbor12 ^ (pv2 - pBNS->vert));
2515
if ( ie1 < 0 || ie2 < 0 ) {
2516
ev->forbidden |= forbidden_mask; /* failed; restore the forbidden bit */
2517
goto case_5_9c_to_8c;
2520
e->forbidden |= forbidden_mask;
2523
pv1->st_edge.flow --;
2524
pv2->st_edge.flow --;
2525
pBNS->tot_st_flow -= 2;
2529
case_5_9c_to_8c: /* #4 */
2530
/*********************************************************************/
2531
if ( 3 == num_bonds_non_metal &&
2532
4 == bonds_valence_non_metal &&
2533
(centerpoint_type == EL_TYPE_S) &&
2534
0 == num_O+num_S && 3 == num_N && 0 == num_P &&
2535
0 == num_forbidden &&
2536
2 == num_diff_t_groups && 2 == num_mgroups && /* all neighbors belong to 2 t-groups */
2537
3 == num_eql_mobile_gr + num_dif_mobile_gr && /* all 3 neighbors belong to t-groups */
2538
2 == num_donor_endpoints && 1 == num_acceptor_endpoints &&
2539
2 == num_donors && 1 == num_acceptors ) {
2540
/********************************************************
2541
*** InChI Tech. Man., Table 5, case 8c->9c ***
2542
********************************************************
2543
is an endpoint ---> O = O, S, Se, Te
2544
tg1 NH2(X) NH2(X) S = S, Se, Te
2545
\ / pv1 pv2 \ / f = fixed bond
2546
\(1) / C=====Z \ / C-----ZH tg = Mobile-H vertex
2547
N===S | | N===S || || X = is N not an endpoint;
2548
is an \ |ev1 | ev2 \ || || -X is not terminal -O,-S,-Se,-Te or
2549
endpoint \ | e | \|| e || any N, P, As
2550
tg1 (2)NH=====tg N------tg C is a centerpoint of a t-group
2551
is an f N, N, X, fixed single
2552
endpoint =====================
2555
N (1) and NH2 are Mobile-H group 1 endpoiints, NH (2) is a Mobile-H group 2 endpoint
2556
Unfixed bonds N==S--NH(2) allows the two Mobile H groups to merge
2557
hence prevents the correct structure restoration:
2558
H can migrate from NH (2) to N (1) because S-NH(1) bond is not fixed
2560
Move H from NH(2) to =Z to make S-NH(2) bond fixed (Table 5, case 8c)
2561
(this unfixes the bond, see fix_special_bonds(...) )
2562
*********************************************************/
2563
int jN1 = -1, jN2 = -1, jX = -1, n = 0;
2564
/* find t-group that is represented by only one neighbor */
2565
for ( j = 0, k = 0; j < num_mgroups; j ++ ) {
2566
if ( 1 == MGroups[k].num && MGroups[k].group_number ) {
2567
k = MGroups[k].group_number;
2572
goto case_5_9c_to_9d;
2574
for ( j = 0; j < num_endpoints; j ++ ) {
2575
if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N ) {
2576
if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE &&
2577
at[MobileGr[j].atom_number].endpoint &&
2578
at[MobileGr[j].atom_number].endpoint != k &&
2583
if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
2584
MobileGr[j].num_bonds == 2 &&
2585
MobileGr[j].bonds_valence == 2 &&
2586
at[MobileGr[j].atom_number].endpoint == k &&
2591
if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
2592
MobileGr[j].num_bonds <= 2 &&
2593
MobileGr[j].bonds_valence <= 3 &&
2594
at[MobileGr[j].atom_number].endpoint &&
2595
at[MobileGr[j].atom_number].endpoint != k &&
2603
goto case_5_9c_to_9d;
2606
e = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1;
2608
goto case_5_9c_to_9d; /* should not happen ??? */
2611
pBNS->vert[e->neighbor1].st_edge.flow --;
2612
pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --;
2613
pBNS->tot_st_flow -= 2;
2614
e->forbidden |= forbidden_mask;
2618
case_5_9c_to_9d: /* #6 */
2619
/*********************************************************************/
2620
if ( 3 == num_bonds_non_metal &&
2621
4 == bonds_valence_non_metal &&
2622
(centerpoint_type == EL_TYPE_S) &&
2623
0 == num_O+num_S && 3 == num_N && 0 == num_P &&
2624
0 == num_forbidden &&
2625
3 == num_mgroups && 2 == num_diff_t_groups &&
2626
2 == num_donor_endpoints && 0 == num_acceptor_endpoints &&
2627
2 == num_donors && 1 == num_acceptors ) {
2628
/********************************************************
2629
*** InChI Tech. Man., Table 5, case 9b->8b ***
2630
********************************************************
2631
3(X) ---> e2| O = O, S, Se, Te
2632
NH---is an N====== S = S, Se, Te
2633
\ / endpoint \ / f = fixed bond
2634
\ 1 / C=====Z \ f / C-----Z tg = Mobile-H vertex
2635
N===S | | N===S || || X = is N not an endpoint;
2636
is an ev \ | | ev \ || || -X is not terminal -O,-S,-Se,-Te or
2637
endpoint 2\ | e1 | \|| e1 || any N, P, As
2639
is an f N, N, X, fixed double
2640
endpoint =====================
2643
N1, N2, and N3 are Mobile-H endpoints and belong to the same Mobile-H group.
2644
Fixed bond N1==S prevents the correct structure restoration:
2645
H cannot migrate N3->N2->N1 because N1=S bond is fixed
2647
Move mobile H to N2 and N3 to make N1=S bond unfixed (Table 5, case 9c)
2648
(see fix_special_bonds(...) for details )
2649
*********************************************************/
2650
int jN1 = -1, jN2 = -1, jX = -1, n = 0;
2651
for ( j = 0; j < num_endpoints; j ++ ) {
2652
if ( (MobileGr[j].atom_type_pVA & EL_TYPE_MASK) == EL_TYPE_N ) {
2653
if ( MobileGr[j].bond_type == BOND_TYPE_DOUBLE &&
2654
!at[MobileGr[j].atom_number].endpoint &&
2655
!(MobileGr[j].forbidden & forbidden_mask) &&
2660
if ( MobileGr[j].bond_type == BOND_TYPE_SINGLE &&
2661
MobileGr[j].num_bonds == 2 &&
2662
MobileGr[j].bonds_valence <= 3 &&
2663
at[MobileGr[j].atom_number].endpoint &&
2664
!(MobileGr[j].forbidden & forbidden_mask) ) {
2679
ev = pBNS->edge + pBNS->vert[i].iedge[MobileGr[jN1].ineigh];
2683
e1 = pBNS->edge + pVA[MobileGr[jN2].atom_number].nTautGroupEdge - 1;
2685
goto case_end; /* should not happen ??? */
2687
e2 = pBNS->edge + pVA[MobileGr[jX].atom_number].nTautGroupEdge - 1;
2689
goto case_end; /* should not happen ??? */
2691
/* take care of edge e1 */
2694
pBNS->vert[e->neighbor1].st_edge.flow --;
2695
pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --;
2696
pBNS->tot_st_flow -= 2;
2697
e->forbidden |= forbidden_mask;
2699
/* take care of edge e2 */
2702
pBNS->vert[e->neighbor1].st_edge.flow --;
2703
pBNS->vert[e->neighbor1 ^ e->neighbor12].st_edge.flow --;
2704
pBNS->tot_st_flow -= 2;
2705
e->forbidden |= forbidden_mask;
2707
/* take care of edge ev: do not let it change */
2708
ev->forbidden |= forbidden_mask;
2716
/******************************************************************************************************/
2717
/* Replace ambiguous neutral (+)edge->flow=0, (-)edge->flow=1 with (+)edge->flow=1, (-)edge->flow=0 */
2718
/******************************************************************************************************/
2719
int RearrangePlusMinusEdgesFlow( BN_STRUCT *pBNS, BN_DATA *pBD, VAL_AT *pVA,
2720
ALL_TC_GROUPS *pTCGroups, int forbidden_edge_mask )
2722
int ret, ePlus, eMinus;
2723
EDGE_LIST NewlyFixedEdges;
2724
BNS_EDGE *pPlus, *pMinus;
2725
int i, k1, k2, num_found, num_tot, delta, v1, v2;
2729
AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR );
2730
for ( i = 0, num_found = num_tot = 0; i < pBNS->num_atoms; i ++ ) {
2731
eMinus = pVA[i].nCMinusGroupEdge - 1;
2732
ePlus = pVA[i].nCPlusGroupEdge - 1;
2733
num_tot += (eMinus >= 0) + (ePlus >= 0);
2734
if ( eMinus >= 0 && ePlus >= 0 ) {
2735
pPlus = pBNS->edge + ePlus;
2736
pMinus = pBNS->edge + eMinus;
2737
if ( (k1=pMinus->flow) > 0 && (k2=pPlus->cap-pPlus->flow) > 0 ) {
2745
if ( ret = AllocEdgeList( &NewlyFixedEdges, num_tot + pBNS->num_bonds ) ) {
2749
for ( i = 0, num_found = num_tot = 0; i < pBNS->num_atoms; i ++ ) {
2750
eMinus = pVA[i].nCMinusGroupEdge - 1;
2751
ePlus = pVA[i].nCPlusGroupEdge - 1;
2752
num_tot += (eMinus >= 0) + (ePlus >= 0);
2753
if ( eMinus >= 0 && ePlus >= 0 ) {
2754
pPlus = pBNS->edge + ePlus;
2755
pMinus = pBNS->edge + eMinus;
2756
if ( (k1=pMinus->flow) > 0 && (k2=pPlus->cap - pPlus->flow) > 0 ) {
2758
v1 = pMinus->neighbor1;
2759
v2 = pMinus->neighbor12 ^ v1;
2760
delta = inchi_min(k1,k2);
2761
pMinus->flow -= delta;
2762
pBNS->vert[v1].st_edge.flow -= delta;
2763
pBNS->vert[v2].st_edge.flow -= delta;
2764
pBNS->tot_st_flow -= 2*delta;
2767
pPlus->forbidden |= forbidden_edge_mask;
2768
pMinus->forbidden |= forbidden_edge_mask;
2769
if ( (ret = AddToEdgeList( &NewlyFixedEdges, eMinus, 0 )) ||
2770
(ret = AddToEdgeList( &NewlyFixedEdges, ePlus, 0 ))) {
2774
if ( eMinus >= 0 ) {
2776
pMinus = pBNS->edge + eMinus;
2777
pMinus->forbidden |= forbidden_edge_mask;
2778
if ( ret = AddToEdgeList( &NewlyFixedEdges, eMinus, 0 )) {
2784
pPlus = pBNS->edge + ePlus;
2785
pPlus->forbidden |= forbidden_edge_mask;
2786
if ( ret = AddToEdgeList( &NewlyFixedEdges, ePlus, 0 )) {
2791
for ( i = 0; i < pBNS->num_bonds; i ++ ) {
2792
pBNS->edge[i].forbidden |= forbidden_edge_mask;
2793
if ( ret = AddToEdgeList( &NewlyFixedEdges, i, 0 )) {
2797
ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
2798
RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask );
2804
AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE );
2807
/******************************************************************************************************/
2808
int IncrementZeroOrderBondsToHeteroat( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
2809
inp_ATOM *at, inp_ATOM *at2,
2810
VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
2811
int *pnNumRunBNS, int *pnTotalDelta,
2812
int forbidden_edge_mask)
2814
#define FIX_BOND_ADD_ALLOC 128
2815
Vertex vPathStart, vPathEnd;
2816
int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
2817
BNS_EDGE *pe, *peZero, *peNeighMeigh = NULL, *peMeFlower;
2818
BNS_VERTEX *pMeFlower = NULL, *pNeigh = NULL, *pNeighNeigh=NULL;
2820
int i, j, k, ret2, ret, bFixedCarbonCharges, num_changes, bSuccess;
2821
int num_at = pStruct->num_atoms;
2822
int num_deleted_H = pStruct->num_deleted_H;
2823
int len_at = num_at + num_deleted_H;
2824
int inv_forbidden_edge_mask = ~forbidden_edge_mask;
2825
Vertex vMeFlower0, vNeigh, vNeighMeigh = NO_VERTEX;
2827
EDGE_LIST CarbonChargeEdges;
2828
EDGE_LIST NewlyFixedEdges;
2833
bFixedCarbonCharges = 0;
2834
AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR );
2835
AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_CLEAR );
2837
if ( !pTCGroups->num_metal_atoms ||
2838
0 > (k=pTCGroups->nGroup[TCG_MeFlower0]) ||
2839
0 > (vMeFlower0 = pTCGroups->pTCG[k].nVertexNumber)) {
2843
memcpy( at2, at, len_at*sizeof(at2[0]));
2845
ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
2851
for ( i = 0; i < num_at; i ++ ) {
2852
if ( !pVA[i].cMetal || pVA[i].nMetalGroupEdge <= 0 ) {
2855
peMeFlower = pBNS->edge + pVA[i].nMetalGroupEdge-1;
2856
if ( vMeFlower0 != (peMeFlower->neighbor12 ^ i) ) {
2860
pMeFlower = pBNS->vert + vMeFlower0;
2862
for ( j = 0; j < at2[i].valence; j ++ ) {
2863
if ( !peMeFlower->flow ) {
2864
break; /* cannot do anything */
2866
if ( !(at2[i].bond_type[j] & BOND_TYPE_MASK) ) {
2867
/* found a zero order bond */
2868
if ( !bFixedCarbonCharges ) {
2869
/* do not let carbon atoms get charged */
2870
if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) {
2873
bFixedCarbonCharges ++;
2875
peZero = pBNS->edge + pBNS->vert[i].iedge[j];
2876
if ( peZero->flow ) {
2880
/* fix other edges */
2881
for ( k = 0; k < at2[i].valence; k ++ ) {
2882
pe = pBNS->edge + pBNS->vert[i].iedge[k];
2883
if ( pe->flow == 1 && !(pe->forbidden & forbidden_edge_mask) ) {
2884
if ( ret = AddToEdgeList( &NewlyFixedEdges, pe - pBNS->edge, FIX_BOND_ADD_ALLOC )) {
2887
pe->forbidden |= forbidden_edge_mask;
2890
/* do not create =N(+)= in a ring or #O(+) terminal */
2891
for ( k = 0; k < num_at; k ++ ) {
2892
if ( !pVA[k].cMetal && pVA[k].cNumValenceElectrons == 5 &&
2893
at2[k].valence == 2 && !at2[k].num_H && pVA[k].cMinRingSize <= 6 &&
2894
pVA[k].nCPlusGroupEdge > 0 &&
2895
(pe=pBNS->edge + pVA[k].nCPlusGroupEdge-1)->flow==1 &&
2896
!(pe->forbidden & forbidden_edge_mask)) {
2898
if ( ret = AddToEdgeList( &NewlyFixedEdges, pe - pBNS->edge, FIX_BOND_ADD_ALLOC )) {
2901
pe->forbidden |= forbidden_edge_mask;
2905
/* metal's neighbor connected by a zero-order bond */
2906
pNeigh = pBNS->vert + (vNeigh = at2[i].neighbor[j]);
2907
/*for ( k = 0; k < pNeigh->num_adj_edges; k ++ )*/
2908
for ( k = pNeigh->num_adj_edges-1; 0 <= k; k -- )
2910
peNeighMeigh = pBNS->edge + pNeigh->iedge[k];
2911
if ( !peNeighMeigh->flow ) {
2914
vNeighMeigh = peNeighMeigh->neighbor12 ^ vNeigh;
2915
if ( vNeighMeigh != i && vNeighMeigh != vMeFlower0 ) {
2916
/* metal neighbor's neighbor connected by a not-zero-order bond */
2917
pNeighNeigh = pBNS->vert + vNeighMeigh;
2922
continue; /* neighbor not found */
2925
peZero->forbidden |= forbidden_edge_mask;
2926
peMeFlower->flow --;
2927
peNeighMeigh->flow --;
2928
pMeFlower->st_edge.flow --;
2929
pNeighNeigh->st_edge.flow --;
2930
pBNS->tot_st_flow -= 2;
2933
ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
2934
&nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
2936
if ( ret == 1 && (vPathEnd == vMeFlower0 && vPathStart == vNeighMeigh ||
2937
vPathEnd == vNeighMeigh && vPathStart == vMeFlower0) && abs(nDeltaCharge) <= 2 ) {
2938
ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
2941
*pnTotalDelta += ret;
2945
if ( ret = AddToEdgeList( &NewlyFixedEdges, peZero - pBNS->edge, FIX_BOND_ADD_ALLOC )) {
2950
peZero->forbidden &= inv_forbidden_edge_mask;
2951
peMeFlower->flow ++;
2952
peNeighMeigh->flow ++;
2953
pMeFlower->st_edge.flow ++;
2954
pNeighNeigh->st_edge.flow ++;
2955
pBNS->tot_st_flow += 2;
2957
RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask );
2958
NewlyFixedEdges.num_edges = 0;
2961
memcpy( at2, at, len_at*sizeof(at2[0]));
2963
ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
2976
RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
2977
RemoveForbiddenEdgeMask( pBNS, &NewlyFixedEdges, forbidden_edge_mask );
2978
AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE );
2979
AllocEdgeList( &NewlyFixedEdges, EDGE_LIST_FREE );
2982
/***********************************************************************
2985
C==S(+)- => C(+)-S- where NH2 are not tautomeric
2988
************************************************************************/
2989
int MovePlusFromS2DiaminoCarbon( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
2990
inp_ATOM *at, inp_ATOM *at2,
2991
VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
2992
int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
2994
int i, j, k, ret, ret2, cur_success;
2996
EdgeIndex ePlusS, ePlusC, eMinusC, e;
2997
BNS_VERTEX *pvS, *pvC, *pv1, *pv2;
2998
BNS_EDGE *pePlusS, *pePlusC, *pe1, *pe2, *peCN[3], *peSC, *pe;
3001
int num_at = pStruct->num_atoms;
3002
int num_deleted_H = pStruct->num_deleted_H;
3003
int len_at = num_at + num_deleted_H;
3005
Vertex vPathStart, vPathEnd, v1, v2;
3006
int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
3008
EDGE_LIST AllChargeEdges;
3013
AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR );
3015
memcpy( at2, at, len_at*sizeof(at2[0]));
3017
ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
3023
/* find (NH2)C=S(+) */
3024
for ( i = 0; i < num_at; i ++ ) {
3025
if ( !pVA[i].cMetal && pVA[i].cNumValenceElectrons == 6 &&
3026
at2[i].valence == 2 &&
3027
(pvS = pBNS->vert+i)->st_edge.cap == pvS->st_edge.flow &&
3028
0 <= (ePlusS = pVA[i].nCPlusGroupEdge-1) && !(pePlusS=pBNS->edge+ePlusS)->flow && /* S(+) */
3029
(pe1=pBNS->edge + pvS->iedge[0])->flow +
3030
(pe2=pBNS->edge + pvS->iedge[1])->flow == 1 /* -S(+)= */ &&
3031
pVA[vC = (peSC=pe1->flow? pe1 : pe2)->neighbor12 ^ i].cNumValenceElectrons == 4 &&
3032
at2[vC].valence == 3 &&
3033
0 <= (ePlusC=pVA[vC].nCPlusGroupEdge-1) && (pePlusC=pBNS->edge+ePlusC)->flow &&
3034
!(0 <= (eMinusC=pVA[vC].nCMinusGroupEdge-1) && pBNS->edge[eMinusC].flow ) ) {
3035
/* found >C=S(+)- */
3036
pvC = pBNS->vert + vC;
3037
for ( j = k = 0; j < at[vC].valence; j ++ ) {
3038
if ( peSC != (peCN[k] = pBNS->edge + pvC->iedge[j]) && !peCN[k]->flow ) {
3039
k ++; /* a single bond from C */
3045
for ( j = 0; j < k; j ++ ) {
3046
vN = peCN[j]->neighbor12 ^ vC;
3047
if ( pVA[vN].cNumValenceElectrons != 5 ||
3048
pBNS->vert[vN].st_edge.cap != pBNS->vert[vN].st_edge.flow ||
3049
at2[vN].num_H != 2 ||
3050
at2[vN].endpoint || (pStruct->endpoint && pStruct->endpoint[vN]) ) {
3051
break; /* does not fit the pattern */
3057
/* fix all charges */
3058
if ( !AllChargeEdges.num_edges ) {
3059
for ( j = 0; j < num_at; j ++ ) {
3060
if ( 0 <= (e = pVA[j].nCPlusGroupEdge-1) && !pBNS->edge[e].forbidden &&
3061
(ret = AddToEdgeList( &AllChargeEdges, e, 2*num_at )) ) {
3064
if ( 0 <= (e = pVA[j].nCMinusGroupEdge-1) && !pBNS->edge[e].forbidden &&
3065
(ret = AddToEdgeList( &AllChargeEdges, e, 2*num_at )) ) {
3070
SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );
3071
pePlusS->forbidden &= ~forbidden_edge_mask;
3076
pv1 = pBNS->vert + (v1 = pe->neighbor1);
3077
pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1);
3080
pv1->st_edge.flow -= delta;
3081
pv2->st_edge.flow -= delta;
3082
pBNS->tot_st_flow -= 2*delta;
3084
ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
3085
&nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
3087
if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
3088
vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == -1 ) {
3089
/* Remover (+)charge from S => nDeltaCharge == -1 */
3090
/* Flow change on pe (+)charge edge (atom S) is not known to RunBnsTestOnce()) */
3091
ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
3098
pv1->st_edge.flow += delta;
3099
pv2->st_edge.flow += delta;
3100
pBNS->tot_st_flow += 2*delta;
3102
RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );
3106
AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE );
3109
/******************************************************************************************************/
3110
int EliminateChargeSeparationOnHeteroatoms( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
3111
inp_ATOM *at, inp_ATOM *at2,
3112
VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
3113
int *pnNumRunBNS, int *pnTotalDelta,
3114
int forbidden_edge_mask, int forbidden_stereo_edge_mask)
3115
/********* Avoid charge separation on heteroatoms ******************/
3117
int i, j, k, ret, ret2, num_pos, num_neg, num_min=0, bFixedCarbonCharges;
3118
int vPlusSuper; /* (+)super vertex */
3119
int ePlusSuper; /* edge from vPlusSuper to (+/-) */
3120
int vPlusMinus; /* (+/-) vertex */
3121
int nDeltaPlus1, nDeltaMinus1, delta;
3122
BNS_VERTEX *pvPlusSuper, *pvPlusMinus;
3125
int num_at = pStruct->num_atoms;
3126
int num_deleted_H = pStruct->num_deleted_H;
3127
int len_at = num_at + num_deleted_H;
3128
int inv_forbidden_edge_mask = ~forbidden_edge_mask;
3130
Vertex vPathStart, vPathEnd;
3131
int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
3133
EDGE_LIST FixedLargeRingStereoEdges, CarbonChargeEdges;
3137
AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_CLEAR );
3138
AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR );
3139
bFixedCarbonCharges = 0;
3141
if ( forbidden_stereo_edge_mask ) {
3142
for ( i = 0; i < num_at; i ++ ) {
3143
for ( j = 0; j < at2[i].valence; j ++ ) {
3144
if ( pBNS->edge[k = pBNS->vert[i].iedge[j]].forbidden == forbidden_stereo_edge_mask ) {
3145
int nMinRingSize = is_bond_in_Nmax_memb_ring( at2, i, j, pStruct->pbfsq->q,
3146
pStruct->pbfsq->nAtomLevel,
3147
pStruct->pbfsq->cSource, 99 /* max ring size */ );
3148
if ( 0 < nMinRingSize && (ret = AddToEdgeList( &FixedLargeRingStereoEdges, k, 64 ))) {
3154
if ( !FixedLargeRingStereoEdges.num_edges ) {
3157
/* allow stereobonds in rings change */
3158
RemoveForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask );
3162
memcpy( at2, at, len_at*sizeof(at2[0]));
3164
ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
3171
num_pos = num_neg = 0;
3172
for ( i = 0; i < num_at; i ++ ) {
3173
if ( !pVA[i].cMetal && !at2[i].radical ) {
3174
num_pos += ( at2[i].charge > 0 );
3175
num_neg += ( at2[i].charge < 0 );
3178
num_min = inchi_min( num_pos, num_neg );
3182
(k = pTCGroups->nGroup[TCG_Plus]) >= 0 &&
3183
(ePlusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 &&
3184
(vPlusSuper = pTCGroups->pTCG[k].nVertexNumber) >= num_at &&
3185
!(pEdge=pBNS->edge + ePlusSuper)->forbidden ) {
3187
vPlusMinus = pEdge->neighbor12 ^ vPlusSuper;
3188
pvPlusSuper = pBNS->vert + vPlusSuper;
3189
pvPlusMinus = pBNS->vert + vPlusMinus;
3190
num_min = inchi_min( num_min, pEdge->flow );
3191
nDeltaPlus1 = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow;
3192
nDeltaMinus1 = pvPlusMinus->st_edge.cap - pvPlusMinus->st_edge.flow;
3193
if ( num_min && (!nDeltaPlus1 && !nDeltaMinus1 ) ) {
3194
if ( !bFixedCarbonCharges ) { /* 02-02-2006 */
3195
/* do not let carbon atoms get charged */
3196
if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) {
3199
bFixedCarbonCharges ++;
3202
pEdge->forbidden |= forbidden_edge_mask;
3203
pBNS->edge_forbidden_mask |= forbidden_edge_mask;
3204
for ( i = 0; i < num_min; i += delta ) {
3205
/* cancel 1 pair of charges at a time */
3206
/* an attempt to cancel all at once may */
3207
/* convert a pair of N(IV)(+) into a pair of N(V) neutral with total charge reduced by 2 */
3208
pEdge->flow -= delta;
3209
pvPlusSuper->st_edge.flow -= delta;
3210
pvPlusMinus->st_edge.flow -= delta;
3211
pBNS->tot_st_flow -= 2*delta;
3212
/* test for charhe cancellation */
3213
ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
3214
&nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
3218
if ( ret == 1 && (vPathEnd == vPlusSuper && vPathStart == vPlusMinus ||
3219
vPathEnd == vPlusMinus && vPathStart == vPlusSuper) && nDeltaCharge < 0 ) {
3220
ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
3226
*pnTotalDelta += ret;
3232
pEdge->flow += delta;
3233
pvPlusSuper->st_edge.flow += delta;
3234
pvPlusMinus->st_edge.flow += delta;
3235
pBNS->tot_st_flow += 2*delta;
3239
num_min -= i; /* how many pairs of charges left */
3240
pEdge->forbidden &= inv_forbidden_edge_mask;
3242
nDeltaPlus1 = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow;
3243
nDeltaMinus1 = pvPlusMinus->st_edge.cap - pvPlusMinus->st_edge.flow;
3244
if ( num_min > 1 && (!nDeltaPlus1 && !nDeltaMinus1 ) ) {
3246
pEdge->forbidden |= forbidden_edge_mask;
3247
pBNS->edge_forbidden_mask |= forbidden_edge_mask;
3248
for ( i = 0; i < num_min; i += delta ) {
3249
/* cancel 2 pairs of opposite charges at a time */
3250
/* 1. test cancellation of a pair of (+) charges */
3251
pvPlusSuper->st_edge.cap += delta;
3252
pBNS->tot_st_cap += delta;
3253
ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
3254
&nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
3258
pvPlusSuper->st_edge.cap -= delta;
3259
pBNS->tot_st_cap -= delta;
3260
if ( ret != 1 || (vPathEnd != vPlusSuper || vPathStart != vPlusSuper) || nDeltaCharge >= 0 ) {
3263
/* 2. test cancellation of a pair of (-) charges */
3264
pvPlusMinus->st_edge.cap += delta;
3265
pBNS->tot_st_cap += delta;
3266
ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
3267
&nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
3271
pvPlusMinus->st_edge.cap -= delta;
3272
pBNS->tot_st_cap -= delta;
3273
if ( ret != 1 || (vPathEnd != vPlusMinus || vPathStart != vPlusMinus) || nDeltaCharge >= 0 ) {
3276
/* 3. Actually cancel the pair of charges */
3277
pEdge->flow -= delta;
3278
pvPlusSuper->st_edge.flow -= delta;
3279
pvPlusMinus->st_edge.flow -= delta;
3280
pBNS->tot_st_flow -= 2*delta;
3281
ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
3287
*pnTotalDelta += ret;
3293
num_min -= i; /* how many pairs of charges left */
3294
pEdge->forbidden &= inv_forbidden_edge_mask;
3297
memcpy( at2, at, len_at*sizeof(at2[0]));
3300
if ( bFixedCarbonCharges ) {
3301
RemoveForbiddenEdgeMask(pBNS, &CarbonChargeEdges, forbidden_edge_mask );
3302
AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE );
3304
if ( forbidden_stereo_edge_mask && FixedLargeRingStereoEdges.num_edges ) {
3305
SetForbiddenEdgeMask( pBNS, &FixedLargeRingStereoEdges, forbidden_stereo_edge_mask );
3307
AllocEdgeList( &FixedLargeRingStereoEdges, EDGE_LIST_FREE );
3309
return ret < 0? ret : num_min;
3311
#if (MOVE_CHARGES_FROM_HETEREO_TO_METAL == 1 )
3312
/********************** not used *************************************************************************/
3313
int MoveChargeFromHeteroatomsToMetals( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
3314
inp_ATOM *at, inp_ATOM *at2,
3315
VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
3316
int *pnNumRunBNS, int *pnTotalDelta,
3317
int forbidden_edge_mask)
3318
/********* Avoid charge separation on heteroatoms ******************/
3320
int i, k, ret, ret2, num_pos, num_neg, num_min;
3321
int vPlusSuper, vMinusSuper; /* (+), (-) super vertices */
3322
int ePlusSuper, eMinusSuper; /* edges from vPlusSuper or vMinusSuper to (+/-) */
3323
int vPlMn; /* (+/-) vertex */
3324
int vPlusHeteroat, vMinusHeteroat; /* (+), (-) heteroatom vertices */
3325
int ePlusHeteroat, eMinusHeteroat; /* edges from (+) or (-) heteroatom vertex to super (+) or (-) */
3326
int vPlusCarbons, vMinusCarbons; /* (+), (-) carbons vertices */
3327
int ePlusCarbons, eMinusCarbons; /* edges from (+), (-) carbons vertices to super (+) or (-) */
3328
int vPlusMetals, vMinusMetals; /* (+), (-) carbons vertices */
3329
int ePlusMetals, eMinusMetals; /* edges from (+), (-) carbons vertices to super (+) or (-) */
3330
int eMinusHeteroToSuper; /* edge (-)vHetero-[eMinusHeteroat]-Y-[eMinusHeteroToSuper]-(-)vPlusSuper */
3332
int nDeltaPlus1, nDeltaMinus1, delta;
3333
BNS_VERTEX *pvPlusSuper, *pvMinusSuper, *pvPlMn;
3334
BNS_VERTEX *pvPlusHeteroat, *pvMinusHeteroat, *pvPlusCarbons, *pvMinusCarbons;
3335
BNS_VERTEX *pvPlusMetals, *pvMinusMetals;
3336
BNS_EDGE *pEdgePlusHeteroat, *pEdgeMinusHeteroat, *pEdgeMinusHeteroToSuper;
3337
BNS_EDGE *pEdgePlusCarbons, *pEdgeMinusCarbons, *pEdgePlusMetals, *pEdgeMinusMetals;
3338
BNS_EDGE *pEdgePlusSuper, *pEdgeMinusSuper;
3340
int num_at = pStruct->num_atoms;
3341
int num_deleted_H = pStruct->num_deleted_H;
3342
int len_at = num_at + num_deleted_H;
3343
int inv_forbidden_edge_mask = ~forbidden_edge_mask;
3345
Vertex vPathStart, vPathEnd;
3346
int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
3351
memcpy( at2, at, len_at*sizeof(at2[0]));
3353
ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
3360
pEdgePlusSuper = NULL;
3362
if ( (k = pTCGroups->nGroup[TCG_Plus]) >= 0 &&
3363
(ePlusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 &&
3364
(vPlusSuper = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) {
3365
pEdgePlusSuper = pBNS->edge + ePlusSuper;
3366
pvPlusSuper = pBNS->vert + vPlusSuper;
3368
pEdgePlusCarbons = NULL;
3369
pvPlusCarbons = NULL;
3370
if ( (k = pTCGroups->nGroup[TCG_Plus_C0] ) > 0 &&
3371
(ePlusCarbons = pTCGroups->pTCG[k].nForwardEdge) > 0 &&
3372
(vPlusCarbons = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) {
3373
pEdgePlusCarbons = pBNS->edge + ePlusCarbons;
3374
pvPlusCarbons = pBNS->vert + vPlusCarbons;
3376
pEdgePlusHeteroat = NULL;
3377
pvPlusHeteroat = NULL;
3378
if ( (k = pTCGroups->nGroup[TCG_Plus0] ) > 0 &&
3379
(ePlusHeteroat = pTCGroups->pTCG[k].nForwardEdge) > 0 &&
3380
(vPlusHeteroat = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) {
3381
pEdgePlusHeteroat = pBNS->edge + ePlusHeteroat;
3382
pvPlusHeteroat = pBNS->vert + vPlusHeteroat;
3384
pEdgePlusMetals = NULL;
3385
pvPlusMetals = NULL;
3386
if ( (k = pTCGroups->nGroup[TCG_Plus_M0] ) > 0 &&
3387
(ePlusMetals = pTCGroups->pTCG[k].nForwardEdge) > 0 &&
3388
(vPlusMetals = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) {
3389
pEdgePlusMetals = pBNS->edge + ePlusMetals;
3390
pvPlusMetals = pBNS->vert + vPlusMetals;
3393
pEdgeMinusSuper = NULL;
3394
pvMinusSuper = NULL;
3395
if ( (k = pTCGroups->nGroup[TCG_Minus]) >= 0 &&
3396
(eMinusSuper = pTCGroups->pTCG[k].nForwardEdge) > 0 &&
3397
(vMinusSuper = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) {
3398
pEdgeMinusSuper = pBNS->edge + eMinusSuper;
3399
pvMinusSuper = pBNS->vert + vMinusSuper;
3401
pEdgeMinusCarbons = NULL;
3402
pvMinusCarbons = NULL;
3403
if ( (k = pTCGroups->nGroup[TCG_Minus_C0] ) > 0 &&
3404
(eMinusCarbons = pTCGroups->pTCG[k].nForwardEdge) > 0 &&
3405
(vMinusCarbons = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) {
3406
pEdgeMinusCarbons = pBNS->edge + eMinusCarbons;
3407
pvMinusCarbons = pBNS->vert + vMinusCarbons;
3409
pEdgeMinusHeteroat = NULL;
3410
pvMinusHeteroat = NULL;
3411
pEdgeMinusHeteroToSuper = NULL;
3412
if ( (k = pTCGroups->nGroup[TCG_Minus0] ) > 0 &&
3413
(eMinusHeteroat = pTCGroups->pTCG[k].nForwardEdge) > 0 &&
3414
(vMinusHeteroat = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) {
3415
BNS_VERTEX *pvYMinusHetero;
3418
pEdgeMinusHeteroat = pBNS->edge + eMinusHeteroat;
3419
pvMinusHeteroat = pBNS->vert + vMinusHeteroat;
3420
/* next edge toward (-)super */
3421
if ( pvMinusSuper ) {
3422
vYMinusHetero = pEdgeMinusHeteroat->neighbor12 ^ vMinusHeteroat;
3423
pvYMinusHetero = pBNS->vert + vYMinusHetero;
3424
for ( i = 0; i < pvYMinusHetero->num_adj_edges; i ++ ) {
3425
pe = pBNS->edge + pvYMinusHetero->iedge[i];
3426
if ( (pe->neighbor12 ^ vYMinusHetero) == vMinusSuper ) {
3427
pEdgeMinusHeteroToSuper = pe;
3428
eMinusHeteroToSuper = pe - pBNS->edge;
3434
pEdgeMinusMetals = NULL;
3435
pvMinusMetals = NULL;
3436
if ( (k = pTCGroups->nGroup[TCG_Minus_M0] ) > 0 &&
3437
(eMinusMetals = pTCGroups->pTCG[k].nForwardEdge) > 0 &&
3438
(vMinusMetals = pTCGroups->pTCG[k].nVertexNumber) >= num_at ) {
3439
pEdgeMinusMetals = pBNS->edge + eMinusMetals;
3440
pvMinusMetals = pBNS->vert + vMinusMetals;
3444
if ( pEdgePlusSuper ) {
3445
vPlMn = pEdgePlusSuper->neighbor12 ^ vPlusSuper;
3446
pvPlMn = pBNS->vert + vPlMn;
3448
if ( pEdgeMinusSuper ) {
3449
vPlMn = pEdgeMinusSuper->neighbor12 ^ vMinusSuper;
3450
pvPlMn = pBNS->vert + vPlMn;
3452
num_pos = num_neg = 0;
3453
/***************************************************************/
3454
/* Positive Charges */
3455
/***************************************************************/
3456
if ( pEdgePlusHeteroat && pEdgePlusMetals ) {
3458
for ( i = 0; i < num_at; i ++ ) {
3459
if ( !at2[i].radical &&
3460
at2[i].charge > 0 &&
3461
(k = pVA[i].nCPlusGroupEdge-1) >= 0 ) {
3462
v1 = pBNS->edge[k].neighbor1;
3463
v2 = pBNS->edge[k].neighbor1 ^ pBNS->edge[k].neighbor12;
3464
if ( v1 == vPlusHeteroat || v2 == vPlusHeteroat ) {
3469
/* attempt to move (+) from heteroatoms to metal atoms */
3470
num_min = inchi_min( num_pos, pEdgePlusHeteroat->flow );
3472
nDeltaPlus1 = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow;
3473
nDeltaMinus1 = pvPlMn->st_edge.cap - pvPlMn->st_edge.flow;
3474
if ( num_min && !nDeltaPlus1 && !nDeltaMinus1 ) {
3475
if ( pEdgePlusSuper ) {
3476
pEdgePlusSuper->forbidden |= forbidden_edge_mask;
3478
if ( pEdgeMinusSuper ) {
3479
pEdgeMinusSuper->forbidden |= forbidden_edge_mask;
3481
if ( pEdgePlusCarbons ) {
3482
pEdgePlusCarbons->forbidden |= forbidden_edge_mask;
3484
if ( pEdgeMinusCarbons ) {
3485
pEdgeMinusCarbons->forbidden |= forbidden_edge_mask;
3487
if ( pEdgePlusHeteroat ) {
3488
pEdgePlusHeteroat->forbidden |= forbidden_edge_mask;
3490
if ( pEdgeMinusHeteroat ) {
3491
pEdgeMinusHeteroat->forbidden |= forbidden_edge_mask;
3494
for ( i = 0; i < num_min; i += delta ) {
3495
v1 = pEdgePlusHeteroat->neighbor1;
3496
v2 = pEdgePlusHeteroat->neighbor12 ^ v1;
3497
pEdgePlusHeteroat->flow -= delta;
3498
pBNS->vert[v1].st_edge.flow -= delta;
3499
pBNS->vert[v2].st_edge.flow -= delta;
3500
pBNS->tot_st_flow -= 2*delta;
3501
/* test for charhe cancellation */
3502
ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
3503
&nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
3507
if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
3508
vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) {
3509
ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
3515
*pnTotalDelta += ret;
3521
pEdgePlusHeteroat->flow += delta;
3522
pBNS->vert[v1].st_edge.flow += delta;
3523
pBNS->vert[v2].st_edge.flow += delta;
3524
pBNS->tot_st_flow += 2*delta;
3528
if ( pEdgePlusSuper ) {
3529
pEdgePlusSuper->forbidden &= inv_forbidden_edge_mask;
3531
if ( pEdgeMinusSuper ) {
3532
pEdgeMinusSuper->forbidden &= inv_forbidden_edge_mask;
3534
if ( pEdgePlusCarbons ) {
3535
pEdgePlusCarbons->forbidden &= inv_forbidden_edge_mask;
3537
if ( pEdgeMinusCarbons ) {
3538
pEdgeMinusCarbons->forbidden &= inv_forbidden_edge_mask;
3540
if ( pEdgePlusHeteroat ) {
3541
pEdgePlusHeteroat->forbidden &= inv_forbidden_edge_mask;
3543
if ( pEdgeMinusHeteroat ) {
3544
pEdgeMinusHeteroat->forbidden &= inv_forbidden_edge_mask;
3548
/***************************************************************/
3549
/* Negative Charges */
3550
/***************************************************************/
3551
if ( pEdgeMinusHeteroToSuper && pEdgeMinusMetals ) {
3553
for ( i = 0; i < num_at; i ++ ) {
3554
if ( !at2[i].radical &&
3555
at2[i].charge < 0 &&
3556
(k = pVA[i].nCMinusGroupEdge-1) >= 0 ) {
3557
v1 = pBNS->edge[k].neighbor1;
3558
v2 = pBNS->edge[k].neighbor1 ^ pBNS->edge[k].neighbor12;
3559
if ( v1 == vMinusHeteroat || v2 == vMinusHeteroat ) {
3565
/* attempt to move (+) from heteroatoms to metal atoms */
3566
num_min = inchi_min( num_neg, pEdgeMinusHeteroToSuper->flow );
3569
nDeltaPlus1 = pvPlusSuper->st_edge.cap - pvPlusSuper->st_edge.flow;
3570
nDeltaMinus1 = pvPlMn->st_edge.cap - pvPlMn->st_edge.flow;
3571
if ( num_min && !nDeltaPlus1 && !nDeltaMinus1 ) {
3572
if ( pEdgePlusSuper ) {
3573
pEdgePlusSuper->forbidden |= forbidden_edge_mask;
3575
if ( pEdgeMinusSuper ) {
3576
pEdgeMinusSuper->forbidden |= forbidden_edge_mask;
3578
if ( pEdgePlusCarbons ) {
3579
pEdgePlusCarbons->forbidden |= forbidden_edge_mask;
3581
if ( pEdgeMinusCarbons ) {
3582
pEdgeMinusCarbons->forbidden |= forbidden_edge_mask;
3584
if ( pEdgePlusHeteroat ) {
3585
pEdgePlusHeteroat->forbidden |= forbidden_edge_mask;
3587
if ( pEdgeMinusHeteroToSuper ) {
3588
pEdgeMinusHeteroToSuper->forbidden |= forbidden_edge_mask;
3591
for ( i = 0; i < num_min; i += delta ) {
3592
v1 = pEdgeMinusHeteroToSuper->neighbor1;
3593
v2 = pEdgeMinusHeteroToSuper->neighbor12 ^ v1;
3594
pEdgeMinusHeteroToSuper->flow -= delta;
3595
pBNS->vert[v1].st_edge.flow -= delta;
3596
pBNS->vert[v2].st_edge.flow -= delta;
3597
pBNS->tot_st_flow -= 2*delta;
3598
/* test for charhe cancellation */
3599
ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
3600
&nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
3604
if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
3605
vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) {
3606
ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
3612
*pnTotalDelta += ret;
3618
pEdgeMinusHeteroToSuper->flow += delta;
3619
pBNS->vert[v1].st_edge.flow += delta;
3620
pBNS->vert[v2].st_edge.flow += delta;
3621
pBNS->tot_st_flow += 2*delta;
3625
if ( pEdgePlusSuper ) {
3626
pEdgePlusSuper->forbidden &= inv_forbidden_edge_mask;
3628
if ( pEdgeMinusSuper ) {
3629
pEdgeMinusSuper->forbidden &= inv_forbidden_edge_mask;
3631
if ( pEdgePlusCarbons ) {
3632
pEdgePlusCarbons->forbidden &= inv_forbidden_edge_mask;
3634
if ( pEdgeMinusCarbons ) {
3635
pEdgeMinusCarbons->forbidden &= inv_forbidden_edge_mask;
3637
if ( pEdgePlusHeteroat ) {
3638
pEdgePlusHeteroat->forbidden &= inv_forbidden_edge_mask;
3640
if ( pEdgeMinusHeteroToSuper ) {
3641
pEdgeMinusHeteroToSuper->forbidden &= inv_forbidden_edge_mask;
3649
/******************************************************************************************************/
3650
int RestoreCyanoGroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
3651
inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
3652
int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
3654
Vertex vPathStart, vPathEnd;
3655
int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
3658
int i, j, ret2, ret;
3659
int num_at = pStruct->num_atoms;
3660
int num_deleted_H = pStruct->num_deleted_H;
3661
int len_at = num_at + num_deleted_H;
3662
int inv_forbidden_edge_mask = ~forbidden_edge_mask;
3665
EDGE_LIST CarbonChargeEdges;
3668
AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR );
3670
memcpy( at2, at, len_at*sizeof(at2[0]));
3672
ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
3679
for ( i = 0; i < num_at && 0 <= ret; i ++ ) {
3680
if ( at2[i].valence == 1 &&
3681
at2[i].num_H == 0 &&
3682
at2[i].chem_bonds_valence == 2 &&
3683
at2[i].charge == -1 &&
3684
at2[i].radical == 0 &&
3685
pVA[i].cNumValenceElectrons == 5 && /* terminal N(-)=, P, As, Sb, Bi */
3686
pVA[i].nCMinusGroupEdge > 0 &&
3687
pVA[i].nTautGroupEdge == 0 &&
3688
at2[j=at2[i].neighbor[0]].valence == 2 &&
3689
at2[j].num_H == 0 &&
3690
at2[j].chem_bonds_valence == 4 &&
3691
at2[j].charge == 0 &&
3692
at2[j].radical == 0 &&
3693
pVA[j].cNumValenceElectrons == 4 && /* C or Si or Ge or Sn or Pb */
3694
pVA[i].cnListIndex > 0 &&
3695
cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) {
3697
pe = pBNS->edge + (pVA[i].nCMinusGroupEdge-1); /* N#N(+) triple bond edge */
3700
continue; /* wrong atom ??? Strange... */
3703
v2 = pe->neighbor12 ^ v1;
3705
pBNS->vert[v1].st_edge.flow --;
3706
pBNS->vert[v2].st_edge.flow --;
3707
pBNS->tot_st_flow -= 2;
3708
pe->forbidden |= forbidden_edge_mask;
3710
/* do not let carbon atoms get charged */
3711
if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) {
3715
ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
3716
&nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
3718
if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
3719
vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 1 ) {
3720
ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
3722
*pnTotalDelta += ret;
3725
pBNS->vert[v1].st_edge.flow ++;
3726
pBNS->vert[v2].st_edge.flow ++;
3727
pBNS->tot_st_flow += 2;
3729
RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
3731
pe->forbidden &= inv_forbidden_edge_mask; /* unmask the edges */
3737
AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE );
3740
/******************************************************************************************************/
3741
int RestoreIsoCyanoGroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
3742
inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
3743
int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
3745
#define INC_EDGE_LIST 16
3746
Vertex vPathStart, vPathEnd;
3747
int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms, num_failed, num_success;
3751
int i, j, ret2, ret, bIsCarbon;
3752
int num_at = pStruct->num_atoms;
3753
int num_deleted_H = pStruct->num_deleted_H;
3754
int len_at = num_at + num_deleted_H;
3755
int inv_forbidden_edge_mask = ~forbidden_edge_mask;
3756
EdgeIndex eNMinusEdge, eNPlusEdge, eNPlusEdge1, eN34Edge;
3757
EdgeIndex eNFlowerEdge1;
3759
EDGE_LIST CarbonChargeEdges, AllChargeEdges, IsoCyanoCarbonChargeEdges;
3762
num_failed = num_success = 0;
3763
AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); /* carbon charge edges */
3764
AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR ); /* heteroatom charge edges */
3765
AllocEdgeList( &IsoCyanoCarbonChargeEdges, EDGE_LIST_CLEAR ); /* C in C(+)#N(+) charge edges */
3767
memcpy( at2, at, len_at*sizeof(at2[0]));
3769
ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
3775
/* 1st attempt: take care of C(+)#N(+)- => C(-)#N(+)- and remove 2 negative charges */
3776
/* This would produce nDeltaCharge = 2 */
3777
AllocEdgeList( &CarbonChargeEdges, 2*num_at );
3778
for ( i = 0; i < num_at && 0 <= ret; i ++ ) {
3779
/* accumulate edges for subsequent fixing them */
3780
bIsCarbon = (pVA[i].cNumValenceElectrons == 4 && pVA[i].cPeriodicRowNumber == 1);
3781
eNFlowerEdge1 = NO_VERTEX;
3782
if ( (eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 &&
3783
!pBNS->edge[eNMinusEdge].forbidden ) {
3785
if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) {
3789
if ( !pVA[i].cMetal && !at2[i].endpoint && at2[i].charge != -1 ) {
3790
if ( ret = AddToEdgeList( &AllChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) {
3795
if ( (eNPlusEdge = pVA[i].nCPlusGroupEdge - 1)>= 0 &&
3796
!pBNS->edge[eNPlusEdge].forbidden ) {
3798
if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) {
3802
if ( !pVA[i].cMetal && !at2[i].endpoint ) {
3803
if ( ret = AddToEdgeList( &AllChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) {
3806
if ( pVA[i].cNumValenceElectrons == 5 &&
3807
NO_VERTEX != (eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge )) &&
3808
!pBNS->edge[eNFlowerEdge1].flow ) {
3809
if ( ret = AddToEdgeList( &AllChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) {
3818
at2[i].valence == 1 &&
3819
at2[i].num_H == 0 &&
3820
at2[i].radical == 0 &&
3821
!pBNS->edge[eNMinusEdge].forbidden &&
3822
pBNS->edge[eNMinusEdge].flow == 0 &&
3823
!pBNS->edge[eNPlusEdge].forbidden &&
3824
pBNS->edge[eNPlusEdge].flow == 0 && /* found terminal C(+) */
3826
at2[j=at2[i].neighbor[0]].valence == 2 &&
3827
at2[j].num_H == 0 &&
3828
at2[j].radical == 0 &&
3829
pVA[j].cNumValenceElectrons == 5 &&
3830
(eNPlusEdge1 = pVA[j].nCPlusGroupEdge - 1)>= 0 &&
3831
pBNS->edge[eNPlusEdge].flow == 0 ) { /* -N(+)- */
3833
#ifdef NEVER /* I have not found a good reason to do this yet */
3834
/* fix (+) charge on -N(+)- as much as C charges are fixed */
3835
if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) {
3838
/* fix floer edge to prevent N(V) ??? */
3839
if ( NO_VERTEX != (eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge1 )) &&
3840
!pBNS->edge[eNFlowerEdge1].flow ) {
3841
if ( ret = AddToEdgeList( &CarbonChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) {
3848
ChargeStruct: ChargeStruct:
3861
3-6 is (-) Charge Edge; 4-5 is (+) Charge Edge
3863
To convert the left pattern to the right one:
3865
We need to release these charge edges and decrement
3866
edge 3-4 flow to change charge from (+) to (-)
3870
/* find vertices 4 and 5 */
3871
v1 = pBNS->edge[eNPlusEdge].neighbor1; /* one of two vertices incident with edge 4-5 */
3872
v2 = pBNS->edge[eNPlusEdge].neighbor12 ^ v1;
3873
if ( IS_BNS_VT_C_GR(pBNS->vert[v1].type) ) {
3879
/* v1 should be 4, v2 - 5(+C) */
3880
if ( !IS_BNS_VT_CHRG_STRUCT(pBNS->vert[v1].type) || pBNS->vert[v1].num_adj_edges != 2 ) {
3881
continue; /* mismatch */
3884
eN34Edge = pBNS->vert[v1].iedge[pBNS->vert[v1].iedge[0] == eNPlusEdge];
3885
if ( pBNS->edge[eN34Edge].forbidden || !pBNS->edge[eN34Edge].flow ) {
3888
/* save 3 edges: 6-3, 4-5, and 3-4 in this order */
3889
if ( ret = AddToEdgeList( &IsoCyanoCarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) {
3892
if ( ret = AddToEdgeList( &IsoCyanoCarbonChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) {
3895
if ( ret = AddToEdgeList( &IsoCyanoCarbonChargeEdges, eN34Edge, INC_EDGE_LIST ) ) {
3900
/* 1st attempt: move (-) charges from heteroatoms to C(+) */
3901
SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
3902
SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );
3903
RemoveForbiddenEdgeMask( pBNS, &IsoCyanoCarbonChargeEdges, forbidden_edge_mask );
3904
for ( i = IsoCyanoCarbonChargeEdges.num_edges-3; 0 <= i; i -= 3 ) {
3905
eNMinusEdge = IsoCyanoCarbonChargeEdges.pnEdges[i];
3906
eNPlusEdge = IsoCyanoCarbonChargeEdges.pnEdges[i+1];
3907
eN34Edge = IsoCyanoCarbonChargeEdges.pnEdges[i+2];
3909
pe = pBNS->edge + eN34Edge;
3910
pe->forbidden |= forbidden_edge_mask;
3912
continue; /* already done */
3916
v2 = pe->neighbor12 ^ v1;
3918
pBNS->vert[v1].st_edge.flow --;
3919
pBNS->vert[v2].st_edge.flow --;
3920
pBNS->tot_st_flow -= 2;
3922
ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
3923
&nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
3925
if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
3926
vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= -2 ) {
3927
ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
3929
*pnTotalDelta += ret;
3933
pBNS->vert[v1].st_edge.flow ++;
3934
pBNS->vert[v2].st_edge.flow ++;
3935
pBNS->tot_st_flow += 2;
3936
pe->forbidden &= inv_forbidden_edge_mask;
3941
/* relax conditions: allow all heteroatoms to change charge */
3942
RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );
3943
for ( i = IsoCyanoCarbonChargeEdges.num_edges-3; 0 <= i; i -= 3 ) {
3944
eNMinusEdge = IsoCyanoCarbonChargeEdges.pnEdges[i];
3945
eNPlusEdge = IsoCyanoCarbonChargeEdges.pnEdges[i+1];
3946
eN34Edge = IsoCyanoCarbonChargeEdges.pnEdges[i+2];
3948
pe = pBNS->edge + eN34Edge;
3949
pe->forbidden |= forbidden_edge_mask;
3951
continue; /* already done */
3955
v2 = pe->neighbor12 ^ v1;
3957
pBNS->vert[v1].st_edge.flow --;
3958
pBNS->vert[v2].st_edge.flow --;
3959
pBNS->tot_st_flow -= 2;
3961
ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
3962
&nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
3964
if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
3965
vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= 2 ) {
3966
ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
3968
*pnTotalDelta += ret;
3972
pBNS->vert[v1].st_edge.flow ++;
3973
pBNS->vert[v2].st_edge.flow ++;
3974
pBNS->tot_st_flow += 2;
3975
pe->forbidden &= inv_forbidden_edge_mask; /* let it change if it wants */
3980
RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
3981
RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );
3982
RemoveForbiddenEdgeMask( pBNS, &IsoCyanoCarbonChargeEdges, forbidden_edge_mask );
3987
AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE );
3988
AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE );
3989
AllocEdgeList( &IsoCyanoCarbonChargeEdges, EDGE_LIST_FREE );
3991
#undef INC_EDGE_LIST
3994
/******************************************************************************************************/
3995
int FixMetal_Nminus_Ominus( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
3996
inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
3997
int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
3999
#define INC_EDGE_LIST 16
4000
Vertex vPathStart, vPathEnd;
4001
int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
4002
int num_failed, num_success, n, nDeltaChargeMax, nMetalCharge;
4006
int i, j, k, ret2, ret;
4007
int num_at = pStruct->num_atoms;
4008
int num_deleted_H = pStruct->num_deleted_H;
4009
int len_at = num_at + num_deleted_H;
4010
int inv_forbidden_edge_mask = ~forbidden_edge_mask;
4011
EdgeIndex e, eNMinusEdge, eNMinusEdge1, eNMinusEdge2, eNPlusEdge2;
4013
EDGE_LIST AllChargeEdges;
4016
num_failed = num_success = 0;
4017
AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR );
4019
memcpy( at2, at, len_at*sizeof(at2[0]));
4021
ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
4027
/* attepmt #1 N#N(+)-N => N(-)=N(+)=N */
4028
for ( i = 0; i < num_at && 0 <= ret; i ++ ) {
4029
if ( at2[i].valence == 1 &&
4030
at2[i].num_H == 0 &&
4031
at2[i].radical == 0 &&
4032
pVA[i].cNumValenceElectrons == 6 && /* terminal -O */
4033
(eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 && pBNS->edge[eNMinusEdge].flow == 1 &&
4034
!pBNS->edge[eNMinusEdge].forbidden && /* terminal O(-) */
4036
at2[j=at2[i].neighbor[0]].valence == 2 &&
4037
at2[j].num_H == 0 &&
4038
at2[j].radical == 0 &&
4039
pVA[j].cNumValenceElectrons == 5 &&
4040
(eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1)>= 0 && pBNS->edge[eNMinusEdge1].flow == 1 &&
4041
!pBNS->edge[eNMinusEdge1].forbidden &&
4043
pVA[k=at2[j].neighbor[at2[j].neighbor[0]==i]].cMetal &&
4045
(eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1)>= 0 &&
4046
!pBNS->edge[eNMinusEdge2].forbidden &&
4047
(eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1)>= 0 &&
4048
!pBNS->edge[eNPlusEdge2].forbidden ) {
4050
/* found M(q)-N(-)-O(-); convert to M(q-2)-N=O */
4052
/* find all charge edges to fix */
4053
if ( 0 == AllChargeEdges.num_edges ) {
4054
for ( n = 0; n < num_at; n ++ ) {
4055
if ( (e = pVA[n].nCMinusGroupEdge - 1)>= 0 &&
4056
!pBNS->edge[e].forbidden ) {
4057
if ( ret = AddToEdgeList( &AllChargeEdges, e, num_at ) ) {
4061
if ( (e = pVA[n].nCPlusGroupEdge - 1)>= 0 &&
4062
!pBNS->edge[e].forbidden ) {
4063
if ( ret = AddToEdgeList( &AllChargeEdges, e, num_at ) ) {
4066
if ( pVA[n].cNumValenceElectrons == 6 &&
4067
NO_VERTEX != (e = GetChargeFlowerUpperEdge( pBNS, pVA, e )) &&
4068
pBNS->edge[e].flow == 0 ) {
4069
if ( ret = AddToEdgeList( &AllChargeEdges, e, num_at ) ) {
4077
nMetalCharge = (pBNS->edge[eNPlusEdge2].cap - pBNS->edge[eNPlusEdge2].flow)
4078
- pBNS->edge[eNMinusEdge2].flow;
4079
if ( nMetalCharge == 0 ) {
4080
/* change on O is invisible; charge from N(-) goes, charge comes to Metal */
4081
nDeltaChargeMax = 0;
4083
if ( nMetalCharge == 2 ) {
4084
/* charges on Metal and N disappear */
4085
nDeltaChargeMax = -2;
4087
/* charge from N disappears */
4088
nDeltaChargeMax = -1;
4091
SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );
4092
pBNS->edge[eNMinusEdge1].forbidden &= inv_forbidden_edge_mask;
4093
pBNS->edge[eNMinusEdge2].forbidden &= inv_forbidden_edge_mask;
4094
pBNS->edge[eNPlusEdge2].forbidden &= inv_forbidden_edge_mask;
4096
pe = pBNS->edge + eNMinusEdge; /* must be already fixed as a charge edge */
4099
v2 = pe->neighbor12 ^ v1;
4101
pBNS->vert[v1].st_edge.flow --;
4102
pBNS->vert[v2].st_edge.flow --;
4103
pBNS->tot_st_flow -= 2;
4105
ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
4106
&nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
4108
if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
4109
vPathEnd == v2 && vPathStart == v1) /*&& nDeltaCharge == nDeltaChargeMax*/ ) {
4110
ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
4112
*pnTotalDelta += ret;
4116
pBNS->vert[v1].st_edge.flow ++;
4117
pBNS->vert[v2].st_edge.flow ++;
4118
pBNS->tot_st_flow += 2;
4121
RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );
4128
AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE );
4131
#undef INC_EDGE_LIST
4133
/******************************************************************************************************/
4134
int RestoreNNNgroup( BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
4135
inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
4136
int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
4138
#define INC_EDGE_LIST 16
4139
Vertex vPathStart, vPathEnd;
4140
int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms, num_failed, num_success, n, nDeltaChargeMax;
4144
int i, j, k, ret2, ret;
4145
int num_at = pStruct->num_atoms;
4146
int num_deleted_H = pStruct->num_deleted_H;
4147
int len_at = num_at + num_deleted_H;
4148
int inv_forbidden_edge_mask = ~forbidden_edge_mask;
4149
EdgeIndex eNMinusEdge, eNPlusEdge, eNMinusEdge1, eNPlusEdge1, eNMinusEdge2, eNPlusEdge2;
4150
EdgeIndex eNFlowerEdge1, eNFlowerEdge2;
4152
EDGE_LIST CarbonChargeEdges, AllChargeEdges, NNNChargeEdges, CurNNNChargeEdges, AllNNNTermAtoms, AllNIIIChargeEdges;
4155
num_failed = num_success = 0;
4156
AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR );
4157
AllocEdgeList( &AllChargeEdges, EDGE_LIST_CLEAR );
4158
AllocEdgeList( &NNNChargeEdges, EDGE_LIST_CLEAR );
4159
AllocEdgeList( &CurNNNChargeEdges, EDGE_LIST_CLEAR );
4160
AllocEdgeList( &AllNNNTermAtoms, EDGE_LIST_CLEAR );
4161
AllocEdgeList( &AllNIIIChargeEdges, EDGE_LIST_CLEAR );
4163
memcpy( at2, at, len_at*sizeof(at2[0]));
4165
ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
4171
/* attepmt #1 N#N(+)-N => N(-)=N(+)=N: naive approach; expecting tp move (-) from some other atom */
4172
for ( i = 0; i < num_at && 0 <= ret; i ++ ) {
4173
if ( at2[i].valence == 1 &&
4174
at2[i].num_H == 0 &&
4175
at2[i].chem_bonds_valence == 3 &&
4176
at2[i].charge == 0 &&
4177
at2[i].radical == 0 &&
4178
pVA[i].cNumValenceElectrons == 5 && /* terminal N# */
4179
(eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 && pBNS->edge[eNMinusEdge].flow == 0 &&
4180
!pBNS->edge[eNMinusEdge].forbidden &&
4181
at2[j=at2[i].neighbor[0]].valence == 2 &&
4182
at2[j].num_H == 0 &&
4183
at2[j].chem_bonds_valence == 4 &&
4184
at2[j].charge == 1 &&
4185
at2[j].radical == 0 &&
4186
pVA[j].cNumValenceElectrons == 5 &&
4187
(eNPlusEdge = pVA[j].nCPlusGroupEdge - 1)>= 0 && pBNS->edge[eNPlusEdge].flow == 0 &&
4188
!pBNS->edge[eNPlusEdge].forbidden &&
4189
at2[k=at2[j].neighbor[at2[j].neighbor[0]==i]].valence == 2 &&
4190
at2[k].num_H == 0 &&
4191
at2[k].chem_bonds_valence == 3 &&
4192
pVA[k].cNumValenceElectrons == 5 &&
4193
(eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1)>= 0 && pBNS->edge[eNPlusEdge2].flow == 1 &&
4194
!pBNS->edge[eNPlusEdge2].forbidden &&
4195
pVA[i].cnListIndex > 0 &&
4196
cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) {
4197
/* found N#N(+)-N~ where the last N (at2[k]) may be charged */
4198
pe = pBNS->edge + pBNS->vert[i].iedge[0]; /* N#N(+) triple bond edge */
4201
v2 = pe->neighbor12 ^ v1;
4203
pBNS->vert[v1].st_edge.flow --;
4204
pBNS->vert[v2].st_edge.flow --;
4205
pBNS->tot_st_flow -= 2;
4207
pe->forbidden |= forbidden_edge_mask;
4208
pBNS->edge[eNPlusEdge].forbidden |= forbidden_edge_mask;
4209
pBNS->edge[eNPlusEdge2].forbidden |= forbidden_edge_mask;
4212
if ( !CarbonChargeEdges.num_edges ) {
4213
/* do not let carbon atoms get charged */
4214
AllocEdgeList( &CarbonChargeEdges, INC_EDGE_LIST );
4215
if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) {
4219
SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
4221
ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
4222
&nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
4224
if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
4225
vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= 0 ) {
4226
ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
4228
*pnTotalDelta += ret;
4230
/* fix charges on N(-)=N(+)=N- */
4231
if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) {
4234
if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) {
4239
pBNS->vert[v1].st_edge.flow ++;
4240
pBNS->vert[v2].st_edge.flow ++;
4241
pBNS->tot_st_flow += 2;
4244
RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
4246
pe->forbidden &= inv_forbidden_edge_mask;
4247
pBNS->edge[eNPlusEdge].forbidden &= inv_forbidden_edge_mask;
4248
pBNS->edge[eNPlusEdge2].forbidden &= inv_forbidden_edge_mask;
4252
/* 2nd attempt: take care of N#N(+)-N=-...=N-N(-) */
4253
/* This would produce nDeltaCharge >= 2 */
4255
AllChargeEdges.num_edges = 0;
4256
AllNNNTermAtoms.num_edges = 0;
4257
NNNChargeEdges.num_edges = 0;
4258
AllNIIIChargeEdges.num_edges = 0;
4260
for ( i = 0; i < num_at && 0 <= ret; i ++ ) {
4261
if ( (eNMinusEdge = pVA[i].nCMinusGroupEdge - 1)>= 0 &&
4262
!pBNS->edge[eNMinusEdge].forbidden ) {
4263
if ( ret = AddToEdgeList( &AllChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) {
4269
if ( (eNPlusEdge = pVA[i].nCPlusGroupEdge - 1)>= 0 &&
4270
!pBNS->edge[eNPlusEdge].forbidden ) {
4271
if ( ret = AddToEdgeList( &AllChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) {
4274
if ( pVA[i].cNumValenceElectrons == 5 && at2[i].valence == 3 && at2[i].chem_bonds_valence == 3) {
4275
if ( ret = AddToEdgeList( &AllNIIIChargeEdges, eNPlusEdge, INC_EDGE_LIST ) ) {
4280
if ( pVA[i].cNumValenceElectrons == 5 && pVA[i].cPeriodicRowNumber == 1 &&
4281
NO_VERTEX != (eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge )) &&
4282
pBNS->edge[eNFlowerEdge1].flow == 0 &&
4283
( ret = AddToEdgeList( &AllChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) ) {
4290
if ( 0 <= eNMinusEdge &&
4291
at2[i].valence == 1 &&
4292
at2[i].num_H == 0 &&
4293
at2[i].radical == 0 &&
4294
pVA[i].cNumValenceElectrons == 5 && /* terminal N# */
4296
at2[j=at2[i].neighbor[0]].valence == 2 &&
4297
at2[j].num_H == 0 &&
4298
at2[j].radical == 0 &&
4299
pVA[j].cNumValenceElectrons == 5 &&
4300
(eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1)>= 0 &&
4301
(eNPlusEdge1 = pVA[j].nCPlusGroupEdge - 1)>= 0 &&
4302
!pBNS->edge[eNMinusEdge1].forbidden &&
4303
!pBNS->edge[eNPlusEdge1].forbidden &&
4305
at2[k=at2[j].neighbor[at2[j].neighbor[0]==i]].valence == 2 &&
4306
at2[k].num_H == 0 &&
4307
at2[k].radical == 0 &&
4308
pVA[k].cNumValenceElectrons == 5 &&
4309
(eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1)>= 0 &&
4310
(eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1)>= 0 &&
4311
!pBNS->edge[eNMinusEdge2].forbidden &&
4312
!pBNS->edge[eNPlusEdge2].forbidden &&
4314
pVA[i].cnListIndex > 0 &&
4315
cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) {
4316
/* found N#N(+)-N~ or N(-)=N-N= where the last N (at2[k]) may be charged */
4318
/* 1. N(-)=N(+)=N- */
4319
if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */
4320
pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
4321
pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) {
4322
continue; /* already good */
4324
/* accumulate terminal atoms of all other NNN */
4325
if ( ret = AddToEdgeList( &AllNNNTermAtoms, i, INC_EDGE_LIST ) ) {
4329
if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */
4330
pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
4331
pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) {
4332
/* unfix (-) edge on terminal N# */
4333
if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) {
4339
if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */
4340
pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 1 && /* N */
4341
pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) {
4342
/* unfix (+) edge on middle N */
4343
if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) {
4348
/* 4. N#N(+)-N(-)- */
4349
if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */
4350
pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
4351
pBNS->edge[eNMinusEdge2].flow == 1 && pBNS->edge[eNPlusEdge2].flow == 1 /* N(-) */ ) {
4352
/* unfix (-) edge on the 1st and 3rd N */
4353
if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) {
4356
if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) {
4361
/* 5. N#N(+)-N(+)# */
4362
if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */
4363
pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
4364
pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 0 /* N(+) */ ) {
4365
/* unfix (-) edge on the 1st and (+) edge on the 3rd N */
4366
if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) {
4369
if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) {
4376
/* try to fix each NNN */
4377
for ( n = AllNNNTermAtoms.num_edges-1; 0 <= n; n -- ) {
4378
i = AllNNNTermAtoms.pnEdges[n];
4379
eNMinusEdge = pVA[i].nCMinusGroupEdge - 1;
4380
/*eNPlusEdge = pVA[i].nCPlusGroupEdge - 1;*/
4381
j=at2[i].neighbor[0];
4382
eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1;
4383
eNPlusEdge1 = pVA[j].nCPlusGroupEdge - 1;
4384
k=at2[j].neighbor[at2[j].neighbor[0]==i];
4385
eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1;
4386
eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1;
4387
/*SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );*/
4388
/* 1. N(-)=N(+)=N- */
4389
if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */
4390
pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
4391
pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) {
4393
RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge );
4394
RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge1 );
4395
RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge1 );
4396
RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge2 );
4397
RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge2 );
4400
} else /* 2. N#N(+)-N= */
4401
if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */
4402
pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
4403
pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) {
4404
/* decrement triple bond on terminal N# */
4405
pe = pBNS->edge + pBNS->vert[i].iedge[0];
4408
if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */
4409
pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 1 && /* N */
4410
pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) {
4411
/* decrement flow on (+) charge edge of the middle =N- */
4412
pe = pBNS->edge + eNPlusEdge1;
4414
/* 4. N#N(+)-N(-)- */
4415
if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */
4416
pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
4417
pBNS->edge[eNMinusEdge2].flow == 1 && pBNS->edge[eNPlusEdge2].flow == 1 /* N(-) */ ) {
4418
/* decrement triple bond on terminal N# */
4419
pe = pBNS->edge + pBNS->vert[i].iedge[0];
4421
/* 5. N#N(+)-N(+)# */
4422
if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */
4423
pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
4424
pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 0 /* N(+) */ ) {
4425
/* decrement triple bond on terminal N# */
4426
pe = pBNS->edge + pBNS->vert[i].iedge[0];
4428
pe = NULL; /* unknown case */
4431
SetForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );
4432
RemoveForbiddenEdgeMask( pBNS, &NNNChargeEdges, forbidden_edge_mask );
4435
v2 = pe->neighbor12 ^ v1;
4437
pBNS->vert[v1].st_edge.flow --;
4438
pBNS->vert[v2].st_edge.flow --;
4439
pBNS->tot_st_flow -= 2;
4441
pe->forbidden |= forbidden_edge_mask;
4443
ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
4444
&nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
4446
if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
4447
vPathEnd == v2 && vPathStart == v1) /*&& nDeltaCharge <= 2*/ ) {
4448
ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
4450
*pnTotalDelta += ret;
4452
/* fix charges on N(-)=N(+)=N- */
4453
RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge );
4454
RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge1 );
4455
RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge1 );
4456
RemoveFromEdgeListByValue( &NNNChargeEdges, eNMinusEdge2 );
4457
RemoveFromEdgeListByValue( &NNNChargeEdges, eNPlusEdge2 );
4460
pBNS->vert[v1].st_edge.flow ++;
4461
pBNS->vert[v2].st_edge.flow ++;
4462
pBNS->tot_st_flow += 2;
4465
pe->forbidden &= inv_forbidden_edge_mask;
4466
RemoveForbiddenEdgeMask( pBNS, &AllChargeEdges, forbidden_edge_mask );
4473
AllChargeEdges.num_edges = 0;
4474
AllNNNTermAtoms.num_edges = 0;
4475
NNNChargeEdges.num_edges = 0;
4477
for ( i = 0; i < num_at && 0 <= ret; i ++ ) {
4479
eNMinusEdge = pVA[i].nCMinusGroupEdge - 1;
4480
/*eNPlusEdge = pVA[i].nCPlusGroupEdge - 1;*/
4482
if ( 0 <= eNMinusEdge &&
4483
at2[i].valence == 1 &&
4484
at2[i].num_H == 0 &&
4485
at2[i].radical == 0 &&
4486
pVA[i].cNumValenceElectrons == 5 && /* terminal N# */
4488
at2[j=at2[i].neighbor[0]].valence == 2 &&
4489
at2[j].num_H == 0 &&
4490
at2[j].radical == 0 &&
4491
pVA[j].cNumValenceElectrons == 5 &&
4492
(eNMinusEdge1 = pVA[j].nCMinusGroupEdge - 1)>= 0 &&
4493
(eNPlusEdge1 = pVA[j].nCPlusGroupEdge - 1)>= 0 &&
4494
!pBNS->edge[eNMinusEdge1].forbidden &&
4495
!pBNS->edge[eNPlusEdge1].forbidden &&
4497
at2[k=at2[j].neighbor[at2[j].neighbor[0]==i]].valence == 2 &&
4498
at2[k].num_H == 0 &&
4499
at2[k].radical == 0 &&
4500
pVA[k].cNumValenceElectrons == 5 &&
4501
(eNMinusEdge2 = pVA[k].nCMinusGroupEdge - 1)>= 0 &&
4502
(eNPlusEdge2 = pVA[k].nCPlusGroupEdge - 1)>= 0 &&
4503
!pBNS->edge[eNMinusEdge2].forbidden &&
4504
!pBNS->edge[eNPlusEdge2].forbidden &&
4506
pVA[i].cnListIndex > 0 &&
4507
cnList[pVA[i].cnListIndex-1].bits == cn_bits_MN ) {
4509
/* found N#N(+)-N~ or N(-)=N-N= where the last N (at2[k]) may be charged */
4510
NNNChargeEdges.num_edges = 0;
4512
eNFlowerEdge1 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge1 );
4513
eNFlowerEdge2 = GetChargeFlowerUpperEdge( pBNS, pVA, eNPlusEdge2 );
4515
/* 1. N(-)=N(+)=N- */
4516
if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */
4517
pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
4518
pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) {
4519
/* fix charges on N(-)=N(+)=N- */
4520
if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) {
4523
if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) {
4526
if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) {
4529
if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) {
4532
if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) {
4535
continue; /* already good */
4538
if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */
4539
pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
4540
pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) {
4541
/* unfix (-) edge on terminal N# */
4542
if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) {
4545
if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) {
4548
if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) {
4551
if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) {
4554
pe = pBNS->edge + pBNS->vert[i].iedge[0];
4555
nDeltaChargeMax = 0;
4556
nDeltaChargeMax = (num_failed && !num_success && pStruct->nNumRemovedProtonsMobHInChI > 0)? 2 : 0;
4559
if ( pBNS->edge[eNMinusEdge].flow == 1 && /* N(-) */
4560
pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 1 && /* N */
4561
pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 1 /* N */ ) {
4562
/* unfix (+) edge on middle N */
4563
if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) {
4566
if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) {
4569
if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) {
4572
if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) {
4575
if ( NO_VERTEX != eNFlowerEdge1 &&
4576
( ret = AddToEdgeList( &NNNChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) ) {
4579
/* decrement flow on (+) charge edge of the middle =N- */
4580
pe = pBNS->edge + eNPlusEdge1;
4581
nDeltaChargeMax = 2;
4583
/* 4. N#N(+)-N(-)- */
4584
if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */
4585
pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
4586
pBNS->edge[eNMinusEdge2].flow == 1 && pBNS->edge[eNPlusEdge2].flow == 1 /* N(-) */ ) {
4587
/* unfix (-) edge on the 1st and 3rd N */
4588
if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) {
4591
if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) {
4594
if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) {
4597
/* decrement triple bond on terminal N# */
4598
pe = pBNS->edge + pBNS->vert[i].iedge[0];
4599
nDeltaChargeMax = 0;
4601
/* 5. N#N(+)-N(+)# */
4602
if ( pBNS->edge[eNMinusEdge].flow == 0 && /* N */
4603
pBNS->edge[eNMinusEdge1].flow == 0 && pBNS->edge[eNPlusEdge1].flow == 0 && /* N(+) */
4604
pBNS->edge[eNMinusEdge2].flow == 0 && pBNS->edge[eNPlusEdge2].flow == 0 /* N(+) */ ) {
4605
/* unfix (-) edge on the 1st and (+) edge on the 3rd N */
4606
if ( ret = AddToEdgeList( &NNNChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) {
4609
if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) {
4612
if ( ret = AddToEdgeList( &NNNChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) {
4615
/* decrement triple bond on terminal N# */
4616
pe = pBNS->edge + pBNS->vert[i].iedge[0];
4617
nDeltaChargeMax = 0;
4622
if ( NO_VERTEX != eNFlowerEdge1 && !pBNS->edge[eNFlowerEdge1].flow ) {
4623
if ( ret = AddToEdgeList( &NNNChargeEdges, eNFlowerEdge1, INC_EDGE_LIST ) ) {
4627
if ( NO_VERTEX != eNFlowerEdge2 && !pBNS->edge[eNFlowerEdge2].flow ) {
4628
if ( ret = AddToEdgeList( &NNNChargeEdges, eNFlowerEdge2, INC_EDGE_LIST ) ) {
4634
v2 = pe->neighbor12 ^ v1;
4636
pBNS->vert[v1].st_edge.flow --;
4637
pBNS->vert[v2].st_edge.flow --;
4638
pBNS->tot_st_flow -= 2;
4640
pe->forbidden |= forbidden_edge_mask;
4642
if ( !CarbonChargeEdges.num_edges ) {
4643
/* do not let carbon atoms get charged */
4644
AllocEdgeList( &CarbonChargeEdges, INC_EDGE_LIST );
4645
if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) {
4649
SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
4651
SetForbiddenEdgeMask( pBNS, &NNNChargeEdges, forbidden_edge_mask );
4653
ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
4654
&nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
4656
if ( ret == 1 && (vPathEnd == v1 && vPathStart == v2 ||
4657
vPathEnd == v2 && vPathStart == v1) && nDeltaCharge <= nDeltaChargeMax ) {
4658
ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
4660
*pnTotalDelta += ret;
4662
/* fix charges on N(-)=N(+)=N- */
4663
if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge, INC_EDGE_LIST ) ) {
4666
if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge1, INC_EDGE_LIST ) ) {
4669
if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge1, INC_EDGE_LIST ) ) {
4672
if ( ret = AddToEdgeList( &CarbonChargeEdges, eNPlusEdge2, INC_EDGE_LIST ) ) {
4675
if ( ret = AddToEdgeList( &CarbonChargeEdges, eNMinusEdge2, INC_EDGE_LIST ) ) {
4680
pBNS->vert[v1].st_edge.flow ++;
4681
pBNS->vert[v2].st_edge.flow ++;
4682
pBNS->tot_st_flow += 2;
4685
RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
4686
RemoveForbiddenEdgeMask( pBNS, &NNNChargeEdges, forbidden_edge_mask );
4687
pe->forbidden &= inv_forbidden_edge_mask;
4688
/*pBNS->edge[eNPlusEdge].forbidden &= inv_forbidden_edge_mask;*/ /* BC: array index out of range */
4691
RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
4696
AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE );
4697
AllocEdgeList( &AllChargeEdges, EDGE_LIST_FREE );
4698
AllocEdgeList( &NNNChargeEdges, EDGE_LIST_FREE );
4699
AllocEdgeList( &CurNNNChargeEdges, EDGE_LIST_FREE );
4700
AllocEdgeList( &AllNNNTermAtoms, EDGE_LIST_FREE );
4701
AllocEdgeList( &AllNIIIChargeEdges, EDGE_LIST_FREE );
4703
#undef INC_EDGE_LIST
4705
/******************************************************************************************************/
4706
int EliminateNitrogen5Val3Bonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
4707
inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
4708
int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
4710
int i, j, k, bForbiddenCarbonCharges, ret2, ret;
4711
int num_at = pStruct->num_atoms;
4712
int num_deleted_H = pStruct->num_deleted_H;
4713
int len_at = num_at + num_deleted_H;
4714
int inv_forbidden_edge_mask = ~forbidden_edge_mask;
4715
EDGE_LIST CarbonChargeEdges;
4718
bForbiddenCarbonCharges = 0;
4719
AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR );
4721
memcpy( at2, at, len_at*sizeof(at2[0]));
4723
ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
4729
/* forbid creation of other N(V) atoms */
4730
/* fix single bonds to metals */
4731
for ( i = 0; i < num_at; i ++ ) {
4732
if ( pVA[i].cNumValenceElectrons == 5 &&
4733
0 <= (k = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 )) &&
4734
1 == pBNS->edge[k].flow) {
4735
pBNS->edge[k].forbidden |= forbidden_edge_mask;
4737
if ( pVA[i].cMetal ) {
4738
for ( j = 0; j < at2[i].valence; j ++ ) {
4739
if ( BOND_TYPE_SINGLE == (at2[i].bond_type[j] & BOND_TYPE_MASK) ) {
4740
pBNS->edge[pBNS->vert[i].iedge[j]].forbidden |= forbidden_edge_mask;
4746
/*------------------------------------------------------------------------------
4747
(+) single line => flow = 0 (+)-(Y)=(+)super
4748
01 // double line => flow = 1 fix-> 01 // <-- fix
4750
\\ // edge eij connects vertices i < j: \ / 02
4751
12 2 02 <--- edge number: e02 connects vertices v0 12 2(..) <- double 'radical'
4753
=N= vertex N has number i =N=
4755
--------------------------------------------------------------------------------*/
4756
for ( i = 0; i < num_at; i ++ ) {
4757
if ( pVA[i].cNumValenceElectrons == 5 && at2[i].valence == 3 &&
4758
at2[i].chem_bonds_valence == 5 && !at2[i].charge && !at2[i].radical &&
4759
!(at2[i].endpoint || pStruct->endpoint && pStruct->endpoint[i]) && pVA[i].cnListIndex > 0 &&
4760
cnList[pVA[i].cnListIndex-1].bits == cn_bits_NPN &&
4761
pVA[i].nCPlusGroupEdge > 0 ) {
4763
Vertex v, v0 = NO_VERTEX, v1 = NO_VERTEX, v2 = NO_VERTEX;
4764
EdgeIndex iePlus, ie, ie12 = NO_VERTEX, ie02, ie01;
4765
BNS_VERTEX *pv0, *pv1, *pv2 = NULL;
4766
BNS_EDGE *pePlus, *pe, *pe12 = NULL, *pe02 = NULL, *pe01 = NULL;
4767
Vertex vPathStart, vPathEnd;
4769
int nDeltaH, nDeltaCharge, nNumVisitedAtoms;
4771
iePlus = pVA[i].nCPlusGroupEdge - 1;
4772
pePlus = pBNS->edge + iePlus;
4774
v0 = IS_BNS_VT_C_GR( pBNS->vert[pePlus->neighbor1].type )?
4775
(pePlus->neighbor1 ^ pePlus->neighbor12) : pePlus->neighbor1;
4776
pv0 = pBNS->vert + v0;
4777
for ( j = 0; j < pv0->num_adj_edges; j ++ ) {
4779
if ( ie == iePlus ) {
4782
pe = pBNS->edge + ie;
4783
if ( pe->flow == 1 && v2 == NO_VERTEX ) {
4784
/* 0 - 2, edge 02 */
4785
v2 = pe->neighbor12 ^ v0;
4786
pv2 = pBNS->vert + v2;
4790
if ( pe->flow == 0 && v1 == NO_VERTEX ) {
4791
/* 0 - 1, edge 01 */
4792
v1 = pe->neighbor12 ^ v0;
4793
pv1 = pBNS->vert + v2;
4801
if ( v1 == NO_VERTEX || v2 == NO_VERTEX ) {
4805
for ( j = 0; j < pv2->num_adj_edges; j ++ ) {
4807
pe = pBNS->edge + ie;
4808
v = pe->neighbor12 ^ v2;
4809
if ( v == v0 || v == i ) {
4812
if ( v == v1 && pe->flow == 1 ) {
4813
/* 1 - 2, edge 12 */
4821
if ( ie12 == NO_VERTEX ) {
4825
/* rearrange cap and flow, forbid 2 edges */
4829
pv2->st_edge.flow -= 2;
4830
pBNS->tot_st_flow -= 2;
4831
pePlus->forbidden |= forbidden_edge_mask;
4832
pe01->forbidden |= forbidden_edge_mask;
4834
if ( !bForbiddenCarbonCharges ) {
4835
if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) {
4838
bForbiddenCarbonCharges = 1;
4842
ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
4843
&nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
4844
if ( ret == 1 && vPathEnd == v2 && vPathStart == v2 && nDeltaCharge <= (pVA[i].cNumBondsToMetal? 2:0) ) {
4845
ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
4850
pv2->st_edge.flow += 2;
4851
pBNS->tot_st_flow += 2;
4853
pePlus->forbidden &= inv_forbidden_edge_mask;
4854
pe01->forbidden &= inv_forbidden_edge_mask;
4860
memcpy( at2, at, len_at*sizeof(at2[0]));
4862
ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
4871
/* allow creation of other N(V) atoms */
4872
for ( i = 0; i < num_at; i ++ ) {
4873
if ( pVA[i].cNumValenceElectrons == 5 &&
4874
0 <= (k = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 )) &&
4875
1 == pBNS->edge[k].flow && (pBNS->edge[k].forbidden & forbidden_edge_mask) ) {
4876
pBNS->edge[k].forbidden &= inv_forbidden_edge_mask;
4878
if ( pVA[i].cMetal ) {
4879
for ( j = 0; j < at2[i].valence; j ++ ) {
4880
if ( BOND_TYPE_SINGLE == (at2[i].bond_type[j] & BOND_TYPE_MASK) ) {
4881
pBNS->edge[pBNS->vert[i].iedge[j]].forbidden &= inv_forbidden_edge_mask;
4886
RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
4887
AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE );
4890
/******************************************************************************************************/
4891
int Convert_SIV_to_SVI(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
4892
inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
4893
int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
4895
int i, j, k, neigh, bForbiddenCarbonCharges, nFlowerEdge, delta, ret2, ret;
4896
int num_at = pStruct->num_atoms;
4897
int num_deleted_H = pStruct->num_deleted_H;
4898
int len_at = num_at + num_deleted_H;
4899
int inv_forbidden_edge_mask = ~forbidden_edge_mask;
4900
EDGE_LIST CarbonChargeEdges, FlowerEdgesList;
4903
bForbiddenCarbonCharges = 0;
4904
AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR );
4905
AllocEdgeList( &FlowerEdgesList, EDGE_LIST_CLEAR );
4907
memcpy( at2, at, len_at*sizeof(at2[0]));
4909
ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
4915
/* forbid creation of other S(IV) atoms */
4916
/* fix single bonds to metals and (N(IV), flow=1), (S(IV), flow=0) */
4917
for ( i = 0; i < num_at; i ++ ) {
4918
if ( (pVA[i].cNumValenceElectrons == 5 /* N(IV)*/ || pVA[i].cNumValenceElectrons == 6 /* S(VI)*/) &&
4919
0 <= (k = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 )) &&
4920
!pBNS->edge[k].forbidden &&
4921
6 == pVA[i].cNumValenceElectrons + pBNS->edge[k].flow ) {
4923
pBNS->edge[k].forbidden |= forbidden_edge_mask;
4924
if ( ret = AddToEdgeList( &FlowerEdgesList, k, 64 )) {
4928
if ( pVA[i].cMetal ) {
4929
for ( j = 0; j < at2[i].valence; j ++ ) {
4930
if ( BOND_TYPE_SINGLE == (at2[i].bond_type[j] & BOND_TYPE_MASK) ) {
4932
pBNS->edge[k=pBNS->vert[i].iedge[j]].forbidden |= forbidden_edge_mask;
4933
if ( ret = AddToEdgeList( &FlowerEdgesList, k, 64 )) {
4939
/* fix bonds to neighbors of S(IV) if they are not O,S,Se,Te with 2 or more bonds */
4940
/* exactly same if(..) as below */
4941
if ( pVA[i].cNumValenceElectrons == 6 && at2[i].valence == 4 &&
4942
at2[i].chem_bonds_valence == 4 && !at2[i].charge && !at2[i].radical &&
4943
!at2[i].endpoint && pVA[i].cnListIndex > 0 &&
4944
cnList[pVA[i].cnListIndex-1].bits == cn_bits_NPN &&
4945
0 <= (nFlowerEdge = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 ) ) &&
4946
pBNS->edge[nFlowerEdge].flow > 0 ) {
4948
for ( j = 0; j < at2[i].valence; j ++ ) {
4949
neigh = at2[i].neighbor[j];
4950
if ( pVA[neigh].cNumValenceElectrons != 6 && at2[neigh].valence > 1 ) {
4951
k = pBNS->vert[i].iedge[j];
4952
if ( !pBNS->edge[k].forbidden ) {
4953
if ( ret = AddToEdgeList( &FlowerEdgesList, k, 64 )) {
4956
pBNS->edge[k].forbidden |= forbidden_edge_mask;
4962
/*------------------------------------------------------------------------------
4963
example: struct #301,
4964
| | disconnected porphyrin with four -SO3(-)
4968
-------------------------------------------------------------------------------*/
4970
/*-------------------------------------------------------------------------------
4971
found: super(+)=(Y) super(+)=(Y)
4973
(+) single line => flow = 0 (+) (+)
4974
01 // double line => flow = 1 fix-> 01 // 01 //
4975
1 === 0 triple line => flow = 2 (.)1 --- 0(.) ---> 1 --- 0
4976
\ / edge eij connects vertices i<j: \ / 02 run \\ // 02
4977
12 2 02 <--- edge number: e02 connects 12 2 BFS 12 2
4978
||| vertices v0 and v2 ||| |
4979
-S- vertex S has number i -S- =S=
4981
--------------------------------------------------------------------------------*/
4982
for ( i = 0; i < num_at; i ++ ) {
4983
if ( pVA[i].cNumValenceElectrons == 6 && at2[i].valence == 4 &&
4984
at2[i].chem_bonds_valence == 4 && !at2[i].charge && !at2[i].radical &&
4985
!at2[i].endpoint && pVA[i].cnListIndex > 0 &&
4986
cnList[pVA[i].cnListIndex-1].bits == cn_bits_NPN &&
4987
/* 01 is nFlowerEdge */
4988
0 <= (nFlowerEdge = GetChargeFlowerUpperEdge( pBNS, pVA, pVA[i].nCPlusGroupEdge-1 ) ) &&
4989
pBNS->edge[nFlowerEdge].flow > 0 ) {
4991
Vertex v1 = NO_VERTEX, v2 = NO_VERTEX;
4992
BNS_VERTEX *pv1, *pv2;
4994
Vertex vPathStart, vPathEnd;
4996
int nDeltaH, nDeltaCharge, nNumVisitedAtoms;
4998
if ( !bForbiddenCarbonCharges ) {
4999
if ( 0 > (ret = ForbidCarbonChargeEdges( pBNS, pTCGroups, &CarbonChargeEdges, forbidden_edge_mask ))) {
5002
bForbiddenCarbonCharges = 1;
5006
pe = pBNS->edge + nFlowerEdge; /* edge 01 */
5007
pv1 = pBNS->vert + (v1 = pe->neighbor1); /* vertex 0 */
5008
pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); /* vertex 1 */
5010
pe->forbidden |= forbidden_edge_mask;
5012
pv1->st_edge.flow -= delta;
5013
pv2->st_edge.flow -= delta;
5014
pBNS->tot_st_flow -= 2*delta;
5016
ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
5017
&nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
5019
(vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) &&
5020
nDeltaCharge <= (pVA[i].cNumBondsToMetal? 2:0) ) {
5021
ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
5023
pe->forbidden &= inv_forbidden_edge_mask;
5025
pv1->st_edge.flow += delta;
5026
pv2->st_edge.flow += delta;
5027
pBNS->tot_st_flow += 2*delta;
5033
memcpy( at2, at, len_at*sizeof(at2[0]));
5035
ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
5040
/* store the fixed edge to unfix it upon exit */
5041
if ( ret = AddToEdgeList( &FlowerEdgesList, nFlowerEdge, 64 )) {
5048
RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
5049
AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE );
5050
RemoveForbiddenEdgeMask( pBNS, &FlowerEdgesList, forbidden_edge_mask );
5051
AllocEdgeList( &FlowerEdgesList, EDGE_LIST_FREE );
5054
/******************************************************************************************************
5061
*******************************************************************************************************/
5062
int PlusFromDB_N_DB_O_to_Metal(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
5063
inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
5064
int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
5066
int i, j, k, n, bForbiddenCarbonCharges, delta, ret2, ret, num_NO, num_M;
5067
int num_at = pStruct->num_atoms;
5068
int num_deleted_H = pStruct->num_deleted_H;
5069
int len_at = num_at + num_deleted_H;
5070
int inv_forbidden_edge_mask = ~forbidden_edge_mask;
5071
EDGE_LIST CarbonChargeEdges, NO_ChargeEdgeList, NO_EdgeList;
5074
BNS_VERTEX *pv1, *pv2;
5076
Vertex vPathStart, vPathEnd;
5078
int nDeltaH, nDeltaCharge, nNumVisitedAtoms;
5081
if ( !pTCGroups->num_metal_atoms )
5085
bForbiddenCarbonCharges = 0;
5086
AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_CLEAR ); /* all charges */
5087
AllocEdgeList( &NO_ChargeEdgeList, EDGE_LIST_CLEAR ); /* charges to be changed */
5088
AllocEdgeList( &NO_EdgeList, EDGE_LIST_CLEAR ); /* N(+)=O edges */
5090
memcpy( at2, at, len_at*sizeof(at2[0]));
5092
ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
5098
/* forbid creation of other S(IV) atoms */
5099
/* fix single bonds to metals and (N(IV), flow=1), (S(IV), flow=0) */
5100
for ( i = 0; i < num_at; i ++ ) {
5101
if ( !pVA[i].cMetal ) {
5102
if ( (k = pVA[i].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) {
5103
if ( ret = AddToEdgeList( &CarbonChargeEdges, k, 64 ) ) {
5107
if ( (k = pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) {
5108
if ( ret = AddToEdgeList( &CarbonChargeEdges, k, 64 ) ) {
5116
if ( pVA[i].cMetal ) {
5117
if ( (k = pVA[i].nCPlusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) {
5118
if ( ret = AddToEdgeList( &NO_ChargeEdgeList, k, 64 ) ) {
5122
if ( (k = pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden ) {
5123
if ( ret = AddToEdgeList( &NO_ChargeEdgeList, k, 64 ) ) {
5129
if ( !pVA[i].cMetal &&
5130
pVA[i].cNumValenceElectrons == 6 &&
5131
at2[i].charge == 0 && !at2[i].num_H &&
5132
1 == at2[i].valence && 2 == at2[i].chem_bonds_valence &&
5133
pVA[j=at2[i].neighbor[0]].cNumValenceElectrons == 5 &&
5134
at2[j].charge == 1 && !at2[j].num_H &&
5135
2 == at2[j].valence && 4 == at2[j].chem_bonds_valence ) {
5137
if ( (k = pVA[i].nCMinusGroupEdge-1) >= 0 && !pBNS->edge[k].forbidden /* O */ &&
5138
(n = pVA[j].nCPlusGroupEdge -1) >= 0 && !pBNS->edge[j].forbidden /* N */ ) {
5139
if ( (ret = AddToEdgeList( &NO_ChargeEdgeList, k, 64 ) ) ||
5140
(ret = AddToEdgeList( &NO_ChargeEdgeList, n, 64 ) ) ) {
5143
k = pBNS->vert[i].iedge[0]; /* N(+)=O bond */
5144
if ( !pBNS->edge[k].forbidden ) {
5145
if ( ret = AddToEdgeList( &NO_EdgeList, k, 64 ) ) {
5153
if ( num_M && num_NO ) {
5154
SetForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
5155
SetForbiddenEdgeMask( pBNS, &NO_EdgeList, forbidden_edge_mask );
5156
RemoveForbiddenEdgeMask( pBNS, &NO_ChargeEdgeList, forbidden_edge_mask );
5157
/* now only N(+), O(-) and metal charges are allowed to change */
5158
for ( i = 0; i < NO_EdgeList.num_edges; i ++ ) {
5159
k = NO_EdgeList.pnEdges[i];
5161
pe = pBNS->edge + k; /* edge N(+)=O */
5162
pv1 = pBNS->vert + (v1 = pe->neighbor1); /* vertex 0 */
5163
pv2 = pBNS->vert + (v2 = pe->neighbor12 ^ v1); /* vertex 1 */
5166
pv1->st_edge.flow -= delta;
5167
pv2->st_edge.flow -= delta;
5168
pBNS->tot_st_flow -= 2*delta;
5170
ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
5171
&nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
5173
(vPathEnd == v1 && vPathStart == v2 || vPathEnd == v2 && vPathStart == v1) &&
5174
nDeltaCharge == 0 ) {
5175
ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
5177
pe->forbidden &= inv_forbidden_edge_mask;
5179
pv1->st_edge.flow += delta;
5180
pv2->st_edge.flow += delta;
5181
pBNS->tot_st_flow += 2*delta;
5189
RemoveForbiddenEdgeMask( pBNS, &CarbonChargeEdges, forbidden_edge_mask );
5190
RemoveForbiddenEdgeMask( pBNS, &NO_EdgeList, forbidden_edge_mask );
5191
AllocEdgeList( &CarbonChargeEdges, EDGE_LIST_FREE );
5192
AllocEdgeList( &NO_EdgeList, EDGE_LIST_FREE );
5193
AllocEdgeList( &NO_ChargeEdgeList, EDGE_LIST_FREE );
5196
/******************************************************************************************************/
5197
int MoveMobileHToAvoidFixedBonds(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
5198
inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
5199
int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
5202
int num_at = pStruct->num_atoms;
5203
int num_deleted_H = pStruct->num_deleted_H;
5204
int len_at = num_at + num_deleted_H;
5205
int nNumFixedEdges, nNumAdjEdges;
5209
if ( pTCGroups->num_tgroups ) {
5210
memcpy( at2, at, len_at*sizeof(at2[0]));
5212
ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
5218
#if ( FIND_RING_SYSTEMS == 1 )
5219
ret2 = MarkRingSystemsInp( at2, num_at, 0 );
5225
/* --- forbidden edges --- */
5226
ret2 = SetForbiddenEdges( pBNS, at2, num_at, forbidden_edge_mask );
5230
nNumFixedEdges = ret2;
5231
ret = AdjustTgroupsToForbiddenEdges2( pBNS, at2, pVA, num_at, forbidden_edge_mask );
5234
pBNS->edge_forbidden_mask |= forbidden_edge_mask;
5235
ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
5240
*pnTotalDelta += ret;
5243
if ( nNumFixedEdges || nNumAdjEdges ) {
5244
/* removes this edge mask from ALL edges */
5245
RemoveForbiddenBondFlowBits( pBNS, forbidden_edge_mask );
5253
/******************************************************************************************************/
5254
/* Find and eliminate cases when Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH */
5255
int RemoveRadFromMobileHEndpoint(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
5256
inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
5257
int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
5259
int i, num_fixes, tot_num_fixes = 0;
5262
int num_at = pStruct->num_atoms;
5263
int num_deleted_H = pStruct->num_deleted_H;
5264
int len_at = num_at + num_deleted_H;
5266
int itg, j, k, n, m;
5267
Vertex vtg1, endpoint0=NO_VERTEX, endpoint1, endpoint2, centerpoint;
5268
Vertex centerpoint_found=NO_VERTEX;
5269
BNS_VERTEX *ptg1, *pEndp0=NULL, *pEndp1, *pEndp2, *pCentp, *pCentp_found, *pEndp2_found=NULL;
5270
BNS_EDGE *etg0=NULL, *etg1, *etg2, *ecp0, *ecp1, *ecp2;
5271
BNS_EDGE *etg1_found=NULL, *ecp0_found=NULL, *ecp1_found=NULL, *ecp2_found=NULL;
5272
int tgroup_number, num_endpoints;
5276
memcpy( at2, at, len_at*sizeof(at2[0]));
5278
ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
5283
while ( pBNS->tot_st_cap > pBNS->tot_st_flow && pTCGroups->num_tgroups ) {
5285
for ( itg = 0; itg < pTCGroups->num_tgroups; itg ++ ) {
5287
tgroup_number = pTCGroups->pTCG[itg].ord_num;
5288
vtg1 = pTCGroups->pTCG[itg].nVertexNumber; /* taut group vertex index */
5289
ptg1 = pBNS->vert + vtg1; /* taut group vertex */
5290
num_endpoints = pTCGroups->pTCG[itg].num_edges;
5291
for ( i = 0; i < num_endpoints; i ++ ) {
5292
etg0 = pBNS->edge + ptg1->iedge[i]; /* edge from t-group to endpoint */
5293
endpoint0 = etg0->neighbor12 ^ vtg1; /* taut endpoint vertex index */
5294
pEndp0 = pBNS->vert + endpoint0; /* taut endpoint vertex (possible location of mobile H */
5295
if ( pEndp0->st_edge.cap > pEndp0->st_edge.flow ) {
5296
/* radical endpoint1 has been detected */
5297
/* find a 1-3 centerpoint that has two or more endpoints */
5298
/* connected to the t-group vertex by edges with flow>0 and */
5299
/* to the centerpoint by edges with flow = 0 */
5300
/* after that: (1) increment etg1 flow to eliminate radical */
5301
/* (2) increment flow on one of the two other edges to the t-group */
5302
/* (3) increment st_cap on the found centerpoint */
5303
/* (4) rerun the BNS and re-create the structure */
5307
if ( i == num_endpoints ) {
5310
if ( i < num_endpoints ) {
5311
/* tautomeric endpoint found; traverse its t-group edges */
5312
for ( j = 0; j < num_endpoints; j ++ ) {
5314
continue; /* avoid the already found radical endpoint */
5316
etg1 = pBNS->edge + ptg1->iedge[j]; /* another edge from t-group to another endpoinr */
5317
endpoint1 = etg1->neighbor12 ^ vtg1; /* another endpoint vertex index */
5318
pEndp1 = pBNS->vert + endpoint1; /* another endpoint vertex */
5319
if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) {
5320
continue; /* one more radical-endpoint! What is going on here??? */
5322
if ( !etg1->flow ) {
5323
continue; /* avoid enpoints that do not have an attachment */
5325
if ( !(pEndp1->type & BNS_VERT_TYPE_ENDPOINT) ) {
5326
continue; /* should not happen */
5328
/* traverse endpoint1 edges to find a single bond connecting it to the centerpoint */
5329
for ( k = 0; k < at2[endpoint1].valence; k ++ ) {
5330
ecp1 = pBNS->edge + pEndp1->iedge[k];
5334
centerpoint = ecp1->neighbor12 ^ endpoint1;
5335
pCentp = pBNS->vert + centerpoint;
5336
/* traverse centerpoint edges to find a single bond to the 2nd endpoint */
5337
for ( n = 0; n < at2[centerpoint].valence; n ++ ) {
5338
ecp2 = pBNS->edge + pCentp->iedge[n];
5342
endpoint2 = ecp2->neighbor12 ^ centerpoint;
5343
if ( endpoint2 <= endpoint1 || !pVA[endpoint2].nTautGroupEdge ) {
5344
continue; /* don't go back: neighbors are in order of ascending ord. numbers */
5346
pEndp2 = pBNS->vert + endpoint2;
5347
if ( !(pEndp2->type & BNS_VERT_TYPE_ENDPOINT) ) {
5350
etg2 = pBNS->edge + pVA[endpoint2].nTautGroupEdge - 1;
5351
if ( !etg2->flow || (etg2->neighbor12 ^ endpoint2) != vtg1 ) {
5354
/* we have found the path:
5356
etg1 // \ ecp1 etg1 / \\ ecp1
5358
Endp0-----tg1 Centp --> Endp0=====tg1 Centp
5360
radical | etg2 \\ / ecp2 etg2 \\ / ecp2
5364
/* compare centerpoints */
5365
if ( !pCentp_found ||
5366
/* try to avoid carbons */
5367
(pVA[centerpoint].cNumValenceElectrons != 4 ||
5368
pVA[centerpoint].cPeriodicRowNumber != 1) &&
5369
pVA[centerpoint_found].cNumValenceElectrons == 4 &&
5370
pVA[centerpoint_found].cPeriodicRowNumber == 1 ||
5371
/* try a better non-carbon */
5372
(pVA[centerpoint].cNumValenceElectrons != 4 ||
5373
pVA[centerpoint].cPeriodicRowNumber != 1 ) &&
5374
(at[centerpoint].valence > at[centerpoint_found].valence ||
5375
at[centerpoint].valence == at[centerpoint_found].valence &&
5376
at[centerpoint].el_number > at[centerpoint_found].el_number) ) {
5378
pCentp_found = pCentp;
5381
centerpoint_found = centerpoint;
5388
if ( pCentp_found ) {
5391
pEndp0->st_edge.flow ++;
5393
etg1_found->flow --;
5395
ecp1_found->flow ++;
5397
pCentp_found->st_edge.flow ++;
5398
pCentp_found->st_edge.cap ++;
5400
pBNS->tot_st_flow += 2;
5401
pBNS->tot_st_cap += 1;
5402
pCentp_found = NULL;
5404
tot_num_fixes ++; /* #1 Mob-H */
5408
/* 2nd attempt: increment flow in centerpoint---radical_endpint edge */
5409
if ( i < num_endpoints ) {
5410
/* tautomeric endpoint found; traverse its t-group edges */
5411
for ( j = 0; j < num_endpoints; j ++ ) {
5413
continue; /* avoid the found radical endpoint */
5415
etg1 = pBNS->edge + ptg1->iedge[j];
5416
endpoint1 = etg1->neighbor12 ^ vtg1;
5417
pEndp1 = pBNS->vert + endpoint1; /* another endpoint */
5418
if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) {
5419
continue; /* one more radical-endpoint! What is going on here??? */
5421
if ( !etg1->flow ) {
5422
continue; /* avoid enpoints that do not have an attachment */
5424
if ( !(pEndp1->type & BNS_VERT_TYPE_ENDPOINT) ) {
5425
continue; /* should not happen */
5427
/* traverse endpoint1 edges to find the edge connecting it to the centerpoint */
5428
for ( k = 0; k < at2[endpoint1].valence; k ++ ) {
5429
ecp1 = pBNS->edge + pEndp1->iedge[k];
5433
centerpoint = ecp1->neighbor12 ^ endpoint1;
5434
pCentp = pBNS->vert + centerpoint;
5435
if ( pCentp->type & BNS_VERT_TYPE_ENDPOINT ) {
5436
continue; /* do not set another endpoint's valence = an unusual value */
5439
/* traverse centerpoint edges to find edge connecting it to the endpoint0 */
5442
for ( n = 0; n < at2[centerpoint].valence; n ++ ) {
5443
ecp0 = pBNS->edge + pCentp->iedge[n];
5445
endpoint2 = ecp0->neighbor12 ^ centerpoint;
5446
if ( (pBNS->vert[endpoint2].type & BNS_VERT_TYPE_ENDPOINT) ) {
5447
continue; /* ignore endpoint2 if it is tautomeric endpoint */
5449
/* check whether ecp0 is stereogenic: if it is then we cannot decrement its flow */
5450
for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[centerpoint].sb_parity[m]; m ++ ) {
5451
if ( at[centerpoint].sb_ord[m] == n ) {
5452
endpoint2 = NO_VERTEX;
5456
if ( endpoint2 == NO_VERTEX ) {
5459
pEndp2 = pBNS->vert + endpoint2; /* found */
5464
for ( n = 0; n < at[centerpoint].valence; n ++ ) {
5465
ecp0 = pBNS->edge + pCentp->iedge[n];
5469
if ( endpoint0 != (ecp0->neighbor12 ^ centerpoint) ) {
5473
Endp2(not endpoint) Endp2(not radical)
5476
ecp0 || ecp1 ecp0 | ecp1
5477
Endp0----Centp----Endp1 Endp0====Centp----Endp1
5481
etg0 \ / etg1 etg0 \ / etg1
5487
/* compare centerpoints */
5488
if ( !pCentp_found ||
5489
/* try to avoid carbons */
5490
(pVA[centerpoint].cNumValenceElectrons != 4 ||
5491
pVA[centerpoint].cPeriodicRowNumber != 1) &&
5492
pVA[centerpoint_found].cNumValenceElectrons == 4 &&
5493
pVA[centerpoint_found].cPeriodicRowNumber == 1 ||
5494
/* try a better non-carbon */
5495
(pVA[centerpoint].cNumValenceElectrons != 4 ||
5496
pVA[centerpoint].cPeriodicRowNumber != 1 ) &&
5497
(at[centerpoint].valence > at[centerpoint_found].valence ||
5498
at[centerpoint].valence == at[centerpoint_found].valence &&
5499
at[centerpoint].el_number > at[centerpoint_found].el_number) ) {
5501
pCentp_found = pCentp;
5504
centerpoint_found = centerpoint;
5505
pEndp2_found = pEndp2;
5513
if ( pCentp_found ) {
5514
ecp0_found->flow ++;
5515
if ( ecp0_found->cap < ecp0_found->flow ) {
5516
ecp0_found->cap = ecp0_found->flow;
5518
pEndp0->st_edge.flow ++;
5519
if ( pEndp2_found && ecp2_found ) {
5520
ecp2_found->flow --;
5521
pEndp2_found->st_edge.flow --;
5523
/* Endp2 not found */
5524
pCentp_found->st_edge.flow ++;
5525
pCentp_found->st_edge.cap ++;
5526
pBNS->tot_st_flow += 2; /* radical elimination */
5527
pBNS->tot_st_cap += 1;
5530
pCentp_found = NULL;
5532
tot_num_fixes ++; /* #2 Mob-H */
5535
/* 3rd attempt: find =C= and move radical to it */
5536
if ( i < num_endpoints ) {
5537
int jj, delta, bNotFixed = 1;
5538
Vertex vPathStart, vPathEnd, v1, v2;
5539
int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
5540
for ( jj = 0; jj < num_at && bNotFixed; jj ++ ) {
5541
if ( at2[i].endpoint ) {
5544
if ( 2 == at2[jj].valence && pBNS->vert[jj].st_edge.cap == pBNS->vert[jj].st_edge.flow &&
5545
4 == pVA[jj].cNumValenceElectrons &&
5546
!(ecp0 = pBNS->edge + pBNS->vert[jj].iedge[0])->forbidden &&
5547
!(ecp1 = pBNS->edge + pBNS->vert[jj].iedge[1])->forbidden &&
5548
1 == ecp0->flow && 1 == ecp1->flow &&
5549
!at2[(int)at2[i].neighbor[0]].sb_parity[0] &&
5550
!at2[(int)at2[i].neighbor[1]].sb_parity[0] ) {
5551
/* found =C=; make a radical and try to cancel the two radicals */
5552
k = ecp0->neighbor12 ^ jj;
5553
if ( at2[k].endpoint ) {
5555
k = ecp0->neighbor12 ^ jj;
5556
if ( at2[k].endpoint ) {
5561
/* decrement C valence */
5562
pBNS->vert[jj].st_edge.flow -= delta;
5563
pBNS->vert[jj].st_edge.cap -= delta;
5564
/* decrement bond order */
5565
ecp0->flow -= delta;
5566
/* reflect the changes in at2[k] to make it a radical */
5567
pBNS->vert[k].st_edge.flow -= delta;
5568
pBNS->tot_st_cap -= delta;
5569
pBNS->tot_st_flow -= 2*delta;
5574
ret2 = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
5575
&nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
5577
if ( ret2 == 1 && (vPathEnd == v1 && vPathStart == v2 ||
5578
vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) {
5579
ret2 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
5582
tot_num_fixes ++; /* #3 Mob-H */
5583
pBNS->vert[jj].st_edge.cap += delta; /* create radical on =C- */
5584
pBNS->tot_st_cap += delta;
5585
pCentp_found = NULL;
5586
bNotFixed = 0; /* exit from the cycle */
5591
pBNS->vert[jj].st_edge.flow += delta;
5592
pBNS->vert[jj].st_edge.cap += delta;
5593
/* decrement bond order */
5594
ecp0->flow += delta;
5595
/* reflect the changes in at2[k] to make it a radical */
5596
pBNS->vert[k].st_edge.flow += delta;
5597
pBNS->tot_st_cap += delta;
5598
pBNS->tot_st_flow += 2*delta;
5612
ret = tot_num_fixes;
5615
memcpy( at2, at, len_at*sizeof(at2[0]));
5618
/******************************************************************************************************/
5619
/* Find and eliminate cases when Mobile H endpoint has radical on it (typical for wrong P(VI)(=O)3OH */
5620
int RemoveRadFromMobileHEndpointFixH(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
5621
inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
5622
int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
5624
#define IS_C(x) (NO_VERTEX != x && pVA[x].cNumValenceElectrons == 4 && pVA[x].cPeriodicRowNumber == 1)
5625
int i, num_fixes, tot_num_fixes = 0;
5628
int num_at = pStruct->num_atoms;
5629
int num_deleted_H = pStruct->num_deleted_H;
5630
int len_at = num_at + num_deleted_H;
5631
int inv_forbidden_edge_mask = ~forbidden_edge_mask;
5632
EDGE_LIST ChargeEdgeList, BondEdgeList;
5633
int itg, j, k, n, m, num_endp;
5634
Vertex endpoint0=NO_VERTEX, endpoint1, endpoint2=NO_VERTEX, centerpoint;
5635
Vertex centerpoint_found=NO_VERTEX, endpoint2_found=NO_VERTEX;
5636
BNS_VERTEX *pEndp0=NULL, *pEndp1, *pEndp2, *pCentp, *pCentp_found, *pEndp2_found=NULL;
5637
BNS_EDGE *ecp0, *ecp1, *ecp2, *ecp0_found=NULL, *ecp1_found=NULL, *ecp2_found=NULL;
5638
int tgroup_number, num_endpoints;
5642
if ( pStruct->iMobileH != TAUT_NON )
5645
AllocEdgeList( &ChargeEdgeList, EDGE_LIST_CLEAR);
5646
AllocEdgeList( &BondEdgeList, EDGE_LIST_CLEAR);
5648
memcpy( at2, at, len_at*sizeof(at2[0]));
5650
ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
5655
while ( pBNS->tot_st_cap > pBNS->tot_st_flow && pStruct->ti.num_t_groups ) {
5658
for ( itg = 0; itg < pStruct->ti.num_t_groups; iEndpoint += num_endpoints, itg ++ ) {
5660
tgroup_number = pStruct->ti.t_group[itg].nGroupNumber;
5661
num_endpoints = pStruct->ti.t_group[itg].nNumEndpoints;
5662
for ( i = 0; i < num_endpoints; i ++ ) {
5663
endpoint0 = pStruct->ti.nEndpointAtomNumber[iEndpoint+i];
5664
pEndp0 = pBNS->vert + endpoint0; /* taut endpoint vertex (possible location of mobile H */
5665
if ( pEndp0->st_edge.cap > pEndp0->st_edge.flow ) {
5666
/* radical endpoint1 has been detected */
5667
/* find a 1-3 centerpoint that has two or more endpoints */
5668
/* connected to the t-group vertex by edges with flow>0 and */
5669
/* to the centerpoint by edges with flow = 0 */
5670
/* after that: (1) increment etg1 flow to eliminate radical */
5671
/* (2) increment flow on one of the two other edges to the t-group */
5672
/* (3) increment st_cap on the found centerpoint */
5673
/* (4) rerun the BNS and re-create the structure */
5678
/* 2nd attempt: increment flow in centerpoint---radical_endpoint edge */
5679
pCentp_found = NULL;
5680
if ( i < num_endpoints ) {
5681
/* tautomeric endpoint found; traverse its t-group edges */
5682
for ( j = 0; j < num_endpoints; j ++ ) {
5684
continue; /* avoid the found radical endpoint */
5686
endpoint1 = pStruct->ti.nEndpointAtomNumber[iEndpoint+j];
5687
pEndp1 = pBNS->vert + endpoint1; /* another endpoint */
5689
if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) {
5690
continue; /* one more radical-endpoint! What is going on here??? */
5692
if ( !at2[endpoint1].num_H && at2[endpoint1].charge != -1 ) {
5693
continue; /* avoid enpoints that do not have an attachment */
5695
if ( !pStruct->endpoint[endpoint1] ) {
5696
continue; /* should not happen */
5698
/* traverse endpoint1 edges to find the edge connecting it to the centerpoint */
5699
for ( k = 0; k < pEndp1->num_adj_edges; k ++ ) {
5700
ecp1 = pBNS->edge + pEndp1->iedge[k];
5704
centerpoint = ecp1->neighbor12 ^ endpoint1;
5705
if ( centerpoint >= pBNS->num_atoms ) {
5706
break; /* no more edges to atoms */
5708
pCentp = pBNS->vert + centerpoint;
5709
if ( pStruct->endpoint[centerpoint] ) {
5710
continue; /* do not set another endpoint's valence = an unusual value */
5712
/* traverse centerpoint edges to find edge connecting it to the endpoint0 */
5713
/* 1. Find a double bond to an endpoint */
5716
for ( n = 0, num_endp = 0; n < at2[centerpoint].valence; n ++ ) {
5717
ecp0 = pBNS->edge + pCentp->iedge[n];
5719
endpoint2 = ecp0->neighbor12 ^ centerpoint;
5720
if ( pStruct->endpoint[endpoint2] /* ??? */ ) {
5723
/* check whether ecp0 is stereogenic: if it is then we cannot decrement its flow */
5724
for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[centerpoint].sb_parity[m]; m ++ ) {
5725
if ( at[centerpoint].sb_ord[m] == n ) {
5726
endpoint2 = NO_VERTEX;
5730
if ( endpoint2 == NO_VERTEX ) {
5733
pEndp2 = pBNS->vert + endpoint2;
5741
/* 2. Find a single bond to an endpoint0 */
5742
for ( n = 0, num_endp = 0; n < at2[centerpoint].valence; n ++ ) {
5743
ecp0 = pBNS->edge + pCentp->iedge[n];
5747
if ( endpoint0 != (ecp0->neighbor12 ^ centerpoint) ) {
5751
Endp2 Endp2(not radical)
5754
ecp0 || ecp1 ecp0 | ecp1
5755
Endp0----Centp----Endp1 Endp0====Centp----Endp1
5759
etg0 \ / etg1 etg0 \ / etg1
5765
/* compare centerpoints */
5766
if ( !pCentp_found ||
5767
/* try to avoid carbons */
5768
(pVA[centerpoint].cNumValenceElectrons != 4 ||
5769
pVA[centerpoint].cPeriodicRowNumber != 1) &&
5770
pVA[centerpoint_found].cNumValenceElectrons == 4 &&
5771
pVA[centerpoint_found].cPeriodicRowNumber == 1 ||
5772
/* try a better non-carbon */
5773
(pVA[centerpoint].cNumValenceElectrons != 4 ||
5774
pVA[centerpoint].cPeriodicRowNumber != 1 ) &&
5775
(at[centerpoint].valence > at[centerpoint_found].valence ||
5776
at[centerpoint].valence == at[centerpoint_found].valence &&
5777
at[centerpoint].el_number > at[centerpoint_found].el_number) ) {
5779
pCentp_found = pCentp;
5781
centerpoint_found = centerpoint;
5782
pEndp2_found = pEndp2;
5790
/* decrement st_flow, st_cap on Endp2; decrement flow on ecp2; decrement st_flow on Centp */
5791
/* result: radicals on Endp0 and Centp => run BNS */
5792
if ( pCentp_found ) {
5793
/* make ecp0 a double bond, make ecp2 a single bond, remove radical */
5794
ecp0_found->flow ++;
5795
if ( ecp0_found->cap < ecp0_found->flow ) {
5796
ecp0_found->cap = ecp0_found->flow;
5798
pEndp0->st_edge.flow ++;
5799
if ( pEndp2_found && ecp2_found ) {
5800
ecp2_found->flow --;
5801
pEndp2_found->st_edge.flow --;
5803
/* Endp2 not found: only make ecp0 a double bond */
5804
pCentp_found->st_edge.flow ++;
5805
pCentp_found->st_edge.cap ++;
5806
pBNS->tot_st_flow += 2; /* radical elimination */
5807
pBNS->tot_st_cap += 1;
5810
pCentp_found = NULL;
5811
num_fixes ++; /* #2 */
5816
pCentp_found = NULL;
5817
if ( i < num_endpoints ) {
5818
/* tautomeric endpoint found; traverse its t-group edges */
5819
for ( j = 0; j < num_endpoints; j ++ ) {
5821
continue; /* avoid the found radical endpoint */
5823
endpoint1 = pStruct->ti.nEndpointAtomNumber[iEndpoint+j];
5824
pEndp1 = pBNS->vert + endpoint1; /* another endpoint */
5825
if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) {
5826
continue; /* one more radical-endpoint! What is going on here??? */
5828
if ( !at2[endpoint1].num_H && at2[endpoint1].charge != -1 ) {
5829
continue; /* avoid enpoints that do not have an attachment */
5831
if ( !pStruct->endpoint[endpoint1] ) {
5832
continue; /* should not happen */
5834
/* traverse endpoint1 edges to find the edge connecting it to the centerpoint */
5835
for ( k = 0; k < pEndp1->num_adj_edges; k ++ ) {
5836
ecp1 = pBNS->edge + pEndp1->iedge[k];
5840
centerpoint = ecp1->neighbor12 ^ endpoint1;
5841
if ( centerpoint >= pBNS->num_atoms ) {
5844
pCentp = pBNS->vert + centerpoint;
5845
/* traverse centerpoint edges to find the 2nd endpoint */
5846
for ( n = 0, num_endp = 0; n < pCentp->num_adj_edges; n ++ ) {
5847
ecp2 = pBNS->edge + pCentp->iedge[n];
5851
endpoint2 = ecp2->neighbor12 ^ centerpoint;
5852
if ( endpoint2 >= pBNS->num_atoms ) {
5855
if ( !pStruct->endpoint[endpoint2] ) {
5858
pEndp2 = pBNS->vert + endpoint2;
5860
if ( at2[endpoint2].num_H || at2[endpoint1].charge == -1 ) {
5864
/* we have found the path:
5866
Endp1 has no attachments, Endp2 has.
5869
etg1 // \ ecp1 etg1 / \\ ecp1
5871
Endp0-----tg1 Centp --> Endp0=====tg1 Centp
5873
radical | etg2 \\ / ecp2 etg2 \\ / ecp2
5877
/* compare centerpoints */
5878
if ( !pCentp_found ||
5879
/* try to avoid carbons */
5880
(pVA[centerpoint].cNumValenceElectrons != 4 ||
5881
pVA[centerpoint].cPeriodicRowNumber != 1) &&
5882
pVA[centerpoint_found].cNumValenceElectrons == 4 &&
5883
pVA[centerpoint_found].cPeriodicRowNumber == 1 ||
5884
/* try a better non-carbon */
5885
(pVA[centerpoint].cNumValenceElectrons != 4 ||
5886
pVA[centerpoint].cPeriodicRowNumber != 1 ) &&
5887
(at[centerpoint].valence > at[centerpoint_found].valence ||
5888
at[centerpoint].valence == at[centerpoint_found].valence &&
5889
at[centerpoint].el_number > at[centerpoint_found].el_number) ) {
5891
pCentp_found = pCentp;
5893
centerpoint_found = centerpoint;
5900
if ( pCentp_found ) {
5901
/* create a new radical at the centerpoint and try to cancel them */
5902
int delta = 1, ret3;
5903
Vertex vPathStart, vPathEnd, v1, v2;
5904
int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
5906
pCentp_found->st_edge.cap += delta;
5907
pBNS->tot_st_cap += delta;
5909
v1 = pCentp_found - pBNS->vert;
5910
v2 = pEndp0 - pBNS->vert;
5911
ret3 = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
5912
&nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
5914
if ( ret3 == 1 && (vPathEnd == v1 && vPathStart == v2 ||
5915
vPathEnd == v2 && vPathStart == v1) && nDeltaCharge % 2 == 0 ) {
5916
ret3 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
5919
tot_num_fixes ++; /* #1 */
5920
pCentp_found = NULL;
5924
pCentp_found->st_edge.cap -= delta;
5925
pBNS->tot_st_cap -= delta;
5932
/*----------------------------------------------------------------------------------------
5933
3rd attempt: add radical to keep
5934
============== u,f=>unfixed, fixed edges (N electrons)%2
5936
e0/ \\ e1 => u/ \\u => // \ v
5937
/ \\ ecp1 ecp2 / \\ u f // \
5938
C--X* Y(-)--C==Z C--X* Y(-)--C*--Z --X(-) Y===C---Z*
5939
rad. endp not rad. endp rad not rad. endp not
5940
Endp0 Endp1 endp endp to endp endp endp
5943
Note: endpoints X and Y may belong to different t-groups
5944
----------------------------------------------------------------------------------------*/
5945
pCentp_found = NULL;
5946
if ( i < num_endpoints ) {
5948
if ( (e0=pVA[endpoint0].nCMinusGroupEdge-1)<0 || pBNS->edge[e0].forbidden ) {
5949
continue; /* no negative charge on Endp0 is possible */
5951
/* a radical-tautomeric endpoint found; traverse all endpoints */
5952
for ( j = 0; j < pStruct->ti.nNumEndpoints; j ++ ) {
5953
if ( iEndpoint+i == j ) {
5954
continue; /* avoid the found radical endpoint */
5957
endpoint1 = pStruct->ti.nEndpointAtomNumber[j];
5958
pEndp1 = pBNS->vert + endpoint1; /* another endpoint */
5960
if ( pEndp1->st_edge.cap > pEndp1->st_edge.flow ) {
5961
continue; /* one more radical-endpoint! What is going on here??? */
5963
if ( ((e1=pVA[endpoint1].nCMinusGroupEdge-1)<0 || !pBNS->edge[e1].flow) || pBNS->edge[e1].forbidden ) {
5964
continue; /* no negative charge on Endp1 */
5966
if ( !pStruct->endpoint[endpoint1] ) {
5967
continue; /* should not happen */
5969
/* traverse endpoint1 edges to find the edge connecting it to the centerpoint */
5970
for ( k = 0; k < pEndp1->num_adj_edges; k ++ ) {
5971
ecp1 = pBNS->edge + pEndp1->iedge[k]; /* e1C */
5972
if ( ecp1->flow || ecp1->forbidden ) {
5975
centerpoint = ecp1->neighbor12 ^ endpoint1;
5976
if ( centerpoint >= pBNS->num_atoms ) {
5977
break; /* no more edges to atoms */
5979
pCentp = pBNS->vert + centerpoint;
5980
if ( pStruct->endpoint[centerpoint] ) {
5981
continue; /* do not set another endpoint's valence = an unusual value */
5983
/* traverse centerpoint edges to find edge connecting it to the endpoint0 */
5984
/* 1. Find a double bond to a not endpoint */
5987
for ( n = 0, num_endp = 0; n < pCentp->num_adj_edges; n ++ ) {
5988
ecp0 = pBNS->edge + pCentp->iedge[n];
5989
if ( ecp0->flow && !ecp0->forbidden ) {
5990
endpoint2 = ecp0->neighbor12 ^ centerpoint;
5991
if ( endpoint2 >= pBNS->num_atoms || pStruct->endpoint[endpoint2] ) {
5994
/* check whether ecp0 is stereogenic: if it is then we cannot decrement its flow */
5995
for ( m = 0; m < MAX_NUM_STEREO_BONDS && at[centerpoint].sb_parity[m]; m ++ ) {
5996
if ( at[centerpoint].sb_ord[m] == n ) {
5997
endpoint2 = NO_VERTEX;
6001
if ( endpoint2 == NO_VERTEX ) {
6004
pEndp2 = pBNS->vert + endpoint2;
6005
ecp2 = ecp0; /* e2C */
6011
/* compare centerpoints */
6012
if ( !pCentp_found ||
6013
/* try to find carbons */
6014
!IS_C(endpoint2_found) && IS_C(endpoint2) ||
6015
IS_C(endpoint2_found) && IS_C(endpoint2) &&
6016
!IS_C(centerpoint_found) && IS_C(centerpoint) ) {
6018
pCentp_found = pCentp;
6019
centerpoint_found = centerpoint;
6020
endpoint2_found = endpoint2;
6023
ecp0_found = pBNS->edge + e0;
6029
/* decrement st_flow, st_cap on Endp2; decrement flow on ecp2; decrement st_flow on Centp */
6030
/* result: radicals on Endp0 and Centp => run BNS */
6031
if ( pCentp_found ) {
6032
Vertex vPathStart, vPathEnd, v1, v2;
6033
int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
6036
Vertex vEndp0 = ecp0_found->neighbor1;
6037
Vertex vEndp1 = ecp1_found->neighbor12 ^ centerpoint_found;
6038
Vertex vEndp2 = ecp2_found->neighbor12 ^ centerpoint_found;
6039
BNS_EDGE *pe0 = ecp0_found;
6040
BNS_EDGE *pe1 = pBNS->edge + (pVA[vEndp1].nCMinusGroupEdge - 1);
6041
pEndp1 = pBNS->vert + vEndp1;
6042
pEndp2 = pBNS->vert + vEndp2;
6043
pCentp = pCentp_found;
6044
if ( !ChargeEdgeList.num_alloc ) {
6045
for ( n = 0; n < pStruct->num_atoms; n ++ ) {
6046
if ( (k = pVA[n].nCMinusGroupEdge)>= 0 && !pBNS->edge[k].forbidden &&
6047
(ret = AddToEdgeList( &ChargeEdgeList, k, pStruct->num_atoms ) ) ) {
6050
if ( (k = pVA[n].nCPlusGroupEdge)>= 0 && !pBNS->edge[k].forbidden &&
6051
(ret = AddToEdgeList( &ChargeEdgeList, k, pStruct->num_atoms ) ) ) {
6056
if ( !BondEdgeList.num_alloc ) {
6057
for ( n = 0; n < pBNS->num_bonds; n ++ ) {
6058
if ( (ret = AddToEdgeList( &BondEdgeList, n, pBNS->num_bonds ) ) ) {
6063
/* fix all bonds and charges */
6064
SetForbiddenEdgeMask( pBNS, &ChargeEdgeList, forbidden_edge_mask );
6065
SetForbiddenEdgeMask( pBNS, &BondEdgeList, forbidden_edge_mask );
6066
/* prepare flow for testing */
6068
ecp2_found->flow -= delta;
6069
pCentp->st_edge.flow -= delta;
6070
pEndp2->st_edge.flow -= delta;
6071
pBNS->tot_st_flow -= 2*delta;
6072
/* unfix edges to be changed */
6073
pe0->forbidden &= inv_forbidden_edge_mask;
6074
pe1->forbidden &= inv_forbidden_edge_mask;
6075
ecp1_found->forbidden &= inv_forbidden_edge_mask;
6077
pBNS->tot_st_cap += delta;
6080
v2 = centerpoint_found;
6081
ret2 = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
6082
&nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
6084
if ( ret2 == 1 && (vPathEnd == v1 && vPathStart == v2 ||
6085
vPathEnd == v2 && vPathStart == v1) && nDeltaCharge == 0 ) {
6086
ret2 = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
6089
tot_num_fixes ++; /* #3 */
6090
pCentp_found = NULL;
6094
ecp2_found->flow += delta;
6095
pCentp->st_edge.flow += delta;
6096
pEndp2->st_edge.flow += delta;
6097
pBNS->tot_st_flow += 2*delta;
6099
RemoveForbiddenEdgeMask( pBNS, &ChargeEdgeList, forbidden_edge_mask );
6100
RemoveForbiddenEdgeMask( pBNS, &BondEdgeList, forbidden_edge_mask );
6105
if ( !pCentp_found )
6115
/************ again ***********************************************************/
6116
while ( pBNS->tot_st_cap > pBNS->tot_st_flow && pStruct->ti.num_t_groups ) {
6119
for ( itg = 0; itg < pStruct->ti.num_t_groups; iEndpoint += num_endpoints, itg ++ ) {
6121
tgroup_number = pStruct->ti.t_group[itg].nGroupNumber;
6122
num_endpoints = pStruct->ti.t_group[itg].nNumEndpoints;
6123
for ( i = 0; i < num_endpoints; i ++ ) {
6124
endpoint0 = pStruct->ti.nEndpointAtomNumber[iEndpoint+i];
6125
pEndp0 = pBNS->vert + endpoint0; /* taut endpoint vertex (possible location of mobile H */
6126
if ( pEndp0->st_edge.cap > pEndp0->st_edge.flow ) {
6127
/* radical endpoint1 has been detected */
6128
/* find a 1-3 centerpoint that has two or more endpoints */
6129
/* connected to the t-group vertex by edges with flow>0 and */
6130
/* to the centerpoint by edges with flow = 0 */
6131
/* after that: (1) increment etg1 flow to eliminate radical */
6132
/* (2) increment flow on one of the two other edges to the t-group */
6133
/* (3) increment st_cap on the found centerpoint */
6134
/* (4) rerun the BNS and re-create the structure */
6139
if ( i < num_endpoints ) {
6140
/* tautomeric endpoint found; traverse its t-group edges */
6141
pEndp2_found = NULL;
6142
for ( j = 0; j < pEndp0->num_adj_edges; j ++ ) {
6143
ecp0 = pBNS->edge + pEndp0->iedge[j];
6144
centerpoint = ecp0->neighbor12 ^ endpoint0;
6145
if ( centerpoint >= pBNS->num_atoms || ecp0->flow || pStruct->endpoint[centerpoint] ) {
6146
continue; /* ignore non-single bonds, orig. InChI endpoints, and fictitious atoms */
6148
pCentp = pBNS->vert + centerpoint;
6149
for ( k = 0; k < pCentp->num_adj_edges; k ++ ) {
6150
ecp1 = pBNS->edge + pCentp->iedge[k];
6151
endpoint1 = ecp1->neighbor12 ^ centerpoint;
6152
if ( endpoint1 >= pBNS->num_atoms || !ecp1->flow || pStruct->endpoint[endpoint1] ) {
6153
continue; /* ignore single bonds, orig. InChI endpoints, and fictitious atoms */
6155
pEndp1 = pBNS->vert + endpoint1;
6156
if ( endpoint1 == endpoint0 || pEndp1->st_edge.cap != pEndp1->st_edge.flow ) {
6157
continue; /* ignore radicals */
6159
if ( !pEndp2_found ||
6160
/* try to find carbons */
6161
!IS_C(endpoint2_found) && IS_C(endpoint1) ||
6162
IS_C(endpoint2_found) && IS_C(endpoint1) &&
6163
!IS_C(centerpoint_found) && IS_C(centerpoint) ) {
6164
pEndp2_found = pEndp1;
6165
pCentp_found = pCentp;
6166
endpoint2_found = endpoint1;
6167
centerpoint_found = centerpoint;
6173
if ( pEndp2_found ) {
6174
/* move radical from pEndp0 to pEndp2 */
6175
pEndp0->st_edge.flow ++;
6176
ecp1_found->flow ++;
6177
ecp2_found->flow --;
6178
pEndp2_found->st_edge.flow --;
6179
pEndp2_found = NULL;
6180
pCentp_found = NULL;
6181
num_fixes ++; /* #4 */
6193
ret = tot_num_fixes;
6195
AllocEdgeList( &ChargeEdgeList, EDGE_LIST_FREE);
6196
AllocEdgeList( &BondEdgeList, EDGE_LIST_FREE);
6199
memcpy( at2, at, len_at*sizeof(at2[0]));
6203
/************************************************************************************************/
6204
/* move (+) charges to >N- and other centerpoints */
6205
int MoveChargeToMakeCenerpoints(BN_STRUCT *pBNS, BN_DATA *pBD, StrFromINChI *pStruct,
6206
inp_ATOM *at, inp_ATOM *at2, VAL_AT *pVA, ALL_TC_GROUPS *pTCGroups,
6207
int *pnNumRunBNS, int *pnTotalDelta, int forbidden_edge_mask)
6209
int i, j, neigh, num_endpoints, tg_group=0, num_success;
6210
int ret2, ret, delta;
6211
int num_at = pStruct->num_atoms;
6212
int num_deleted_H = pStruct->num_deleted_H;
6213
int len_at = num_at + num_deleted_H;
6214
int inv_forbidden_edge_mask = ~forbidden_edge_mask;
6216
/* for RunBnsTestOnce */
6217
Vertex vPathStart, vPathEnd;
6218
int nPathLen, nDeltaH, nDeltaCharge, nNumVisitedAtoms;
6220
BNS_EDGE *pEdgePlus, *pEdgeMinus;
6221
Vertex v1p, v2p, v1m, v2m;
6222
BNS_VERTEX *pv1p, *pv2p, *pv1m, *pv2m;
6226
/* to simplify, prepare new at[] from pBNS */
6227
memcpy( at2, at, len_at*sizeof(at2[0]));
6229
ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );
6236
for ( i = 0; i < num_at; i ++ ) {
6237
if ( pVA[i].cNumValenceElectrons != 4 && /* not C, Si, Ge */
6238
!pVA[i].cMetal && !pVA[i].nTautGroupEdge &&
6239
!at2[i].num_H && at2[i].valence >= 3 &&
6240
at2[i].valence == at2[i].chem_bonds_valence &&
6241
!at2[i].charge && pVA[i].nCPlusGroupEdge > 0 &&
6242
is_centerpoint_elem( at2[i].el_number ) ) {
6243
for ( j = 0, num_endpoints = 0; j < at2[i].valence; j ++ ) {
6244
neigh = at2[i].neighbor[j];
6245
if ( at2[neigh].endpoint ) {
6246
if ( !num_endpoints ) {
6247
tg_group = at2[neigh].endpoint;
6249
if ( tg_group != at2[neigh].endpoint ) {
6250
break; /* not a centerpoint */
6255
if ( j == at2[i].valence && num_endpoints > 1 ) {
6256
/* found possible centerpoint */
6257
pEdgePlus = pBNS->edge + (pVA[i].nCPlusGroupEdge-1);
6258
pEdgeMinus = (pVA[i].nCMinusGroupEdge > 0)? pBNS->edge + (pVA[i].nCMinusGroupEdge-1) : NULL;
6259
if ( pEdgePlus->flow + (pEdgeMinus? pEdgeMinus->flow : 0) != 1 ) {
6262
v1p = pEdgePlus->neighbor1;
6263
v2p = pEdgePlus->neighbor12 ^ v1p;
6264
pv1p = pBNS->vert + v1p;
6265
pv2p = pBNS->vert + v2p;
6267
v1m = pEdgeMinus->neighbor1;
6268
v2m = pEdgeMinus->neighbor12 ^ v1m;
6269
pv1m = pBNS->vert + v1m;
6270
pv2m = pBNS->vert + v2m;
6278
/* set new flow to run BNS Search */
6279
if ( delta = pEdgePlus->flow ) {
6280
/* positive charge <=> flow=0 on (=) edge */
6281
pEdgePlus->flow -= delta;
6282
pv1p->st_edge.flow -= delta;
6283
pv2p->st_edge.flow -= delta;
6284
pBNS->tot_st_flow -= 2*delta;
6285
pEdgePlus->forbidden |= forbidden_edge_mask;
6287
pEdgeMinus->forbidden |= forbidden_edge_mask;
6289
ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
6290
&nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
6294
if ( ret == 1 && (vPathEnd == v1p && vPathStart == v2p ||
6295
vPathEnd == v2p && vPathStart == v1p) &&
6296
nDeltaCharge == -1 /* charge moving to this atom disappers*/ ) {
6297
ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
6303
*pnTotalDelta += ret;
6310
pEdgePlus->flow += delta;
6311
pv1p->st_edge.flow += delta;
6312
pv2p->st_edge.flow += delta;
6313
pBNS->tot_st_flow += 2*delta;
6315
pEdgePlus->forbidden &= inv_forbidden_edge_mask;
6317
pEdgeMinus->forbidden &= inv_forbidden_edge_mask;
6320
if ( pEdgeMinus && (delta == pEdgeMinus->flow) && pEdgePlus->flow == 0 ) {
6321
/* positive charge <=> flow=0 on (=) edge and flow=0 on (-) edge */
6322
pEdgeMinus->flow -= delta;
6323
pv1m->st_edge.flow -= delta;
6324
pv2m->st_edge.flow -= delta;
6325
pBNS->tot_st_flow -= 2*delta;
6326
pEdgePlus->forbidden |= forbidden_edge_mask;
6327
pEdgeMinus->forbidden |= forbidden_edge_mask;
6328
ret = RunBnsTestOnce( pBNS, pBD, pVA, &vPathStart, &vPathEnd, &nPathLen,
6329
&nDeltaH, &nDeltaCharge, &nNumVisitedAtoms );
6333
if ( ret == 1 && (vPathEnd == v1m && vPathStart == v2m ||
6334
vPathEnd == v2m && vPathStart == v1m) &&
6335
nDeltaCharge == -1 /* charge moving to this atom disappers*/ ) {
6336
ret = RunBnsRestoreOnce( pBNS, pBD, pVA, pTCGroups );
6342
*pnTotalDelta += ret;
6349
pEdgeMinus->flow += delta;
6350
pv1m->st_edge.flow += delta;
6351
pv2m->st_edge.flow += delta;
6352
pBNS->tot_st_flow += 2*delta;
6354
pEdgePlus->forbidden &= inv_forbidden_edge_mask;
6355
pEdgeMinus->forbidden &= inv_forbidden_edge_mask;
6359
memcpy( at2, at, len_at*sizeof(at2[0]));
6361
ret2 = CopyBnsToAtom( pStruct, pBNS, pVA, pTCGroups, 1 );