diff --git a/INCHI-1-SRC/INCHI_API/libinchi/src/CMakeLists.txt b/INCHI-1-SRC/INCHI_API/libinchi/src/CMakeLists.txt index 8fd3b273..45bae4c8 100644 --- a/INCHI-1-SRC/INCHI_API/libinchi/src/CMakeLists.txt +++ b/INCHI-1-SRC/INCHI_API/libinchi/src/CMakeLists.txt @@ -54,6 +54,8 @@ target_sources(libinchi PRIVATE ${P_IXA}/ixa_read_mol.c ${P_IXA}/ixa_status.c ${P_IXA}/ixa_status.h + ${P_BASE}/atropisomers.h + ${P_BASE}/atropisomers.c ${P_BASE}/bcf_s.h ${P_BASE}/bcf_s.c ${P_BASE}/extr_ct.h @@ -127,6 +129,8 @@ target_sources(libinchi PRIVATE ${P_BASE}/permutation_util.c ${P_BASE}/readinch.c ${P_BASE}/readinch.h + ${P_BASE}/ring_detection.c + ${P_BASE}/ring_detection.h ${P_BASE}/runichi.c ${P_BASE}/runichi2.c ${P_BASE}/runichi3.c diff --git a/INCHI-1-SRC/INCHI_BASE/src/atropisomers.c b/INCHI-1-SRC/INCHI_BASE/src/atropisomers.c new file mode 100644 index 00000000..5592afda --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/atropisomers.c @@ -0,0 +1,133 @@ + + +#include "inpdef.h" +#include "ring_detection.h" + + +void find_atropisomeric_atoms_and_bonds(inp_ATOM* out_at, + int num_atoms, + RingSystems *ring_result, + ORIG_ATOM_DATA *orig_inp_data, + int *fused_atom_partner) { + + + printf("-------------------\n"); + + for (int i = 0; i < num_atoms; i++) { + + int atom_id1 = i; + const inp_ATOM atom_i = out_at[atom_id1]; + + int num_neighbors_i = atom_i.valence; + + const AT_NUMB *neighbors = atom_i.neighbor; + + if (num_neighbors_i == 3) { + for (int j = 0; j < num_neighbors_i; j++) { + + int atom_id2 = neighbors[j]; + + if (atom_id1 >= atom_id2) { + continue; + } + + const inp_ATOM atom_j = out_at[atom_id2]; + + + + int num_neighbors_j = atom_j.valence; + + if (num_neighbors_j == 3 && + atom_i.bond_stereo[j] == 0 && + atom_i.bond_type[j] == 1) { + + if (ring_result->atom_to_ring_mapping[atom_id1].ring_count > 0 || + ring_result->atom_to_ring_mapping[atom_id2].ring_count > 0) { + + int nof_wedge_bonds_i = 0; + int has_double_bond_i = 0; + for (int k = 0; k < num_neighbors_i; k++) { + if (atom_i.bond_stereo[k] == 1 || + atom_i.bond_stereo[k] == 4 || + atom_i.bond_stereo[k] == 6) { + nof_wedge_bonds_i++; + } + if (atom_i.bond_type[k] == 2) { + has_double_bond_i = 1; + } + } + int nof_wedge_bonds_j = 0; + int has_double_bond_j = 0; + for (int k = 0; k < num_neighbors_j; k++) { + if (atom_j.bond_stereo[k] == 1 || + atom_j.bond_stereo[k] == 4 || + atom_j.bond_stereo[k] == 6) { + nof_wedge_bonds_j++; + } + if (atom_j.bond_type[k] == 2) { + has_double_bond_j = 1; + } + } + + if (nof_wedge_bonds_i > 0 || + nof_wedge_bonds_j > 0) { + + if ((has_double_bond_i || has_double_bond_j)) { + if ((fused_atom_partner[atom_id1] != j && + fused_atom_partner[atom_id2] != i) || + (fused_atom_partner[atom_id1] == -1 || + fused_atom_partner[atom_id2] == -1)) { + + if (are_atoms_in_same_small_ring(out_at, + num_atoms, + ring_result, + atom_id1, atom_id2, + 6) == 0) { + printf(">>> is atropisomer\n"); + printf("infos: atom %d with atom %d; bond type %d; bond stereo %d\n", + atom_id1, atom_id2, atom_i.bond_type[j], atom_i.bond_stereo[j]); + // printf("atom type %d %d\n", atom_i.el_number, atom_j.el_number); + // printf("has double bond %d %d\n", has_double_bond_i, has_double_bond_j); + // printf("fused pivot atoms %d %d\n", fused_atom_partner[atom_id1], fused_atom_partner[atom_id2]); + + //TODO set atoms with atropisomeric bonds in out_at + orig_inp_data->is_atropisomer = 1; + } + } + } + } + } + } + } + } + } + + for (int i = 0; i < ring_result->count; i++) { + const Ring *cur_ring = &ring_result->rings[i]; + if (cur_ring->size >= 8) { + int count_single_bonds = 0; + int count_double_bonds = 0; + for (int j = 0; j < cur_ring->size; j++) { + int atom_id1 = cur_ring->atom_ids[j]; + int atom_id2 = cur_ring->atom_ids[(j + 1) % cur_ring->size]; + const inp_ATOM atom1 = out_at[atom_id1]; + for (int k = 0; k < atom1.valence; k++) { + if (atom1.neighbor[k] == atom_id2) { + if (atom1.bond_type[k] == 1) { + count_single_bonds++; + } else if (atom1.bond_type[k] == 2) { + count_double_bonds++; + } + } + } + } + + if (cur_ring->is_fused_ring == 0) { + printf(">>> ring id %d size %d nof single bonds %d nof double bonds %d fused ring %d\n", + cur_ring->id, cur_ring->size, + count_single_bonds, count_double_bonds, + cur_ring->is_fused_ring); + } + } + } +} diff --git a/INCHI-1-SRC/INCHI_BASE/src/atropisomers.h b/INCHI-1-SRC/INCHI_BASE/src/atropisomers.h new file mode 100644 index 00000000..db4eabc7 --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/atropisomers.h @@ -0,0 +1,9 @@ + +#include "inpdef.h" +#include "ring_detection.h" + +void find_atropisomeric_atoms_and_bonds(inp_ATOM* out_at, + int num_atoms, + RingSystems *ring_result, + ORIG_ATOM_DATA *orig_inp_data, + int *fused_atom_partner); diff --git a/INCHI-1-SRC/INCHI_BASE/src/ichidrp.h b/INCHI-1-SRC/INCHI_BASE/src/ichidrp.h index 0d8e27ff..181ed4d2 100644 --- a/INCHI-1-SRC/INCHI_BASE/src/ichidrp.h +++ b/INCHI-1-SRC/INCHI_BASE/src/ichidrp.h @@ -189,7 +189,8 @@ typedef struct tagInputParms { int bMolecularInorganics; /* (@nnuk : Nauman Ullah Khan) :: Varaible for Molecular Inorganics parameter */ int bMolecularInorganicsReconnectedInChI; /* (@nnuk : Nauman Ullah Khan) :: Custom flag to indicate reconnected InChI requirement */ - int bEnhancedStereo; + int bEnhancedStereo; /* v. 1.0?+ enable enhanced stereochemistry */ + int Atropisomers; /* v. 1.0?+ enable atropisomeric stereochemistry */ /* */ INCHI_MODE bTautFlags; diff --git a/INCHI-1-SRC/INCHI_BASE/src/ichimake.c b/INCHI-1-SRC/INCHI_BASE/src/ichimake.c index 0826571d..7a088797 100644 --- a/INCHI-1-SRC/INCHI_BASE/src/ichimake.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichimake.c @@ -54,6 +54,9 @@ #include "bcf_s.h" +#include "atropisomers.h" +#include "ring_detection.h" + /* Local functions */ @@ -3901,6 +3904,38 @@ int Create_INChI(CANON_GLOBALS* pCG, /*fix_odd_things( num_atoms, out_at );*/ #if ( FIND_RING_SYSTEMS == 1 ) MarkRingSystemsInp(out_at, num_atoms, 0); + + orig_inp_data->is_atropisomer = 0; + if (ip->Atropisomers) { + RingSystems *ring_result = find_rings(out_at, num_atoms); + + // print_ring_result(ring_result); + int fused_atom_partner[num_atoms]; + for (i = 0; i < num_atoms; i++) { + // out_at[i].fused_partner_atom_id = -1; + fused_atom_partner[i] = -1; + } + for (i = 0; i < num_atoms; i++) { + for (int j = i + 1; j < num_atoms; j++) { + if(is_fused_ring_pivot(ring_result, out_at, i, j)) { + fused_atom_partner[i] = j; + fused_atom_partner[j] = i; + } + } + } + + find_atropisomeric_atoms_and_bonds(out_at, num_atoms, ring_result, orig_inp_data, fused_atom_partner); + + //map values to orig_inp_data + if (orig_inp_data->is_atropisomer) { + for (i = 0; i < num_atoms; i++) { + //todo set atoms with atropisomeric bonds in orig_atom_data + } + } + + free_ring_system(ring_result); + } + #endif /* duplicate the preprocessed structure so that all supplied out_norm_data[]->at buffers are filled */ if (out_at != out_norm_data[TAUT_YES]->at && out_norm_data[TAUT_YES]->at) diff --git a/INCHI-1-SRC/INCHI_BASE/src/ichiparm.c b/INCHI-1-SRC/INCHI_BASE/src/ichiparm.c index a988a17b..16f24c04 100644 --- a/INCHI-1-SRC/INCHI_BASE/src/ichiparm.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichiparm.c @@ -124,7 +124,8 @@ int set_common_options_by_parg(const char* pArg, int* pbMergeHash, int* pbHideInChI, int* pbMolecularInorganics, /* @nnuk */ - int* pbEnhancedStereochemistry); + int* pbEnhancedStereochemistry, + int* pbAtropisomers); /**************************************************************************** @@ -169,7 +170,8 @@ int set_common_options_by_parg(const char* pArg, int* pbMergeHash, int* pbHideInChI, int* pbMolecularInorganics, /*@nnuk*/ - int* pbEnhancedStereochemistry + int* pbEnhancedStereochemistry, + int* pbAtropisomers ) { int got = 0; @@ -275,6 +277,11 @@ int set_common_options_by_parg(const char* pArg, *pbEnhancedStereochemistry = 1; got = 1; } + else if (!inchi_stricmp(pArg, "Atropisomers")) + { + *pbAtropisomers = 1; + got = 1; + } #ifndef USE_STDINCHI_API /* These options DO TURN OFF Std flag */ @@ -664,6 +671,7 @@ int ReadCommandLineParms(int argc, int bUnchargedAcidTaut = (CHARGED_SALTS_ONLY == 0); int bMergeSaltTGroups = (DISCONNECT_SALTS == 1); int bEnhancedStereochemistry = 0; + int bAtropisomers = 0; #if ( MIN_SB_RING_SIZE > 0 ) int nMinDbRinSize = MIN_SB_RING_SIZE, mdbr = 0; #endif @@ -816,7 +824,7 @@ int ReadCommandLineParms(int argc, &bLargeMolecules, &bPolymers, &bFoldPolymerSRU, &bFrameShiftScheme, &bStereoAtZz, &bNPZz, - &bNoWarnings, &bMergeHash, &bHideInChI, &bMolecularInorganics, &bEnhancedStereochemistry); + &bNoWarnings, &bMergeHash, &bHideInChI, &bMolecularInorganics, &bEnhancedStereochemistry, &bAtropisomers); if (got) { ; @@ -1264,7 +1272,7 @@ int ReadCommandLineParms(int argc, &bLargeMolecules, &bPolymers, &bFoldPolymerSRU, &bFrameShiftScheme, &bStereoAtZz, &bNPZz, - &bNoWarnings, &bMergeHash, &bHideInChI, &bMolecularInorganics, &bEnhancedStereochemistry); + &bNoWarnings, &bMergeHash, &bHideInChI, &bMolecularInorganics, &bEnhancedStereochemistry, &bAtropisomers); if ( got ) { @@ -2143,6 +2151,8 @@ int ReadCommandLineParms(int argc, ip->bEnhancedStereo = bEnhancedStereochemistry; + ip->Atropisomers = bAtropisomers; + return 0; } diff --git a/INCHI-1-SRC/INCHI_BASE/src/ichiprt1.c b/INCHI-1-SRC/INCHI_BASE/src/ichiprt1.c index 1ff39c8f..f362561e 100644 --- a/INCHI-1-SRC/INCHI_BASE/src/ichiprt1.c +++ b/INCHI-1-SRC/INCHI_BASE/src/ichiprt1.c @@ -1623,6 +1623,11 @@ int OutputINChI1( CANON_GLOBALS *pCG, { set_EnhancedStereo_t_m_layers(orig_inp_data, pINChI, pINChI_Aux); } + + if (ip->Atropisomers) + { + set_Atropisomer_t_m_layers(orig_inp_data, pINChI, pINChI_Aux); + } } } if (bCompExists) @@ -1714,6 +1719,10 @@ int OutputINChI1( CANON_GLOBALS *pCG, { is_beta = 1; } + else if (ip->Atropisomers) + { + is_beta = 1; + } OutputINCHI_VersionAndKind(out_file, strbuf, bINChIOutputOptions, is_beta, pLF, pTAB); } diff --git a/INCHI-1-SRC/INCHI_BASE/src/inpdef.h b/INCHI-1-SRC/INCHI_BASE/src/inpdef.h index 2981528e..ec3c5823 100644 --- a/INCHI-1-SRC/INCHI_BASE/src/inpdef.h +++ b/INCHI-1-SRC/INCHI_BASE/src/inpdef.h @@ -459,7 +459,9 @@ typedef struct tagOrigAtom OAD_Polymer *polymer; OAD_V3000 *v3000; int valid_polymer; - int n_zy; /* number of non-polymeric pseudoatoms (Zy) */ + int n_zy; /* number of non-polymeric pseudoatoms (Zy) */ + + int is_atropisomer; /* flag indicating whether the structure is an atropisomer; it is set to 1 if the structure has been identified as an atropisomer during input processing, and 0 otherwise */ } ORIG_ATOM_DATA; diff --git a/INCHI-1-SRC/INCHI_BASE/src/ring_detection.c b/INCHI-1-SRC/INCHI_BASE/src/ring_detection.c new file mode 100644 index 00000000..9b7732ef --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/ring_detection.c @@ -0,0 +1,512 @@ + +#include "mode.h" +#include "inpdef.h" + +#include "ring_detection.h" + + +int is_subset(Ring* child, + Ring* potential_parent) { + + if (child == NULL || potential_parent == NULL) { + return 0; + } + + if (child->atom_ids == NULL || potential_parent->atom_ids == NULL) { + return 0; + } + + + if (child->size >= potential_parent->size) { + return 0; + } + + for (int i = 0; i < child->size; i++) { + int found = 0; + for (int j = 0; j < potential_parent->size; j++) { + if (child->atom_ids[i] == potential_parent->atom_ids[j]) { + found = 1; + break; + } + } + if (!found) { + return 0; + } + } + return 1; +} + +void determine_ring_hierarchy(RingSystems* rs) { + + if (rs == NULL) { + return; + } + + if (rs->rings == NULL) { + return; + } + + for (int i = 0; i < rs->count; i++) { + rs->rings[i].parent_id = -1; + + int smallest_parent_size = 1e6; + + for (int j = 0; j < rs->count; j++) { + if (i == j) { + continue; + } + + if (is_subset(&rs->rings[i], &rs->rings[j])) { + rs->rings[i].parent_id = rs->rings[j].id; + + rs->rings[j].child_ids = (int*)inchi_realloc(rs->rings[j].child_ids, (rs->rings[j].child_count + 1) * sizeof(int)); + rs->rings[j].child_ids[rs->rings[j].child_count] = rs->rings[i].id; + rs->rings[j].child_count++; + } + } + } +} + +int get_ring_atom_overlap(const Ring *r1, const Ring *r2) { + int overlap = 0; + + if (r1 == NULL || r2 == NULL) { + return overlap; + } + + if (r1->atom_ids == NULL || r2->atom_ids == NULL) { + return overlap; + } + + for (int i = 0; i < r1->size; i++) { + for (int j = 0; j < r2->size; j++) { + if (r1->atom_ids[i] == r2->atom_ids[j]) { + overlap++; + } + } + } + return overlap; +} + +int get_number_of_overlapping_rings(const Ring *r1, const Ring *r2) { + + int count = 0; + + if (r1 == NULL || r2 == NULL) { + return count; + } + + if (r1->child_ids == NULL || r2->child_ids == NULL) { + return count; + } + + for (int i = 0; i < r1->child_count; i++) { + for (int j = 0; j < r2->child_count; j++) { + if (r1->child_ids[i] == r2->child_ids[j]) { + count++; + } + } + } + return count; +} + +int get_number_of_atomic_rings_from_atom(const RingSystems *rs, + int atom_id) { + + if (rs == NULL) { + return 0; + } + + if (rs->rings == NULL) { + return 0; + } + + if (atom_id < 0) { + return 0; + } + + int count = 0; + for (int i = 0; i < rs->count; i++) { + const Ring *cur_ring = &rs->rings[i]; + if (cur_ring->child_count == 0) { + for (int j = 0; j < cur_ring->size; j++) { + if (cur_ring->atom_ids[j] == atom_id) { + count++; + } + } + } + } + return count; +} + +int is_atom_in_ring(const Ring *r, int atom_id) { + + if (r == NULL) { + return 0; + } + + if (r->atom_ids == NULL) { + return 0; + } + + for (int i = 0; i < r->size; i++) { + if (r->atom_ids[i] == atom_id) { + return 1; + } + } + return 0; +} + +int are_atoms_in_same_small_ring(const inp_ATOM* atoms, + int num_atoms, + const RingSystems *rs, + int atom_id1, int atom_id2, + int max_ring_size) { + + if (atoms == NULL) { + return 0; + } + + if (rs == NULL) { + return 0; + } + + if (rs->atom_to_ring_mapping == NULL) { + return 0; + } + + if (atom_id1 >= num_atoms || atom_id2 >= num_atoms) { + return 0; + } + + const inp_ATOM atom1 = atoms[atom_id1]; + const inp_ATOM atom2 = atoms[atom_id2]; + + for (int i = 0; i < rs->atom_to_ring_mapping[atom_id1].ring_count; i++) { + int ring_id1 = rs->atom_to_ring_mapping[atom_id1].ring_ids[i]; + if (rs->rings[ring_id1].size <= max_ring_size) { + for (int j = 0; j < rs->atom_to_ring_mapping[atom_id2].ring_count; j++) { + int ring_id2 = rs->atom_to_ring_mapping[atom_id2].ring_ids[j]; + if (ring_id1 == ring_id2) { + return 1; + } + } + } + } + return 0; +} + +void print_ring(const Ring *r) { + + if (r == NULL) { + return; + } + + printf("Ring ID: %d, Size: %d, nof fused ring %d, parent %d, Atoms: ", + r->id, r->size, r->nof_atomic_rings, r->parent_id); + for (int i = 0; i < r->size; i++) { + printf("%d ", r->atom_ids[i]); + } + printf("\n"); + printf(" Child Ring IDs: "); + for(int i = 0; i < r->child_count; i++) { + printf(" %d ", r->child_ids[i]); + } + printf("\n"); +} + +void print_ring_result(const RingSystems *rs) { + + if (rs == NULL) { + return; + } + + printf("Number of rings: %d\n", rs->count); + + for (int i = 0; i < rs->count; i++) { + const Ring *r = &rs->rings[i]; + print_ring(r); + } +} + +void free_ring_system(RingSystems *rs) { + + if (rs == NULL) { + return; + } + + for (int i = 0; i < rs->count; i++) { + inchi_free(rs->rings[i].atom_ids); + inchi_free(rs->rings[i].child_ids); + } + inchi_free(rs->rings); + + for (int i = 0; i < rs->num_atoms; i++) { + inchi_free(rs->atom_to_ring_mapping[i].ring_ids); + } + inchi_free(rs->atom_to_ring_mapping); + inchi_free(rs); +} + +int sub_ring_counter(RingSystems* rs, const Ring *r, int *ring_counter) { + if (r->child_count == 0) { + ring_counter[r->id] = 1; + } else { + for (int i = 0; i < r->child_count; i++) { + sub_ring_counter(rs, &rs->rings[r->child_ids[i]], ring_counter); + } + } +} + +void determine_fused_rings(RingSystems* rs) { + + if (rs == NULL) { + return; + } + + if (rs->rings == NULL) { + return 0; + } + + for (int i = 0; i < rs->count; i++) { + Ring *cur_ring = &rs->rings[i]; + + int ring_counter[rs->count]; + for (int j = 0; j < rs->count; j++) { + ring_counter[j] = 0; + } + sub_ring_counter(rs, cur_ring, ring_counter); + + int count = 0; + for (int j = 0; j < rs->count; j++) { + if (ring_counter[j] > 0) { + count++; + } + } + + cur_ring->nof_atomic_rings = count; + } +} + +void *create_new_ring(RingSystems *rs, + inp_ATOM *atoms, + int *path, + int path_len) { + + + rs->rings = (Ring*)inchi_realloc(rs->rings, (rs->count + 1) * sizeof(Ring)); + + Ring *r = &rs->rings[rs->count]; + + r->id = rs->count; + r->size = path_len; + r->parent_id = -1; + r->child_count = 0; + r->child_ids = NULL; + r->nof_atomic_rings = 0; + r->is_fused_ring = 0; + r->atom_ids = (int*)inchi_calloc(path_len, sizeof(int)); + for (int i = 0; i < path_len; i++) { + r->atom_ids[i] = path[i]; + + rs->atom_to_ring_mapping[path[i]].atom_id = path[i]; + rs->atom_to_ring_mapping[path[i]].ring_ids = (int*)inchi_realloc(rs->atom_to_ring_mapping[path[i]].ring_ids, + (rs->atom_to_ring_mapping[path[i]].ring_count + 1) * sizeof(int)); + rs->atom_to_ring_mapping[path[i]].ring_ids[rs->atom_to_ring_mapping[path[i]].ring_count] = r->id; + rs->atom_to_ring_mapping[path[i]].ring_count++; + + inp_ATOM atom = atoms[path[i]]; + for (int j = i + 1; j < path_len; j++) { + // prev, i, next + int prev_atom_id = path[i - 1 < 0 ? path_len - 1 : i - 1]; + int cur_atom_id = path[i]; + int next_atom_id = path[i + 1 >= path_len ? 0 : i + 1]; + int other_atom_id = path[j]; + for (int k = 0; k < atom.valence; k++) { + if (atom.neighbor[k] == prev_atom_id || atom.neighbor[k] == next_atom_id) { + continue; + } else if (atom.neighbor[k] == other_atom_id) + { + r->is_fused_ring = 1; + } + } + } + } + + rs->rings[rs->count] = *r; + rs->count++; +} + +int is_new_ring(RingSystems *rs, + int *path, + int path_len) { + + for (int i = 0; i < rs->count; ++i) { + if (rs->rings[i].size != path_len) continue; + int match = 1; + for (int j = 0; j < path_len; ++j) { + int found = 0; + for (int k = 0; k < path_len; ++k) { + if (rs->rings[i].atom_ids[k] == path[j]) { + found = 1; + break; + } + } + if (!found) { + match = 0; + break; + } + } + if (match) return 0; + } + return 1; +} + +void dfs(RingSystems *rs, + inp_ATOM* atoms, + int **adj, + int num_atoms, + int start, + int curr, + int *visited, + int *path, + int path_len) { + + visited[curr] = 1; + path[path_len] = curr; + path_len++; + + for (int i = 0; i < num_atoms; ++i) { + if (adj[curr][i]) { + if (i == start && path_len > 2) { + if (is_new_ring(rs, path, path_len)) { + + create_new_ring(rs, atoms, path, path_len); + + } + } else if (!visited[i]) { + // dfs(start, i, visited, path, path_len); + dfs(rs, atoms, adj, num_atoms, start, i, visited, path, path_len); + } + } + } + visited[curr] = 0; +} + +int is_fused_ring_pivot(const RingSystems *rs, + const inp_ATOM * atoms, + int atom_id1, int atom_id2) { + + if (atoms == NULL || rs == NULL || atom_id1 < 0 || atom_id2 < 0) { + return 0; // Invalid input + } + + int are_neighbours = 0; + + const inp_ATOM *atom1 = &atoms[atom_id1]; + const inp_ATOM *atom2 = &atoms[atom_id2]; + + if (atom1 == NULL || atom2 == NULL) { + return 0; // Invalid input + } + + if (atom1->valence <= 2 || atom2->valence <= 2) { + return 0; + } + + for (int i = 0; i < atom1->valence; i++) { + if (atom1->neighbor[i] == atom_id2) { + are_neighbours = 1; + break; + } + } + if (get_number_of_atomic_rings_from_atom(rs, atom_id1) < 2 && + get_number_of_atomic_rings_from_atom(rs, atom_id2) < 2) { + return 0; + } + + if (are_neighbours) { + for (int i = 0; i < rs->count; i++) { + const Ring *r = &rs->rings[i]; + int found_atom1 = 0; + int found_atom2 = 0; + for (int j = 0; j < r->size; j++) { + if (r->atom_ids[j] == atom_id1) { + found_atom1 = 1; + } + if (r->atom_ids[j] == atom_id2) { + found_atom2 = 1; + } + } + if (found_atom1 && found_atom2) { + if (r->nof_atomic_rings == 2) { + // printf("atom1 %d atom2 %d ring id %d nof atomic rings %d\n", + // atom_id1, atom_id2, r->id, r->nof_atomic_rings); + // print_ring(r); + int count = 0; + for (int j = 0; j < r->child_count; j++) { + const Ring *child_ring = &rs->rings[r->child_ids[j]]; + if (is_atom_in_ring(child_ring, atom_id1) && is_atom_in_ring(child_ring, atom_id2)) { + count++; + } + } + if (count == r->child_count) { + // printf("atom1 %d atom2 %d ring id %d nof atomic rings %d\n", + // atom_id1, atom_id2, r->id, r->nof_atomic_rings); + // print_ring(r); + // printf(">>> Found pivot atom pair: %d, %d\n", atom_id1, atom_id2); + return 1; + } + } + } + } + } + + return 0; +} + +RingSystems *find_rings(inp_ATOM* atoms, + int num_atoms) { + + if (atoms == NULL || num_atoms <= 0) { + return NULL; + } + + RingSystems *rs = (RingSystems*)inchi_calloc(1, sizeof(RingSystems)); + rs->rings = NULL; + rs->count = 0; + rs->num_atoms = num_atoms; + rs->atom_to_ring_mapping = (Atom2RingMapping*)inchi_calloc(num_atoms, sizeof(Atom2RingMapping)); + + int visited[num_atoms]; + int path[num_atoms]; + int **adj = (int**)inchi_calloc(num_atoms, sizeof(int*)); + + for (int i = 0; i < num_atoms; ++i) { + adj[i] = (int*)inchi_calloc(num_atoms, sizeof(int)); + visited[i] = 0; + path[i] = -1; + + const inp_ATOM *atom_i = &atoms[i]; + for (int j = 0; j < atom_i->valence; j++) { + int neighbor = atom_i->neighbor[j]; + adj[i][neighbor] = 1; + } + } + + for (int i = 0; i < num_atoms; ++i) { + dfs(rs, atoms, adj, num_atoms, i, i, visited, path, 0); + } + + determine_ring_hierarchy(rs); + determine_fused_rings(rs); + + for (int i = 0; i < num_atoms; ++i) { + inchi_free(adj[i]); + } + inchi_free(adj); + + return rs; +} + + diff --git a/INCHI-1-SRC/INCHI_BASE/src/ring_detection.h b/INCHI-1-SRC/INCHI_BASE/src/ring_detection.h new file mode 100644 index 00000000..f965baf2 --- /dev/null +++ b/INCHI-1-SRC/INCHI_BASE/src/ring_detection.h @@ -0,0 +1,48 @@ + +#ifndef _RING_DETECTION_H_ +#define _RING_DETECTION_H_ + +#define RS_MAX_RINGS_PER_ATOM 30 + +typedef struct Ring { + int id; + int *atom_ids; + int size; + int nof_atomic_rings; + int parent_id; // Added: ID of the larger ring containing this one + int *child_ids; + int child_count; + int is_fused_ring; +} Ring; + +typedef struct Atom2RingMapping { + int atom_id; + int *ring_ids; + int ring_count; +} Atom2RingMapping; + +typedef struct { + Ring* rings; + int count; + Atom2RingMapping* atom_to_ring_mapping; + int num_atoms; +} RingSystems; + +RingSystems *find_rings(inp_ATOM* atoms, int num_atoms); + +int is_fused_ring_pivot(const RingSystems *rs, + const inp_ATOM * atoms, + int atom_id1, int atom_id2); + +void print_ring_result(const RingSystems *rs); + +void free_ring_system(RingSystems *rs); + +int are_atoms_in_same_small_ring(const inp_ATOM* atoms, + int num_atoms, + const RingSystems *rs, + int atom_id1, int atom_id2, + int max_ring_size); + + +#endif diff --git a/INCHI-1-SRC/INCHI_BASE/src/runichi3.c b/INCHI-1-SRC/INCHI_BASE/src/runichi3.c index fec395e5..89f8d33d 100644 --- a/INCHI-1-SRC/INCHI_BASE/src/runichi3.c +++ b/INCHI-1-SRC/INCHI_BASE/src/runichi3.c @@ -1538,7 +1538,7 @@ int OAD_ValidatePolymerAndPseudoElementData(ORIG_ATOM_DATA* orig_at_data, /* Assign polymer type and subunits type and check polymer data for consistency */ /* djb-rwth: addressing coverity ID #499497 -- TREAT_ERR properly used in all cases */ - + orig_at_data->valid_polymer = 0; if ( treat_polymers && pd ) { @@ -4433,7 +4433,7 @@ void OAD_ValidateAndSortOutPseudoElementAtoms(ORIG_ATOM_DATA* orig_at_data, TREAT_ERR(*err, (70 + 5), "Invalid element(s):"); TREAT_ERR(*err, (70 + 5), orig_at_data->at[k].elname); continue; -#endif +#endif } is_star = !strcmp(orig_at_data->at[k].elname, "*"); if ( !is_star ) @@ -4527,4 +4527,4 @@ int Inp_Atom_GetBondType(inp_ATOM* at, int iatom1, int iatom2) } return -1; -} \ No newline at end of file +} diff --git a/INCHI-1-SRC/INCHI_BASE/src/strutil.c b/INCHI-1-SRC/INCHI_BASE/src/strutil.c index 753c5450..e11f79c9 100644 --- a/INCHI-1-SRC/INCHI_BASE/src/strutil.c +++ b/INCHI-1-SRC/INCHI_BASE/src/strutil.c @@ -7102,6 +7102,52 @@ int invert_parities(const INChI *inchi, return 0; } + +/** + * @brief Set t- and m-layers object for atropisomer stereochemistry + * + * @param orig_inp_data Pointer to original input atom data + * @param inchi Pointer to INChI structure + * @param aux Pointer to INChI auxiliary data + * @return int + */ +int set_Atropisomer_t_m_layers( const ORIG_ATOM_DATA *orig_inp_data, + const INChI *inchi, + const INChI_Aux *aux) +{ + int ret = 0; + + if (orig_inp_data == NULL) + { + return 1; + } + + if (inchi == NULL || aux == NULL) + { + return 1; + } + + if (aux->nOrigAtNosInCanonOrd == NULL || + aux->nNumberOfAtoms <= 0) { + return 1; + } + + //TODO + // - t layer parities for atropisomers + // -> t-parity[atom] = 1 (-) + // - m layer for atropisomers + // -> enantiomeric atropisomers: m1 (inchi->Stereo->nCompInv2Abs = -1; //m1) + // -> diastereomeric atropisomers: m0 (inchi->Stereo->nCompInv2Abs = 1; //m0) + // -> check number of stereocenters: if #stereocenter >= 2 + + if (orig_inp_data->is_atropisomer) { + printf(">>>>> TODO set t- and m-layers for atropisomers\n"); + } + + + return ret; +} + /** * @brief Set the enhanced stereochemistry information for t- and m-layers * diff --git a/INCHI-1-SRC/INCHI_BASE/src/strutil.h b/INCHI-1-SRC/INCHI_BASE/src/strutil.h index 57e2c4ab..b6394589 100644 --- a/INCHI-1-SRC/INCHI_BASE/src/strutil.h +++ b/INCHI-1-SRC/INCHI_BASE/src/strutil.h @@ -124,6 +124,18 @@ extern "C" const INChI *inchi, const INChI_Aux *aux); + /** + * @brief Set t- and m-layers object for atropisomer stereochemistry + * + * @param orig_inp_data Pointer to original input atom data + * @param inchi Pointer to INChI structure + * @param aux Pointer to INChI auxiliary data + * @return int + */ + int set_Atropisomer_t_m_layers(const ORIG_ATOM_DATA *orig_inp_data, + const INChI *inchi, + const INChI_Aux *aux); + /** * @brief Get the canonical atom number object * diff --git a/INCHI-1-SRC/INCHI_EXE/inchi-1/src/CMakeLists.txt b/INCHI-1-SRC/INCHI_EXE/inchi-1/src/CMakeLists.txt index 8b83d19d..187f0cb8 100644 --- a/INCHI-1-SRC/INCHI_EXE/inchi-1/src/CMakeLists.txt +++ b/INCHI-1-SRC/INCHI_EXE/inchi-1/src/CMakeLists.txt @@ -26,6 +26,8 @@ add_executable(inchi-1) target_sources(inchi-1 PRIVATE main.c + ${P_BASE}/atropisomers.h + ${P_BASE}/atropisomers.c ${P_BASE}/ichimain.c ${P_BASE}/dispstru.c ${P_BASE}/dispstru.h @@ -102,6 +104,8 @@ target_sources(inchi-1 PRIVATE ${P_BASE}/permutation_util.c ${P_BASE}/readinch.c ${P_BASE}/readinch.h + ${P_BASE}/ring_detection.c + ${P_BASE}/ring_detection.h ${P_BASE}/runichi.c ${P_BASE}/runichi2.c ${P_BASE}/runichi3.c diff --git a/INCHI-1-TEST/tests/test_unit/test_atropisomers.cpp b/INCHI-1-TEST/tests/test_unit/test_atropisomers.cpp new file mode 100644 index 00000000..d449352c --- /dev/null +++ b/INCHI-1-TEST/tests/test_unit/test_atropisomers.cpp @@ -0,0 +1,941 @@ +#include +#include + +extern "C" +{ +#include "../../../INCHI-1-SRC/INCHI_BASE/src/inchi_api.h" +#include "../../../INCHI-1-SRC/INCHI_BASE/src/mode.h" +} + +TEST(test_atropisomers, test_dummy_1) +{ + const char *molblock = + "atropisomer test mol \n" + " Ketcher 2192614182D 1 1.00000 0.00000 0 \n" + " \n" + " 16 17 0 0 1 0 0 0 0 0999 V2000 \n" + " 6.7160 -7.9750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.8500 -7.4750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.9840 -7.9750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.9840 -8.9750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.8500 -9.4750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.7160 -8.9750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.7160 -5.9750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.8500 -6.4750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.9840 -5.9750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.9840 -4.9750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.7160 -4.9750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.8500 -4.4750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.5821 -6.4750 0.0000 Br 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.1179 -6.4750 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.1179 -7.4749 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.5821 -7.4750 0.0000 Br 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 1 6 1 0 0 0 0 \n" + " 1 2 2 0 0 0 0 \n" + " 2 3 1 1 0 0 0 \n" + " 2 8 1 0 0 0 0 \n" + " 3 4 2 0 0 0 0 \n" + " 5 4 1 0 0 0 0 \n" + " 5 6 2 0 0 0 0 \n" + " 8 7 1 1 0 0 0 \n" + " 7 11 2 0 0 0 0 \n" + " 8 9 2 0 0 0 0 \n" + " 9 10 1 0 0 0 0 \n" + " 10 12 2 0 0 0 0 \n" + " 12 11 1 0 0 0 0 \n" + " 7 13 1 0 0 0 0 \n" + " 9 14 1 0 0 0 0 \n" + " 3 15 1 0 0 0 0 \n" + " 1 16 1 0 0 0 0 \n" + "M END \n"; + + char options[] = "-Atropisomers"; + inchi_Output output; + inchi_Output *poutput = &output; + const char expected_inchi[] = "InChI=1B"; + + EXPECT_EQ(MakeINCHIFromMolfileText(molblock, options, poutput), 1); + EXPECT_STREQ(poutput->szInChI, expected_inchi); + + FreeINCHI(poutput); +} + +TEST(test_atropisomers, test_dummy_2) +{ + const char *molblock = + "atropisomer test mol \n" + " Ketcher 2202610402D 1 1.00000 0.00000 0 \n" + " \n" + " 25 28 0 0 1 0 0 0 0 0999 V2000 \n" + " 6.2229 -3.4324 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 8.0096 -3.6108 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.1896 -3.0101 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.8699 -4.5953 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.0956 -4.4735 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.9105 -5.0541 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.0587 -6.8139 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.8275 -6.8325 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.8777 -6.3069 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.9085 -7.7852 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.1259 -7.8397 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.0613 -8.3249 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 8.4672 -5.1980 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 8.4510 -6.2674 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 9.4062 -5.3893 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 9.4009 -6.0976 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 10.3049 -4.9122 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 10.2817 -6.5709 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 10.9561 -5.7358 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 10.5172 -3.9146 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 10.4810 -7.5491 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.4299 -2.8268 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.5119 -3.2022 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.2852 -8.3732 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.3939 -7.8951 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3 1 2 0 0 0 \n" + " 1 5 1 0 0 0 \n" + " 5 6 2 0 0 0 \n" + " 6 4 1 1 0 0 \n" + " 4 2 2 0 0 0 \n" + " 9 7 1 1 0 0 \n" + " 7 11 2 0 0 0 \n" + " 12 10 2 0 0 0 \n" + " 10 8 1 0 0 0 \n" + " 8 9 2 0 0 0 \n" + " 6 9 1 0 0 0 \n" + " 4 13 1 0 0 0 \n" + " 8 14 1 0 0 0 \n" + " 13 15 1 0 0 0 \n" + " 14 16 1 0 0 0 \n" + " 15 16 1 0 0 0 \n" + " 15 17 1 0 0 0 \n" + " 16 18 1 0 0 0 \n" + " 17 19 1 0 0 0 \n" + " 18 19 1 0 0 0 \n" + " 17 20 1 6 0 0 \n" + " 18 21 1 1 0 0 \n" + " 1 22 1 0 0 0 \n" + " 22 23 1 0 0 0 \n" + " 11 24 1 0 0 0 \n" + " 24 25 1 0 0 0 \n" + " 12 11 1 1 0 0 \n" + " 3 2 1 1 0 0 \n" + "M END \n"; + + + char options[] = "-Atropisomers"; + inchi_Output output; + inchi_Output *poutput = &output; + const char expected_inchi[] = "InChI=1B"; + + EXPECT_EQ(MakeINCHIFromMolfileText(molblock, options, poutput), 1); + EXPECT_STREQ(poutput->szInChI, expected_inchi); + + FreeINCHI(poutput); +} + +TEST(test_atropisomers, test_dummy_3) +{ + const char *molblock = + "atropisomer test mol \n" + " Ketcher 2202610482D 1 1.00000 0.00000 0 \n" + " \n" + " 22 25 0 0 1 0 0 0 0 0999 V2000 \n" + " 2.8848 -2.2501 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.6152 -2.2496 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3.7516 -1.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.6152 -3.2505 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 2.8848 -3.2550 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3.7538 -3.7500 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.4796 -1.7512 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.3468 -2.2515 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.4858 -3.7529 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.3490 -3.2475 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 2.8348 -5.3751 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.5652 -5.3746 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3.7016 -4.8750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.5652 -6.3755 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 2.8348 -6.3800 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3.7038 -6.8750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.4296 -4.8762 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.2968 -5.3765 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.4358 -6.8779 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.2990 -6.3725 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.2171 -3.7439 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.1622 -4.8755 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3 1 2 0 0 0 \n" + " 1 5 1 0 0 0 \n" + " 5 6 2 0 0 0 \n" + " 6 4 1 0 0 0 \n" + " 4 2 1 0 0 0 \n" + " 2 3 1 0 0 0 \n" + " 4 9 2 0 0 0 \n" + " 9 10 1 1 0 0 \n" + " 10 8 1 0 0 0 \n" + " 7 2 2 0 0 0 \n" + " 13 11 2 0 0 0 \n" + " 11 15 1 0 0 0 \n" + " 15 16 2 0 0 0 \n" + " 16 14 1 0 0 0 \n" + " 14 12 1 0 0 0 \n" + " 12 13 1 0 0 0 \n" + " 14 19 2 0 0 0 \n" + " 19 20 1 0 0 0 \n" + " 20 18 2 0 0 0 \n" + " 18 17 1 0 0 0 \n" + " 17 12 2 0 0 0 \n" + " 9 17 1 0 0 0 \n" + " 10 21 1 0 0 0 \n" + " 18 22 1 0 0 0 \n" + " 7 8 1 1 0 0 \n" + "M END \n"; + + char options[] = "-Atropisomers"; + inchi_Output output; + inchi_Output *poutput = &output; + const char expected_inchi[] = "InChI=1B"; + + EXPECT_EQ(MakeINCHIFromMolfileText(molblock, options, poutput), 1); + EXPECT_STREQ(poutput->szInChI, expected_inchi); + + FreeINCHI(poutput); +} + +TEST(test_atropisomers, test_dummy_4_atypical_no_2_rings) +{ + const char *molblock = + "atropisomer atypical no 2 rings \n" + " Ketcher 2272612 82D 1 1.00000 0.00000 0 \n" + " \n" + " 24 25 0 0 1 0 0 0 0 0999 V2000 \n" + " 6.7791 -6.8681 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 8.4871 -6.8676 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.6347 -6.3744 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 8.4871 -7.8557 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.7791 -7.8601 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.6369 -8.3488 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 8.8265 -3.8573 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 8.8265 -4.8444 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 9.6814 -5.3380 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 10.5363 -4.8444 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 10.5363 -3.8573 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 9.6814 -3.3637 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.6353 -5.3873 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.8053 -4.8531 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.8529 -3.8671 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.9276 -5.3048 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.0975 -4.7705 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 9.3422 -6.3745 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.9243 -6.3744 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 9.3422 -5.3874 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 10.1971 -6.8680 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.4307 -7.2293 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.4179 -5.5196 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.0694 -5.8809 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3 1 1 1 0 0 \n" + " 1 5 2 0 0 0 \n" + " 5 6 1 0 0 0 \n" + " 6 4 2 0 0 0 \n" + " 4 2 1 0 0 0 \n" + " 2 3 2 0 0 0 \n" + " 12 7 1 0 0 0 \n" + " 7 8 1 0 0 0 \n" + " 8 9 1 0 0 0 \n" + " 9 10 1 0 0 0 \n" + " 10 11 1 0 0 0 \n" + " 11 12 1 0 0 0 \n" + " 3 13 1 0 0 0 \n" + " 13 8 1 0 0 0 \n" + " 13 14 1 6 0 0 \n" + " 14 15 2 0 0 0 \n" + " 14 16 1 0 0 0 \n" + " 16 17 2 0 0 0 \n" + " 2 18 1 0 0 0 \n" + " 1 19 1 0 0 0 \n" + " 18 20 2 0 0 0 \n" + " 18 21 1 0 0 0 \n" + " 22 19 1 0 0 0 \n" + " 19 23 1 0 0 0 \n" + " 19 24 1 0 0 0 \n" + "M STY 1 1 SUP \n" + "M SLB 1 1 1 \n" + "M SAP 1 1 18 0 \n" + "M SAL 1 3 18 20 21 \n" + "M SBL 1 1 19 \n" + "M SMT 1 CONH2 \n" + "M STY 1 2 SUP \n" + "M SLB 1 2 2 \n" + "M SAP 2 1 19 0 \n" + "M SAL 2 4 19 22 23 24 \n" + "M SBL 2 1 20 \n" + "M SMT 2 tBu \n" + "M END \n"; + + char options[] = "-Atropisomers"; + inchi_Output output; + inchi_Output *poutput = &output; + const char expected_inchi[] = "InChI=1B"; + + EXPECT_EQ(MakeINCHIFromMolfileText(molblock, options, poutput), 1); + EXPECT_STREQ(poutput->szInChI, expected_inchi); + + FreeINCHI(poutput); +} + +TEST(test_atropisomers, test_dummy_5_no_atropisomer_no_wedge_bonds) +{ + const char *molblock = + "atropisomer test mol \n" + " Ketcher 2202613492D 1 1.00000 0.00000 0 \n" + " \n" + " 33 37 0 0 0 0 0 0 0 0999 V2000 \n" + " -4.6637 -1.0934 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " -3.9945 -1.8366 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " -4.2024 -2.8147 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " -3.3364 -3.3147 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " -2.5933 -2.6456 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " -3.0000 -1.7321 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " -2.5000 -0.8660 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " -3.0000 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " -2.5000 0.8660 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " -1.5000 0.8660 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " -1.0000 -0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " -1.5000 -0.8660 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 0.5000 0.8660 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 0.0000 1.7321 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " -1.0000 1.7321 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 1.4781 0.6581 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 1.5827 -0.3364 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 2.4487 -0.8364 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 0.6691 -0.7431 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 0.6342 -1.7425 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " -0.2487 -2.2120 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " -0.2836 -3.2114 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 0.5644 -3.7413 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 1.4474 -3.2718 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 1.4823 -2.2725 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 2.3652 -1.8030 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3.2133 -2.3329 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " -1.0968 -1.6821 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 2.2213 1.3272 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " -4.0000 -0.0000 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " -4.5000 0.8660 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " -3.2319 -4.3092 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 1 2 1 0 0 0 \n" + " 2 3 2 0 0 0 \n" + " 3 4 1 0 0 0 \n" + " 4 5 1 0 0 0 \n" + " 5 6 2 0 0 0 \n" + " 2 6 1 0 0 0 \n" + " 6 7 1 0 0 0 \n" + " 7 8 1 0 0 0 \n" + " 8 9 2 0 0 0 \n" + " 9 10 1 0 0 0 \n" + " 10 11 2 0 0 0 \n" + " 11 12 1 0 0 0 \n" + " 7 12 2 0 0 0 \n" + " 11 13 1 0 0 0 \n" + " 13 14 2 0 0 0 \n" + " 14 15 1 0 0 0 \n" + " 15 16 2 0 0 0 \n" + " 10 16 1 0 0 0 \n" + " 14 17 1 0 0 0 \n" + " 17 18 1 0 0 0 \n" + " 18 19 2 0 0 0 \n" + " 18 20 1 0 0 0 \n" + " 13 20 1 0 0 0 \n" + " 20 21 1 0 0 0 \n" + " 21 22 1 0 0 0 \n" + " 22 23 2 0 0 0 \n" + " 23 24 1 0 0 0 \n" + " 24 25 2 0 0 0 \n" + " 25 26 1 0 0 0 \n" + " 21 26 2 0 0 0 \n" + " 26 27 1 0 0 0 \n" + " 27 28 1 0 0 0 \n" + " 22 29 1 0 0 0 \n" + " 17 30 1 0 0 0 \n" + " 8 31 1 0 0 0 \n" + " 31 32 1 0 0 0 \n" + " 4 33 1 0 0 0 \n" + "M END \n"; + + char options[] = "-Atropisomers"; + inchi_Output output; + inchi_Output *poutput = &output; + const char expected_inchi[] = "InChI=1B/C23H21FN6O3/c1-12-15(11-28(2)27-12)13-6-14-17(7-19(13)32-4)26-9-18-21(14)30(23(31)29(18)3)22-16(24)8-25-10-20(22)33-5/h6-11H,1-5H3"; + + EXPECT_EQ(MakeINCHIFromMolfileText(molblock, options, poutput), 1); + EXPECT_STREQ(poutput->szInChI, expected_inchi); + + FreeINCHI(poutput); +} + +TEST(test_atropisomers, test_dummy_6_two_atropisomer_bonds) +{ + const char *molblock = + "test mol atropisomer \n" + " -INDIGO-02232612272D \n" + " \n" + " 0 0 0 0 0 0 0 0 0 0 0 V3000 \n" + "M V30 BEGIN CTAB \n" + "M V30 COUNTS 35 38 0 0 0 \n" + "M V30 BEGIN ATOM \n" + "M V30 1 C 7.64682 -5.78999 0.0 0 \n" + "M V30 2 C 9.47015 -5.85956 0.0 0 \n" + "M V30 3 C 8.58333 -5.30104 0.0 0 \n" + "M V30 4 C 9.42738 -6.91249 0.0 0 \n" + "M V30 5 C 7.60313 -6.84054 0.0 0 \n" + "M V30 6 C 8.49613 -7.40423 0.0 0 \n" + "M V30 7 C 7.83065 -2.75985 0.0 0 \n" + "M V30 8 C 9.58077 -2.8408 0.0 0 \n" + "M V30 9 C 8.73115 -2.29671 0.0 0 \n" + "M V30 10 C 9.53681 -3.85648 0.0 0 \n" + "M V30 11 C 7.7836 -3.77279 0.0 0 \n" + "M V30 12 C 8.64006 -4.32505 0.0 0 \n" + "M V30 13 Br 6.90205 -7.15024 0.0 0 \n" + "M V30 14 Br 10.3254 -7.4481 0.0 0 \n" + "M V30 15 Br 6.93185 -4.22329 0.0 0 \n" + "M V30 16 Br 10.4302 -4.38621 0.0 0 \n" + "M V30 17 Cl 10.4134 -5.37898 0.0 0 \n" + "M V30 18 Cl 6.83923 -5.25149 0.0 0 \n" + "M V30 19 C 8.55343 -8.50701 0.0 0 \n" + "M V30 20 C 7.7261 -9.10432 0.0 0 \n" + "M V30 21 C 7.02177 -10.6261 0.0 0 \n" + "M V30 22 C 7.81179 -10.0708 0.0 0 \n" + "M V30 23 C 6.13959 -10.2141 0.0 0 \n" + "M V30 24 C 6.84915 -8.69665 0.0 0 \n" + "M V30 25 C 6.05278 -9.25201 0.0 0 \n" + "M V30 26 C 5.14001 -8.79074 0.0 0 \n" + "M V30 27 C 3.39085 -8.89047 0.0 0 \n" + "M V30 28 C 4.29687 -9.34501 0.0 0 \n" + "M V30 29 C 3.33353 -7.88488 0.0 0 \n" + "M V30 30 C 5.08172 -7.77981 0.0 0 \n" + "M V30 31 C 4.18147 -7.33019 0.0 0 \n" + "M V30 32 Br 5.94146 -7.18944 0.0 0 \n" + "M V30 33 Br 4.36521 -10.2925 0.0 0 \n" + "M V30 34 Cl 5.34468 -10.7571 0.0 0 \n" + "M V30 35 Cl 6.77442 -7.90166 0.0 0 \n" + "M V30 END ATOM \n" + "M V30 BEGIN BOND \n" + "M V30 1 1 3 1 CFG=1 \n" + "M V30 2 2 1 5 \n" + "M V30 3 1 5 6 \n" + "M V30 4 2 6 4 \n" + "M V30 5 1 4 2 \n" + "M V30 6 2 2 3 \n" + "M V30 7 2 9 7 \n" + "M V30 8 1 7 11 \n" + "M V30 9 2 11 12 \n" + "M V30 10 1 12 10 CFG=1 \n" + "M V30 11 2 10 8 \n" + "M V30 12 1 8 9 \n" + "M V30 13 1 12 3 \n" + "M V30 14 1 5 13 \n" + "M V30 15 1 4 14 \n" + "M V30 16 1 11 15 \n" + "M V30 17 1 10 16 \n" + "M V30 18 1 2 17 \n" + "M V30 19 1 1 18 \n" + "M V30 20 1 6 19 \n" + "M V30 21 1 19 20 \n" + "M V30 22 2 22 20 \n" + "M V30 23 1 20 24 \n" + "M V30 24 2 24 25 \n" + "M V30 25 1 25 23 CFG=1 \n" + "M V30 26 2 23 21 \n" + "M V30 27 1 21 22 \n" + "M V30 28 1 25 26 \n" + "M V30 29 2 28 26 \n" + "M V30 30 1 26 30 CFG=1 \n" + "M V30 31 2 30 31 \n" + "M V30 32 1 31 29 \n" + "M V30 33 2 29 27 \n" + "M V30 34 1 27 28 \n" + "M V30 35 1 30 32 \n" + "M V30 36 1 28 33 \n" + "M V30 37 1 23 34 \n" + "M V30 38 1 24 35 \n" + "M V30 END BOND \n" + "M V30 BEGIN COLLECTION \n" + "M V30 MDLV30/STEABS ATOMS=(4 3 12 25 26) \n" + "M V30 END COLLECTION \n" + "M V30 END CTAB \n" + "M END \n"; + + char options[] = "-Atropisomers"; + inchi_Output output; + inchi_Output *poutput = &output; + const char expected_inchi[] = "InChI=1B"; + + EXPECT_EQ(MakeINCHIFromMolfileText(molblock, options, poutput), 1); + EXPECT_STREQ(poutput->szInChI, expected_inchi); + + FreeINCHI(poutput); +} + +TEST(test_atropisomers, test_dummy_7_no_atropisomer_1) +{ + const char *molblock = + "non-atropisomer test mol \n" + " Ketcher 2202614 02D 1 1.00000 0.00000 0 \n" + " \n" + " 14 15 0 0 1 0 0 0 0 0999 V2000 \n" + " -0.6402 -0.4251 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 1.0902 -0.4246 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 0.2266 0.0750 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 1.0902 -1.4255 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " -0.6402 -1.4300 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 0.2288 -1.9250 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 1.9546 0.0738 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 2.8218 -0.4265 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 1.9608 -1.9279 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 2.8240 -1.4225 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 1.9548 1.0738 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 0.2272 1.0750 0.0000 Br 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 0.2327 -2.9250 0.0000 Br 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 1.9640 -2.9279 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3 1 1 0 0 0 \n" + " 1 5 2 0 0 0 \n" + " 5 6 1 0 0 0 \n" + " 6 4 2 0 0 0 \n" + " 4 2 1 0 0 0 \n" + " 2 3 2 0 0 0 \n" + " 4 9 1 6 0 0 \n" + " 9 10 2 0 0 0 \n" + " 10 8 1 0 0 0 \n" + " 8 7 2 0 0 0 \n" + " 7 11 1 0 0 0 \n" + " 3 12 1 0 0 0 \n" + " 6 13 1 0 0 0 \n" + " 9 14 1 0 0 0 \n" + " 2 7 1 1 0 0 \n" + "M END \n"; + + char options[] = "-Atropisomers"; + inchi_Output output; + inchi_Output *poutput = &output; + const char expected_inchi[] = "InChI=1B/C12H10Br2/c1-7-3-4-8(2)12-10(14)6-5-9(13)11(7)12/h3-6H,1-2H3"; + + EXPECT_EQ(MakeINCHIFromMolfileText(molblock, options, poutput), 1); + EXPECT_STREQ(poutput->szInChI, expected_inchi); + + FreeINCHI(poutput); +} + +TEST(test_atropisomers, test_dummy_8_no_atropisomer) +{ + const char *molblock = + "no atropisomer \n" + " Ketcher 2272613152D 1 1.00000 0.00000 0 \n" + " \n" + " 19 22 0 0 1 0 0 0 0 0999 V2000 \n" + " 1.7386 -5.4033 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3.4728 -5.4028 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 2.6073 -4.9019 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3.4728 -6.4059 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 1.7386 -6.4105 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 2.6095 -6.9066 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.3393 -4.9032 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.2084 -5.4047 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.3455 -6.9095 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.2106 -6.4028 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.0695 -4.9058 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.9354 -5.4029 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.0800 -6.9019 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.9398 -6.3960 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.3470 -3.9142 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.0676 -3.9065 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.2026 -3.4159 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.3487 -7.9117 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.0854 -7.9042 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3 1 2 0 0 0 \n" + " 1 5 1 0 0 0 \n" + " 5 6 2 0 0 0 \n" + " 6 4 1 0 0 0 \n" + " 4 2 2 0 0 0 \n" + " 2 3 1 0 0 0 \n" + " 4 9 1 0 0 0 \n" + " 9 10 2 0 0 0 \n" + " 10 8 1 0 0 0 \n" + " 8 7 2 0 0 0 \n" + " 7 2 1 0 0 0 \n" + " 10 13 1 1 0 0 \n" + " 13 14 2 0 0 0 \n" + " 14 12 1 0 0 0 \n" + " 12 11 2 0 0 0 \n" + " 11 16 1 0 0 0 \n" + " 16 17 2 0 0 0 \n" + " 17 15 1 0 0 0 \n" + " 15 7 1 0 0 0 \n" + " 9 18 1 0 0 0 \n" + " 13 19 1 0 0 0 \n" + " 8 11 1 6 0 0 \n" + "M END \n"; + + char options[] = "-Atropisomers"; + inchi_Output output; + inchi_Output *poutput = &output; + const char expected_inchi[] = "InChI=1B/C18H14O/c1-11-7-8-13-9-10-19-18-15-6-4-3-5-14(15)12(2)16(11)17(13)18/h3-10H,1-2H3"; + + EXPECT_EQ(MakeINCHIFromMolfileText(molblock, options, poutput), 1); + EXPECT_STREQ(poutput->szInChI, expected_inchi); + + FreeINCHI(poutput); +} + +TEST(test_atropisomers, test_dummy_9_no_atropisomer) +{ + const char *molblock = + "no atropisomer \n" + " Ketcher 2272613262D 1 1.00000 0.00000 0 \n" + " \n" + " 11 12 0 0 1 0 0 0 0 0999 V2000 \n" + " 10.0575 -5.5286 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 11.7917 -5.5281 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 10.9262 -5.0272 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 11.7917 -6.5312 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 10.0575 -6.5357 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 10.9284 -7.0319 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 13.3281 -6.0349 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 12.7485 -5.2267 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 12.7368 -6.8411 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 10.9323 -8.0341 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 13.0431 -7.7954 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3 1 1 0 0 0 \n" + " 1 5 2 0 0 0 \n" + " 5 6 1 0 0 0 \n" + " 6 4 2 0 0 0 \n" + " 4 2 1 0 0 0 \n" + " 2 3 1 1 0 0 \n" + " 4 9 1 1 0 0 \n" + " 9 7 2 0 0 0 \n" + " 7 8 1 0 0 0 \n" + " 8 2 2 0 0 0 \n" + " 6 10 1 0 0 0 \n" + " 9 11 1 0 0 0 \n" + "M END \n"; + + char options[] = "-Atropisomers"; + inchi_Output output; + inchi_Output *poutput = &output; + const char expected_inchi[] = "InChI=1B/C9H9NO/c1-6-3-4-11-9-8(6)7(2)5-10-9/h3-5H,1-2H3"; + + EXPECT_EQ(MakeINCHIFromMolfileText(molblock, options, poutput), 1); + EXPECT_STREQ(poutput->szInChI, expected_inchi); + + FreeINCHI(poutput); +} + +TEST(test_atropisomers, test_dummy_10_no_atropisomer) +{ + const char *molblock = + "no atropisomer \n" + " Ketcher 2272613352D 1 1.00000 0.00000 0 \n" + " \n" + " 13 15 0 0 1 0 0 0 0 0999 V2000 \n" + " 6.1045 -3.7191 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.8417 -4.0800 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.0986 -3.3949 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.5866 -5.0469 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.8981 -4.7379 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.6718 -5.3556 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.9176 -6.9529 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.5935 -6.6481 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.6749 -6.3496 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.8562 -7.5835 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.1260 -7.9570 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.1229 -8.2742 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 8.2186 -5.8385 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3 1 2 0 0 0 \n" + " 1 5 1 0 0 0 \n" + " 5 6 2 0 0 0 \n" + " 6 4 1 1 0 0 \n" + " 4 2 2 0 0 0 \n" + " 9 7 1 1 0 0 \n" + " 7 11 2 0 0 0 \n" + " 12 10 2 0 0 0 \n" + " 10 8 1 0 0 0 \n" + " 8 9 2 0 0 0 \n" + " 6 9 1 0 0 0 \n" + " 4 13 1 0 0 0 \n" + " 13 8 1 0 0 0 \n" + " 3 2 1 1 0 0 \n" + " 12 11 1 1 0 0 \n" + "M END \n"; + + char options[] = "-Atropisomers"; + inchi_Output output; + inchi_Output *poutput = &output; + const char expected_inchi[] = "InChI=1B/C13H10/c1-3-7-12-10(5-1)9-11-6-2-4-8-13(11)12/h1-8H,9H2"; + + EXPECT_EQ(MakeINCHIFromMolfileText(molblock, options, poutput), 1); + EXPECT_STREQ(poutput->szInChI, expected_inchi); + + FreeINCHI(poutput); +} + +TEST(test_atropisomers, test_dummy_11_no_atropisomer_3_fragments) +{ + const char *molblock = + "no atropisomers 3 fragments \n" + " Ketcher 2272613322D 1 1.00000 0.00000 0 \n" + " \n" + " 43 50 0 0 1 0 0 0 0 0999 V2000 \n" + " 6.1045 -3.7191 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.8417 -4.0800 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.0986 -3.3949 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.5866 -5.0469 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.8981 -4.7379 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.6718 -5.3556 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.9176 -6.9529 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.5935 -6.6481 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.6749 -6.3496 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.8562 -7.5835 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.1260 -7.9570 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.1229 -8.2742 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 8.2186 -5.8385 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 9.5037 -3.9296 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 11.2375 -3.9224 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 10.3782 -3.4263 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 11.2430 -4.9363 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 9.5000 -4.9492 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 10.3729 -5.4522 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 9.5486 -6.9885 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 11.2820 -6.9498 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 10.3915 -6.4693 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 11.3184 -7.9397 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 9.5860 -7.9925 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 10.4799 -8.4673 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 12.1187 -5.4213 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 12.1416 -6.4195 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 14.4400 -3.7870 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 16.1721 -3.9293 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 15.3591 -3.3591 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 16.0862 -4.9426 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 14.3481 -4.8068 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 15.1682 -5.3798 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 14.1926 -6.8106 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 15.9249 -6.9467 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 15.0897 -6.3821 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 15.8590 -7.9312 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 14.1208 -7.8014 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 14.9645 -8.3690 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 16.9116 -5.5047 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 16.8379 -6.5024 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 13.4463 -5.2285 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 13.3619 -6.2344 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3 1 2 0 0 0 \n" + " 1 5 1 0 0 0 \n" + " 5 6 2 0 0 0 \n" + " 6 4 1 1 0 0 \n" + " 4 2 2 0 0 0 \n" + " 9 7 1 1 0 0 \n" + " 7 11 2 0 0 0 \n" + " 12 10 2 0 0 0 \n" + " 10 8 1 0 0 0 \n" + " 8 9 2 0 0 0 \n" + " 6 9 1 0 0 0 \n" + " 4 13 1 0 0 0 \n" + " 13 8 1 0 0 0 \n" + " 16 14 2 0 0 0 \n" + " 14 18 1 0 0 0 \n" + " 18 19 2 0 0 0 \n" + " 19 17 1 1 0 0 \n" + " 17 15 2 0 0 0 \n" + " 22 20 1 1 0 0 \n" + " 20 24 2 0 0 0 \n" + " 25 23 2 0 0 0 \n" + " 23 21 1 0 0 0 \n" + " 21 22 2 0 0 0 \n" + " 19 22 1 0 0 0 \n" + " 17 26 1 0 0 0 \n" + " 26 27 2 0 0 0 \n" + " 27 21 1 0 0 0 \n" + " 30 28 2 0 0 0 \n" + " 28 32 1 0 0 0 \n" + " 32 33 2 0 0 0 \n" + " 33 31 1 1 0 0 \n" + " 31 29 2 0 0 0 \n" + " 36 34 1 1 0 0 \n" + " 34 38 2 0 0 0 \n" + " 39 37 2 0 0 0 \n" + " 37 35 1 0 0 0 \n" + " 35 36 2 0 0 0 \n" + " 33 36 1 0 0 0 \n" + " 31 40 1 0 0 0 \n" + " 35 41 1 0 0 0 \n" + " 41 40 2 0 0 0 \n" + " 32 42 1 0 0 0 \n" + " 42 43 2 0 0 0 \n" + " 43 34 1 0 0 0 \n" + " 3 2 1 1 0 0 \n" + " 12 11 1 1 0 0 \n" + " 16 15 1 1 0 0 \n" + " 25 24 1 1 0 0 \n" + " 30 29 1 1 0 0 \n" + " 39 38 1 1 0 0 \n" + "M END \n"; + + char options[] = "-Atropisomers"; + inchi_Output output; + inchi_Output *poutput = &output; + const char expected_inchi[] = "InChI=1B/C16H10.C14H10.C13H10/c1-3-11-7-9-13-5-2-6-14-10-8-12(4-1)15(11)16(13)14;1-3-7-13-11(5-1)9-10-12-6-2-4-8-14(12)13;1-3-7-12-10(5-1)9-11-6-2-4-8-13(11)12/h1-10H;1-10H;1-8H,9H2"; + + EXPECT_EQ(MakeINCHIFromMolfileText(molblock, options, poutput), 1); + EXPECT_STREQ(poutput->szInChI, expected_inchi); + + FreeINCHI(poutput); +} + +TEST(test_atropisomers, test_dummy_12_atropisomer) +{ + const char *molblock = + "atropisomer test mol \n" + " Ketcher 2272615132D 1 1.00000 0.00000 0 \n" + " \n" + " 15 17 0 0 1 0 0 0 0 0999 V2000 \n" + " 5.4357 -3.2991 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.1745 -3.2986 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.3067 -2.7965 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.1745 -4.3043 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.4357 -4.3088 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.3090 -4.8062 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.4357 -6.6150 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.1745 -6.6145 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.3067 -6.1125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 7.1745 -7.6203 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.4357 -7.6248 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.3090 -8.1222 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 8.0449 -6.1125 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 8.0452 -4.8059 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 8.2722 -5.4088 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3 1 2 0 0 0 \n" + " 1 5 1 0 0 0 \n" + " 5 6 2 0 0 0 \n" + " 6 4 1 1 0 0 \n" + " 4 2 2 0 0 0 \n" + " 9 7 1 1 0 0 \n" + " 7 11 2 0 0 0 \n" + " 12 10 2 0 0 0 \n" + " 10 8 1 0 0 0 \n" + " 8 9 2 0 0 0 \n" + " 6 9 1 0 0 0 \n" + " 8 13 1 0 0 0 \n" + " 4 14 1 0 0 0 \n" + " 14 15 1 0 0 0 \n" + " 13 15 1 0 0 0 \n" + " 3 2 1 1 0 0 \n" + " 12 11 1 1 0 0 \n" + "M END \n"; + + char options[] = "-Atropisomers"; + inchi_Output output; + inchi_Output *poutput = &output; + const char expected_inchi[] = "InChI=1B"; + + EXPECT_EQ(MakeINCHIFromMolfileText(molblock, options, poutput), 1); + EXPECT_STREQ(poutput->szInChI, expected_inchi); + + FreeINCHI(poutput); +} + +TEST(test_atropisomers, test_dummy_13_atropisomer_Caryophyllene) +{ + const char *molblock = + "5281515 Caryophyllene \n" + " -OEChem-03022602172D \n" + " \n" + " 39 40 0 1 0 0 0 0 0999 V2000 \n" + " 2.9665 1.2303 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 2.7095 0.2553 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 \n" + " 3.6754 -0.0036 0.0000 C 0 0 2 0 0 0 0 0 0 0 0 0 \n" + " 3.9404 0.9693 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 2.4507 -0.7107 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3.2233 2.1967 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 2.0000 1.4871 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.5415 0.4964 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 2.9507 -1.5767 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.4075 -0.0036 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3.8167 -1.0767 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.5488 -1.0767 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.5415 1.4964 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.6827 -1.5767 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3.8167 -0.0767 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 1.8880 0.4736 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3.7864 -0.8463 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.5388 0.8072 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.1017 1.5679 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 1.9137 -0.4007 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 2.0123 -1.1491 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3.8225 2.0375 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3.3825 2.7959 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 2.6241 2.3559 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 2.1592 2.0863 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 1.4008 1.6463 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 1.8408 0.8879 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 2.4137 -1.8867 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3.2607 -2.1136 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.0251 -0.0576 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.5939 0.5877 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.7860 -1.6495 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 6.1476 -0.9162 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 5.0784 1.8064 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.0045 1.8064 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.6827 -2.1967 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 4.4367 -0.0767 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3.8167 0.5433 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 3.1967 -0.0767 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 \n" + " 1 2 1 0 0 0 0 \n" + " 1 4 1 0 0 0 0 \n" + " 1 6 1 0 0 0 0 \n" + " 1 7 1 0 0 0 0 \n" + " 2 3 1 0 0 0 0 \n" + " 2 5 1 0 0 0 0 \n" + " 2 16 1 6 0 0 0 \n" + " 3 4 1 0 0 0 0 \n" + " 3 8 1 0 0 0 0 \n" + " 3 17 1 1 0 0 0 \n" + " 4 18 1 0 0 0 0 \n" + " 4 19 1 0 0 0 0 \n" + " 5 9 1 0 0 0 0 \n" + " 5 20 1 0 0 0 0 \n" + " 5 21 1 0 0 0 0 \n" + " 6 22 1 0 0 0 0 \n" + " 6 23 1 0 0 0 0 \n" + " 6 24 1 0 0 0 0 \n" + " 7 25 1 0 0 0 0 \n" + " 7 26 1 0 0 0 0 \n" + " 7 27 1 0 0 0 0 \n" + " 8 10 1 0 0 0 0 \n" + " 8 13 2 0 0 0 0 \n" + " 9 11 1 0 0 0 0 \n" + " 9 28 1 0 0 0 0 \n" + " 9 29 1 0 0 0 0 \n" + " 10 12 1 0 0 0 0 \n" + " 10 30 1 0 0 0 0 \n" + " 10 31 1 0 0 0 0 \n" + " 11 14 2 0 0 0 0 \n" + " 11 15 1 0 0 0 0 \n" + " 12 14 1 0 0 0 0 \n" + " 12 32 1 0 0 0 0 \n" + " 12 33 1 0 0 0 0 \n" + " 13 34 1 0 0 0 0 \n" + " 13 35 1 0 0 0 0 \n" + " 14 36 1 0 0 0 0 \n" + " 15 37 1 0 0 0 0 \n" + " 15 38 1 0 0 0 0 \n" + " 15 39 1 0 0 0 0 \n" + "M END \n"; + + char options[] = "-Atropisomers"; + inchi_Output output; + inchi_Output *poutput = &output; + const char expected_inchi[] = "InChI=1B"; + + EXPECT_EQ(MakeINCHIFromMolfileText(molblock, options, poutput), 1); + EXPECT_STREQ(poutput->szInChI, expected_inchi); + + FreeINCHI(poutput); +} diff --git a/INCHI-1-TEST/tests/test_unit/test_enhancedStereo.cpp b/INCHI-1-TEST/tests/test_unit/test_enhancedStereo.cpp index 0a771802..5ee2cf51 100644 --- a/INCHI-1-TEST/tests/test_unit/test_enhancedStereo.cpp +++ b/INCHI-1-TEST/tests/test_unit/test_enhancedStereo.cpp @@ -1117,6 +1117,53 @@ TEST(test_enhancedStereo, test_EnhancedStereochemistry_differing_AND_groups_of_s FreeINCHI(poutput); } +TEST(test_enhancedStereo, test_EnhancedStereochemistry_abs_center_test_1) +{ + const char *molblock = + "test mol \n" + " -INDIGO-02232607492D \n" + " \n" + " 0 0 0 0 0 0 0 0 0 0 0 V3000 \n" + "M V30 BEGIN CTAB \n" + "M V30 COUNTS 7 6 0 0 0 \n" + "M V30 BEGIN ATOM \n" + "M V30 1 O 7.38571 -6.2125 0.0 0 \n" + "M V30 2 C 8.25174 -5.7125 0.0 0 \n" + "M V30 3 C 9.11776 -6.2125 0.0 0 CFG=1 \n" + "M V30 4 C 9.98379 -5.7125 0.0 0 CFG=2 \n" + "M V30 5 Cl 9.11776 -7.2125 0.0 0 \n" + "M V30 6 C 10.8498 -6.2125 0.0 0 \n" + "M V30 7 Br 9.98379 -4.7125 0.0 0 \n" + "M V30 END ATOM \n" + "M V30 BEGIN BOND \n" + "M V30 1 1 1 2 \n" + "M V30 2 1 2 3 \n" + "M V30 3 1 3 4 \n" + "M V30 4 1 3 5 CFG=1 \n" + "M V30 5 1 4 6 \n" + "M V30 6 1 4 7 CFG=1 \n" + "M V30 END BOND \n" + "M V30 BEGIN COLLECTION \n" + "M V30 MDLV30/STEABS ATOMS=(2 3 4) \n" + "M V30 END COLLECTION \n" + "M V30 END CTAB \n" + "M END \n"; + + + char options[] = "-EnhancedStereochemistry"; + inchi_Output output; + inchi_Output *poutput = &output; + const char expected_inchi[] = "InChI=1B/C4H8BrClO/c1-3(5)4(6)2-7/h3-4,7H,2H2,1H3/t3-,4-/m0/s1(3,4)"; + + EXPECT_EQ(MakeINCHIFromMolfileText(molblock, options, poutput), 0); + EXPECT_STREQ(poutput->szInChI, expected_inchi); + + poutput->szLog = nullptr; + poutput->szMessage = nullptr; + + FreeINCHI(poutput); +} + TEST(test_enhancedStereo, test_EnhancedStereochemistry_test_file_1) {