0 | module Chem.AtomType
  1 |
  2 | import Chem.Atom
  3 | import Chem.Elem
  4 | import Chem.Types
  5 | import Data.Maybe
  6 | import Data.SortedMap
  7 | import Derive.Prelude
  8 |
  9 | %language ElabReflection
 10 | %default total
 11 |
 12 | --------------------------------------------------------------------------------
 13 | --          Types
 14 | --------------------------------------------------------------------------------
 15 |
 16 | ||| CDK atom types
 17 | public export
 18 | record AtomType where
 19 |   constructor AT
 20 |   name          : String
 21 |   lonePairCount : Nat
 22 |   hybridization : Hybridization
 23 |   single        : Nat
 24 |   double        : Nat
 25 |   triple        : Nat
 26 |
 27 | %runElab derive "AtomType" [Show,Eq]
 28 |
 29 | ||| Counts of bond types connected to an atom
 30 | public export
 31 | record Bonds where
 32 |   constructor BS
 33 |   single : Nat
 34 |   double : Nat
 35 |   triple : Nat
 36 |
 37 | %runElab derive "Bonds" [Show,Eq,Ord]
 38 |
 39 | ||| Converts a single bond to a value of type `Bonds`
 40 | export
 41 | toBonds : BondOrder -> Bonds
 42 | toBonds Single = BS 1 0 0
 43 | toBonds Dbl    = BS 0 1 0
 44 | toBonds Triple = BS 0 0 1
 45 |
 46 | ||| Add single bonds from implicit hydrogens to the list of bonds
 47 | ||| connected to an atom
 48 | export %inline
 49 | addH : HCount -> Bonds -> Bonds
 50 | addH h = {single $= (+ cast h.value)}
 51 |
 52 | ||| Placeholder for unknown atom types
 53 | |||
 54 | ||| We use this instead of returning an error type such
 55 | ||| as a `Maybe` or an `Either` because it facilitates
 56 | ||| writing algorithms with a certain tolerance for invalid
 57 | ||| molecules
 58 | export
 59 | unknown : AtomType
 60 | unknown = AT "unknown" 0 None 0 0 0
 61 |
 62 | export
 63 | hasPiBonds : Bonds -> Bool
 64 | hasPiBonds (BS _ d t) = d > 0 || t > 0
 65 |
 66 | export
 67 | Semigroup Bonds where
 68 |   BS s1 d1 t1 <+> BS s2 d2 t2 = BS (s1 + s2) (d1 + d2) (t1 + t2)
 69 |
 70 | export
 71 | Monoid Bonds where
 72 |   neutral = BS 0 0 0
 73 |
 74 | ||| Perceiving atom types
 75 | |||
 76 | ||| This is to be used if no implicit hydrogen atoms are given.
 77 | ||| Implicit hydrogen count can then be determined from the atom type.
 78 | export
 79 | atomType : Elem -> Radical -> Charge -> Bonds -> AtomType
 80 |
 81 | ||| General purpose atom type perception
 82 | |||
 83 | ||| This is to be used if no implicit hydrogen atoms are given
 84 | ||| and these should be perceived from the atom type as well.
 85 | export %inline
 86 | atomTypeAndHydrogens : Elem -> Radical -> Charge -> Bonds -> (HCount, AtomType)
 87 | atomTypeAndHydrogens e r c bs =
 88 |   let at := atomType e r c bs
 89 |       h  := fromMaybe 0 $ refineHCount (cast at.single - cast bs.single)
 90 |    in (h,at)
 91 |
 92 | ||| Utility for those cases where the number of implict hydrogens
 93 | ||| (if any) is already included in the number of bonds.
 94 | |||
 95 | ||| This returns `unknown` if the number of single bonds do not
 96 | ||| match exactly.
 97 | export
 98 | exactAtomType : Elem -> Radical -> Charge -> Bonds -> AtomType
 99 |
