servalcat 0.4.99__cp310-cp310-macosx_10_14_x86_64.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 (45) hide show
  1. servalcat/__init__.py +10 -0
  2. servalcat/__main__.py +120 -0
  3. servalcat/ext.cpython-310-darwin.so +0 -0
  4. servalcat/refine/__init__.py +0 -0
  5. servalcat/refine/cgsolve.py +100 -0
  6. servalcat/refine/refine.py +906 -0
  7. servalcat/refine/refine_geom.py +233 -0
  8. servalcat/refine/refine_spa.py +366 -0
  9. servalcat/refine/refine_xtal.py +281 -0
  10. servalcat/refine/spa.py +144 -0
  11. servalcat/refine/xtal.py +276 -0
  12. servalcat/refmac/__init__.py +0 -0
  13. servalcat/refmac/exte.py +182 -0
  14. servalcat/refmac/refmac_keywords.py +639 -0
  15. servalcat/refmac/refmac_wrapper.py +395 -0
  16. servalcat/spa/__init__.py +0 -0
  17. servalcat/spa/fofc.py +479 -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 +977 -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 +1547 -0
  27. servalcat/utils/fileio.py +744 -0
  28. servalcat/utils/generate_operators.py +296 -0
  29. servalcat/utils/hkl.py +714 -0
  30. servalcat/utils/logger.py +140 -0
  31. servalcat/utils/maps.py +345 -0
  32. servalcat/utils/model.py +782 -0
  33. servalcat/utils/refmac.py +760 -0
  34. servalcat/utils/restraints.py +781 -0
  35. servalcat/utils/symmetry.py +295 -0
  36. servalcat/xtal/__init__.py +0 -0
  37. servalcat/xtal/french_wilson.py +258 -0
  38. servalcat/xtal/run_refmac_small.py +240 -0
  39. servalcat/xtal/sigmaa.py +1644 -0
  40. servalcat/xtal/twin.py +121 -0
  41. servalcat-0.4.99.dist-info/METADATA +55 -0
  42. servalcat-0.4.99.dist-info/RECORD +45 -0
  43. servalcat-0.4.99.dist-info/WHEEL +5 -0
  44. servalcat-0.4.99.dist-info/entry_points.txt +4 -0
  45. servalcat-0.4.99.dist-info/licenses/LICENSE +373 -0
