servalcat 0.4.60__cp312-cp312-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of servalcat might be problematic. Click here for more details.

Files changed (44) hide show
  1. servalcat/__init__.py +10 -0
  2. servalcat/__main__.py +120 -0
  3. servalcat/ext.cp312-win_amd64.pyd +0 -0
  4. servalcat/refine/__init__.py +0 -0
  5. servalcat/refine/cgsolve.py +100 -0
  6. servalcat/refine/refine.py +733 -0
  7. servalcat/refine/refine_geom.py +207 -0
  8. servalcat/refine/refine_spa.py +327 -0
  9. servalcat/refine/refine_xtal.py +242 -0
  10. servalcat/refine/spa.py +132 -0
  11. servalcat/refine/xtal.py +227 -0
  12. servalcat/refmac/__init__.py +0 -0
  13. servalcat/refmac/exte.py +182 -0
  14. servalcat/refmac/refmac_keywords.py +536 -0
  15. servalcat/refmac/refmac_wrapper.py +360 -0
  16. servalcat/spa/__init__.py +0 -0
  17. servalcat/spa/fofc.py +462 -0
  18. servalcat/spa/fsc.py +385 -0
  19. servalcat/spa/localcc.py +188 -0
  20. servalcat/spa/realspcc_from_var.py +128 -0
  21. servalcat/spa/run_refmac.py +961 -0
  22. servalcat/spa/shift_maps.py +293 -0
  23. servalcat/spa/shiftback.py +137 -0
  24. servalcat/spa/translate.py +129 -0
  25. servalcat/utils/__init__.py +35 -0
  26. servalcat/utils/commands.py +1277 -0
  27. servalcat/utils/fileio.py +745 -0
  28. servalcat/utils/generate_operators.py +296 -0
  29. servalcat/utils/hkl.py +699 -0
  30. servalcat/utils/logger.py +116 -0
  31. servalcat/utils/maps.py +340 -0
  32. servalcat/utils/model.py +774 -0
  33. servalcat/utils/refmac.py +747 -0
  34. servalcat/utils/restraints.py +605 -0
  35. servalcat/utils/symmetry.py +295 -0
  36. servalcat/xtal/__init__.py +0 -0
  37. servalcat/xtal/french_wilson.py +250 -0
  38. servalcat/xtal/run_refmac_small.py +240 -0
  39. servalcat/xtal/sigmaa.py +1403 -0
  40. servalcat-0.4.60.dist-info/METADATA +56 -0
  41. servalcat-0.4.60.dist-info/RECORD +44 -0
  42. servalcat-0.4.60.dist-info/WHEEL +5 -0
  43. servalcat-0.4.60.dist-info/entry_points.txt +4 -0
  44. servalcat-0.4.60.dist-info/licenses/LICENSE +373 -0