100 | ||| True, if the given atom type is considered to be invalid.
101 | |||
102 | ||| Currently, the following atom types are considered to be invalid:
103 | ||| "unknown", "N.oxide", "N.sp2.3"
104 | export
105 | isInvalid : AtomType -> Bool
106 | isInvalid at =
107 |   case at.name of
108 |     "unknown" => True
109 |     _         => False
110 |
111 | --------------------------------------------------------------------------------
112 | --          Implementation
113 | --------------------------------------------------------------------------------
114 |
115 | hash : Elem -> Charge -> Radical -> (single,double,triple : Nat) -> Bits32
116 | hash e c r s d t =
117 |   let he := cast (conIndexElem e)      * 100
118 |       hc := (he + cast (c.value + 15)) * 10000
119 |       ht := hc + cast (s * 1000 + d * 100 + t * 10) + cast (conIndexRadical r)
120 |    in ht
121 |
122 | -- convert information about an atom type to a hash for fast lookup
123 | -- in a dictionary, plus the atom type itself
124 | pair :
125 |      (name      : String)
126 |   -> (elem      : Elem)
127 |   -> (charge    : Charge)
128 |   -> (radical   : Radical)
129 |   -> (bonds     : Bonds)
130 |   -> (hybr      : Hybridization)
131 |   -> (lonePairs : Nat)
132 |   -> (Bits32,AtomType)
133 | pair n e c r (BS s d t) h l = (hash e c r s d t, AT n l h s d t)
134 |
135 | -- sorted map from hash to atom type
136 | -- TODO: C.minus.planar
137 | --       N.minus.planar
138 | --       N.nitro
139 | --       N.amide
140 | --       N.thioamide
141 | --       N.planar3
142 | --       O.minus.co2
143 | --       O.sp2.co2
144 | --       O.planar3
145 | --       S.planar3
146 | --       S.only
147 | --       S.thionyl
148 | atMap : SortedMap Bits32 AtomType
149 | atMap =
150 |   SortedMap.fromList
151 |     [ pair "Ag.1"                   Ag 0    NoRadical (BS 1 0 0) None        0
152 |     , pair "Ag.plus"                Ag 1    NoRadical (BS 0 0 0) None        0
153 |     , pair "Al.3minus"              Al (-3) NoRadical (BS 6 0 0) None        0
154 |     , pair "Al"                     Al 0    NoRadical (BS 3 0 0) SP3         0
155 |     , pair "Al.3plus"               Al 3    NoRadical (BS 0 0 0) S           0
156 |     , pair "As.minus"               As (-1) NoRadical (BS 6 0 0) None        0
157 |     , pair "As.2"                   As 0    NoRadical (BS 1 1 0) SP2         1
158 |     , pair "As"                     As 0    NoRadical (BS 3 0 0) SP3         1
159 |     , pair "As.5"                   As 0    NoRadical (BS 3 1 0) SP3         0
160 |     , pair "As.plus"                As 1    NoRadical (BS 4 0 0) SP3         0
161 |     , pair "As.3plus"               As 3    NoRadical (BS 0 0 0) None        0
162 |     , pair "Au.1"                   Au 0    NoRadical (BS 1 0 0) None        0
163 |     , pair "B"                      B  0    NoRadical (BS 3 0 0) SP2         0
164 |     , pair "B.minus"                B  (-1) NoRadical (BS 4 0 0) SP3         0
165 |     , pair "B.3plus"                B  3    NoRadical (BS 4 0 0) SP3         0
166 |     , pair "Ba.2plus"               Ba 2    NoRadical (BS 0 0 0) None        0
167 |     , pair "Be.2minus"              Be (-2) NoRadical (BS 4 0 0) SP3         0
168 |     , pair "Br.minus"               Br (-1) NoRadical (BS 0 0 0) SP3         4
169 |     , pair "Br"                     Br 0    NoRadical (BS 1 0 0) SP3         3
170 |     , pair "Br.3"                   Br 0    NoRadical (BS 1 2 0) SP3         1
171 |     , pair "Br.radical"             Br 0    Singlet   (BS 0 0 0) SP3         3
172 |     , pair "Br.plus.sp2"            Br 1    NoRadical (BS 0 1 0) SP2         2
173 |     , pair "Br.plus.sp3"            Br 1    NoRadical (BS 2 0 0) SP3         2
174 |     , pair "Br.plus.radical"        Br 1    Singlet   (BS 1 0 0) SP3         2
175 |     , pair "C.minus.sp1"            C  (-1) NoRadical (BS 0 0 1) SP          1
176 |     , pair "C.minus.sp2"            C  (-1) NoRadical (BS 1 1 0) SP2         1
177 |     , pair "C.minus.sp3"            C  (-1) NoRadical (BS 3 0 0) SP3         1
178 |     , pair "C.radical.sp1"          C  0    Singlet   (BS 0 0 1) SP          0
179 |     , pair "C.radical.sp2"          C  0    Singlet   (BS 1 1 0) SP2         0
180 |     , pair "C.radical.planar"       C  0    Singlet   (BS 3 0 0) Planar      0
181 |     , pair "C.plus.sp1"             C  1    NoRadical (BS 0 0 1) SP          0
182 |     , pair "C.plus.sp2"             C  1    NoRadical (BS 1 1 0) SP2         0
183 |     , pair "C.plus.planar"          C  1    NoRadical (BS 3 0 0) Planar      0
184 |     , pair "Ca.1"                   Ca 0    NoRadical (BS 0 1 0) None        0
185 |     , pair "Ca.2"                   Ca 0    NoRadical (BS 2 0 0) None        0
186 |     , pair "Ca.2plus"               Ca 2    NoRadical (BS 0 0 0) S           0
187 |     , pair "Cd.2"                   Cd 0    NoRadical (BS 2 0 0) SP          0
188 |     , pair "Cd.2plus"               Cd 2    NoRadical (BS 0 0 0) None        0
189 |     , pair "Cl.minus"               Cl (-1) NoRadical (BS 0 0 0) SP3         4
190 |     , pair "Cl"                     Cl 0    NoRadical (BS 1 0 0) SP3         3
191 |     , pair "Cl.2"                   Cl 0    NoRadical (BS 1 1 0) SP3         2
192 |     , pair "Cl.chlorate"            Cl 0    NoRadical (BS 1 2 0) SP2         0
193 |     , pair "Cl.perchlorate"         Cl 0    NoRadical (BS 1 3 0) SP3         0
194 |     , pair "Cl.radical"             Cl 0    Singlet   (BS 0 0 0) SP3         3
195 |     , pair "Cl.plus.sp2"            Cl 1    NoRadical (BS 0 1 0) SP2         2
196 |     , pair "Cl.plus.sp3"            Cl 1    NoRadical (BS 2 0 0) SP3         2
197 |     , pair "Cl.plus.radical"        Cl 1    Singlet   (BS 1 0 0) SP3         2
198 |     , pair "Cl.perchlorate.charged" Cl 3    NoRadical (BS 4 0 0) SP3         0
199 |     , pair "Co.1"                   Co 0    NoRadical (BS 1 0 0) None        0
200 |     , pair "Co.2"                   Co 0    NoRadical (BS 2 0 0) None        0
201 |     , pair "Co.4"                   Co 0    NoRadical (BS 4 0 0) None        0
202 |     , pair "Co.6"                   Co 0    NoRadical (BS 6 0 0) None        0
203 |     , pair "Co.plus"                Co 1    NoRadical (BS 0 0 0) None        0
204 |     , pair "Co.plus.1"              Co 1    NoRadical (BS 1 0 0) None        0
205 |     , pair "Co.plus.2"              Co 1    NoRadical (BS 2 0 0) None        0
206 |     , pair "Co.plus.4"              Co 1    NoRadical (BS 4 0 0) None        0
207 |     , pair "Co.plus.5"              Co 1    NoRadical (BS 5 0 0) None        0
208 |     , pair "Co.plus.6"              Co 1    NoRadical (BS 6 0 0) None        0
209 |     , pair "Co.2plus"               Co 2    NoRadical (BS 0 0 0) None        0
210 |     , pair "Co.3plus"               Co 3    NoRadical (BS 0 0 0) None        0
211 |     , pair "Cr.4"                   Cr 0    NoRadical (BS 2 2 0) SP3         0
212 |     , pair "Cr"                     Cr 0    NoRadical (BS 6 0 0) Octahedral  0
213 |     , pair "Cr.3plus"               Cr 3    NoRadical (BS 0 0 0) None        0
214 |     , pair "Cr.6plus"               Cr 6    NoRadical (BS 0 0 0) None        0
215 |     , pair "Cu.1"                   Cu 0    NoRadical (BS 1 0 0) None        0
216 |     , pair "Cu.plus"                Cu 1    NoRadical (BS 0 0 0) None        0
217 |     , pair "Cu.2plus"               Cu 2    NoRadical (BS 0 0 0) None        0
218 |     , pair "F.minus"                F  (-1) NoRadical (BS 0 0 0) SP3         4
219 |     , pair "F"                      F  0    NoRadical (BS 1 0 0) SP3         3
220 |     , pair "F.radical"              F  0    Singlet   (BS 0 0 0) SP3         3
221 |     , pair "F.plus.sp2"             F  1    NoRadical (BS 0 1 0) SP2         2
222 |     , pair "F.plus.sp3"             F  1    NoRadical (BS 2 0 0) SP3         2
223 |     , pair "F.plus.radical"         F  1    Singlet   (BS 1 0 0) SP3         2
224 |     , pair "Fe.4minus"              Fe (-4) NoRadical (BS 6 0 0) None        0
225 |     , pair "Fe.3minus"              Fe (-3) NoRadical (BS 6 0 0) None        0
226 |     , pair "Fe.2minus"              Fe (-2) NoRadical (BS 6 0 0) None        0
227 |     , pair "Fe.2"                   Fe 0    NoRadical (BS 2 0 0) None        0
228 |     , pair "Fe.3"                   Fe 0    NoRadical (BS 3 0 0) None        0
229 |     , pair "Fe.4"                   Fe 0    NoRadical (BS 4 0 0) None        0
230 |     , pair "Fe.5"                   Fe 0    NoRadical (BS 5 0 0) None        0
231 |     , pair "Fe.6"                   Fe 0    NoRadical (BS 6 0 0) None        0
232 |     , pair "Fe.plus"                Fe 1    NoRadical (BS 2 0 0) None        0
233 |     , pair "Fe.2plus"               Fe 2    NoRadical (BS 0 0 0) None        0
234 |     , pair "Fe.3plus"               Fe 3    NoRadical (BS 0 0 0) None        0
235 |     , pair "Ga"                     Ga 0    NoRadical (BS 3 0 0) Octahedral  0
236 |     , pair "Ga.3plus"               Ga 3    NoRadical (BS 0 0 0) Octahedral  0
237 |     , pair "Gd.3plus"               Gd 3    NoRadical (BS 0 0 0) None        0
238 |     , pair "Ge.3"                   Ge 0    NoRadical (BS 2 1 0) SP2         0
239 |     , pair "Ge"                     Ge 0    NoRadical (BS 4 0 0) SP3         0
240 |     , pair "H.minus"                H  (-1) NoRadical (BS 0 0 0) S           0
241 |     , pair "H"                      H  0    NoRadical (BS 1 0 0) S           0
242 |     , pair "H.radical"              H  0    Singlet   (BS 0 0 0) S           0
243 |     , pair "H.plus"                 H  1    NoRadical (BS 0 0 0) S           0
244 |     , pair "Hg.minus"               Hg (-1) NoRadical (BS 2 0 0) None        0
245 |     , pair "Hg.1"                   Hg 0    NoRadical (BS 0 1 0) None        0
246 |     , pair "Hg.2"                   Hg 0    NoRadical (BS 2 0 0) None        0
247 |     , pair "Hg.plus"                Hg 1    NoRadical (BS 1 0 0) None        0
248 |     , pair "Hg.2plus"               Hg 2    NoRadical (BS 0 0 0) None        0
249 |     , pair "I.minus"                I  (-1) NoRadical (BS 0 0 0) SP3         4
250 |     , pair "I.minus.5"              I  (-1) NoRadical (BS 2 0 0) SP3D1       3
251 |     , pair "I"                      I  0    NoRadical (BS 1 0 0) SP3         3
252 |     , pair "I.3"                    I  0    NoRadical (BS 1 1 0) SP2         1
253 |     , pair "I.5"                    I  0    NoRadical (BS 1 2 0) SP2         0
254 |     , pair "I.sp3d2.3"              I  0    NoRadical (BS 3 0 0) SP3D2       2
255 |     , pair "I.radical"              I  0    Singlet   (BS 0 0 0) SP3         3
256 |     , pair "I.plus.sp2"             I  1    NoRadical (BS 0 1 0) SP2         2
257 |     , pair "I.plus.sp3"             I  1    NoRadical (BS 2 0 0) SP3         2
258 |     , pair "I.plus.radical"         I  1    Singlet   (BS 1 0 0) SP3         2
259 |     , pair "In.1"                   In 0    NoRadical (BS 0 0 1) None        0
260 |     , pair "In.3"                   In 0    NoRadical (BS 3 0 0) None        0
261 |     , pair "In.3plus"               In 3    NoRadical (BS 0 0 0) None        0
262 |     , pair "K.neutral"              K  0    NoRadical (BS 1 0 0) None        0
263 |     , pair "K.plus"                 K  1    NoRadical (BS 0 0 0) S           0
264 |     , pair "Li"                     Li 0    NoRadical (BS 1 0 0) S           0
265 |     , pair "Li.plus"                Li 1    NoRadical (BS 0 0 0) None        0
266 |     , pair "Mg.neutral.1"           Mg 0    NoRadical (BS 0 1 0) None        0
267 |     , pair "Mg.neutral.2"           Mg 0    NoRadical (BS 2 0 0) None        0
268 |     , pair "Mg.neutral"             Mg 0    NoRadical (BS 4 0 0) None        0
269 |     , pair "Mg.2plus"               Mg 2    NoRadical (BS 0 0 0) S           0
270 |     , pair "Mn.2"                   Mn 0    NoRadical (BS 2 0 0) None        0
271 |     , pair "Mn.2plus"               Mn 2    NoRadical (BS 0 0 0) None        0
272 |     , pair "Mn.3plus"               Mn 3    NoRadical (BS 0 0 0) None        0
273 |     , pair "Mo.4"                   Mo 0    NoRadical (BS 2 2 0) None        0
274 |     , pair "N.minus.sp2"            N  (-1) NoRadical (BS 0 1 0) SP2         2
275 |     , pair "N.minus.sp3"            N  (-1) NoRadical (BS 2 0 0) SP3         2
276 |     , pair "N.sp1"                  N  0    NoRadical (BS 0 0 1) SP          1
277 |     , pair "N.sp1.2"                N  0    NoRadical (BS 0 1 1) SP          0
278 |     , pair "N.sp2"                  N  0    NoRadical (BS 1 1 0) SP2         1
279 |     , pair "N.sp2.3"                N  0    NoRadical (BS 1 2 0) SP2         0
280 |     , pair "N.sp3"                  N  0    NoRadical (BS 3 0 0) SP3         1
281 |     , pair "N.oxide"                N  0    NoRadical (BS 3 1 0) SP2         0
282 |     , pair "N.sp2.radical"          N  0    Singlet   (BS 0 1 0) SP2         1
283 |     , pair "N.sp3.radical"          N  0    Singlet   (BS 2 0 0) SP3         1
284 |     , pair "N.plus.sp1"             N  1    NoRadical (BS 1 0 1) SP          0
285 |     , pair "N.plus.sp2"             N  1    NoRadical (BS 2 1 0) SP2         0
286 |     , pair "N.plus"                 N  1    NoRadical (BS 4 0 0) SP3         0
287 |     , pair "N.plus.sp2.radical"     N  1    Singlet   (BS 1 1 0) SP2         0
288 |     , pair "N.plus.sp3.radical"     N  1    Singlet   (BS 3 0 0) SP3         0
289 |     , pair "Na"                     Na 0    NoRadical (BS 1 0 0) S           0
290 |     , pair "Na.plus"                Na 1    NoRadical (BS 0 0 0) S           0
291 |     , pair "Ni"                     Ni 0    NoRadical (BS 2 0 0) None        0
292 |     , pair "Ni.plus"                Ni 1    NoRadical (BS 1 0 0) None        0
293 |     , pair "Ni.2plus"               Ni 2    NoRadical (BS 0 0 0) None        0
294 |     , pair "O.minus2"               O  (-2) NoRadical (BS 0 0 0) SP3         4
295 |     , pair "O.minus"                O  (-1) NoRadical (BS 1 0 0) SP3         3
296 |     , pair "O.sp2"                  O  0    NoRadical (BS 0 1 0) SP2         2
297 |     , pair "O.sp3"                  O  0    NoRadical (BS 2 0 0) SP3         2
298 |     , pair "O.sp3.radical"          O  0    Singlet   (BS 1 0 0) SP3         2
299 |     , pair "O.plus.sp1"             O  1    NoRadical (BS 0 0 1) SP          1
300 |     , pair "O.plus.sp2"             O  1    NoRadical (BS 1 1 0) SP2         1
301 |     , pair "O.plus"                 O  1    NoRadical (BS 3 0 0) SP3         1
302 |     , pair "O.plus.sp2.radical"     O  1    Singlet   (BS 0 1 0) SP2         1
303 |     , pair "O.plus.radical"         O  1    Singlet   (BS 2 0 0) SP3         1
304 |     , pair "P.ide"                  P  0    NoRadical (BS 0 0 1) SP          1
305 |     , pair "P.irane"                P  0    NoRadical (BS 1 1 0) Planar      1
306 |     , pair "P.ine"                  P  0    NoRadical (BS 3 0 0) SP3         1
307 |     , pair "P.ate"                  P  0    NoRadical (BS 3 1 0) SP3         0
308 |     , pair "P.ane"                  P  0    NoRadical (BS 5 0 0) SP3D1       0
309 |     , pair "P.se.3"                 P  0    Singlet   (BS 0 0 0) SP3         0
310 |     , pair "P.sp1.plus"             P  1    NoRadical (BS 0 2 0) SP          0
311 |     , pair "P.anium"                P  1    NoRadical (BS 2 1 0) SP2         0
312 |     , pair "P.ate.charged"          P  1    NoRadical (BS 4 0 0) SP3         0
313 |     , pair "Pb.1"                   Pb 0    NoRadical (BS 0 1 0) SP          0
314 |     , pair "Pb.2plus"               Pb 2    NoRadical (BS 0 0 0) None        0
315 |     , pair "Po"                     Po 0    NoRadical (BS 2 0 0) None        0
316 |     , pair "Pt.2"                   Pt 0    NoRadical (BS 2 0 0) None        0
317 |     , pair "Pt.4"                   Pt 0    NoRadical (BS 4 0 0) None        0
318 |     , pair "Pt.6"                   Pt 0    NoRadical (BS 6 0 0) None        0
319 |     , pair "Pt.2plus"               Pt 2    NoRadical (BS 0 0 0) None        0
320 |     , pair "Pt.2plus.4"             Pt 2    NoRadical (BS 4 0 0) None        0
321 |     , pair "Rb.neutral"             Rb 0    NoRadical (BS 1 0 0) None        0
322 |     , pair "Rb.plus"                Rb 1    NoRadical (BS 0 0 0) None        0
323 |     , pair "Ru.3minus.6"            Ru (-3) NoRadical (BS 6 0 0) Octahedral  0
324 |     , pair "Ru.2minus.6"            Ru (-2) NoRadical (BS 6 0 0) Octahedral  0
325 |     , pair "Ru.6"                   Ru 0    NoRadical (BS 6 0 0) SP3D2       0
326 |     , pair "S.2minus"               S  (-2) NoRadical (BS 0 0 0) None        4
327 |     , pair "S.minus"                S  (-1) NoRadical (BS 1 0 0) SP3         3
328 |     , pair "S.2"                    S  0    NoRadical (BS 0 1 0) SP2         2
329 |     , pair "S.oxide"                S  0    NoRadical (BS 0 2 0) Planar      3
330 |     , pair "S.trioxide"             S  0    NoRadical (BS 0 3 0) SP2         0
331 |     , pair "S.inyl.2"               S  0    NoRadical (BS 1 0 1) SP2         0
332 |     , pair "S.3"                    S  0    NoRadical (BS 2 0 0) SP3         2
333 |     , pair "S.inyl"                 S  0    NoRadical (BS 2 1 0) SP2         0
334 |     , pair "S.sp3.4"                S  0    NoRadical (BS 2 2 0) SP3         0
335 |     , pair "S.anyl"                 S  0    NoRadical (BS 4 0 0) SP3D2       1
336 |     , pair "S.sp3d1"                S  0    NoRadical (BS 4 1 0) SP3D1       0
337 |     , pair "S.octahedral"           S  0    NoRadical (BS 6 0 0) SP3D2       0
338 |     , pair "S.plus"                 S  1    NoRadical (BS 1 1 0) SP2         1
339 |     , pair "S.inyl.charged"         S  1    NoRadical (BS 3 0 0) SP2         0
340 |     , pair "S.onyl.charged"         S  2    NoRadical (BS 2 2 0) SP3         0
341 |     , pair "Sb.3"                   Sb 0    NoRadical (BS 3 0 0) SP3         1
342 |     , pair "Sb.4"                   Sb 0    NoRadical (BS 3 1 0) None        0
343 |     , pair "Sc.3minus"              Sc (-3) NoRadical (BS 6 0 0) Octahedral  0
344 |     , pair "Se.2minus"              Se (-2) NoRadical (BS 0 0 0) None        4
345 |     , pair "Se.1"                   Se 0    NoRadical (BS 0 1 0) SP2         2
346 |     , pair "Se.sp2.2"               Se 0    NoRadical (BS 0 2 0) SP2         1
347 |     , pair "Se.3"                   Se 0    NoRadical (BS 2 0 0) SP3         2
348 |     , pair "Se.sp3.3"               Se 0    NoRadical (BS 2 1 0) SP3         1
349 |     , pair "Se.sp3.4"               Se 0    NoRadical (BS 2 2 0) SP3         0
350 |     , pair "Se.sp3d1.4"             Se 0    NoRadical (BS 4 0 0) SP3D1       1
351 |     , pair "Se.5"                   Se 0    NoRadical (BS 4 1 0) SP3D1       0
352 |     , pair "Se.plus.3"              Se 1    NoRadical (BS 3 0 0) SP3         1
353 |     , pair "Se.4plus"               Se 4    NoRadical (BS 0 0 0) None        0
354 |     , pair "Si.2minus.6"            Si (-2) NoRadical (BS 6 0 0) SP3D2       0
355 |     , pair "Si.2"                   Si 0    NoRadical (BS 0 2 0) SP          0
356 |     , pair "Si.3"                   Si 0    NoRadical (BS 2 1 0) SP3         0
357 |     , pair "Si.sp3"                 Si 0    NoRadical (BS 4 0 0) SP3         0
358 |     , pair "Sn.sp3"                 Sn 0    NoRadical (BS 4 0 0) SP3         0
359 |     , pair "Sr.2plus"               Sr 2    NoRadical (BS 0 0 0) None        0
360 |     , pair "Te.3"                   Te 0    NoRadical (BS 2 0 0) SP3         2
361 |     , pair "Te.4plus"               Te 4    NoRadical (BS 0 0 0) None        1
362 |     , pair "Ti.3minus"              Ti (-3) NoRadical (BS 6 0 0) Octahedral  0
363 |     , pair "Ti.2"                   Ti 0    NoRadical (BS 0 2 0) Octahedral  0
364 |     , pair "Ti.sp3"                 Ti 0    NoRadical (BS 4 0 0) SP3         0
365 |     , pair "Tl.1"                   Tl 0    NoRadical (BS 1 0 0) SP          1
366 |     , pair "Tl.plus"                Tl 1    NoRadical (BS 0 0 0) None        0
367 |     , pair "V.3minus.4"             V  (-3) NoRadical (BS 3 1 0) Tetrahedral 0
368 |     , pair "V.3minus"               V  (-3) NoRadical (BS 6 0 0) Octahedral  0
369 |     , pair "Xe.3"                   Xe 0    NoRadical (BS 4 0 0) SP3D2       0
370 |     , pair "Zn.1"                   Zn 0    NoRadical (BS 0 1 0) None        0
371 |     , pair "Zn.metallic"            Zn 0    NoRadical (BS 0 0 0) None        0
372 |     , pair "Zn"                     Zn 0    NoRadical (BS 2 0 0) None        0
373 |     , pair "Zn.2plus"               Zn 2    NoRadical (BS 0 0 0) S           0
374 |     ]
375 |
376 | tryAT : Elem -> Radical -> Charge -> Bonds -> AtomType
377 | tryAT e r c (BS s d t) =
378 |   fromMaybe unknown $
379 |         lookup (hash e c r s     d t) atMap
380 |     <|> lookup (hash e c r (s+1) d t) atMap
381 |     <|> lookup (hash e c r (s+2) d t) atMap
382 |     <|> lookup (hash e c r (s+3) d t) atMap
383 |     <|> lookup (hash e c r (s+4) d t) atMap
384 |
385 | elemental : Elem -> AtomType
386 | elemental H  = AT "H" 0 S 1 0 0
387 | elemental N  = AT "N.sp3" 0 SP3 3 0 0
388 | elemental O  = AT "O.sp3" 0 SP3 2 0 0
389 | elemental F  = AT "F" 0 SP3 1 0 0
390 | elemental Cl = AT "Cl" 0 SP3 1 0 0
391 | elemental Br = AT "Br" 0 SP3 1 0 0
392 | elemental I  = AT "I" 0 SP3 1 0 0
393 | elemental B  = AT "B" 0 SP2 3 0 0
394 | elemental P  = AT "P.red" 0 None 0 0 0
395 | elemental e  =
396 |   if isMetal e
397 |      then AT "\{e}.metallic" 0 None 0 0 0
398 |      else AT "\{e}.elemental" 0 None 0 0 0
399 |
400 | c_graphite, c_allene, c_sp, c_sp2, c_sp3 : AtomType
401 | c_graphite = AT "C.graphite" 0 None 0 0 0
402 | c_allene   = AT "C.allene"   0 SP   0 2 0
403 | c_sp       = AT "C.sp"       0 SP   1 0 1
404 | c_sp2      = AT "C.sp2"      0 SP2  2 1 0
405 | c_sp3      = AT "C.sp3"      0 SP3  4 0 0
406 |
407 | -- performance optimized implementation for neutral carbons
408 | atomType C NoRadical 0 bs =
409 |   case bs of
410 |     BS 0 0 0 => c_graphite
411 |     BS s 0 0 => if s <= 4 then c_sp3 else unknown
412 |     BS s 1 0 => if s <= 2 then c_sp2 else unknown
413 |     BS s 0 1 => if s <= 1 then c_sp  else unknown
414 |     BS 0 2 0 => c_allene
415 |     _        => unknown
416 |
417 | -- in the general case, we try to find an atom type
418 | -- by adding up to 4 single bonds to implicit hydrogen atoms
419 | atomType e NoRadical 0 (BS 0 0 0) = elemental e
420 | atomType e r c bs = tryAT e r c bs
421 |
422 | -- as with `radical` this is performance optimized for carbons
423 | exactAtomType C NoRadical 0 bs =
424 |   case bs of
425 |     BS 4 0 0 => c_sp3
426 |     BS 2 1 0 => c_sp2
427 |     BS 1 0 3 => c_sp
428 |     BS 0 2 0 => c_allene
429 |     _        => unknown
430 | exactAtomType e r c (BS s d t) =
431 |   fromMaybe unknown $ lookup (hash e c r s d t) atMap
432 |