@@ -0,0 +1,639 @@
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.utils import model as model_util
11
+ import gemmi
12
+ b_to_u = model_util.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
+ itk += 2
41
+ else:
42
+ break
43
+
44
+ return ret, itk
45
+ # parse_atom_spec()
46
+
47
+ def parse_from_to(s, itk):
48
+ # s: list of keywords
49
+ ret = {}
50
+ if not s[itk].lower().startswith("from"):
51
+ raise RuntimeError("invalid from_to instruction: {}".format(s))
52
+
53
+ if s[itk+1] == "*":
54
+ ret["ifirst"] = None # Refmac sets -9999
55
+ else:
56
+ ret["ifirst"] = int(s[itk+1])
57
+
58
+ if s[itk+2].lower() != "to":
59
+ ret["chain"] = s[itk+2]
60
+ assert s[itk+3].lower() == "to"
61
+ itk += 4
62
+ else:
63
+ itk += 3
64
+
65
+ if s[itk] == "*":
66
+ ret["ilast"] = None # Refmac sets 9999
67
+ else:
68
+ ret["ilast"] = int(s[itk])
69
+
70
+ if "chain" not in ret:
71
+ ret["chain"] = s[itk+1]
72
+ itk += 2
73
+ else:
74
+ itk += 2
75
+
76
+ return ret, itk
77
+ # parse_from_to()
78
+
79
+ def read_exte(s):
80
+ # using the same variable names as used in read_extra_restraints.f
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
+ itk += 1
107
+ if itk >= len(s): break
108
+ try:
109
+ defs["scale_sigma_dist"] = float(s[itk]) # scale_sigma_loc
110
+ itk += 1
111
+ continue
112
+ except ValueError:
113
+ pass
114
+ for k in ("angl", "tors", "chir", "plan", "dist", "inte"):
115
+ if s[itk].lower().startswith(k):
116
+ itk += 1
117
+ if itk >= len(s): break
118
+ try:
119
+ defs["scale_sigma_{}".format(k)] = float(s[itk])
120
+ itk += 1
121
+ break
122
+ except ValueError:
123
+ pass
124
+ elif s[itk].lower().startswith("sgmn"):
125
+ defs["sigma_min_loc"] = float(s[itk+1])
126
+ itk += 2
127
+ elif s[itk].lower().startswith("sgmx"):
128
+ defs["sigma_max_loc"] = float(s[itk+1])
129
+ itk += 2
130
+ else:
131
+ raise RuntimeError("Error==> EXTE keyword interpretation: {}".format(" ".join(s)))
132
+ elif s[1].lower().startswith(("miss", "unde")): # undefined
133
+ defs["ignore_undefined"] = s[2].lower().startswith("igno")
134
+ elif s[1].lower().startswith("hydr"): # hydrogen
135
+ defs["ignore_hydrogens"] = s[2].lower().startswith("igno")
136
+ elif s[1].lower().startswith("cut"): # TODO I think this should be parsed outside
137
+ ret["sd_ext_cut"] = float(s[2]) # as this affects everything (not only following blocks)
138
+ elif s[1].lower().startswith("dmax"):
139
+ defs["dist_max_external"] = float(s[2])
140
+ elif s[1].lower().startswith("dmin"):
141
+ defs["dist_min_external"] = float(s[2])
142
+ elif s[1].lower().startswith("use"):
143
+ defs["use_atoms"] = s[2][0].lower()
144
+ if defs["use_atoms"] not in ("a", "m", "h"):
145
+ logger.writeln("invalid exte use keyword: {}".format(s[2]))
146
+ defs["use_atoms"] = "a"
147
+ elif s[1].lower().startswith("conv"):
148
+ if s[2].lower().startswith("pref"):
149
+ defs["prefix_ch"] = s[3] # ???
150
+ elif s[1].lower().startswith(("dist", "plan", "chir", "angl", "inte", "tors")):
151
+ ret["rest_type"] = s[1][:4].lower()
152
+ itk = 2
153
+ iat = 0
154
+ ret["restr"] = {}
155
+ n_expect = dict(plan=0, dist=2, inte=2, angl=3).get(ret["rest_type"], 4)
156
+ ret["restr"]["specs"] = [None for _ in range(n_expect)]
157
+ while itk < len(s):
158
+ if s[itk].lower().startswith(("firs", "seco", "thir", "four", "next", "atre", "atin")):
159
+ iat = dict(firs=0, seco=1, thir=2, four=3).get(s[itk][:4].lower(), iat+1)
160
+ atoms, itk = parse_atom_spec(s, itk+1)
161
+ if ret["rest_type"] == "plan":
162
+ ret["restr"]["specs"].append(atoms)
163
+ else:
164
+ ret["restr"]["specs"][iat] = atoms
165
+ elif s[itk].lower().startswith("type"):
166
+ try:
167
+ ret["restr"]["itype_in"] = int(s[itk+1])
168
+ except ValueError:
169
+ ret["restr"]["itype_in"] = dict(o=0, f=2).get(s[itk+1][0].lower(), 1)
170
+ if not (0 <= ret["restr"]["itype_in"] <= 2):
171
+ logger.writeln("WARNING: wrong type is given. setting to 2.\n=> {}".format(" ".join(s)))
172
+ ret["restr"]["itype_in"] = 2
173
+ itk += 2
174
+ elif s[itk].lower().startswith("symm"): # only for distance and angle
175
+ ret["restr"]["symm_in"] = s[itk+1][0].lower() == "y"
176
+ itk += 2
177
+ else:
178
+ d = dict(valu="value", dmin="dmin", dmax="dmax", smin="smin_value", smax="smax_value",
179
+ sigm="sigma_value", alph="alpha_in", prob="prob_in")
180
+ k = s[itk][:4].lower()
181
+ if k in d:
182
+ ret["restr"][d[k]] = float(s[itk+1])
183
+ itk += 2
184
+ else:
185
+ logger.writeln("unrecognised key: {}\n=> {}".format(s[itk], " ".join(s)))
186
+ break
187
+ elif s[1].lower().startswith("stac"):
188
+ ret["rest_type"] = "stac"
189
+ ret["restr"] = {}
190
+ ret["restr"]["specs"] = [[] for _ in range(2)]
191
+ itk = 2
192
+ #if s[itk].lower().startswith("dist"):
193
+ ip = 0
194
+ while itk < len(s):
195
+ if s[itk].lower().startswith("plan"):
196
+ ip = int(s[itk+1])
197
+ itk += 2
198
+ if ip not in (1, 2):
199
+ raise RuntimeError("Problem with stacking instructions. Plane number can be 1 or 2.\n=> {}".format(" ".join(s)))
200
+ elif s[itk].lower().startswith(("firs", "next")):
201
+ atoms, itk = parse_atom_spec(s, itk+1)
202
+ ret["restr"]["specs"][ip-1] = atoms
203
+ elif s[itk].lower().startswith(("dist", "sddi", "angl", "sdan", "type")):
204
+ k = dict(dist="dist_id", sddi="dist_sd", angl="angle_id", sdan="angle_sd", type="type_r")[s[itk][:4].lower()]
205
+ ret["restr"][k] = float(s[itk+1]) if k != "type_r" else int(s[itk+1])
206
+ itk += 2
207
+ else:
208
+ logger.writeln("WARNING: unrecognised keyword: {}\n=> {}".format(s[itk], " ".join(s)))
209
+ itk += 1
210
+ elif s[1].lower().startswith(("harm", "spec")):
211
+ ret["rest_type"] = s[1][:4].lower() # in Refmac, irest_type = 1 if harm else 2
212
+ ret["restr"] = dict(rectype="", toler=0.5, sigma_t=0.5, sigma_u=2.0 * b_to_u, u_val_incl=False)
213
+ itk = 2
214
+ while itk < len(s):
215
+ if s[itk].lower().startswith("auto"):
216
+ ret["restr"]["rectype"] = "auto"
217
+ itk += 1
218
+ elif s[itk].lower().startswith("atin"):
219
+ ret["restr"]["rectype"] = "atom"
220
+ atoms, itk = parse_atom_spec(s, itk+1)
221
+ ret["restr"]["specs"] = [atoms]
222
+ elif s[itk].lower().startswith("resi"):
223
+ ret["restr"]["rectype"] = "resi"
224
+ fromto, itk = parse_from_to(s, itk+1)
225
+ ret["restr"]["specs"] = [fromto]
226
+ if s[itk].lower().startswith("atom"):
227
+ ret["restr"]["specs"][0]["atom"] = s[itk+1] # called atom_resi in Refmac
228
+ itk += 2
229
+ elif s[itk].lower().startswith("sigm"):
230
+ ret["restr"]["sigma_t"] = float(s[itk+1])
231
+ itk += 2
232
+ elif s[itk].lower().startswith("tole"):
233
+ ret["restr"]["toler"] = float(s[itk+1])
234
+ itk += 2
235
+ elif s[itk].lower().startswith(("uval", "bval")):
236
+ if len(s) > itk+1 and s[itk+1].lower().startswith("incl"):
237
+ ret["restr"]["u_val_incl"] = True
238
+ itk += 2
239
+ else:
240
+ ret["restr"]["u_val_incl"] = False
241
+ itk += 1
242
+ elif s[itk].lower().startswith(("sigb", "sigu")):
243
+ ret["restr"]["sigma_u"] = float(s[itk+1]) * b_to_u
244
+ itk += 2
245
+ else:
246
+ logger.writeln("WARNING: unrecognised keyword: {}\n=> {}".format(s[itk], " ".join(s)))
247
+ itk += 1
248
+
249
+ else:
250
+ logger.writeln("WARNING: cannot parse: {}".format(" ".join(s)))
251
+ return ret
252
+ # read_exte()
253
+
254
+ def read_ridge_params(l, r):
255
+ s = l.split()
256
+ assert s[0].lower().startswith("ridg")
257
+ ntok = len(s)
258
+ if s[1].lower().startswith("dist") and ntok > 2:
259
+ if s[2].lower().startswith("with"):
260
+ r.setdefault("groups", []).append({})
261
+ #r["groups"][-1]["sigma"] = sigma_dist_r
262
+ #r["groups"][-1]["dmax"] = dmax_dist_r
263
+ itk = 3
264
+ while itk < ntok:
265
+ if s[itk].lower().startswith("chai"):
266
+ r["groups"][-1]["chain"] = s[itk+1]
267
+ itk += 2
268
+ elif s[itk].lower().startswith("resi"):
269
+ r["groups"][-1]["resi"] = (int(s[itk+1]), int(s[itk+2]))
270
+ itk += 3
271
+ elif s[itk].lower().startswith("sigm"):
272
+ v = float(s[itk+1])
273
+ if v < 0: v = 0.01
274
+ r["groups"][-1]["sigma"] = v
275
+ itk += 2
276
+ elif s[itk].lower().startswith("dmax"):
277
+ v = float(s[itk+1])
278
+ if v < 0: v = 4.2
279
+ r["groups"][-1]["dmax"] = v
280
+ itk += 2
281
+ elif s[2].lower().startswith("incl") and ntok > 3:
282
+ # a: ridge_dist_include_all
283
+ # h: ridge_dist_include_hbond
284
+ # m: ridge_dist_include_main
285
+ v = s[3][0].lower()
286
+ if v in ("a", "h", "m"): r["include"] = v
287
+ elif s[2].lower().startswith("sigm"):
288
+ v = float(s[3])
289
+ r["sigma"] = v if v > 0 else 0.01
290
+ elif s[2].lower().startswith("dmax"):
291
+ v = float(s[3])
292
+ r["dmax"] = v if v > 0 else 4.2
293
+ elif s[2].lower().startswith("inte") and ntok > 3:
294
+ r["interchain"] = s[3][0].lower() == "y"
295
+ elif s[2].lower().startswith("symm") and ntok > 3:
296
+ r["intersym"] = s[3][0].lower() == "y"
297
+ elif s[2].lower().startswith("long") and ntok > 3:
298
+ r["long_range"] = max(0, int(s[3])) # long_range_residue_gap
299
+ elif s[2].lower().startswith("shor") and ntok > 3:
300
+ r["short_range"] = max(0, int(s[3])) # short_range_residue_gap
301
+ elif s[2].lower().startswith("hydr"):
302
+ r["hydrogen"] = ntok < 4 or s[3][0].lower() == "i" # hydrogens_include
303
+ elif s[2].lower().startswith("side") and ntok > 3:
304
+ r["sidechain"] = s[3][0].lower() == "i" # rb_side_chain_include
305
+ elif s[2].lower().startswith("filt"):
306
+ r["bvalue_filter"] = True
307
+ if s[3].lower().startswith("bran"):
308
+ v = float(s[4])
309
+ r["bvalue_filter_range"] = v if v > 0 else 2.0
310
+ else:
311
+ logger.writeln("WARNING: unrecognised keyword: {}\n=> {}".format(s[2], l))
312
+ elif s[1].lower().startswith(("atom", "posi")): # not used
313
+ r["sigma_pos"] = float(s[2]) if ntok > 2 else 0.1
314
+ elif s[1].lower().startswith(("bval", "uval")) and ntok > 2:
315
+ if s[2].lower().startswith("diff"):
316
+ itk = 3
317
+ while itk < ntok:
318
+ if s[itk].lower().startswith("sigm"):
319
+ if ntok > itk + 1:
320
+ r["sigma_uval_diff"] = float(s[itk+1])
321
+ itk += 2
322
+ else:
323
+ r["sigma_uval_diff"] = 0.025
324
+ itk += 1
325
+ elif s[itk].lower().startswith("dmax"):
326
+ if ntok > itk + 1:
327
+ r["dmax_uval_diff"] = float(s[itk+1])
328
+ itk += 2
329
+ else:
330
+ r["dmax_uval_diff"] = 4.2
331
+ itk += 1
332
+ elif s[itk].lower().startswith("dmwe"): # not used
333
+ if ntok > itk + 1:
334
+ r["dmax_uval_weight"] = float(s[itk+1]) * b_to_u
335
+ itk += 2
336
+ else:
337
+ r["dmax_uval_weight"] = 3.0
338
+ itk += 1
339
+ else:
340
+ itk += 1
341
+ else:
342
+ r["sigma_b"] = float(s[2])
343
+ r["sigma_u"] = float(s[2])
344
+ else:
345
+ logger.writeln("WARNING: unrecognised keyword: {}\n=> {}".format(s[1], l))
346
+
347
+ return r
348
+ # read_ridge_params()
349
+
350
+ def read_occupancy_params(l, r):
351
+ s = l.split()
352
+ if not s[0].lower().startswith("occu"):
353
+ return r
354
+ ntok = len(s)
355
+ r.setdefault("groups", {}) # {igr: [{selection}, {selection}, ..]}
356
+ r.setdefault("const", []) # [[is_comp, group_ids]]
357
+ r.setdefault("ncycle", 0) # 0 means no refine
358
+ if (ntok > 4 and
359
+ s[1].lower().startswith("grou") and
360
+ s[2].lower().startswith("id")):
361
+ igr = s[3]
362
+ gr = r["groups"].setdefault(igr, [])
363
+ gr.append({})
364
+ itk = 4
365
+ while itk < ntok:
366
+ if s[itk].lower().startswith(("chai", "segm")):
367
+ gr[-1]["chains"] = []
368
+ itk += 1
369
+ while itk < ntok and not s[itk].lower().startswith(("resi","atom","alt")):
370
+ gr[-1]["chains"].append(s[itk])
371
+ itk += 1
372
+ elif s[itk].lower().startswith("resi"):
373
+ if s[itk+1].lower().startswith("from"):
374
+ gr[-1]["resi_from"] = gemmi.SeqId(s[itk+2])
375
+ if s[itk+3].lower().startswith("to"):
376
+ gr[-1]["resi_to"] = gemmi.SeqId(s[itk+4])
377
+ itk += 5
378
+ else:
379
+ gr[-1]["resi"] = gemmi.SeqId(s[itk+1])
380
+ itk += 2
381
+ elif s[itk].lower().startswith("atom"):
382
+ gr[-1]["atom"] = s[itk+1]
383
+ itk += 2
384
+ elif s[itk].lower().startswith("alt"):
385
+ gr[-1]["alt"] = s[itk+1]
386
+ itk += 2
387
+ elif (ntok > 4 and
388
+ s[1].lower().startswith("grou") and
389
+ s[2].lower().startswith("alts")):
390
+ r["const"].append((s[3].lower().startswith("comp"), s[4:]))
391
+ elif ntok > 1 and s[1].lower().startswith("refi"):
392
+ if ntok > 3 and s[2].lower().startswith("ncyc"):
393
+ r["ncycle"] = max(int(s[3]), r["ncycle"])
394
+ elif r["ncycle"] == 0:
395
+ r["ncycle"] = 1 # default
396
+
397
+ return r
398
+ # read_occupancy_params()
399
+
400
+ def read_restr_params(l, r):
401
+ s = l.split()
402
+
403
+ def read_tors_params(itk):
404
+ ret = {"flag": True}
405
+ if s[itk].lower().startswith("none"): # remove all
406
+ ret["flag"] = False
407
+ itk += 1
408
+ elif s[itk].lower().startswith("resi"):
409
+ ret["residue"] = s[itk+1]
410
+ itk += 2
411
+ elif s[itk].lower().startswith("grou"):
412
+ ret["group"] = s[itk+1] # group_name_tors_restr_o
413
+ itk += 2
414
+ elif s[itk].lower().startswith("link"):
415
+ ret["link"] = s[itk+1] #link_name_tors_restr_o
416
+ itk += 2
417
+ else:
418
+ pass # raise error?
419
+ while itk < len(s):
420
+ if s[itk].lower().startswith("name"):
421
+ itk += 1
422
+ ret["tors_name"] = s[itk] # RES_NAME_TORS_NAME_O
423
+ elif s[itk].lower().startswith("valu"):
424
+ itk += 1
425
+ ret["tors_value"] = float(s[itk]) # RES_NAME_TORS_VALUE_O
426
+ elif s[itk].lower().startswith("sigm"):
427
+ itk += 1
428
+ ret["tors_sigma"] = float(s[itk]) # RES_NAME_TORS_SIGMA_O
429
+ elif s[itk].lower().startswith("peri"):
430
+ itk += 1
431
+ ret["tors_period"] = int(s[itk]) # RES_NAME_TORS_PERIOD_O
432
+ itk += 1
433
+ return ret, itk
434
+ # read_tors_params()
435
+
436
+ if not s[0].lower().startswith("rest"):
437
+ return r
438
+ if s[1].lower().startswith("excl"):
439
+ # restr_params.f90 subroutine exclude_restraints
440
+ #r["exclude"] = True
441
+ pass
442
+ elif s[1].lower().startswith("conf"): # not implemented in Refmac
443
+ pass
444
+ elif s[1].lower().startswith(("bp", "pair", "base")):
445
+ if s[2].lower().startswith("dist"):
446
+ r["plane_dist"] = True
447
+ else:
448
+ r["basepair"] = True
449
+ # TODO read dnarna_params.txt
450
+ elif s[1].lower().startswith("tors") and len(s) > 2:
451
+ itk = 2
452
+ if s[itk].lower().startswith("hydr"):
453
+ itk += 1
454
+ if s[itk].lower().startswith("incl"):
455
+ itk += 1
456
+ r["htors_restraint"] = True
457
+ if s[itk].lower().startswith("all"): # this is servalcat default for now
458
+ r["htors_restraint_type"] = "all"
459
+ elif s[itk].lower().startswith("sele"):
460
+ r["htors_restraint_type"] = "sele"
461
+ r["htors_restraint_list"] = s[itk+1:]
462
+ else:
463
+ itk += 1
464
+ r["htors_restraint"] = False
465
+ elif s[itk].lower().startswith("fbas"):
466
+ pass # set user_basepair_file
467
+ elif s[itk].lower().startswith("incl"):
468
+ tmp, itk = read_tors_params(itk+1)
469
+ r.setdefault("torsion_include", []).append(tmp)
470
+ elif s[itk].lower().startswith("excl"):
471
+ # Should warn if value/sigma/period given?
472
+ tmp, itk = read_tors_params(itk+1)
473
+ r.setdefault("torsion_exclude", []).append(tmp)
474
+ elif s[1].lower().startswith("resi"):
475
+ pass
476
+ elif s[1].lower().startswith("chir"):
477
+ pass
478
+ # read_restr_params()
479
+
480
+ def read_make_params(l, r):
481
+ # TODO: hout,ribo,valu,spec,form,sdmi,segi
482
+ s = l.split()
483
+ assert s[0].lower().startswith("make")
484
+ itk = 1
485
+ ntok = len(s)
486
+ # keyword, key, func, possible, default
487
+ keys = (("hydr", "hydr", lambda x: x[0].lower(), set("aynf"), "a"),
488
+ ("hout", "hout", lambda x: x[0].lower() in ("y", "p"), (True, False), True),
489
+ ("chec", "check", lambda x: "0" if x.lower().startswith(("none", "0"))
490
+ else ("n" if x.lower().startswith(("liga", "n"))
491
+ else ("y" if x.lower().startswith(("all", "y")) else None)),
492
+ set("0ny"), None), # no default
493
+ ("newl", "newligand", lambda x: x.lower().startswith(("c", "y", "noex")), (True, False), False),
494
+ ("buil", "build", lambda x: x[0].lower(), set("ny"), "n"),
495
+ ("pept", "pept", lambda x: x[0].lower(), set("yn"), "y"),
496
+ ("link", "link", lambda x: x[0].lower(), set("ynd0"), "y"),
497
+ ("suga", "sugar", lambda x: x[0].lower(), set("ynds"), "y"),
498
+ #("conn", "conn", lambda x: x[0].lower(), set("nyd0"), "n"), # TODO read conn_tolerance? (make conn tole val)
499
+ ("symm", "symm", lambda x: x[0].lower(), set("ync"), "y"),
500
+ ("chai", "chain", lambda x: x[0].lower(), set("yn"), "y"),
501
+ ("cisp", "cispept", lambda x: x[0].lower(), set("yn"), "y"),
502
+ ("ss", "ss", lambda x: x[0].lower(), set("ydn"), "y"),
503
+ ("exit", "exit", lambda x: x[0].lower() == "y", (True, False), True),
504
+ )
505
+ while itk < ntok:
506
+ found = False
507
+ for k, t, f, p, d in keys:
508
+ if s[itk].lower().startswith(k):
509
+ if itk + 1 < len(s):
510
+ r[t] = f(s[itk+1])
511
+ if r[t] not in p:
512
+ raise SystemExit("Invalid make instruction: {}".format(l))
513
+ itk += 2
514
+ elif d is not None:
515
+ r[t] = d # set default
516
+ itk += 1
517
+ else:
518
+ raise SystemExit("Invalid make instruction: {}".format(l))
519
+ break
520
+ else: # if no keywords match (can raise an error if all make keywords are implemented)
521
+ itk += 1
522
+ return r
523
+ # read_make_params()
524
+
525
+ def parse_line(l, ret):
526
+ s = l.split()
527
+ ntok = len(s)
528
+ if ntok == 0: return
529
+ if s[0].lower().startswith("exte"):
530
+ ret.setdefault("exte", []).append(read_exte(s))
531
+ elif s[0].lower().startswith("make"):
532
+ read_make_params(l, ret.setdefault("make", {}))
533
+ elif s[0].lower().startswith(("sour", "scat")):
534
+ k = s[1].lower()
535
+ if k.startswith("em"):
536
+ ret["source"] = "em"
537
+ elif k.startswith("e"):
538
+ ret["source"] = "ec"
539
+ elif k.startswith("n"):
540
+ ret["source"] = "ne"
541
+ else:
542
+ ret["source"] = "xr"
543
+ # TODO check mb, lamb
544
+ elif s[0].lower().startswith("refi"): # TODO support this. Note that only valid with hklin
545
+ ret.setdefault("refi", {})
546
+ itk = 1
547
+ while itk < ntok:
548
+ if s[itk].lower().startswith("type"):
549
+ if itk+1 < ntok and s[itk+1].lower().startswith("unre"):
550
+ ret["refi"]["type"] = "unre"
551
+ itk += 2
552
+ else:
553
+ itk += 1
554
+ elif s[0].lower().startswith("dist"):
555
+ try:
556
+ ret["wbond"] = float(s[1])
557
+ except:
558
+ pass
559
+ # TODO read sdex, excu, dele, dmxe, dmne
560
+ elif s[0].lower().startswith("ridg"):
561
+ read_ridge_params(l, ret.setdefault("ridge", {}))
562
+ elif s[0].lower().startswith("occu"):
563
+ read_occupancy_params(l, ret.setdefault("occu", {}))
564
+ elif s[0].lower().startswith("rest"):
565
+ read_restr_params(l, ret.setdefault("restr", {}))
566
+ elif s[0].lower().startswith("angl") and ntok > 1:
567
+ ret["wangle"] = float(s[1])
568
+ elif s[0].lower().startswith("tors") and ntok > 1:
569
+ ret["wtors"] = float(s[1])
570
+ elif s[0].lower().startswith(("bfac", "temp", "bval")):
571
+ pass # TODO
572
+ elif s[0].lower().startswith("plan") and ntok > 1:
573
+ ret["wplane"] = float(s[1])
574
+ elif s[0].lower().startswith("chir") and ntok > 1:
575
+ ret["wchir"] = float(s[1])
576
+ # TODO read calp
577
+ elif s[0].lower().startswith(("vdwr", "vand", "nonb")) and ntok > 1:
578
+ itk = 1
579
+ try:
580
+ ret["wvdw"] = float(s[itk])
581
+ itk += 1
582
+ except ValueError:
583
+ pass
584
+ # TODO read maxr, over, sigm, incr, chan, vdwc, excl
585
+ # parse_line()
586
+
587
+ def get_lines(lines, depth=0):
588
+ ret = []
589
+ cont = ""
590
+ for l in lines:
591
+ if "!" in l: l = l[:l.index("!")]
592
+ if "#" in l: l = l[:l.index("#")]
593
+ l = l.strip()
594
+ if not l: continue
595
+ if l[0] == "@":
596
+ f = l[1:]
597
+ try:
598
+ yield from get_lines(open(f).readlines(), depth+1)
599
+ except RuntimeError:
600
+ return
601
+ continue
602
+ if l.split()[-1] == "-":
603
+ cont += l[:l.rfind("-")] + " "
604
+ continue
605
+ if cont:
606
+ l = cont + l
607
+ cont = ""
608
+ if l.split()[0].lower().startswith("end"):
609
+ # refmac stops reading keywords when "exit" is seen in stdin or a file
610
+ # but won't do this from nested files
611
+ if depth == 1:
612
+ raise RuntimeError
613
+ break
614
+ yield l
615
+ # get_lines()
616
+
617
+ def update_params(ret, inputs):
618
+ if not inputs:
619
+ return
620
+ for l in get_lines(inputs):
621
+ parse_line(l, ret)
622
+ # update_keywords()
623
+
624
+ def parse_keywords(inputs):
625
+ ret = {"make":{}, "ridge":{}, "refi":{}}
626
+ update_params(ret, inputs)
627
+ return ret
628
+ # parse_keywords()
629
+
630
+ if __name__ == "__main__":
631
+ import sys
632
+ import json
633
+ print("waiting for input")
634
+ ret = {} #{"make":{}, "ridge":{}, "refi":{}}
635
+ for l in get_lines(sys.stdin):
636
+ parse_line(l, ret)
637
+ print()
638
+ print("Parsed:")
639
+ print(json.dumps(ret, indent=1))