source: utils/boxmaker.py@ b82ede

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 Candidate_v1.7.0 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 b82ede was 0ad49cc, checked in by Frederik Heber <heber@…>, 14 years ago

Update: Automatic mass calculation

Also:

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