source: utils/boxmaker.py.in@ 2aa9ef

Action_Thermostats Add_AtomRandomPerturbation Add_FitFragmentPartialChargesAction Add_RotateAroundBondAction Add_SelectAtomByNameAction Added_ParseSaveFragmentResults AddingActions_SaveParseParticleParameters Adding_Graph_to_ChangeBondActions Adding_MD_integration_tests Adding_ParticleName_to_Atom Adding_StructOpt_integration_tests AtomFragments Automaking_mpqc_open AutomationFragmentation_failures Candidate_v1.5.4 Candidate_v1.6.0 Candidate_v1.6.1 ChangeBugEmailaddress ChangingTestPorts ChemicalSpaceEvaluator CombiningParticlePotentialParsing Combining_Subpackages Debian_Package_split Debian_package_split_molecuildergui_only Disabling_MemDebug Docu_Python_wait EmpiricalPotential_contain_HomologyGraph EmpiricalPotential_contain_HomologyGraph_documentation Enable_parallel_make_install Enhance_userguide Enhanced_StructuralOptimization Enhanced_StructuralOptimization_continued Example_ManyWaysToTranslateAtom Exclude_Hydrogens_annealWithBondGraph FitPartialCharges_GlobalError Fix_BoundInBox_CenterInBox_MoleculeActions Fix_ChargeSampling_PBC Fix_ChronosMutex Fix_FitPartialCharges Fix_FitPotential_needs_atomicnumbers Fix_ForceAnnealing Fix_IndependentFragmentGrids Fix_ParseParticles Fix_ParseParticles_split_forward_backward_Actions Fix_PopActions Fix_QtFragmentList_sorted_selection Fix_Restrictedkeyset_FragmentMolecule Fix_StatusMsg Fix_StepWorldTime_single_argument Fix_Verbose_Codepatterns Fix_fitting_potentials Fixes ForceAnnealing_goodresults ForceAnnealing_oldresults ForceAnnealing_tocheck ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion FragmentAction_writes_AtomFragments FragmentMolecule_checks_bonddegrees GeometryObjects Gui_Fixes Gui_displays_atomic_force_velocity ImplicitCharges IndependentFragmentGrids IndependentFragmentGrids_IndividualZeroInstances IndependentFragmentGrids_IntegrationTest IndependentFragmentGrids_Sole_NN_Calculation JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool JobMarket_unresolvable_hostname_fix MoreRobust_FragmentAutomation ODR_violation_mpqc_open PartialCharges_OrthogonalSummation PdbParser_setsAtomName PythonUI_with_named_parameters QtGui_reactivate_TimeChanged_changes Recreated_GuiChecks Rewrite_FitPartialCharges RotateToPrincipalAxisSystem_UndoRedo SaturateAtoms_findBestMatching SaturateAtoms_singleDegree StoppableMakroAction Subpackage_CodePatterns Subpackage_JobMarket Subpackage_LinearAlgebra Subpackage_levmar Subpackage_mpqc_open Subpackage_vmg Switchable_LogView ThirdParty_MPQC_rebuilt_buildsystem TrajectoryDependenant_MaxOrder TremoloParser_IncreasedPrecision TremoloParser_MultipleTimesteps TremoloParser_setsAtomName Ubuntu_1604_changes stable
Last change on this file since 2aa9ef was 2aa9ef, checked in by Frederik Heber <heber@…>, 13 years ago

UpdateSource function of boxmaker.py is not an external call to molecuilder anymore.

  • this is unusable in a test and was contradictory to the intention of a script. As now we may safely reinit() the pyMoleCuilder module, we may use it to do the same as the external call did.
  • Property mode set to 100755
