ncrystal-python 3.9.81__py3-none-any.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.
Files changed (53) hide show
  1. NCrystal/__init__.py +85 -0
  2. NCrystal/__main__.py +98 -0
  3. NCrystal/_chooks.py +854 -0
  4. NCrystal/_cli_cif2ncmat.py +269 -0
  5. NCrystal/_cli_endf2ncmat.py +503 -0
  6. NCrystal/_cli_hfg2ncmat.py +144 -0
  7. NCrystal/_cli_mcstasunion.py +74 -0
  8. NCrystal/_cli_ncmat2cpp.py +31 -0
  9. NCrystal/_cli_ncmat2hkl.py +180 -0
  10. NCrystal/_cli_nctool.py +1018 -0
  11. NCrystal/_cli_vdos2ncmat.py +463 -0
  12. NCrystal/_cli_verifyatompos.py +257 -0
  13. NCrystal/_cliimpl.py +307 -0
  14. NCrystal/_cliwrap_config.py +36 -0
  15. NCrystal/_common.py +499 -0
  16. NCrystal/_coreimpl.py +114 -0
  17. NCrystal/_hfgdata.py +546 -0
  18. NCrystal/_hklobjects.py +136 -0
  19. NCrystal/_is_std.py +0 -0
  20. NCrystal/_locatelib.py +210 -0
  21. NCrystal/_miscimpl.py +354 -0
  22. NCrystal/_mmc.py +757 -0
  23. NCrystal/_msg.py +60 -0
  24. NCrystal/_ncmat2cpp_impl.py +445 -0
  25. NCrystal/_ncmatimpl.py +2131 -0
  26. NCrystal/_numpy.py +76 -0
  27. NCrystal/_testimpl.py +579 -0
  28. NCrystal/api.py +56 -0
  29. NCrystal/atomdata.py +177 -0
  30. NCrystal/cfgstr.py +77 -0
  31. NCrystal/cifutils.py +1795 -0
  32. NCrystal/cli.py +96 -0
  33. NCrystal/constants.py +134 -0
  34. NCrystal/core.py +1910 -0
  35. NCrystal/datasrc.py +226 -0
  36. NCrystal/exceptions.py +66 -0
  37. NCrystal/hfg2ncmat.py +270 -0
  38. NCrystal/mcstasutils.py +438 -0
  39. NCrystal/misc.py +317 -0
  40. NCrystal/mmc.py +35 -0
  41. NCrystal/ncmat.py +778 -0
  42. NCrystal/ncmat2cpp.py +80 -0
  43. NCrystal/obsolete.py +67 -0
  44. NCrystal/plot.py +484 -0
  45. NCrystal/plugins.py +49 -0
  46. NCrystal/test.py +76 -0
  47. NCrystal/vdos.py +1034 -0
  48. ncrystal_python-3.9.81.dist-info/LICENSE +206 -0
  49. ncrystal_python-3.9.81.dist-info/METADATA +515 -0
  50. ncrystal_python-3.9.81.dist-info/RECORD +53 -0
  51. ncrystal_python-3.9.81.dist-info/WHEEL +5 -0
  52. ncrystal_python-3.9.81.dist-info/entry_points.txt +10 -0
  53. ncrystal_python-3.9.81.dist-info/top_level.txt +1 -0