@@ -0,0 +1,536 @@
1
+ """
2
+ Author: "Keitaro Yamashita, Garib N. Murshudov"
3
+ MRC Laboratory of Molecular Biology
4
+
5
+ This software is released under the
6
+ Mozilla Public License, version 2.0; see LICENSE.
7
+ """
8
+ from __future__ import absolute_import, division, print_function, generators
9
+ from servalcat.utils import logger
10
+ from servalcat import utils
11
+ import gemmi
12
+ b_to_u = utils.model.b_to_u
13
+
14
+ def parse_atom_spec(s, itk):
15
+ # s: list of keywords
16
+ ret = {}
17
+ while itk < len(s):
18
+ if s[itk].lower().startswith(("chai", "segm")):
19
+ ret["chain"] = s[itk+1]
20
+ itk += 2
21
+ elif s[itk].lower().startswith("resi"):
22
+ ret["resi"] = int(s[itk+1])
23
+ itk += 2
24
+ elif s[itk].lower().startswith("ins"):
25
+ ret["icode"] = s[itk+1] if s[itk+1] != "." else " "
26
+ itk += 2
27
+ elif s[itk].lower().startswith(("atom", "atna", "name")):
28
+ if s[itk+1] == "{":
29
+ idx_close = s[itk+1:].index("}") + itk + 1
30
+ ret["names"] = s[itk+2:idx_close]
31
+ itk = idx_close + 1
32
+ else:
33
+ ret["names"] = [s[itk+1]]
34
+ itk += 2
35
+ elif s[itk].lower().startswith("alt"):
36
+ ret["altloc"] = s[itk+1]
37
+ itk += 2
38
+ elif s[itk].lower().startswith("symm"):
39
+ ret["symm"] = s[itk+1][0].lower() == "y"
40
+ else:
41
+ break
42
+
43
+ return ret, itk
44
+ # parse_atom_spec()
45
+
46
+ def parse_from_to(s, itk):
47
+ # s: list of keywords
48
+ ret = {}
49
+ if not s[itk].lower().startswith("from"):
50
+ raise RuntimeError("invalid from_to instruction: {}".format(s))
51
+
52
+ if s[itk+1] == "*":
53
+ ret["ifirst"] = None # Refmac sets -9999
54
+ else:
55
+ ret["ifirst"] = int(s[itk+1])
56
+
57
+ if s[itk+2].lower() != "to":
58
+ ret["chain"] = s[itk+2]
59
+ assert s[itk+3].lower() == "to"
60
+ itk += 4
61
+ else:
62
+ itk += 3
63
+
64
+ if s[itk] == "*":
65
+ ret["ilast"] = None # Refmac sets 9999
66
+ else:
67
+ ret["ilast"] = int(s[itk])
68
+
69
+ if "chain" not in ret:
70
+ ret["chain"] = s[itk+1]
71
+ itk += 2
72
+ else:
73
+ itk += 2
74
+
75
+ return ret, itk
76
+ # parse_from_to()
77
+
78
+ def read_exte_line(l):
79
+ # using the same variable names as used in read_extra_restraints.f
80
+ s = l.split()
81
+ ret = dict(defaults={})
82
+ if not s: return ret
83
+ defs = ret["defaults"]
84
+ rest_flag_old = rest_flag = False
85
+ if s[0].lower().startswith("exte"):
86
+ if s[1].lower().startswith("gene"): return ret
87
+ elif s[1].lower().startswith("file"): # XXX not supported
88
+ file_ext_now = s[2]
89
+ elif s[1].lower().startswith("syma"): # symall
90
+ # refmac sets "n" by default if "syma" given - but it is not good!
91
+ defs["symall_block"] = "y" if s[2][0].lower() == "y" else "n"
92
+ if len(s) > 3 and s[3].lower().startswith("excl"): # exclude
93
+ defs["exclude_self_block"] = s[4].lower().startswith("self")
94
+ elif s[1].lower().startswith("typa"): # typeall
95
+ #type_default = 2
96
+ defs["type_default"] = max(0, min(2, int(s[2])))
97
+ elif s[1].lower().startswith("alph"): # alphall
98
+ defs["alpha_default"] = float(s[2])
99
+ elif s[1].lower().startswith("verb"): # verbose
100
+ defs["ext_verbose"] = s[2][0].lower() != "n" and not s[2].lower().startswith("off")
101
+ # print("External verbose is on, i.e.") ...
102
+ elif s[1].lower().startswith("weig"): # weight
103
+ itk = 2
104
+ while itk < len(s): # FIXME check out-of-bounds in s[]
105
+ if s[itk].lower().startswith("scal"):
106
+ try:
107
+ defs["scale_sigma_dist"] = float(s[itk+1]) # scale_sigma_loc
108
+ itk += 2
109
+ except ValueError:
110
+ pass
111
+ for k in ("angl", "tors", "chir", "plan", "dist", "inte"):
112
+ if s[itk+1].lower().startswith(k):
113
+ defs["scale_sigma_{}".format(k)] = float(s[itk+2])
114
+ itk += 3
115
+ elif s[itk].lower().startswith("sgmn"):
116
+ defs["sigma_min_loc"] = float(s[itk+1])
117
+ itk += 2
118
+ elif s[itk].lower().startswith("sgmx"):
119
+ defs["sigma_max_loc"] = float(s[itk+1])
120
+ itk += 2
121
+ else:
122
+ raise RuntimeError("Error==> EXTE keyword interpretation: {}".format(l))
123
+ elif s[1].lower().startswith(("miss", "unde")): # undefined
124
+ defs["ignore_undefined"] = s[2].lower().startswith("igno")
125
+ elif s[1].lower().startswith("hydr"): # hydrogen
126
+ defs["ignore_hydrogens"] = s[2].lower().startswith("igno")
127
+ elif s[1].lower().startswith("cut"): # TODO I think this should be parsed outside
128
+ ret["sd_ext_cut"] = float(s[2]) # as this affects everything (not only following blocks)
129
+ elif s[1].lower().startswith("dmax"):
130
+ defs["dist_max_external"] = float(s[2])
131
+ elif s[1].lower().startswith("dmin"):
132
+ defs["dist_min_external"] = float(s[2])
133
+ elif s[1].lower().startswith("use"):
134
+ defs["use_atoms"] = s[2][0].lower()
135
+ if defs["use_atoms"] not in ("a", "m", "h"):
136
+ logger.writeln("invalid exte use keyword: {}".format(s[2]))
137
+ defs["use_atoms"] = "a"
138
+ elif s[1].lower().startswith("conv"):
139
+ if s[2].lower().startswith("pref"):
140
+ defs["prefix_ch"] = s[3] # ???
141
+ elif s[1].lower().startswith(("dist", "plan", "chir", "angl", "inte", "tors")):
142
+ ret["rest_type"] = s[1][:4].lower()
143
+ itk = 2
144
+ iat = 0
145
+ ret["restr"] = {}
146
+ n_expect = dict(plan=0, dist=2, inte=2, angl=3).get(ret["rest_type"], 4)
147
+ ret["restr"]["specs"] = [None for _ in range(n_expect)]
148
+ while itk < len(s):
149
+ if s[itk].lower().startswith(("firs", "seco", "thir", "four", "next", "atre", "atin")):
150
+ iat = dict(firs=0, seco=1, thir=2, four=3).get(s[itk][:4].lower(), iat+1)
151
+ atoms, itk = parse_atom_spec(s, itk+1)
152
+ if ret["rest_type"] == "plan":
153
+ ret["restr"]["specs"].append(atoms)
154
+ else:
155
+ ret["restr"]["specs"][iat] = atoms
156
+ elif s[itk].lower().startswith("type"):
157
+ try:
158
+ ret["restr"]["itype_in"] = int(s[itk+1])
159
+ except ValueError:
160
+ ret["restr"]["itype_in"] = dict(o=0, f=2).get(s[itk+1][0].lower(), 1)
161
+ if not (0 <= ret["restr"]["itype_in"] <= 2):
162
+ logger.writeln("WARNING: wrong type is given. setting to 2.\n=> {}".format(l))
163
+ ret["restr"]["itype_in"] = 2
164
+ itk += 2
165
+ elif s[itk].lower().startswith("symm"): # only for distance
166
+ ret["restr"]["symm_in"] = s[itk+1][0].lower() == "y"
167
+ itk += 2
168
+ else:
169
+ d = dict(valu="value", dmin="dmin", dmax="dmax", smin="smin_value", smax="smax_value",
170
+ sigm="sigma_value", alph="alpha_in", prob="prob_in")
171
+ k = s[itk][:4].lower()
172
+ if k in d:
173
+ ret["restr"][d[k]] = float(s[itk+1])
174
+ itk += 2
175
+ else:
176
+ logger.writeln("unrecognised key: {}\n=> {}".format(s[itk], l))
177
+ elif s[1].lower().startswith("stac"):
178
+ ret["rest_type"] = "stac"
179
+ ret["restr"] = {}
180
+ ret["restr"]["specs"] = [[] for _ in range(2)]
181
+ itk = 2
182
+ #if s[itk].lower().startswith("dist"):
183
+ ip = 0
184
+ while itk < len(s):
185
+ if s[itk].lower().startswith("plan"):
186
+ ip = int(s[itk+1])
187
+ itk += 2
188
+ if ip not in (1, 2):
189
+ raise RuntimeError("Problem with stacking instructions. Plane number can be 1 or 2.\n=> {}".format(l))
190
+ elif s[itk].lower().startswith(("firs", "next")):
191
+ atoms, itk = parse_atom_spec(s, itk+1)
192
+ ret["restr"]["specs"][ip-1] = atoms
193
+ elif s[itk].lower().startswith(("dist", "sddi", "angl", "sdan", "type")):
194
+ k = dict(dist="dist_id", sddi="dist_sd", angl="angle_id", sdan="angle_sd", type="type_r")[s[itk][:4].lower()]
195
+ ret["restr"][k] = float(s[itk+1]) if k != "type_r" else int(s[itk+1])
196
+ itk += 2
197
+ else:
198
+ logger.writeln("WARNING: unrecognised keyword: {}\n=> {}".format(s[itk], l))
199
+ itk += 1
200
+ elif s[1].lower().startswith(("harm", "spec")):
201
+ ret["rest_type"] = s[1][:4].lower() # in Refmac, irest_type = 1 if harm else 2
202
+ ret["restr"] = dict(rectype="", toler=0.5, sigma_t=0.5, sigma_u=2.0 * b_to_u, u_val_incl=False)
203
+ itk = 2
204
+ while itk < len(s):
205
+ if s[itk].lower().startswith("auto"):
206
+ ret["restr"]["rectype"] = "auto"
207
+ itk += 1
208
+ elif s[itk].lower().startswith("atin"):
209
+ ret["restr"]["rectype"] = "atom"
210
+ atoms, itk = parse_atom_spec(s, itk+1)
211
+ ret["restr"]["specs"] = [atoms]
212
+ elif s[itk].lower().startswith("resi"):
213
+ ret["restr"]["rectype"] = "resi"
214
+ fromto, itk = parse_from_to(s, itk+1)
215
+ ret["restr"]["specs"] = [fromto]
216
+ if s[itk].lower().startswith("atom"):
217
+ ret["restr"]["specs"][0]["atom"] = s[itk+1] # called atom_resi in Refmac
218
+ itk += 2
219
+ elif s[itk].lower().startswith("sigm"):
220
+ ret["restr"]["sigma_t"] = float(s[itk+1])
221
+ itk += 2
222
+ elif s[itk].lower().startswith("tole"):
223
+ ret["restr"]["toler"] = float(s[itk+1])
224
+ itk += 2
225
+ elif s[itk].lower().startswith(("uval", "bval")):
226
+ if len(s) > itk+1 and s[itk+1].lower().startswith("incl"):
227
+ ret["restr"]["u_val_incl"] = True
228
+ itk += 2
229
+ else:
230
+ ret["restr"]["u_val_incl"] = False
231
+ itk += 1
232
+ elif s[itk].lower().startswith(("sigb", "sigu")):
233
+ ret["restr"]["sigma_u"] = float(s[itk+1]) * b_to_u
234
+ itk += 2
235
+ else:
236
+ logger.writeln("WARNING: unrecognised keyword: {}\n=> {}".format(s[itk], l))
237
+ itk += 1
238
+
239
+ else:
240
+ logger.writeln("WARNING: cannot parse: {}".format(l))
241
+ return ret
242
+ # read_exte_line()
243
+
244
+ def read_ridge_params(l, r):
245
+ s = l.split()
246
+ assert s[0].lower().startswith("ridg")
247
+ ntok = len(s)
248
+ if s[1].lower().startswith("dist") and ntok > 2:
249
+ if s[2].lower().startswith("with"):
250
+ r.setdefault("groups", []).append({})
251
+ #r["groups"][-1]["sigma"] = sigma_dist_r
252
+ #r["groups"][-1]["dmax"] = dmax_dist_r
253
+ itk = 3
254
+ while itk < ntok:
255
+ if s[itk].lower().startswith("chai"):
256
+ r["groups"][-1]["chain"] = s[itk+1]
257
+ itk += 2
258
+ elif s[itk].lower().startswith("resi"):
259
+ r["groups"][-1]["resi"] = (int(s[itk+1]), int(s[itk+2]))
260
+ itk += 3
261
+ elif s[itk].lower().startswith("sigm"):
262
+ v = float(s[itk+1])
263
+ if v < 0: v = 0.01
264
+ r["groups"][-1]["sigma"] = v
265
+ itk += 2
266
+ elif s[itk].lower().startswith("dmax"):
267
+ v = float(s[itk+1])
268
+ if v < 0: v = 4.2
269
+ r["groups"][-1]["dmax"] = v
270
+ itk += 2
271
+ elif s[2].lower().startswith("incl") and ntok > 3:
272
+ # a: ridge_dist_include_all
273
+ # h: ridge_dist_include_hbond
274
+ # m: ridge_dist_include_main
275
+ v = s[3][0].lower()
276
+ if v in ("a", "h", "m"): r["include"] = v
277
+ elif s[2].lower().startswith("sigm"):
278
+ v = float(s[3])
279
+ r["sigma"] = v if v > 0 else 0.01
280
+ elif s[2].lower().startswith("dmax"):
281
+ v = float(s[3])
282
+ r["dmax"] = v if v > 0 else 4.2
283
+ elif s[2].lower().startswith("inte") and ntok > 3:
284
+ r["interchain"] = s[3][0].lower() == "y"
285
+ elif s[2].lower().startswith("symm") and ntok > 3:
286
+ r["intersym"] = s[3][0].lower() == "y"
287
+ elif s[2].lower().startswith("long") and ntok > 3:
288
+ r["long_range"] = max(0, int(s[3])) # long_range_residue_gap
289
+ elif s[2].lower().startswith("shor") and ntok > 3:
290
+ r["short_range"] = max(0, int(s[3])) # short_range_residue_gap
291
+ elif s[2].lower().startswith("hydr"):
292
+ r["hydrogen"] = ntok < 4 or s[3][0].lower() == "i" # hydrogens_include
293
+ elif s[2].lower().startswith("side") and ntok > 3:
294
+ r["sidechain"] = s[3][0].lower() == "i" # rb_side_chain_include
295
+ elif s[2].lower().startswith("filt"):
296
+ r["bvalue_filter"] = True
297
+ if s[3].lower().startswith("bran"):
298
+ v = float(s[4])
299
+ r["bvalue_filter_range"] = v if v > 0 else 2.0
300
+ else:
301
+ logger.writeln("WARNING: unrecognised keyword: {}\n=> {}".format(s[2], l))
302
+ elif s[1].lower().startswith(("atom", "posi")): # not used
303
+ r["sigma_pos"] = float(s[2]) if ntok > 2 else 0.1
304
+ elif s[1].lower().startswith(("bval", "uval")) and ntok > 2:
305
+ if s[2].lower().startswith("diff"):
306
+ itk = 3
307
+ while itk < ntok:
308
+ if s[itk].lower().startswith("sigm"):
309
+ if ntok > itk + 1:
310
+ r["sigma_uval_diff"] = float(s[itk+1])
311
+ itk += 2
312
+ else:
313
+ r["sigma_uval_diff"] = 0.025
314
+ itk += 1
315
+ elif s[itk].lower().startswith("dmax"):
316
+ if ntok > itk + 1:
317
+ r["dmax_uval_diff"] = float(s[itk+1])
318
+ itk += 2
319
+ else:
320
+ r["dmax_uval_diff"] = 4.2
321
+ itk += 1
322
+ elif s[itk].lower().startswith("dmwe"): # not used
323
+ if ntok > itk + 1:
324
+ r["dmax_uval_weight"] = float(s[itk+1]) * b_to_u
325
+ itk += 2
326
+ else:
327
+ r["dmax_uval_weight"] = 3.0
328
+ itk += 1
329
+ else:
330
+ itk += 1
331
+ else:
332
+ r["sigma_b"] = float(s[2])
333
+ r["sigma_u"] = float(s[2])
334
+ else:
335
+ logger.writeln("WARNING: unrecognised keyword: {}\n=> {}".format(s[1], l))
336
+
337
+ return r
338
+ # read_ridge_params()
339
+
340
+ def read_occupancy_params(l, r):
341
+ s = l.split()
342
+ if not s[0].lower().startswith("occu"):
343
+ return r
344
+ ntok = len(s)
345
+ r.setdefault("groups", {}) # {igr: [{selection}, {selection}, ..]}
346
+ r.setdefault("const", []) # [[is_comp, group_ids]]
347
+ r.setdefault("ncycle", 0) # 0 means no refine
348
+ if (ntok > 4 and
349
+ s[1].lower().startswith("grou") and
350
+ s[2].lower().startswith("id")):
351
+ igr = s[3]
352
+ gr = r["groups"].setdefault(igr, [])
353
+ gr.append({})
354
+ itk = 4
355
+ while itk < ntok:
356
+ if s[itk].lower().startswith(("chai", "segm")):
357
+ gr[-1]["chains"] = []
358
+ itk += 1
359
+ while itk < ntok and not s[itk].lower().startswith(("resi","atom","alt")):
360
+ gr[-1]["chains"].append(s[itk])
361
+ itk += 1
362
+ elif s[itk].lower().startswith("resi"):
363
+ if s[itk+1].lower().startswith("from"):
364
+ gr[-1]["resi_from"] = gemmi.SeqId(s[itk+2])
365
+ if s[itk+3].lower().startswith("to"):
366
+ gr[-1]["resi_to"] = gemmi.SeqId(s[itk+4])
367
+ itk += 5
368
+ else:
369
+ gr[-1]["resi"] = gemmi.SeqId(s[itk+1])
370
+ itk += 2
371
+ elif s[itk].lower().startswith("atom"):
372
+ gr[-1]["atom"] = s[itk+1]
373
+ itk += 2
374
+ elif s[itk].lower().startswith("alt"):
375
+ gr[-1]["alt"] = s[itk+1]
376
+ itk += 2
377
+ elif (ntok > 4 and
378
+ s[1].lower().startswith("grou") and
379
+ s[2].lower().startswith("alts")):
380
+ r["const"].append((s[3].lower().startswith("comp"), s[4:]))
381
+ elif ntok > 1 and s[1].lower().startswith("refi"):
382
+ if ntok > 3 and s[2].lower().startswith("ncyc"):
383
+ r["ncycle"] = max(int(s[3]), r["ncycle"])
384
+ elif r["ncycle"] == 0:
385
+ r["ncycle"] = 1 # default
386
+
387
+ return r
388
+ # read_occupancy_params()
389
+
390
+ def read_make_params(l, r):
391
+ # TODO: hout,ribo,valu,spec,form,sdmi,segi
392
+ s = l.split()
393
+ assert s[0].lower().startswith("make")
394
+ itk = 1
395
+ ntok = len(s)
396
+ # keyword, key, func, possible, default
397
+ keys = (("hydr", "hydr", lambda x: x[0].lower(), set("aynf"), "a"),
398
+ ("hout", "hout", lambda x: x[0].lower() in ("y", "p"), (True, False), True),
399
+ ("chec", "check", lambda x: "0" if x.lower().startswith(("none", "0"))
400
+ else ("n" if x.lower().startswith(("liga", "n"))
401
+ else ("y" if x.lower().startswith(("all", "y")) else None)),
402
+ set("0ny"), None), # no default
403
+ ("newl", "newligand", lambda x: x.lower().startswith(("c", "y", "noex")), (True, False), False),
404
+ ("buil", "build", lambda x: x[0].lower(), set("ny"), "n"),
405
+ ("pept", "pept", lambda x: x[0].lower(), set("yn"), "y"),
406
+ ("link", "link", lambda x: x[0].lower(), set("ynd0"), "y"),
407
+ ("suga", "sugar", lambda x: x[0].lower(), set("ynds"), "y"),
408
+ #("conn", "conn", lambda x: x[0].lower(), set("nyd0"), "n"), # TODO read conn_tolerance? (make conn tole val)
409
+ ("symm", "symm", lambda x: x[0].lower(), set("ync"), "y"),
410
+ ("chai", "chain", lambda x: x[0].lower(), set("yn"), "y"),
411
+ ("cisp", "cispept", lambda x: x[0].lower(), set("yn"), "y"),
412
+ ("ss", "ss", lambda x: x[0].lower(), set("ydn"), "y"),
413
+ ("exit", "exit", lambda x: x[0].lower() == "y", (True, False), True),
414
+ )
415
+ while itk < ntok:
416
+ found = False
417
+ for k, t, f, p, d in keys:
418
+ if s[itk].lower().startswith(k):
419
+ if itk + 1 < len(s):
420
+ r[t] = f(s[itk+1])
421
+ if r[t] not in p:
422
+ raise SystemExit("Invalid make instruction: {}".format(l))
423
+ itk += 2
424
+ elif d is not None:
425
+ r[t] = d # set default
426
+ itk += 1
427
+ else:
428
+ raise SystemExit("Invalid make instruction: {}".format(l))
429
+ break
430
+ else: # if no keywords match (can raise an error if all make keywords are implemented)
431
+ itk += 1
432
+ return r
433
+ # read_make_params()
434
+
435
+ def parse_line(l, ret):
436
+ s = l.split()
437
+ ntok = len(s)
438
+ if ntok == 0: return
439
+ if s[0].lower().startswith("make"):
440
+ read_make_params(l, ret.setdefault("make", {}))
441
+ elif s[0].lower().startswith(("sour", "scat")):
442
+ k = s[1].lower()
443
+ if k.startswith("em"):
444
+ ret["source"] = "em"
445
+ elif k.startswith("e"):
446
+ ret["source"] = "ec"
447
+ elif k.startswith("n"):
448
+ ret["source"] = "ne"
449
+ else:
450
+ ret["source"] = "xr"
451
+ # TODO check mb, lamb
452
+ elif s[0].lower().startswith("refi"): # TODO support this. Note that only valid with hklin
453
+ ret.setdefault("refi", {})
454
+ itk = 1
455
+ while itk < ntok:
456
+ if s[itk].startswith("type"):
457
+ if itk+1 < ntok and s[itk+1].startswith("unre"):
458
+ ret["refi"]["type"] = "unre"
459
+ itk += 2
460
+ else:
461
+ itk += 1
462
+ elif s[0].lower().startswith("dist"):
463
+ try:
464
+ ret["wbond"] = float(s[1])
465
+ except:
466
+ pass
467
+ # TODO read sdex, excu, dele, dmxe, dmne
468
+ elif s[0].lower().startswith("ridg"):
469
+ read_ridge_params(l, ret.setdefault("ridge", {}))
470
+ elif s[0].lower().startswith("occu"):
471
+ read_occupancy_params(l, ret.setdefault("occu", {}))
472
+ elif s[0].lower().startswith("angl") and ntok > 1:
473
+ ret["wangle"] = float(s[1])
474
+ elif s[0].lower().startswith("tors") and ntok > 1:
475
+ ret["wtors"] = float(s[1])
476
+ elif s[0].lower().startswith(("bfac", "temp", "bval")):
477
+ pass # TODO
478
+ elif s[0].lower().startswith("plan") and ntok > 1:
479
+ ret["wplane"] = float(s[1])
480
+ elif s[0].lower().startswith("chir") and ntok > 1:
481
+ ret["wchir"] = float(s[1])
482
+ # TODO read calp
483
+ elif s[0].lower().startswith(("vdwr", "vand", "nonb")) and ntok > 1:
484
+ itk = 1
485
+ try:
486
+ ret["wvdw"] = float(s[itk])
487
+ itk += 1
488
+ except ValueError:
489
+ pass
490
+ # TODO read maxr, over, sigm, incr, chan, vdwc, excl
491
+ # parse_line()
492
+
493
+ def get_lines(lines):
494
+ ret = []
495
+ cont = ""
496
+ for l in lines:
497
+ if "!" in l: l = l[:l.index("!")]
498
+ if "#" in l: l = l[:l.index("#")]
499
+ l = l.strip()
500
+ if not l: continue
501
+ if l[0] == "@":
502
+ f = l[1:]
503
+ yield from get_lines(open(f).readlines())
504
+ continue
505
+ if l.split()[-1] == "-":
506
+ cont += l[:l.rfind("-")] + " "
507
+ continue
508
+ if cont:
509
+ l = cont + l
510
+ cont = ""
511
+ yield l
512
+ # get_lines()
513
+
514
+ def parse_keywords(inputs):
515
+ ret = {"make":{}, "ridge":{}, "refi":{}}
516
+ if not inputs:
517
+ return ret
518
+ for l in get_lines(inputs):
519
+ if l.split()[0].lower().startswith("end"):
520
+ break
521
+ parse_line(l, ret)
522
+ return ret
523
+ # parse_keywords()
524
+
525
+ if __name__ == "__main__":
526
+ import sys
527
+ import json
528
+ print("waiting for input")
529
+ ret = {} #{"make":{}, "ridge":{}, "refi":{}}
530
+ for l in get_lines(sys.stdin):
531
+ if l.split()[0].lower().startswith("end"):
532
+ break
533
+ parse_line(l, ret)
534
+ print()
535
+ print("Parsed:")
536
+ print(json.dumps(ret, indent=1))