File size: 10.6 KB
RevLine 
[bcfb77]1#!@PYTHON@
[bfbb62]2
3# Boxmaker 1.0
[239cc5]4# Creates tremolo-datafiles with arbitrary size and specific density from a single input molecule,
5# supporting numerous pre- and postprocessing features such as unit conversion.
[bfbb62]6# Gregor Bollerhey - bollerhe@ins.uni-bonn.de
7
8
[5735ba]9import re, os, os.path, sys, operator
[2aa9ef]10import pyMoleCuilder as mol
[5735ba]11
[0ad49c]12avogadro = 6.022143e23
13
[0c83d8]14class c_opt():
15 basename = None
16 tremofiledir = './'
17 potentialsfiledir = './'
18 outfilename = 'out'
19
20 source = None
21 molarmass = None
22 density = None
23 temp = None
24
25 number = '1000'
26
27 cubicdomain = 'on'
28 cubiccell = 'off'
[32bc47]29 autorotate = 'off'
[0c83d8]30 autodim = 'on'
[32bc47]31 postprocess = 'on'
[0ad49c]32 automass = 'on'
[0c83d8]33
34 def update(self, name, value):
[0ad49c]35 shortcuts = {'tf': 'temofiledir', 'pf': 'potentialsfiledir', 'o': 'outfilename',
36 'i': 'source', 'm': 'molarmass', 'rho': 'density',
37 't': 'temp', 'n': 'number', 'cd': 'cubicdomain',
38 'cc': 'cubiccell', 'ar': 'autorotate', 'ad': 'autodim',
39 'pp': 'postprocess', 'am': 'automass'}
[751d7f1]40
41 if name in shortcuts:
42 name = shortcuts[name]
43
[0c83d8]44 if name in dir(self):
45 exec('self.%s = "%s"' % (name, value))
46 else:
47 print 'Warning: Unknown option:', name
48
49
50def ReadSettings(opt):
51 # Obtain basename
[5735ba]52 if len(sys.argv) >= 2:
[0c83d8]53 opt.basename = sys.argv[1]
[5735ba]54 else:
[0c83d8]55 print 'Usage: boxmaker.py <basename> [options]'
[5735ba]56 exit()
[0c83d8]57
58 # Read settings file
[751d7f1]59 try:
60 with open('boxmaker.' + opt.basename) as f:
61 for line in f:
62 if len(line) > 0 and line[0] != '#':
63 L, S, R = line.partition('=')
64 opt.update(L.strip(), R.strip())
65 except IOError:
66 print 'Warning: Configuration file not readable, CLI only'
[0c83d8]67
68 # Parse parameters
69 i = 2
70 while i < len(sys.argv):
71 L = sys.argv[i]
72
73 if L[0] in '+-':
74 LN = L[1:]
75
76 if L[0] == '+':
77 R = 'on'
78 else:
79 R = 'off'
80 else:
81 LN = L
82 i += 1
83 R = sys.argv[i]
84
85 opt.update(LN, R)
86 i += 1
87
88
89def ReadUnits(opt):
90 lines = [] # The file needs to be processed twice, so we save the lines in the first run
91
92 with open(opt.tremofiledir + opt.basename + '.tremolo') as f:
[5735ba]93 for line in f:
94 if len(line) > 0 and line[0] != '#':
95 line = line.strip()
[0c83d8]96 lines.append(line)
97
[5735ba]98 if 'systemofunits' in line:
99 L, S, SOU = line.partition('=')
[0c83d8]100 SOU = SOU.strip()[:-1] # Remove semicolon
101
[5735ba]102 if SOU == 'custom':
103 units = {}
[39cbae]104 quantities = ['length', 'mass', 'temperature']
[0c83d8]105
[5735ba]106 for quantity in quantities:
[0c83d8]107 units[quantity] = [None, None] # Init with scaling factor and unit 'None'.
108
[5735ba]109 for line in lines:
110 for quantity in quantities:
111 if quantity in line:
112 L, S, R = line.partition('=')
[0c83d8]113 R = R.strip()[:-1] # Remove semicolon
114
[5735ba]115 if 'scalingfactor' in line:
[39cbae]116 units[quantity][0] = float(R)
[5735ba]117 else:
118 units[quantity][1] = R
[0c83d8]119
[5735ba]120 elif SOU == 'kcalpermole':
[39cbae]121 units = {'length': [1.0, 'angstrom'], 'mass': [1.0, 'u'], 'temperature': [503.556, 'K']}
[0c83d8]122
[5735ba]123 elif SOU == 'evolt':
[39cbae]124 units = {'length': [1.0, 'angstrom'], 'mass': [1.0, 'u'], 'temperature': [11604.0, 'K']}
[0c83d8]125
[5735ba]126 else: # SI
[39cbae]127 units = {'length': [1.0, 'm'], 'mass': [1.0, 'kg'], 'temperature': [1.0, 'K']}
[0c83d8]128
[5735ba]129 return units
[0c83d8]130
131
[5735ba]132def ConvertUnits(have, want):
[39cbae]133 if have[0] == '!':
134 return float(have[1:])
135
[0c83d8]136 # Redo with pipes?
[5735ba]137 ret = os.system("units '%s' '%s' > temp_units_output" % (have, want))
[0c83d8]138
[5735ba]139 if ret == 0:
140 with open('temp_units_output') as f:
141 line = f.readline()
[0c83d8]142
[5735ba]143 os.system('rm temp_units_output')
[0c83d8]144
[5735ba]145 return float(line[3:-1])
146 else:
147 raise NameError('UnitError')
[0ad49c]148
149
[c0c85f]150def GetSourceMolareMass(opt):
[0ad49c]151 with open(opt.potentialsfiledir+opt.basename+'.potentials') as f:
152 potfile = f.read()
153
154 elementmasses = dict(re.findall(r'element_name=(\w{1,2}).*?mass=([0-9.]*)', potfile))
155
156 for key in elementmasses:
157 elementmasses[key] = float(elementmasses[key])
158
159 mass_sum = 0.0
160
161 with open('temp_source.xyz') as f:
162 N = int(f.readline())
163 comment = f.readline()
164
165 for i in range(N):
166 elem = f.readline().split(None, 1)[0].strip()
167 mass_sum += elementmasses[elem]
[c0c85f]168
[0ad49c]169 return mass_sum*avogadro
[0c83d8]170
171
[c0c85f]172def UpdateSettingsAndSource(opt):
[0c83d8]173 # Map boolean values
[c0c85f]174 boolmap = {'on': True, 'off': False}
175
[0ad49c]176 for name in ['cubicdomain', 'cubiccell', 'autorotate', 'autodim', 'postprocess', 'automass']:
[0c83d8]177 value = eval('opt.' + name)
[5735ba]178
[c0c85f]179 if value in boolmap:
180 value = boolmap[value]
[0c83d8]181 else:
182 print 'Not a boolean value:', value
183 exit()
184
185 exec('opt.' + name + '= value')
186
187 # Convert dimensions
188 if opt.autodim:
189 units = ReadUnits(opt)
190
[0ad49c]191 if not opt.automass:
192 have = opt.molarmass
193 want = '%f*%s / mol' % tuple(units['mass'])
194 opt.molarmass = ConvertUnits(have, want)
[0c83d8]195
196 have = opt.density
[39cbae]197 want = '(%f*%s) ' % tuple(units['mass']) + '/ (%f*%s)**3' % tuple(units['length'])
[0c83d8]198 opt.density = ConvertUnits(have, want)
[39cbae]199
200 if opt.temp:
201 have = opt.temp
202 want = '%f*%s' % tuple(units['temperature'])
203 opt.temp = ConvertUnits(have, want)
[0c83d8]204 else:
[0ad49c]205 if not opt.automass:
206 opt.molarmass = float(opt.molarmass)
207
[0c83d8]208 opt.density = float(opt.density)
[0ad49c]209
210 if opt.temp:
211 opt.temp = float(opt.temp)
[0c83d8]212
213 # Number might be an integer or a 3-vector
214 nvec = opt.number.split()
215 if len(nvec) == 3:
216 opt.number = [0]*3
217
[5735ba]218 for i in range(3):
[0c83d8]219 opt.number[i] = int(nvec[i])
[5735ba]220 else:
[0c83d8]221 opt.number = int(opt.number)
[0ad49c]222
[c0c85f]223 UpdateSource(opt)
224
[0ad49c]225 # Automatic source mass
226 if opt.automass:
[c0c85f]227 opt.molarmass = GetSourceMolareMass(opt)
[0ad49c]228 print '======== MOLAR MASS:', opt.molarmass
[0c83d8]229
230
231def FindBestCube(opt):
232 newroot = int( round(opt.number**(1./3)) )
233 newnumber = newroot**3
234
235 if newnumber != opt.number:
236 print 'Warning: Number changed to %d.' % newnumber
237
238 return [newroot] * 3
239
240
241def FindBestCuboid(opt):
242 n = opt.number
243
244 # Prime factors of n
245 factors = []
246
247 for i in [2, 3]:
248 while n % i == 0:
249 factors.append(i)
250 n /= 2
251
[5735ba]252 t = 5
253 diff = 2
[0c83d8]254
[5735ba]255 while t*t <= n:
[0c83d8]256 while n % t == 0:
257 factors.append(t)
258 n /= t
259
[5735ba]260 t = t + diff
261 diff = 6 - diff
[0c83d8]262
[5735ba]263 if n > 1:
[0c83d8]264 factors.append(n)
265
266 # Even distribution of current biggest prime to each vector -> similar sizes
267 if len(factors) < 3:
268 print 'Warning: Not enough prime factors - falling back to cubic placement'
269 return FindBestCube(opt)
270
271 factors.sort()
[5735ba]272 distri = [[],[],[]]
273 current = 0
[0c83d8]274
275 for factor in factors:
276 distri[current].append(factor)
[5735ba]277 current += 1
278 if current == 3:
279 current = 0
[0c83d8]280
281 result = [0]*3
282
283 print '======== CUBOID USED:',
284
[5735ba]285 for i in range(3):
[0c83d8]286 result[i] = int( reduce(operator.mul, distri[i]) )
287
288 print result
[5735ba]289 return result
[0c83d8]290
291
292def GetSourceBBabs(opt):
[5735ba]293 bbmax = [0.0]*3
[0c83d8]294 bbmin = [float('inf')]*3
295
296 s_name_ext = os.path.basename(opt.source).rsplit('.', 1)
[5735ba]297 s_namepart = s_name_ext[0]
[0c83d8]298
299 if len(s_name_ext) > 1:
[5735ba]300 s_ext = s_name_ext[1]
301 else:
302 s_ext = ''
[0c83d8]303
304 # Calculate bounding box from xyz-file
[5735ba]305 with open('temp_source.xyz') as f:
306 N = int(f.readline())
307 comment = f.readline()
[0c83d8]308
[5735ba]309 for i in xrange(N):
310 buf = f.readline()
311 xyz = buf.split()[1:]
[0c83d8]312
[5735ba]313 for i in range(3):
314 bbmax[i] = max(bbmax[i], float(xyz[i]))
315 bbmin[i] = min(bbmin[i], float(xyz[i]))
316
317 bb = [0.0]*3
[0c83d8]318
[5735ba]319 for i in range(3):
[0c83d8]320 bb[i] = abs(bbmax[i] - bbmin[i])
321
[5735ba]322 return bb
[c0c85f]323
324
325def UpdateSource(opt):
326 potfilepath = opt.potentialsfiledir + opt.basename + '.potentials'
[2aa9ef]327 mol.ParserSetOutputFormats("xyz tremolo")
328 mol.ParserParseTremoloPotentials(potfilepath)
329 mol.MoleculeLoad(opt.source)
[c0c85f]330
[2aa9ef]331 #cmd = 'molecuilder -o xyz tremolo --parse-tremolo-potentials %s -i temp_source.xyz -l %s' % (potfilepath, opt.source)
[c0c85f]332
333 if opt.autorotate:
[2aa9ef]334 mol.SelectAllAtoms()
335 mol.RotateToPrincipalAxisSystem("0 1 0")
336 #cmd += ' --select-all-atoms --rotate-to-principal-axis-system "0, 1, 0"'
337 mol.WorldOutput('temp_source' + '.data')
338 mol.WorldOutput('temp_source' + '.xyz')
[c0c85f]339
[2aa9ef]340 #os.system(cmd)
341 mol.reinit()
[c0c85f]342
343 opt.source = 'temp_source.data'
344
[5735ba]345
[0c83d8]346# Global options with sensible default parameters
347opt = c_opt()
[5735ba]348
[0c83d8]349ReadSettings(opt)
[c0c85f]350UpdateSettingsAndSource(opt)
[5735ba]351
[0c83d8]352if type(opt.number) == type([]):
353 # Number is a vector - use it without any modification
354 nbox = opt.number
[5735ba]355else:
[0c83d8]356 if opt.cubicdomain:
357 nbox = FindBestCube(opt)
[5735ba]358 else:
[0c83d8]359 nbox = FindBestCuboid(opt)
[5735ba]360
[0c83d8]361VolumePerMolecule = opt.molarmass / (avogadro * opt.density)
362cell = [VolumePerMolecule**(1./3)] * 3
[5735ba]363
[0c83d8]364if not opt.cubiccell:
365 try:
366 bb = GetSourceBBabs(opt)
367 print '======== BBOX:', bb
368 # Scaling factor - the molecules bounding box is scaled to fit the volume suiting the density
369 s = (VolumePerMolecule / (bb[0]*bb[1]*bb[2])) ** (1./3)
370
371 if s < 1:
372 print 'Warning: Molecular cells will overlap.'
373
374 for i in range(3):
375 cell[i] = bb[i]*s
376 except ZeroDivisionError:
377 print 'Warning: Singularity in bounding box, falling back to cubic cell.'
[5735ba]378
[0c83d8]379
[5735ba]380print '======== CELL: ', cell
381
382mol.CommandVerbose('0')
[0c83d8]383mol.ParserParseTremoloPotentials(opt.potentialsfiledir + opt.basename + '.potentials')
384mol.WorldInput(opt.source)
[5735ba]385mol.WorldCenterInBox('%f 0 0 %f 0 %f' % tuple(cell))
386mol.WorldRepeatBox('%d %d %d' % tuple(nbox))
[0c83d8]387mol.WorldOutput(opt.outfilename + '.data')
388mol.WorldOutput(opt.outfilename + '.xyz')
[5735ba]389
390domain = [0.0]*3
391
392for i in range(3):
393 domain[i] = cell[i]*nbox[i]
[0c83d8]394
[5735ba]395print '======== DOMAIN: ', domain
[32bc47]396
397# Postprocessing
398
399if opt.postprocess:
400 with open(opt.outfilename + '.data') as f:
401 ofile = f.read()
402
403 with open(opt.outfilename + '.data', 'w') as f:
404 f.write('# INPUTCONV shift center\n')
405
406 if opt.temp:
407 f.write('# INPUTCONV temp %.4f\n' % opt.temp)
408
409 f.write(ofile)
410
[c0c85f]411os.system('rm temp_source.data temp_source.xyz')
Note: See TracBrowser for help on using the repository browser.