@@ -0,0 +1,438 @@
1
+
2
+ ################################################################################
3
+ ## ##
4
+ ## This file is part of NCrystal (see https://mctools.github.io/ncrystal/) ##
5
+ ## ##
6
+ ## Copyright 2015-2024 NCrystal developers ##
7
+ ## ##
8
+ ## Licensed under the Apache License, Version 2.0 (the "License"); ##
9
+ ## you may not use this file except in compliance with the License. ##
10
+ ## You may obtain a copy of the License at ##
11
+ ## ##
12
+ ## http://www.apache.org/licenses/LICENSE-2.0 ##
13
+ ## ##
14
+ ## Unless required by applicable law or agreed to in writing, software ##
15
+ ## distributed under the License is distributed on an "AS IS" BASIS, ##
16
+ ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ##
17
+ ## See the License for the specific language governing permissions and ##
18
+ ## limitations under the License. ##
19
+ ## ##
20
+ ################################################################################
21
+
22
+ """
23
+
24
+ Module containing various McStas-related utilities, such as functions helping
25
+ with automatic setup of McStas-Union materials from NCrystal cfg-strings, or
26
+ production of .laz/.lau files for McStas components such as PowderN.comp or
27
+ Single_crystal.comp (with reduced physics capabilities of course).
28
+
29
+ """
30
+
31
+ from . import api as _NC
32
+ from ._common import warn as _nc_warn
33
+
34
+ def cfgstr_2_unioncfg( *, cfgstr, split_by_physics = False ):
35
+ """Analyse cfg-string and return data needed in order to set up Union
36
+ components accordingly. Specifically the function returns
37
+ (inv_pen_depth_2200,proclist) where inv_pen_depth is the inverse penetration
38
+ depth in inverse meters for a neutron at 2200m/s, and proclist is a list
39
+ [(cfgstr1,compname1), (cfgstr2,compname2),...] with the relevant
40
+ components. If split_by_physics is True, this process list will be as
41
+ provided by cfgstr_detect_components(cfgstr), and otherwise it will simply
42
+ be [(normaliseCfg(cfgstr),'total')].
43
+
44
+ """
45
+ info=_NC.createInfo(cfgstr)
46
+ inv_pen_depth_2200 = ( 100.0 * info.getNumberDensity()
47
+ * info.getXSectAbsorption() )# in inverse meters
48
+ if split_by_physics:
49
+ from .misc import cfgstr_detect_components as cdc
50
+ return inv_pen_depth_2200, cdc( cfgstr )
51
+ else:
52
+ from .cfgstr import normaliseCfg
53
+ return inv_pen_depth_2200, [(normaliseCfg(cfgstr),'total')]
54
+
55
+ def cfgstr_2_union_instrument_code( *, cfgstr, name, split_by_physics = False ):
56
+ """Analyse cfg-string (via a call to
57
+ cfgstr_2_unioncfg(cfgstr,split_by_physics)), and use the results to produce
58
+ (and return) McStas-instrument code suitable for defining a McStas-Union
59
+ material with the given name as specified via the "name"
60
+ parameter. Depending on the value of split_by_physics, this Union material
61
+ will either be set up with either just a single NCrystal_process, or a whole
62
+ list of NCrystal_processes. Using split_by_physics=False is likely slightly
63
+ more efficient, but split_by_physics=True will allow insight into the
64
+ contributions of the various physics components ("inelas", "coh_elas",
65
+ "sans", "incoh_elas", ...) at the McStas-Union level.
66
+
67
+ """
68
+ for ch in ';.:':
69
+ if ch in name:
70
+ raise ValueError(f'union process name "{name}" contains a "{ch}" '
71
+ 'character which is likely not what was intended.')
72
+ assert '/*' not in cfgstr
73
+ assert '*/' not in cfgstr
74
+ assert '/*' not in name
75
+ assert '*/' not in name
76
+
77
+ ( out_absorption,
78
+ physlist ) = cfgstr_2_unioncfg( cfgstr=cfgstr,
79
+ split_by_physics=split_by_physics )
80
+ res = f"""
81
+ /*
82
+ The following code was auto generated by NCrystal v{_NC.get_version()} via Python:
83
+
84
+ NCrystal.mcstasutils.cfgstr_2_union_instrument_code(
85
+ cfgstr = {repr(str(cfgstr))},
86
+ name = {repr(str(name))}"""
87
+ if split_by_physics:
88
+ res += """,
89
+ split_by_physics = True )\n"""
90
+ else:
91
+ res += ' )\n'
92
+ res+="""
93
+ Please re-generate in case of major changes to input data or NCrystal.
94
+ */
95
+ """
96
+
97
+ procnames=[]
98
+ mcstas_string_max = 256
99
+ for proccfgstr, proctypename in physlist:
100
+ if len(physlist)==1 and proctypename=='total':
101
+ procname=f'{name}_ncrystal_proc'
102
+ else:
103
+ procname=f'{name}_ncrystal_{proctypename}_proc'
104
+ procnames.append( procname )
105
+ if len(proccfgstr) > mcstas_string_max:
106
+ _nc_warn('cfg_string might be too long for'
107
+ f' a McStas string: "{proccfgstr}"')
108
+
109
+ res += f"""
110
+ COMPONENT {procname} = NCrystal_process(
111
+ cfg = "{proccfgstr}"
112
+ )
113
+ AT (0,0,0) ABSOLUTE
114
+ """
115
+ procnamestr = ','.join(procnames)
116
+ if len(procnamestr) > mcstas_string_max:
117
+ _nc_warn('process_string might be too long for a'
118
+ f' McStas string: "{procnamestr}" (it might'
119
+ ' work if you use a shorter material name)')
120
+ res += f"""
121
+ COMPONENT {name} = Union_make_material(
122
+ process_string = "{procnamestr}",
123
+ my_absorption = {out_absorption:.15g}
124
+ )
125
+ AT (0,0,0) ABSOLUTE
126
+
127
+ /* End of auto generated code from NCrystal. */
128
+ """
129
+ return res
130
+
131
+ def cfgstr_2_hkl(*, cfgstr, tgtformat, verbose=True, fp_format = '%.14g' ):
132
+ """Function which can be used to create input files with reflections for
133
+ McStas crystalline sample components like PowderN and Single_crystal, based
134
+ on NCrystal cfg-strings (usually referring to NCMAT files with crystalline
135
+ single-phase materials). The tgtformat must be either 'laz' or 'lau'. If
136
+ verbose is True, the files might contain strictly unneccessary content
137
+ (e.g. white-space for adjusting columns, a dspacing column in .lau files,
138
+ ...). Finally the fp_format parameter can be used to change the precision of
139
+ floating point numbers in the file in the data rows. Numbers in the header
140
+ will on the other hand always be produced using %.14g.
141
+
142
+ This function returns an iterable, yielding one line of the output file at a
143
+ time.
144
+
145
+ """
146
+
147
+ import numbers
148
+ import functools
149
+ import math
150
+
151
+ assert tgtformat in ('laz','lau')
152
+ doPowder = (tgtformat=='laz')
153
+ doDsp = (doPowder or verbose)
154
+ doMult = True#Always needed, even in lau
155
+
156
+ from .cfgstr import normaliseCfg
157
+ cfgstr = normaliseCfg(cfgstr)
158
+ info = _NC.createInfo(cfgstr)
159
+
160
+ if info.isMultiPhase():
161
+ raise RuntimeError('this converter does not handle multiphase'
162
+ ' materials')
163
+ if not info.hasHKLInfo():
164
+ raise RuntimeError('this converter does not handle non-crystalline'
165
+ ' materials')
166
+ if not info.hasStructureInfo():
167
+ raise RuntimeError('this converter does not handle crystalline'
168
+ ' materials without unit cell structure')
169
+ si = info.getStructureInfo()
170
+ if not si.get('spacegroup',None):
171
+ raise RuntimeError('this converter does not handle crystalline'
172
+ ' materials without spacegroup number')
173
+ if not info.hasAtomInfo():
174
+ raise RuntimeError('this converter does not handle crystalline'
175
+ ' materials without info of atoms placement'
176
+ ' in the unit cell')
177
+ if info.hklInfoType() != _NC.HKLInfoType.SymEqvGroup:
178
+ raise RuntimeError('this converter does not handle crystalline'
179
+ ' materials without symmetry equivalent HKL'
180
+ ' groupings')
181
+
182
+ orig_header = []
183
+ decoded_cfg = _NC.decodeCfg(cfgstr)
184
+ data_name = decoded_cfg.get('data_name',None)
185
+ cfgstr_nodataname = cfgstr.replace(data_name,'')
186
+ if cfgstr_nodataname.startswith(';'):
187
+ cfgstr_nodataname = cfgstr_nodataname[1:]
188
+ cfgstr_nodataname = cfgstr_nodataname.strip()
189
+
190
+ if ( decoded_cfg.get('density',{}).get('type',None) != 'scalefactor'
191
+ or decoded_cfg.get('density',{}).get('value',None) != 1.0 ):
192
+ raise RuntimeError('this converter does not handle configurations'
193
+ ' with density overrides (since resulting'
194
+ ' files might not load correctly by all clients)')
195
+
196
+ if data_name:
197
+ for i,line in enumerate(_NC.createTextData(data_name)):
198
+ if line.startswith('#') or not line.strip():
199
+ orig_header.append(line.rstrip())
200
+ else:
201
+ if not (i==0 and line.startswith('NCMAT')):
202
+ break
203
+ assert info.hasStructureInfo()
204
+ assert info.hasHKLInfo()
205
+ assert info.hasTemperature()
206
+ assert info.hasAtomInfo()
207
+
208
+ si = info.getStructureInfo()
209
+ def fmtfp_datarows(x):
210
+ return fp_format%x if isinstance(x, numbers.Real) else str(x)
211
+
212
+ def fmtfp_header(x):
213
+ return '%.14g'%x if isinstance(x, numbers.Real) else str(x)
214
+
215
+ yield f'# File created by NCrystal v{_NC.get_version()}'
216
+ yield '#'
217
+ yield f'# ncrystal_cfgstr "{cfgstr}"'
218
+ yield '#'
219
+ if tgtformat=='laz':
220
+ yield '# Format: "laz" (suitable for McStas PowderN component)'
221
+ else:
222
+ assert tgtformat=='lau'
223
+ yield '# Format: "lau" (suitable for McStas Single_crystal component)'
224
+ yield '#'
225
+
226
+ if orig_header:
227
+ had_embedded_cfg = any( 'NCRYSTALMATCFG[' in e for e in orig_header)
228
+ guard=' orighdr :'#something that hopefully will cause McStas code to
229
+ #ignore the line
230
+ yield f'#{guard} Original file had the following comments at the top:'
231
+ if had_embedded_cfg:
232
+ yield f'#{guard}'
233
+ yield (f'#{guard} (note the ncrystalmatcfg statement is'
234
+ ' disabled in this')
235
+ yield f'#{guard} reproduction by adding "<disable>" around it)'
236
+ yield f'#{guard}'
237
+ for line in orig_header:
238
+ if 'NCRYSTALMATCFG[' in line:
239
+ line=line.replace('NCRYSTALMATCFG[','NCRYSTAL<disable>MATCFG[')
240
+ yield f'#{guard} {line}'
241
+ yield '#'
242
+ if cfgstr_nodataname:
243
+ yield '#'
244
+ yield f'# ncrystal_embedded_cfg : NCRYSTALMATCFG[{cfgstr_nodataname}]'
245
+ yield '#'
246
+ yield f'# temperature {fmtfp_header(info.getTemperature())} [kelvin]'
247
+ yield '#'
248
+ yield f'# spacegroup {si["spacegroup"]}'
249
+ yield f'# lattice_a {fmtfp_header(si["a"])} [Aa]'
250
+ yield f'# lattice_b {fmtfp_header(si["b"])} [Aa]'
251
+ yield f'# lattice_c {fmtfp_header(si["c"])} [Aa]'
252
+ yield f'# lattice_aa {fmtfp_header(si["alpha"])} [degrees]'
253
+ yield f'# lattice_bb {fmtfp_header(si["beta"])} [degrees]'
254
+ yield f'# lattice_cc {fmtfp_header(si["gamma"])} [degrees]'
255
+ yield f'# Vc {fmtfp_header(si["volume"])} [Aa^3]'
256
+ yield '#'
257
+ n_atoms = sum( ai.count for ai in info.atominfos )
258
+ assert si["n_atoms"] == n_atoms
259
+ yield f'# multiplicity {n_atoms} [atoms/unit cell]'
260
+ yield f'# density {fmtfp_header(info.density)} [g/cm^3]'
261
+ yield f'# number_density {fmtfp_header(info.numberdensity)} [atoms/Aa^3]'
262
+ daltons_per_unitcell = sum(ai.atomData.averageMassAMU() * ai.count
263
+ for ai in info.atominfos)
264
+ yield f'# weight {fmtfp_header(daltons_per_unitcell)} [g/mol of entire unit cell]'
265
+ yield f'# average_mass {fmtfp_header(daltons_per_unitcell/n_atoms)} [average atomic g/mol]'
266
+
267
+ sigma_coh = sum(ai.atomData.coherentXS() * ai.count for ai in info.atominfos)
268
+ sigma_inc = sum(ai.atomData.incoherentXS() * ai.count for ai in info.atominfos)
269
+ sigma_abs = sum(ai.atomData.captureXS() * ai.count for ai in info.atominfos)
270
+ yield '#'
271
+ yield f'# sigma_coh {fmtfp_header(sigma_coh)} [barn/unitcell]'
272
+ yield f'# sigma_inc {fmtfp_header(sigma_inc)} [barn/unitcell]'
273
+ yield f'# sigma_abs {fmtfp_header(sigma_abs)} [barn/unitcell]'
274
+ yield '#'
275
+
276
+ has_debye_temp = True
277
+ debye_temp_sum = 0.0
278
+ debye_temp_sumw = 0.0
279
+
280
+ if all(ai.atomData.isElement() for ai in info.atominfos):
281
+ d = {}
282
+ for ai in info.atominfos:
283
+ if has_debye_temp:
284
+ if ai.debyeTemperature is not None:
285
+ _dt = ai.debyeTemperature
286
+ elif ai.msd is not None:
287
+ _dt = _NC.debyeTempFromIsotropicMSD( msd = ai.msd,
288
+ temperature = info.getTemperature(),
289
+ mass = ai.atomData.averageMassAMU() )
290
+ else:
291
+ has_debye_temp = False
292
+ _dt = 0.0
293
+ _dtw = ai.count * ai.atomData.scatteringXS()
294
+ debye_temp_sum += _dt*_dtw
295
+ debye_temp_sumw += _dtw
296
+
297
+ d[ai.atomData.elementName()] = d.get(ai.atomData.elementName(),0) + ai.count
298
+ nformula_per_unitcell = functools.reduce(math.gcd, list(c for _,c in d.items()))
299
+ formula = ''.join(( '%s%i'%(k,v/nformula_per_unitcell) if v!=nformula_per_unitcell else k) for k,v in sorted(d.items()))
300
+ yield f'# formula {formula}'
301
+ yield f'# nformula_per_unitcell {nformula_per_unitcell}'
302
+ if debye_temp_sum:
303
+ yield f'# debye_temperature {fmtfp_header(debye_temp_sum/debye_temp_sumw)} [kelvin, weighted by sigma_b]'
304
+
305
+ cols = ['h','k','l']
306
+ if doMult:
307
+ cols.append('j')
308
+ if doDsp:
309
+ cols.append('d')
310
+ cols.append('F2')
311
+
312
+ colwidths={'h':3,'k':3,'l':3,'j':2,'d':16,'F2':16}
313
+ header = '#'
314
+ for i,c in enumerate(cols):
315
+ header += ' '
316
+ if verbose:
317
+ header += c.rjust(colwidths[c])
318
+ else:
319
+ header += c
320
+
321
+ col_comments={
322
+ 'd':"[d-spacing in Aa]",
323
+ 'j': "multiplicity",
324
+ 'F2':"[norm of scattering factor |F|^2 in barn]"
325
+ }
326
+
327
+ yield '#'
328
+ maxcw=max(len(c) for c in col_comments.keys())
329
+ for i,c in enumerate(cols):
330
+ cc = col_comments.get(c,'')
331
+ if cc:
332
+ cc = ' '+cc
333
+ yield f'# column_{c.ljust(maxcw)} {i+1}{cc}'
334
+ yield '# unit_F2 barn'
335
+ yield '#'
336
+
337
+ yield header
338
+ def format_line(data):
339
+ s=' ' if verbose else ''
340
+ for c in cols:
341
+ if s:
342
+ s+=' '
343
+ s+=fmtfp_datarows(data[c]).rjust(colwidths[c])
344
+ return s
345
+
346
+ if doPowder:
347
+ for h,k,ll,mult,dsp,F2_barn in info.hklList():
348
+ yield format_line({'h':h,'k':k,'l':ll,'d':dsp,'j':mult,'F2':F2_barn})
349
+ else:
350
+ for h,k,ll,mult,dsp,F2_barn in info.hklList(all_indices=True):
351
+ for i in range(len(h)):
352
+ #Yield both hkl and -hkl and put mult=1 (since we put all planes explicitly)
353
+ yield format_line({'h':h[i],'k':k[i],'l':ll[i],'d':dsp,'j':1,'F2':F2_barn})
354
+ yield format_line({'h':-h[i],'k':-k[i],'l':-ll[i],'d':dsp,'j':1,'F2':F2_barn})
355
+
356
+ def cfgstr_detect_components( cfgstr ):
357
+ """OBSOLETE"""
358
+ _nc_warn('The cfgstr_detect_components(..) method is now in the'
359
+ ' NCrystal.misc module. Please stop using it from'
360
+ ' the NCrystal.mcstasutils module.')
361
+ from .misc import cfgstr_detect_components as cdc
362
+ return cdc(cfgstr)
363
+
364
+ def _main( argv ):
365
+ #NOTE: This usage here is obsolete!!! We keep it around a bit longer, so we
366
+ #have a chance to update the documentation and examples everywhere.
367
+
368
+ if False:
369
+ #After ~1 year or so, emit the following instead:
370
+ raise SystemExit(
371
+ 'Please use the dedicated ncrystal_mcstasunion or'
372
+ ' ncrystal_ncmat2hkl cmdline scripts instead of'
373
+ ' "python -mNCrystal.mcstasutils"'
374
+ )
375
+
376
+
377
+
378
+ args = argv[1:]
379
+ if args and isinstance(args[0],bytes):
380
+ args = list(e.decode() for e in args)
381
+ def usage(*,err):
382
+ if err:
383
+ print("ERROR - wrong usage!")
384
+ print()
385
+ import os
386
+ pn=os.path.basename(argv[0])
387
+ if pn.endswith('.py'):
388
+ pn = pn[0:-3]
389
+ print("""Usage:
390
+
391
+ NOTE: THIS USAGE IS OBSOLETE!!!!!!!! PLEASE USE THE ncrystal_mcstasunion OR ncrystal_ncmat2hkl SCRIPTS INSTEAD!!
392
+ NOTE: THIS USAGE IS OBSOLETE!!!!!!!! PLEASE USE THE ncrystal_mcstasunion OR ncrystal_ncmat2hkl SCRIPTS INSTEAD!!
393
+ NOTE: THIS USAGE IS OBSOLETE!!!!!!!! PLEASE USE THE ncrystal_mcstasunion OR ncrystal_ncmat2hkl SCRIPTS INSTEAD!!
394
+
395
+ --help|-h Show these instructions
396
+
397
+ --union [--split] NAME CFGSTR Output McStas-Union code to define material
398
+ with NAME based on specified NCrystal CFGSTR.
399
+ Providing --split will split NCrystal processes
400
+ into physics types at the McStas-Union level,
401
+ rather than internally in NCrystal.
402
+
403
+ --laz CFGSTR Convert NCrystal CFGSTR to McStas .laz format
404
+ for PowderN.
405
+
406
+ --lau CFGSTR Convert NCrystal CFGSTR to McStas .lau format
407
+ for Single_crystal.
408
+
409
+ NOTE: THIS USAGE IS OBSOLETE!!!!!!!! PLEASE USE THE ncrystal_mcstasunion OR ncrystal_ncmat2hkl SCRIPTS INSTEAD!!
410
+ NOTE: THIS USAGE IS OBSOLETE!!!!!!!! PLEASE USE THE ncrystal_mcstasunion OR ncrystal_ncmat2hkl SCRIPTS INSTEAD!!
411
+ NOTE: THIS USAGE IS OBSOLETE!!!!!!!! PLEASE USE THE ncrystal_mcstasunion OR ncrystal_ncmat2hkl SCRIPTS INSTEAD!!
412
+
413
+ """)
414
+ raise SystemExit(1 if err else 0)
415
+ def err():
416
+ usage(err=True)
417
+ if '-h' in args or '--help' in args:
418
+ usage(err=False)
419
+ if not args or len(args)<2:
420
+ err()
421
+ if args[0] == '--union':
422
+ dosplit = False
423
+ if '--split' in args:
424
+ args.remove('--split')
425
+ dosplit=True
426
+ if len(args)<3:
427
+ err()
428
+ print(cfgstr_2_union_instrument_code( cfgstr=';'.join(args[2:]), name=args[1], split_by_physics = dosplit ))
429
+ return
430
+ if args[0] in ('--laz','--lau'):
431
+ for e in cfgstr_2_hkl( cfgstr=';'.join(args[1:]),tgtformat=args[0][2:]):
432
+ print(e)
433
+ return
434
+ err()
435
+
436
+ if __name__ == '__main__':
437
+ import sys
438
+ _main(sys.argv)