1use chematic_core::{AtomIdx, BondOrder, Element, Molecule};
22use chematic_perception::ring_sizes_for_atom;
23
24#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct NumericTypeError(pub String);
28
29impl std::fmt::Display for NumericTypeError {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 write!(f, "MMFF94 numeric type error: {}", self.0)
32 }
33}
34
35static MMFF94_PBCI: &[(u8, f64, f64)] = &[
39 (1, 0.000, 0.000), (2, -0.135, 0.000), (3, -0.095, 0.000), (4, -0.200, 0.000), (5, -0.023, 0.000), (6, -0.243, 0.000), (7, -0.687, 0.000), (8, -0.253, 0.000), (9, -0.306, 0.000), (10, -0.244, 0.000), (11, -0.317, 0.000), (12, -0.304, 0.000), (13, -0.238, 0.000), (14, -0.208, 0.000), (15, -0.236, 0.000), (16, -0.475, 0.000), (17, -0.191, 0.000), (18, -0.118, 0.000), (19, 0.094, 0.000), (20, -0.019, 0.000), (21, 0.157, 0.000), (22, -0.095, 0.000), (23, 0.193, 0.000), (24, 0.257, 0.000), (25, 0.012, 0.000), (26, -0.142, 0.000), (27, 0.094, 0.000), (28, 0.058, 0.000), (29, 0.207, 0.000), (30, -0.166, 0.000), (31, 0.161, 0.000), (32, -0.732, 0.500), (33, 0.257, 0.000), (34, -0.491, 0.000), (35, -0.456, 0.500), (36, -0.031, 0.000), (37, -0.127, 0.000), (38, -0.437, 0.000), (39, -0.104, 0.000), (40, -0.264, 0.000), (41, 0.052, 0.000), (42, -0.757, 0.000), (43, -0.326, 0.000), (44, -0.237, 0.000), (45, -0.260, 0.000), (46, -0.429, 0.000), (47, -0.418, 0.000), (48, -0.525, 0.000), (49, -0.283, 0.000), (50, 0.284, 0.000), (51, -1.046, 0.000), (52, -0.546, 0.000), (53, -0.048, 0.000), (54, -0.424, 0.000), (55, -0.476, 0.000), (56, -0.438, 0.000), (57, -0.105, 0.000), (58, -0.488, 0.000), (59, -0.337, 0.000), (60, -0.635, 0.000), (61, -0.265, 0.000), (62, -0.125, 0.250), (63, -0.180, 0.000), (64, -0.181, 0.000), (65, -0.475, 0.000), (66, -0.467, 0.000), (67, -0.099, 0.000), (68, -0.135, 0.000), (69, -0.099, 0.000), (70, -0.269, 0.000), (71, -0.071, 0.000), (72, -0.580, 0.500), (73, -0.200, 0.000), (74, -0.301, 0.000), (75, -0.255, 0.000), (76, -0.568, 0.250), (77, -0.282, 0.000), (78, -0.168, 0.000), (79, -0.471, 0.000), (80, -0.144, 0.000), (81, -0.514, 0.000), (82, -0.099, 0.000), (83, 0.000, 0.000), (84, 0.000, 0.000), (85, 0.000, 0.000), (86, 0.000, 0.000), (87, 2.000, 0.000), (88, 3.000, 0.000), (89, -1.000, 0.000), (90, -1.000, 0.000), (91, -1.000, 0.000), (92, 1.000, 0.000), (93, 1.000, 0.000), (94, 1.000, 0.000), (95, 2.000, 0.000), (96, 2.000, 0.000), (97, 1.000, 0.000), (98, 2.000, 0.000), (99, 2.000, 0.000), ];
139
140static MMFF94_CHG: &[(u8, u8, u8, f64)] = &[
144 (0, 1, 1, 0.0),
145 (0, 1, 2, -0.1382),
146 (0, 1, 3, -0.061),
147 (0, 1, 4, -0.2),
148 (0, 1, 5, 0.0),
149 (0, 1, 6, -0.28),
150 (0, 1, 8, -0.27),
151 (0, 1, 9, -0.246),
152 (0, 1, 10, -0.3001),
153 (0, 1, 11, -0.34),
154 (0, 1, 12, -0.29),
155 (0, 1, 13, -0.23),
156 (0, 1, 14, -0.19),
157 (0, 1, 15, -0.23),
158 (0, 1, 17, -0.1935),
159 (0, 1, 18, -0.1052),
160 (0, 1, 19, 0.0805),
161 (0, 1, 20, 0.0),
162 (0, 1, 22, -0.095),
163 (0, 1, 25, 0.0),
164 (0, 1, 26, -0.1669),
165 (0, 1, 34, -0.503),
166 (0, 1, 35, -0.4274),
167 (0, 1, 37, -0.1435),
168 (0, 1, 39, -0.2556),
169 (0, 1, 40, -0.3691),
170 (0, 1, 41, 0.106),
171 (0, 1, 43, -0.3557),
172 (0, 1, 45, -0.2402),
173 (0, 1, 46, -0.3332),
174 (0, 1, 54, -0.3461),
175 (0, 1, 55, -0.4895),
176 (0, 1, 56, -0.3276),
177 (0, 1, 57, -0.105),
178 (0, 1, 58, -0.488),
179 (0, 1, 61, -0.2657),
180 (0, 1, 62, -0.2),
181 (0, 1, 63, -0.18),
182 (0, 1, 64, -0.181),
183 (0, 1, 67, -0.099),
184 (0, 1, 68, -0.256),
185 (0, 1, 72, -0.55),
186 (0, 1, 73, -0.0877),
187 (0, 1, 75, -0.255),
188 (0, 1, 78, -0.168),
189 (0, 1, 80, -0.144),
190 (0, 1, 81, -0.514),
191 (0, 2, 2, 0.0),
192 (1, 2, 2, 0.0),
193 (1, 2, 3, -0.0144),
194 (0, 2, 4, -0.065),
195 (1, 2, 4, -0.065),
196 (0, 2, 5, 0.15),
197 (0, 2, 6, -0.0767),
198 (1, 2, 9, -0.171),
199 (0, 2, 10, -0.109),
200 (0, 2, 11, -0.1495),
201 (0, 2, 12, -0.14),
202 (0, 2, 13, -0.11),
203 (0, 2, 14, -0.09),
204 (0, 2, 15, -0.101),
205 (0, 2, 17, -0.056),
206 (0, 2, 18, 0.017),
207 (0, 2, 19, 0.229),
208 (0, 2, 20, 0.116),
209 (0, 2, 22, 0.04),
210 (0, 2, 25, 0.147),
211 (0, 2, 30, -0.031),
212 (0, 2, 34, -0.356),
213 (0, 2, 35, -0.35),
214 (1, 2, 37, 0.0284),
215 (1, 2, 39, 0.031),
216 (0, 2, 40, -0.1),
217 (0, 2, 41, 0.25),
218 (0, 2, 43, -0.191),
219 (0, 2, 45, -0.2044),
220 (0, 2, 46, -0.294),
221 (0, 2, 55, -0.341),
222 (0, 2, 56, -0.303),
223 (0, 2, 62, -0.05),
224 (1, 2, 63, -0.045),
225 (1, 2, 64, -0.046),
226 (1, 2, 67, 0.036),
227 (0, 2, 72, -0.45),
228 (1, 2, 81, -0.379),
229 (1, 3, 3, 0.0),
230 (1, 3, 4, -0.105),
231 (0, 3, 5, 0.06),
232 (0, 3, 6, -0.15),
233 (0, 3, 7, -0.57),
234 (0, 3, 9, -0.45),
235 (1, 3, 9, -0.211),
236 (0, 3, 10, -0.06),
237 (0, 3, 11, -0.222),
238 (0, 3, 12, -0.209),
239 (0, 3, 15, -0.141),
240 (0, 3, 16, -0.38),
241 (0, 3, 17, -0.096),
242 (0, 3, 18, -0.023),
243 (0, 3, 20, 0.053),
244 (0, 3, 22, 0.0),
245 (0, 3, 25, 0.107),
246 (1, 3, 30, -0.071),
247 (0, 3, 35, -0.361),
248 (1, 3, 37, 0.0862),
249 (1, 3, 39, -0.009),
250 (0, 3, 40, -0.05),
251 (0, 3, 41, 0.147),
252 (0, 3, 43, -0.2363),
253 (0, 3, 45, -0.165),
254 (0, 3, 48, -0.43),
255 (0, 3, 51, -0.95),
256 (0, 3, 53, -0.0134),
257 (0, 3, 54, -0.4),
258 (1, 3, 54, -0.329),
259 (0, 3, 55, -0.381),
260 (0, 3, 56, -0.343),
261 (1, 3, 57, -0.01),
262 (1, 3, 58, -0.393),
263 (0, 3, 62, -0.03),
264 (1, 3, 63, -0.085),
265 (1, 3, 64, -0.086),
266 (0, 3, 67, -0.004),
267 (0, 3, 74, -0.319),
268 (0, 3, 75, -0.2474),
269 (1, 3, 78, -0.073),
270 (1, 3, 80, -0.049),
271 (0, 4, 5, 0.177),
272 (0, 4, 6, -0.043),
273 (0, 4, 7, -0.487),
274 (0, 4, 9, -0.3),
275 (1, 4, 9, -0.106),
276 (0, 4, 10, -0.044),
277 (0, 4, 15, -0.036),
278 (0, 4, 20, 0.181),
279 (0, 4, 22, 0.105),
280 (0, 4, 30, 0.034),
281 (1, 4, 37, 0.073),
282 (0, 4, 40, -0.064),
283 (0, 4, 42, -0.5571),
284 (0, 4, 43, -0.126),
285 (1, 4, 63, 0.02),
286 (1, 4, 64, 0.019),
287 (0, 5, 19, 0.2),
288 (0, 5, 20, 0.0),
289 (0, 5, 22, -0.1),
290 (0, 5, 30, -0.15),
291 (0, 5, 37, -0.15),
292 (0, 5, 41, 0.2203),
293 (0, 5, 57, -0.15),
294 (0, 5, 63, -0.15),
295 (0, 5, 64, -0.15),
296 (0, 5, 78, -0.15),
297 (0, 5, 80, -0.15),
298 (0, 6, 6, 0.0),
299 (0, 6, 8, -0.1),
300 (0, 6, 9, -0.063),
301 (0, 6, 10, 0.0355),
302 (0, 6, 15, 0.007),
303 (0, 6, 17, 0.052),
304 (0, 6, 18, 0.1837),
305 (0, 6, 19, 0.2974),
306 (0, 6, 20, 0.2579),
307 (0, 6, 21, 0.4),
308 (0, 6, 22, 0.148),
309 (0, 6, 24, 0.5),
310 (0, 6, 25, 0.2712),
311 (0, 6, 26, 0.101),
312 (0, 6, 29, 0.45),
313 (0, 6, 30, 0.077),
314 (0, 6, 33, 0.5),
315 (0, 6, 37, 0.0825),
316 (0, 6, 39, 0.139),
317 (0, 6, 40, -0.021),
318 (0, 6, 41, 0.295),
319 (0, 6, 43, -0.083),
320 (0, 6, 45, -0.009),
321 (0, 6, 54, -0.181),
322 (0, 6, 55, -0.233),
323 (0, 6, 57, 0.138),
324 (0, 6, 58, -0.245),
325 (0, 6, 63, 0.063),
326 (0, 6, 64, 0.062),
327 (0, 7, 17, 0.5),
328 (0, 7, 46, 0.1618),
329 (0, 7, 74, 0.5),
330 (0, 8, 8, 0.0),
331 (0, 8, 9, -0.053),
332 (0, 8, 10, 0.009),
333 (0, 8, 12, -0.051),
334 (0, 8, 15, 0.017),
335 (0, 8, 17, 0.062),
336 (0, 8, 19, 0.347),
337 (0, 8, 20, 0.2096),
338 (0, 8, 22, 0.158),
339 (0, 8, 23, 0.36),
340 (0, 8, 25, 0.2679),
341 (0, 8, 26, 0.111),
342 (0, 8, 34, -0.238),
343 (0, 8, 39, 0.149),
344 (0, 8, 40, -0.011),
345 (0, 8, 43, -0.073),
346 (0, 8, 45, -0.007),
347 (0, 8, 46, -0.176),
348 (0, 8, 55, -0.223),
349 (0, 8, 56, -0.185),
350 (0, 9, 9, 0.0),
351 (0, 9, 10, 0.062),
352 (0, 9, 12, 0.002),
353 (0, 9, 15, 0.07),
354 (0, 9, 18, 0.188),
355 (0, 9, 19, 0.4),
356 (0, 9, 20, 0.287),
357 (0, 9, 25, 0.318),
358 (0, 9, 27, 0.4),
359 (0, 9, 34, -0.185),
360 (0, 9, 35, -0.15),
361 (1, 9, 37, 0.179),
362 (1, 9, 39, 0.202),
363 (0, 9, 40, 0.042),
364 (0, 9, 41, 0.358),
365 (0, 9, 45, 0.046),
366 (0, 9, 53, 0.3179),
367 (0, 9, 54, -0.118),
368 (0, 9, 55, -0.17),
369 (0, 9, 56, -0.132),
370 (1, 9, 57, 0.201),
371 (0, 9, 62, 0.181),
372 (1, 9, 63, 0.126),
373 (1, 9, 64, 0.125),
374 (0, 9, 67, 0.207),
375 (1, 9, 78, 0.138),
376 (1, 9, 81, -0.208),
377 (0, 10, 10, 0.0),
378 (0, 10, 13, 0.006),
379 (0, 10, 14, 0.036),
380 (0, 10, 15, 0.008),
381 (0, 10, 17, 0.053),
382 (0, 10, 20, 0.225),
383 (0, 10, 22, 0.149),
384 (0, 10, 25, 0.256),
385 (0, 10, 26, 0.102),
386 (0, 10, 28, 0.37),
387 (0, 10, 34, -0.247),
388 (0, 10, 35, -0.212),
389 (0, 10, 37, 0.117),
390 (0, 10, 39, 0.14),
391 (0, 10, 40, -0.02),
392 (0, 10, 41, 0.296),
393 (0, 10, 45, -0.016),
394 (0, 10, 63, 0.064),
395 (0, 10, 64, 0.063),
396 (0, 11, 20, 0.298),
397 (0, 11, 22, 0.2317),
398 (0, 11, 25, 0.329),
399 (0, 11, 26, 0.175),
400 (0, 11, 37, 0.19),
401 (0, 11, 40, 0.053),
402 (0, 12, 15, 0.068),
403 (0, 12, 18, 0.186),
404 (0, 12, 19, 0.3701),
405 (0, 12, 20, 0.29),
406 (0, 12, 22, 0.2273),
407 (0, 12, 25, 0.316),
408 (0, 12, 26, 0.2112),
409 (0, 12, 37, 0.177),
410 (0, 12, 40, 0.04),
411 (0, 12, 57, 0.199),
412 (0, 12, 63, 0.124),
413 (0, 12, 64, 0.123),
414 (0, 13, 20, 0.219),
415 (0, 13, 22, 0.143),
416 (0, 13, 37, 0.111),
417 (0, 13, 64, 0.057),
418 (0, 14, 20, 0.189),
419 (0, 14, 37, 0.081),
420 (0, 15, 15, 0.0),
421 (0, 15, 18, 0.118),
422 (0, 15, 19, 0.33),
423 (0, 15, 20, 0.217),
424 (0, 15, 22, 0.141),
425 (0, 15, 25, 0.248),
426 (0, 15, 26, 0.094),
427 (0, 15, 30, 0.07),
428 (0, 15, 37, 0.1015),
429 (0, 15, 40, -0.028),
430 (0, 15, 43, -0.09),
431 (0, 15, 57, 0.131),
432 (0, 15, 63, 0.056),
433 (0, 15, 64, 0.055),
434 (0, 15, 71, 0.18),
435 (0, 16, 16, 0.0),
436 (0, 17, 17, 0.0),
437 (0, 17, 20, 0.172),
438 (0, 17, 22, 0.096),
439 (0, 17, 37, 0.064),
440 (0, 17, 43, -0.135),
441 (0, 18, 18, 0.0),
442 (0, 18, 20, 0.099),
443 (0, 18, 22, 0.023),
444 (0, 18, 32, -0.65),
445 (0, 18, 37, -0.009),
446 (0, 18, 39, 0.014),
447 (0, 18, 43, -0.138),
448 (0, 18, 48, -0.5895),
449 (0, 18, 55, -0.358),
450 (0, 18, 58, -0.37),
451 (0, 18, 62, 0.2099),
452 (0, 18, 63, -0.062),
453 (0, 18, 64, -0.063),
454 (0, 18, 80, -0.026),
455 (0, 19, 19, 0.0),
456 (0, 19, 20, -0.113),
457 (0, 19, 37, -0.221),
458 (0, 19, 40, -0.358),
459 (0, 19, 63, -0.274),
460 (0, 19, 75, -0.349),
461 (0, 20, 20, 0.0),
462 (0, 20, 22, -0.076),
463 (0, 20, 25, 0.031),
464 (0, 20, 26, -0.123),
465 (0, 20, 30, -0.138),
466 (0, 20, 34, -0.472),
467 (0, 20, 37, -0.108),
468 (0, 20, 40, -0.245),
469 (0, 20, 41, 0.071),
470 (0, 20, 43, -0.307),
471 (0, 20, 45, -0.241),
472 (0, 22, 22, 0.0),
473 (0, 22, 30, -0.071),
474 (0, 22, 34, -0.396),
475 (0, 22, 37, -0.032),
476 (0, 22, 40, -0.169),
477 (0, 22, 41, 0.147),
478 (0, 22, 43, -0.231),
479 (0, 22, 45, -0.165),
480 (0, 23, 39, -0.27),
481 (0, 23, 62, -0.4),
482 (0, 23, 67, -0.292),
483 (0, 23, 68, -0.36),
484 (0, 25, 25, 0.0),
485 (0, 25, 32, -0.7),
486 (0, 25, 37, -0.139),
487 (0, 25, 39, -0.116),
488 (0, 25, 40, -0.276),
489 (0, 25, 43, -0.338),
490 (0, 25, 57, -0.117),
491 (0, 25, 63, -0.192),
492 (0, 25, 71, -0.0362),
493 (0, 25, 72, -0.6773),
494 (0, 26, 26, 0.0),
495 (0, 26, 34, -0.349),
496 (0, 26, 37, 0.015),
497 (0, 26, 40, -0.122),
498 (0, 26, 71, 0.096),
499 (0, 28, 40, -0.4),
500 (0, 28, 43, -0.42),
501 (0, 28, 48, -0.4),
502 (0, 30, 30, 0.0),
503 (0, 30, 40, -0.098),
504 (1, 30, 67, 0.067),
505 (0, 31, 70, -0.43),
506 (0, 32, 41, 0.65),
507 (0, 32, 45, 0.52),
508 (0, 32, 67, 0.633),
509 (0, 32, 68, 0.75),
510 (0, 32, 69, 0.75),
511 (0, 32, 73, 0.35),
512 (0, 32, 77, 0.45),
513 (0, 32, 82, 0.633),
514 (0, 34, 36, 0.45),
515 (0, 34, 37, 0.364),
516 (0, 34, 43, 0.165),
517 (0, 35, 37, 0.329),
518 (0, 35, 63, 0.276),
519 (0, 36, 54, -0.4),
520 (0, 36, 55, -0.45),
521 (0, 36, 56, -0.45),
522 (0, 36, 58, -0.457),
523 (4, 36, 58, -0.45),
524 (0, 36, 81, -0.45),
525 (0, 37, 37, 0.0),
526 (1, 37, 37, 0.0),
527 (0, 37, 38, -0.31),
528 (0, 37, 39, 0.023),
529 (1, 37, 39, 0.023),
530 (0, 37, 40, -0.1),
531 (0, 37, 41, 0.179),
532 (0, 37, 43, -0.199),
533 (0, 37, 45, -0.133),
534 (0, 37, 46, -0.302),
535 (0, 37, 55, -0.349),
536 (0, 37, 56, -0.311),
537 (1, 37, 57, 0.022),
538 (0, 37, 58, -0.361),
539 (1, 37, 58, -0.361),
540 (4, 37, 58, -0.35),
541 (0, 37, 61, -0.138),
542 (0, 37, 62, 0.002),
543 (0, 37, 63, 0.0),
544 (1, 37, 63, -0.053),
545 (0, 37, 64, 0.0),
546 (1, 37, 64, -0.054),
547 (1, 37, 67, 0.028),
548 (0, 37, 69, -0.0895),
549 (0, 37, 78, -0.041),
550 (0, 37, 81, -0.387),
551 (1, 37, 81, -0.387),
552 (0, 38, 38, 0.0),
553 (0, 38, 63, 0.257),
554 (0, 38, 64, 0.256),
555 (0, 38, 69, 0.338),
556 (0, 38, 78, 0.269),
557 (1, 39, 39, 0.0),
558 (0, 39, 40, -0.16),
559 (0, 39, 45, -0.156),
560 (0, 39, 63, -0.1516),
561 (1, 39, 63, -0.076),
562 (0, 39, 64, -0.077),
563 (1, 39, 64, -0.077),
564 (0, 39, 65, -0.418),
565 (0, 39, 78, -0.064),
566 (0, 40, 40, 0.0),
567 (0, 40, 45, 0.004),
568 (0, 40, 46, -0.165),
569 (0, 40, 54, -0.16),
570 (0, 40, 63, 0.084),
571 (0, 40, 64, 0.083),
572 (0, 40, 78, 0.096),
573 (0, 41, 41, 0.0),
574 (0, 41, 55, -0.528),
575 (0, 41, 62, -0.177),
576 (0, 41, 72, -0.5),
577 (0, 41, 80, -0.196),
578 (0, 42, 61, 0.492),
579 (0, 43, 43, 0.0),
580 (0, 43, 45, 0.066),
581 (0, 43, 64, 0.145),
582 (0, 44, 63, 0.04),
583 (0, 44, 65, -0.2207),
584 (0, 44, 78, 0.069),
585 (0, 44, 80, 0.093),
586 (0, 45, 63, 0.08),
587 (0, 45, 64, 0.079),
588 (0, 45, 78, 0.092),
589 (0, 47, 53, 0.37),
590 (0, 49, 50, 0.5673),
591 (0, 51, 52, 0.5),
592 (0, 55, 57, 0.3544),
593 (0, 55, 62, 0.351),
594 (0, 55, 64, 0.295),
595 (0, 55, 80, 0.332),
596 (0, 56, 57, 0.4),
597 (0, 56, 63, 0.258),
598 (0, 56, 80, 0.27),
599 (4, 57, 58, -0.4),
600 (1, 57, 63, -0.075),
601 (1, 57, 64, -0.076),
602 (0, 58, 63, 0.308),
603 (0, 58, 64, 0.307),
604 (0, 59, 63, 0.14),
605 (0, 59, 65, -0.1209),
606 (0, 59, 78, 0.169),
607 (0, 59, 80, 0.193),
608 (0, 59, 82, 0.238),
609 (0, 60, 61, 0.37),
610 (0, 62, 63, -0.055),
611 (0, 62, 64, -0.056),
612 (0, 63, 63, 0.0),
613 (1, 63, 63, 0.0),
614 (0, 63, 64, 0.0),
615 (0, 63, 66, -0.3381),
616 (0, 63, 72, -0.4),
617 (0, 63, 78, 0.012),
618 (0, 63, 81, -0.334),
619 (0, 64, 64, 0.0),
620 (0, 64, 65, -0.2888),
621 (0, 64, 66, -0.2272),
622 (0, 64, 78, 0.013),
623 (0, 64, 81, -0.333),
624 (0, 64, 82, 0.082),
625 (0, 65, 66, 0.0),
626 (0, 65, 78, 0.307),
627 (0, 65, 81, -0.039),
628 (0, 65, 82, 0.376),
629 (0, 66, 66, 0.0),
630 (0, 66, 78, 0.299),
631 (0, 66, 81, -0.047),
632 (0, 71, 75, -0.0958),
633 (0, 72, 73, 0.45),
634 (0, 76, 76, 0.0),
635 (0, 76, 78, 0.4),
636 (0, 78, 78, 0.0),
637 (1, 78, 78, 0.0),
638 (0, 78, 79, -0.303),
639 (0, 78, 81, -0.35),
640 (0, 79, 81, -0.043),
641 (0, 80, 81, -0.4),
642];
643
644pub fn pbci_for(atom_type: u8) -> (f64, f64) {
648 for &(t, pbci, fcadj) in MMFF94_PBCI {
649 if t == atom_type {
650 return (pbci, fcadj);
651 }
652 }
653 (0.0, 0.0)
654}
655
656fn lookup_chg_contribution(bond_type: u8, type_i: u8, type_j: u8) -> Option<f64> {
663 for &(bt, a, b, bci) in MMFF94_CHG {
665 if bt == bond_type && a == type_j && b == type_i {
666 return Some(bci); }
668 }
669 for &(bt, a, b, bci) in MMFF94_CHG {
671 if bt == bond_type && a == type_i && b == type_j {
672 return Some(-bci); }
674 }
675 None
676}
677
678fn bond_type_for(order: BondOrder) -> u8 {
679 match order {
680 BondOrder::Single | BondOrder::Up | BondOrder::Down => 0,
681 BondOrder::Double => 1,
682 BondOrder::Triple => 2,
683 BondOrder::Aromatic => 4,
684 _ => 0,
685 }
686}
687
688pub fn assign_mmff94_numeric_types(mol: &Molecule) -> Result<Vec<u8>, NumericTypeError> {
695 let n = mol.atom_count();
696 let mut types = vec![0u8; n];
697
698 for i in 0..n {
699 let idx = AtomIdx(i as u32);
700 let atom = mol.atom(idx);
701 let t = match atom.element {
702 Element::C => assign_c_type(mol, idx)?,
703 Element::N => assign_n_type(mol, idx)?,
704 Element::O => assign_o_type(mol, idx)?,
705 Element::S => assign_s_type(mol, idx)?,
706 Element::P => assign_p_type(mol, idx)?,
707 Element::SI => 19,
708 Element::F => 11,
709 Element::CL => 12,
710 Element::BR => 13,
711 Element::I => 14,
712 Element::H => assign_h_type(mol, idx)?,
713 _ => return Err(NumericTypeError(format!(
714 "unsupported element {:?} at atom {i}", atom.element
715 ))),
716 };
717 types[i] = t;
718 }
719 Ok(types)
720}
721
722struct BondInfo {
725 neighbor: AtomIdx,
726 order: BondOrder,
727}
728
729fn bonds_of(mol: &Molecule, idx: AtomIdx) -> Vec<BondInfo> {
730 mol.bonds()
731 .filter_map(|(_, b)| {
732 if b.atom1 == idx {
733 Some(BondInfo { neighbor: b.atom2, order: b.order })
734 } else if b.atom2 == idx {
735 Some(BondInfo { neighbor: b.atom1, order: b.order })
736 } else {
737 None
738 }
739 })
740 .collect()
741}
742
743fn count_bond_order(mol: &Molecule, idx: AtomIdx, order: BondOrder) -> usize {
744 bonds_of(mol, idx).iter().filter(|b| b.order == order).count()
745}
746
747fn neighbor_elements(mol: &Molecule, idx: AtomIdx) -> Vec<Element> {
748 bonds_of(mol, idx).iter().map(|b| mol.atom(b.neighbor).element).collect()
749}
750
751fn is_bonded_to(mol: &Molecule, idx: AtomIdx, elem: Element, order: BondOrder) -> bool {
752 bonds_of(mol, idx).iter().any(|b| mol.atom(b.neighbor).element == elem && b.order == order)
753}
754
755fn is_neighbor(mol: &Molecule, idx: AtomIdx, elem: Element) -> bool {
757 neighbor_elements(mol, idx).contains(&elem)
758}
759
760fn is_in_aromatic_ring_of_size(mol: &Molecule, idx: AtomIdx, sz: usize) -> bool {
762 ring_sizes_for_atom(mol, idx.0 as usize)
763 .into_iter()
764 .any(|s| s == sz && mol.atom(idx).aromatic)
765}
766
767fn assign_c_type(mol: &Molecule, idx: AtomIdx) -> Result<u8, NumericTypeError> {
770 let atom = mol.atom(idx);
771
772 if atom.aromatic {
774 return Ok(aromatic_c_type(mol, idx));
775 }
776
777 let double_bonds = count_bond_order(mol, idx, BondOrder::Double);
778 let triple_bonds = count_bond_order(mol, idx, BondOrder::Triple);
779
780 if triple_bonds > 0 {
782 return Ok(4); }
784
785 if double_bonds > 0 {
787 if is_bonded_to(mol, idx, Element::O, BondOrder::Double)
789 || is_bonded_to(mol, idx, Element::S, BondOrder::Double)
790 {
791 return Ok(3); }
793 return Ok(2); }
796
797 Ok(1) }
800
801fn aromatic_c_type(mol: &Molecule, idx: AtomIdx) -> u8 {
802 let ring_sizes = ring_sizes_for_atom(mol, idx.0 as usize);
804 let in_6 = ring_sizes.iter().any(|&s| s == 6);
805 let in_5 = ring_sizes.iter().any(|&s| s == 5);
806
807 if in_6 && !in_5 {
808 return 63; }
810
811 if in_5 {
813 let has_hetero_neighbor = neighbor_elements(mol, idx)
815 .into_iter()
816 .any(|e| matches!(e, Element::N | Element::O | Element::S));
817 if has_hetero_neighbor {
818 return 37; }
820 return 38; }
822
823 64 }
826
827fn assign_n_type(mol: &Molecule, idx: AtomIdx) -> Result<u8, NumericTypeError> {
830 let atom = mol.atom(idx);
831
832 if atom.aromatic {
834 return Ok(aromatic_n_type(mol, idx));
835 }
836
837 let double_bonds = count_bond_order(mol, idx, BondOrder::Double);
838 let triple_bonds = count_bond_order(mol, idx, BondOrder::Triple);
839 let nbrs = bonds_of(mol, idx);
840
841 if atom.charge > 0 {
843 return Ok(32); }
845
846 if triple_bonds > 0 {
848 return Ok(9); }
850
851 if double_bonds > 0 {
853 return Ok(9); }
855
856 let is_amide = nbrs.iter().any(|b| {
858 let nbr = mol.atom(b.neighbor);
859 nbr.element == Element::C && {
860 bonds_of(mol, b.neighbor)
862 .iter()
863 .any(|bb| bb.order == BondOrder::Double && mol.atom(bb.neighbor).element == Element::O)
864 }
865 });
866
867 if is_amide {
868 return Ok(10); }
870
871 let double_o = bonds_of(mol, idx)
873 .iter()
874 .filter(|b| b.order == BondOrder::Double && mol.atom(b.neighbor).element == Element::O)
875 .count();
876 if double_o >= 2 {
877 return Ok(46); }
879
880 Ok(8) }
882
883fn aromatic_n_type(mol: &Molecule, idx: AtomIdx) -> u8 {
884 let ring_sizes = ring_sizes_for_atom(mol, idx.0 as usize);
885 let in_5 = ring_sizes.iter().any(|&s| s == 5);
886
887 let has_h = is_neighbor(mol, idx, Element::H);
889
890 if in_5 {
891 if has_h {
892 return 40; }
894 return 58; }
896
897 67 }
900
901fn assign_o_type(mol: &Molecule, idx: AtomIdx) -> Result<u8, NumericTypeError> {
904 if count_bond_order(mol, idx, BondOrder::Double) > 0 {
906 return Ok(7); }
908
909 if mol.atom(idx).charge < 0 {
911 return Ok(34); }
913
914 Ok(6) }
917
918fn assign_s_type(mol: &Molecule, idx: AtomIdx) -> Result<u8, NumericTypeError> {
921 let atom = mol.atom(idx);
922 if atom.aromatic {
923 return Ok(44); }
925
926 let double_o = bonds_of(mol, idx)
927 .iter()
928 .filter(|b| b.order == BondOrder::Double && mol.atom(b.neighbor).element == Element::O)
929 .count();
930
931 match double_o {
932 2.. => Ok(18), 1 => Ok(17), 0 => {
935 if count_bond_order(mol, idx, BondOrder::Double) > 0 {
937 return Ok(16); }
939 Ok(15) }
941 }
942}
943
944fn assign_p_type(mol: &Molecule, idx: AtomIdx) -> Result<u8, NumericTypeError> {
947 if is_bonded_to(mol, idx, Element::O, BondOrder::Double) {
949 return Ok(25); }
951 Ok(20) }
953
954fn assign_h_type(mol: &Molecule, idx: AtomIdx) -> Result<u8, NumericTypeError> {
957 let nbrs = bonds_of(mol, idx);
958 if nbrs.is_empty() {
959 return Ok(5); }
961 let nbr_atom = mol.atom(nbrs[0].neighbor);
962
963 Ok(match nbr_atom.element {
964 Element::C => 5, Element::O => 24, Element::S => 71, Element::N => {
968 let n_idx = nbrs[0].neighbor;
970 let n_atom = mol.atom(n_idx);
971 if n_atom.aromatic {
972 return Ok(23); }
974 let n_is_amide = bonds_of(mol, n_idx).iter().any(|b| {
975 b.order == BondOrder::Single && mol.atom(b.neighbor).element == Element::C
976 && bonds_of(mol, b.neighbor).iter().any(|bb| {
977 bb.order == BondOrder::Double
978 && mol.atom(bb.neighbor).element == Element::O
979 })
980 });
981 if n_is_amide {
982 28 } else if count_bond_order(mol, n_idx, BondOrder::Double) > 0 {
984 27 } else {
986 23 }
988 }
989 _ => 5,
990 })
991}
992
993pub fn mmff94_charges_numeric(
1003 mol: &Molecule,
1004) -> Result<Vec<f64>, NumericTypeError> {
1005 let types = assign_mmff94_numeric_types(mol)?;
1006 let n = mol.atom_count();
1007 let mut charges = vec![0.0f64; n];
1008
1009 for i in 0..n {
1011 let idx = AtomIdx(i as u32);
1012 let atom = mol.atom(idx);
1013 let (_, fcadj) = pbci_for(types[i]);
1014 let q0 = atom.charge as f64;
1015 let m = bonds_of(mol, idx).len() as f64;
1018 charges[i] = (1.0 - m * fcadj) * q0;
1019 }
1020
1021 for (_, bond) in mol.bonds() {
1023 let i = bond.atom1.0 as usize;
1024 let j = bond.atom2.0 as usize;
1025 let ti = types[i];
1026 let tj = types[j];
1027 let bt = bond_type_for(bond.order);
1028
1029 let ci = lookup_chg_contribution(bt, ti, tj)
1031 .unwrap_or_else(|| pbci_for(ti).0 - pbci_for(tj).0);
1032
1033 let cj = lookup_chg_contribution(bt, tj, ti)
1035 .unwrap_or_else(|| pbci_for(tj).0 - pbci_for(ti).0);
1036
1037 charges[i] += ci;
1038 charges[j] += cj;
1039 }
1040
1041 for i in 0..n {
1043 let idx = AtomIdx(i as u32);
1044 let (_, fcadj_i) = pbci_for(types[i]);
1045 let m = bonds_of(mol, idx).len() as f64;
1046 if fcadj_i > 0.0 {
1047 let sum_fc: f64 = bonds_of(mol, idx)
1049 .iter()
1050 .map(|b| mol.atom(b.neighbor).charge as f64)
1051 .sum();
1052 charges[i] += fcadj_i * sum_fc;
1053 }
1054 for b in bonds_of(mol, idx) {
1057 let nbr = mol.atom(b.neighbor);
1058 if nbr.charge < 0 {
1059 let deg = bonds_of(mol, b.neighbor).len() as f64;
1060 charges[i] += (nbr.charge as f64) / (2.0 * deg);
1061 }
1062 }
1063 }
1064
1065 Ok(charges)
1066}
1067
1068#[cfg(test)]
1071mod tests {
1072 use super::*;
1073 use chematic_smiles::parse;
1074
1075 fn mol(s: &str) -> Molecule {
1076 parse(s).unwrap()
1077 }
1078
1079 #[test]
1082 fn glycine_types_match_mmff94_reference() {
1083 let m = mol("NCC(=O)O");
1087 let types = assign_mmff94_numeric_types(&m).unwrap();
1088
1089 let mut n_types: Vec<u8> = Vec::new();
1091 let mut c_types: Vec<u8> = Vec::new();
1092 let mut o_types: Vec<u8> = Vec::new();
1093
1094 for i in 0..m.atom_count() {
1095 let a = m.atom(AtomIdx(i as u32));
1096 match a.element {
1097 Element::N => n_types.push(types[i]),
1098 Element::C => c_types.push(types[i]),
1099 Element::O => o_types.push(types[i]),
1100 _ => {}
1101 }
1102 }
1103
1104 assert!(n_types.iter().all(|&t| t == 8), "N should be type 8 (NR), got {:?}", n_types);
1106 assert!(c_types.contains(&1), "should have sp3 C (type 1), got {:?}", c_types);
1108 assert!(c_types.contains(&3), "should have carbonyl C (type 3), got {:?}", c_types);
1109 assert!(o_types.contains(&6), "should have OR oxygen (type 6), got {:?}", o_types);
1111 assert!(o_types.contains(&7), "should have O=C oxygen (type 7), got {:?}", o_types);
1112 }
1113
1114 #[test]
1115 fn benzene_aromatic_c_is_type_63() {
1116 let m = mol("c1ccccc1");
1117 let types = assign_mmff94_numeric_types(&m).unwrap();
1118 for i in 0..m.atom_count() {
1119 let a = m.atom(AtomIdx(i as u32));
1120 if a.element == Element::C {
1121 assert_eq!(types[i], 63, "benzene C should be type 63 (CB)");
1122 }
1123 }
1124 }
1125
1126 #[test]
1127 fn pyridine_n_is_type_67() {
1128 let m = mol("c1ccncc1");
1129 let types = assign_mmff94_numeric_types(&m).unwrap();
1130 for i in 0..m.atom_count() {
1131 let a = m.atom(AtomIdx(i as u32));
1132 if a.element == Element::N {
1133 assert_eq!(types[i], 67, "pyridine N should be type 67 (N6A)");
1134 }
1135 }
1136 }
1137
1138 #[test]
1139 fn halogens_map_correctly() {
1140 let m = mol("CF");
1141 let types = assign_mmff94_numeric_types(&m).unwrap();
1142 for i in 0..m.atom_count() {
1143 let a = m.atom(AtomIdx(i as u32));
1144 match a.element {
1145 Element::F => assert_eq!(types[i], 11),
1146 Element::C => assert_eq!(types[i], 1),
1147 _ => {}
1148 }
1149 }
1150 let m2 = mol("CCl");
1151 let types2 = assign_mmff94_numeric_types(&m2).unwrap();
1152 for i in 0..m2.atom_count() {
1153 if m2.atom(AtomIdx(i as u32)).element == Element::CL {
1154 assert_eq!(types2[i], 12);
1155 }
1156 }
1157 }
1158
1159 #[test]
1160 fn amide_n_is_type_10() {
1161 let m = mol("NC(=O)C");
1163 let types = assign_mmff94_numeric_types(&m).unwrap();
1164 for i in 0..m.atom_count() {
1165 let a = m.atom(AtomIdx(i as u32));
1166 if a.element == Element::N {
1167 assert_eq!(types[i], 10, "amide N should be type 10 (NC=O)");
1168 }
1169 }
1170 }
1171
1172 #[test]
1173 fn sulfoxide_is_type_17_sulfone_is_type_18() {
1174 let m_so = mol("CS(=O)C"); let types_so = assign_mmff94_numeric_types(&m_so).unwrap();
1176 let m_s2 = mol("CS(=O)(=O)C"); let types_s2 = assign_mmff94_numeric_types(&m_s2).unwrap();
1178
1179 for i in 0..m_so.atom_count() {
1180 if m_so.atom(AtomIdx(i as u32)).element == Element::S {
1181 assert_eq!(types_so[i], 17, "DMSO S should be type 17 (SO)");
1182 }
1183 }
1184 for i in 0..m_s2.atom_count() {
1185 if m_s2.atom(AtomIdx(i as u32)).element == Element::S {
1186 assert_eq!(types_s2[i], 18, "DMSO2 S should be type 18 (SO2)");
1187 }
1188 }
1189 }
1190
1191 #[test]
1194 fn charge_sum_equals_formal_charge_methane() {
1195 let m = mol("C");
1196 let q = mmff94_charges_numeric(&m).unwrap();
1197 let total: f64 = q.iter().sum();
1198 assert!(total.abs() < 0.1, "methane net charge = {total:.4}");
1199 }
1200
1201 #[test]
1202 fn charge_sum_equals_formal_charge_glycine() {
1203 let m = mol("NCC(=O)O");
1204 let q = mmff94_charges_numeric(&m).unwrap();
1205 let total: f64 = q.iter().sum();
1206 assert!(total.abs() < 0.15, "glycine net charge = {total:.4}");
1207 }
1208
1209 #[test]
1210 fn carbonyl_oxygen_is_most_negative() {
1211 let m = mol("CC(=O)C");
1213 let types = assign_mmff94_numeric_types(&m).unwrap();
1214 let q = mmff94_charges_numeric(&m).unwrap();
1215 let (o_idx, _) = m.atoms()
1216 .find(|(_, a)| a.element == Element::O)
1217 .unwrap();
1218 let o_charge = q[o_idx.0 as usize];
1219 assert!(o_charge < -0.3, "ketone O charge = {o_charge:.3}, expected < -0.3");
1220 assert_eq!(types[o_idx.0 as usize], 7, "ketone O should be type 7");
1222 }
1223
1224 #[test]
1225 fn amine_n_is_negative() {
1226 let m = mol("CCN");
1227 let q = mmff94_charges_numeric(&m).unwrap();
1228 let n_charge = m.atoms()
1229 .find(|(_, a)| a.element == Element::N)
1230 .map(|(i, _)| q[i.0 as usize])
1231 .unwrap();
1232 assert!(n_charge < -0.1, "amine N charge = {n_charge:.3}, expected negative");
1233 }
1234
1235 #[test]
1236 fn h_on_nitrogen_is_positive() {
1237 let m = mol("C[NH2]");
1239 let q = mmff94_charges_numeric(&m).unwrap();
1240 let types = assign_mmff94_numeric_types(&m).unwrap();
1241 let h_charges: Vec<f64> = m.atoms()
1243 .filter(|(i, a)| a.element == Element::H && types[i.0 as usize] == 23)
1244 .map(|(i, _)| q[i.0 as usize])
1245 .collect();
1246 if h_charges.is_empty() {
1247 let n_charge = m.atoms()
1249 .find(|(_, a)| a.element == Element::N)
1250 .map(|(i, _)| q[i.0 as usize])
1251 .unwrap();
1252 assert!(n_charge < 0.0, "amine N charge = {n_charge:.3}, expected negative");
1253 } else {
1254 for &hq in &h_charges {
1255 assert!(hq > 0.05, "H-N charge = {hq:.3}, expected positive");
1256 }
1257 }
1258 }
1259
1260 #[test]
1261 fn pbci_table_has_99_entries() {
1262 assert_eq!(MMFF94_PBCI.len(), 99);
1263 }
1264
1265 #[test]
1266 fn chg_table_has_498_entries() {
1267 assert_eq!(MMFF94_CHG.len(), 498);
1268 }
1269
1270 #[test]
1271 fn glycine_h_types_correct() {
1272 let m = mol("[NH2]CC(=O)O");
1276 let types = assign_mmff94_numeric_types(&m).unwrap();
1277 for i in 0..m.atom_count() {
1278 let a = m.atom(AtomIdx(i as u32));
1279 if a.element == Element::H {
1280 let t = types[i];
1281 assert!(
1282 matches!(t, 5 | 23 | 24),
1283 "H type should be 5/23/24, got {t}"
1284 );
1285 }
1286 }
1287 }
1288
1289 #[test]
1290 fn furan_o_is_type_43() {
1291 let m = mol("o1cccc1"); let types_result = assign_mmff94_numeric_types(&m);
1297 if let Ok(types) = types_result {
1300 for i in 0..m.atom_count() {
1301 if m.atom(AtomIdx(i as u32)).element == Element::O {
1302 let t = types[i];
1304 assert!(matches!(t, 43 | 6), "furan O type = {t}");
1305 }
1306 }
1307 }
1308 }
1309}