source: utils/boxmaker.py@ 39cbae

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 39cbae was 39cbae, checked in by Frederik Heber <heber@…>, 14 years ago

Update: Unit handling

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