4 # Created by Toshi Nagata.
5 # Copyright 2009 Toshi Nagata. All rights reserved.
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation version 2 of the License.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
20 keys = arena.to_hash.keys
21 # Sort the keys so that they are (a little more) readable
22 [:timestep, :temperature, :cutoff, :electro_cutoff, :pairlist_distance,
23 :scale14_vdw, :scale14_elect, :use_xplor_shift, :dielectric,
24 :andersen_freq, :andersen_coupling, :random_seed, :relocate_center,
25 :use_graphite, :surface_probe_radius, :surface_tension, :surface_potential_freq,
26 :gradient_convergence, :coordinate_convergence,
27 :log_file, :coord_file, :vel_file, :force_file, :debug_file, :debug_output_level,
28 :coord_output_freq, :energy_output_freq].each_with_index { |k, i|
32 # Arrange the items in vertical direction
33 n = (keys.count + 1) / 2
35 keys = keys.sort_by { |k| i += 1; (i > n ? i - n + 0.5 : i) }
37 hash = RubyDialog.run("Molecular Dynamics Advanced Settings") {
40 enabled = (k == :step || k == :coord_frame ? false : true)
41 items.push(item(:text, :title=>k.to_s))
42 it = item(:textfield, :width=>120, :value=>arena[k].to_s)
46 set_attr(it, :enabled=>false)
67 if !arena.prepare(true) # Parameter check only
68 RubyDialog.run("MD Error") {
70 item(:text, :title=>"Some parameters are missing. Please open\nthe 'parameters' table and examine."))
71 set_attr(1, :hidden=>true) # Hide the cancel button
76 # Initialize some fields at first invocation
78 if arena.log_file == nil && self.dir != nil
79 arena.log_file = self.dir + "/" + self.name.gsub(/\.\w+$/, ".log")
93 arena = self.prepare_arena
102 files_save = Hash.new
103 [:log_file, :coord_file, :vel_file, :force_file, :debug_file].each { |k|
107 arena[k] = File.basename(s)
110 title = (minimize ? "Minimize" : "Molecular Dynamics")
111 hash = RubyDialog.run(title) {
114 items.push item(:text, :title=>"Timestep (fs)")
115 items.push item(:textfield, :width=>120, :value=>arena.timestep.to_s, :tag=>"timestep")
116 items.push item(:text, :title=>"Target temperature (K)")
117 items.push item(:textfield, :width=>120, :value=>arena.temperature.to_s, :tag=>"temperature")
119 items.push item(:text, :title=>"Steps per frame")
120 items.push item(:textfield, :width=>120, :value=>arena.coord_output_freq.to_s, :tag=>"steps_per_frame")
121 items.push item(:text, :title=>"Number of frames")
122 items.push item(:textfield, :width=>120, :value=>nf.to_s, :tag=>"number_of_frames")
123 items.push item(:text, :title=>"Log file")
124 items.push item(:textfield, :width=>120, :value=>(arena.log_file || ""), :tag=>"log_file")
125 items.push item(:button, :title=>"Advanced...",
129 set_value("timestep", arena[:timestep].to_s)
130 set_value("temperature", arena[:temperature].to_s)
132 set_value("log_file", arena[:log_file])
138 dirstr = (self.dir || document_home)
140 arena[:log_file] = hash[:log_file]
142 [:log_file, :coord_file, :vel_file, :force_file, :debug_file].each { |k|
145 if s != nil && s != ""
146 arena[k] = dirstr + "/" + s
151 arena[k] = files_save[k]
158 arena.temperature = Float(hash["temperature"])
159 arena.timestep = Float(hash["timestep"])
161 arena.coord_output_freq = Integer(hash["steps_per_frame"])
162 arena.energy_output_freq = arena.coord_output_freq
163 return Integer(hash["number_of_frames"])
166 def cmd_define_unit_cell
168 hash = RubyDialog.run("Define Unit Cell") {
172 ["o0", "o1", "o2", "a0", "a1", "a2", "b0", "b1", "b2", "c0", "c1", "c2"].each { |k|
175 if s == nil || s == ""
178 h[k] = Float(eval(s))
179 set_value(k, h[k].to_s)
182 mes = "Cannot evaluate #{value(k)}: " + $!.to_s
183 RubyDialog.run("Value Error") {
184 layout(1, item(:text, :title=>mes))
185 set_attr(1, :hidden=>true)
190 ax = Vector3D[h["a0"], h["a1"], h["a2"]]
191 bx = Vector3D[h["b0"], h["b1"], h["b2"]]
192 cx = Vector3D[h["c0"], h["c1"], h["c2"]]
193 ox = Vector3D[h["o0"], h["o1"], h["o2"]]
194 @mol.set_box(ax, bx, cx, ox)
200 return # Cannot set box: dialog is not dismissed
207 item(:text, :title=>"Unit cell:"),
209 item(:text, :title=>"origin"),
210 item(:textfield, :width=>140, :tag=>"o0", :value=>(box ? box[3].x.to_s : "")),
211 item(:textfield, :width=>140, :tag=>"o1", :value=>(box ? box[3].y.to_s : "")),
212 item(:textfield, :width=>140, :tag=>"o2", :value=>(box ? box[3].z.to_s : "")),
213 item(:text, :title=>"a-axis"),
214 item(:textfield, :width=>140, :tag=>"a0", :value=>(box ? box[0].x.to_s : "")),
215 item(:textfield, :width=>140, :tag=>"a1", :value=>(box ? box[0].y.to_s : "")),
216 item(:textfield, :width=>140, :tag=>"a2", :value=>(box ? box[0].z.to_s : "")),
217 item(:text, :title=>"b-axis"),
218 item(:textfield, :width=>140, :tag=>"b0", :value=>(box ? box[1].x.to_s : "")),
219 item(:textfield, :width=>140, :tag=>"b1", :value=>(box ? box[1].y.to_s : "")),
220 item(:textfield, :width=>140, :tag=>"b2", :value=>(box ? box[1].z.to_s : "")),
221 item(:text, :title=>"c-axis"),
222 item(:textfield, :width=>140, :tag=>"c0", :value=>(box ? box[2].x.to_s : "")),
223 item(:textfield, :width=>140, :tag=>"c1", :value=>(box ? box[2].y.to_s : "")),
224 item(:textfield, :width=>140, :tag=>"c2", :value=>(box ? box[2].z.to_s : "")),
225 item(:button, :title=>"Set", :action=>:set_box_value),
226 item(:text, :title=>"(Ruby expressions are allowed as the values)"),
231 def cmd_show_periodic_image
233 hash = RubyDialog.run("Show Periodic Image") {
235 def set_periodic_image(n)
237 ["amin", "amax", "bmin", "bmax", "cmin", "cmax"].each_with_index { |k, i|
239 if s == nil || s == ""
245 @mol.show_periodic_image(a)
249 set_periodic_image(n)
253 pimage = @mol.show_periodic_image
255 item(:text, :title=>"Show Periodic Image:"),
257 item(:text, :title=>"a-axis"),
258 item(:textfield, :width=>80, :tag=>"amin", :value=>pimage[0].to_s),
259 item(:text, :title=>"to"),
260 item(:textfield, :width=>80, :tag=>"amax", :value=>pimage[1].to_s),
261 item(:text, :title=>"b-axis"),
262 item(:textfield, :width=>80, :tag=>"bmin", :value=>pimage[2].to_s),
263 item(:text, :title=>"to"),
264 item(:textfield, :width=>80, :tag=>"bmax", :value=>pimage[3].to_s),
265 item(:text, :title=>"c-axis"),
266 item(:textfield, :width=>80, :tag=>"cmin", :value=>pimage[4].to_s),
267 item(:text, :title=>"to"),
268 item(:textfield, :width=>80, :tag=>"cmax", :value=>pimage[5].to_s),
269 item(:button, :title=>"Set", :action=>:set_periodic_image))
273 def cmd_pressure_control
276 RubyDialog.run("Pressure Control Error") {
278 item(:text, :title=>"Unit cell is not defined. Please open 'Unit Cell...' \nfrom the MM/MD menu and set up the unit cell."))
279 set_attr(1, :hidden=>true) # Hide the cancel button
285 arena = self.prepare_arena
291 # Create pressure arena
292 if arena.pressure_freq == nil
293 arena.pressure_freq = 20 # This assignment will automatically create pressure arena
296 hash = RubyDialog.run("Pressure Control") {
299 item(:text, :title=>"Frequency"),
300 item(:textfield, :width=>100, :tag=>"freq", :value=>arena.pressure_freq.to_s),
301 item(:text, :title=>"Coupling"),
302 item(:textfield, :width=>100, :tag=>"coupling", :value=>arena.pressure_coupling.to_s),
303 item(:text, :title=>"Pressure a"),
304 item(:textfield, :width=>100, :tag=>"pa", :value=>arena.pressure[0].to_s),
305 item(:text, :title=>"Pressure b"),
306 item(:textfield, :width=>100, :tag=>"pb", :value=>arena.pressure[1].to_s),
307 item(:text, :title=>"Pressure c"),
308 item(:textfield, :width=>100, :tag=>"pc", :value=>arena.pressure[2].to_s),
309 item(:text, :title=>"Cell flexibility a"),
310 item(:textfield, :width=>100, :tag=>"fa", :value=>arena.pressure_cell_flexibility[0].to_s),
311 item(:text, :title=>"Cell flexibility b"),
312 item(:textfield, :width=>100, :tag=>"fb", :value=>arena.pressure_cell_flexibility[1].to_s),
313 item(:text, :title=>"Cell flexibility c"),
314 item(:textfield, :width=>100, :tag=>"fc", :value=>arena.pressure_cell_flexibility[2].to_s),
315 item(:text, :title=>"Cell flexibility bc"),
316 item(:textfield, :width=>100, :tag=>"fbc", :value=>arena.pressure_cell_flexibility[3].to_s),
317 item(:text, :title=>"Cell flexibility ca"),
318 item(:textfield, :width=>100, :tag=>"fca", :value=>arena.pressure_cell_flexibility[4].to_s),
319 item(:text, :title=>"Cell flexibility ab"),
320 item(:textfield, :width=>100, :tag=>"fab", :value=>arena.pressure_cell_flexibility[5].to_s),
321 item(:text, :title=>"Cell flexibility origin"),
322 item(:textfield, :width=>100, :tag=>"forig", :value=>arena.pressure_cell_flexibility[6].to_s),
323 item(:text, :title=>"Cell flexibility orientation"),
324 item(:textfield, :width=>100, :tag=>"forient", :value=>arena.pressure_cell_flexibility[7].to_s),
325 item(:text, :title=>"Fluctuate cell origin"),
326 item(:textfield, :width=>100, :tag=>"orig", :value=>arena.pressure_fluctuate_cell_origin.to_s),
327 item(:text, :title=>"Fluctuate cell orientation"),
328 item(:textfield, :width=>100, :tag=>"orient", :value=>arena.pressure_fluctuate_cell_orientation.to_s))
331 arena.pressure_freq = hash["freq"]
332 arena.pressure_coupling = hash["coupling"]
333 arena.pressure = [hash["pa"], hash["pb"], hash["pc"]]
334 arena.pressure_cell_flexibility = [hash["fa"], hash["fb"], hash["fc"], hash["fbc"], hash["fca"], hash["fab"], hash["forig"], hash["forient"]]
335 arena.pressure_fluctuate_cell_origin = hash["orig"]
336 arena.pressure_fluctuate_cell_orientation = hash["orient"]
340 def ambertools_dialog(tool)
341 ante_dir = get_global_settings("antechamber.ante_dir")
342 log_dir = get_global_settings("antechamber.log_dir")
344 ante_dir = ($platform == "mac" ? "/Applications/amber10" : ($platform == "win" ? "c:/opt/amber10" : ""))
347 log_dir = document_home + "/amber10"
349 if $platform == "win"
354 log_level = (get_global_settings("antechamber.log_level") || "none")
355 hash = RubyDialog.run("Run " + tool.capitalize) {
356 @toolname = tool + suffix
357 def valid_antechamber_dir(s)
358 FileTest.exist?(s + "/" + @toolname)
362 if valid_antechamber_dir(attr(n, :value))
363 set_attr(0, :enabled=>true)
365 elsif n == 0 || n == 1
366 # Settings are stored in the global settings (even if Cancel is pressed)
368 if (tag = attr(i, :tag))
369 value = attr(i, :value)
370 v = [["log_none", "none"], ["log_error_only", "error_only"], ["log_keep_latest", "latest"], ["log_all", "all"]].assoc(tag)
376 set_global_settings("antechamber.#{tag}", value)
383 item(:text, :title=>"Ambertools directory:"),
384 [ item(:button, :title=>"Choose...",
386 # print "action method called\n"
387 dir = RubyDialog.open_panel(nil, nil, nil, true)
389 if valid_antechamber_dir(dir)
390 set_value("ante_dir", dir)
391 set_attr(0, :enabled=>true)
395 item(:text, :title=>"Cannot find #{tool} in #{dir}."))
397 set_attr(0, :enabled=>false)
401 ), {:align=>:right} ],
402 item(:textfield, :width=>360, :height=>40, :tag=>"ante_dir", :value=>ante_dir),
404 item(:text, :title=>"Log directory:"),
405 [ item(:button, :title=>"Choose...",
407 dir = RubyDialog.open_panel(nil, nil, nil, true)
409 set_value("log_dir", dir)
412 ), {:align=>:right} ],
413 item(:textfield, :width=>360, :height=>40, :tag=>"log_dir", :value=>log_dir),
415 item(:text, :title=>"Log handling"),
418 item(:radio, :title=>"Do not keep logs", :tag=>"log_none", :value=>(log_level == "none" ? 1 : 0)),
419 item(:radio, :title=>"Keep only when error occurred", :tag=>"log_error_only", :value=>(log_level == "error_only" ? 1 : 0)),
421 item(:radio, :title=>"Keep latest ", :tag=>"log_keep_latest", :value=>(log_level == "latest" ? 1 : 0)),
422 item(:textfield, :width=>80, :tag=>"log_keep_number", :value=>(get_global_settings("antechamber.log_keep_number") || "10")),
423 item(:text, :title=>"logs"),
424 {:margin=>0, :padding=>0}),
425 item(:radio, :title=>"Keep All", :tag=>"log_all", :value=>(log_level == "all" ? 1 : 0)),
426 {:margin=>0, :padding=>0}
429 item(:text, :title=>"Net Molecular Charge:"),
430 item(:textfield, :width=>"80", :tag=>"nc", :value=>(get_global_settings("antechamber.nc") || "0"))
432 set_attr(0, :enabled=>valid_antechamber_dir(ante_dir))
434 # The hash values are set in the action() method
435 # print "retval = #{hash ? 1 : 0}\n"
436 return (hash ? 1 : 0)
439 def create_ante_log_dir(name, key)
440 log_dir = get_global_settings("antechamber.log_dir")
443 if !FileTest.directory?(log_dir)
444 Dir.mkdir(log_dir) rescue ((msg = "Cannot create directory #{log_dir}") && raise)
447 while FileTest.exist?(dname = log_dir + "/#{name}_#{key}.#{n}")
450 Dir.mkdir(dname) rescue ((msg = "Cannot create directory #{dname}") && raise)
453 error_message_box(msg + ": " + $!.to_s)
458 def clean_ante_log_dir(nkeep)
460 if FileTest.directory?(f)
461 Dir.entries(f).each { |file|
462 next if file == "." || file == ".."
463 rm_recursive(f + "/" + file)
470 log_dir = get_global_settings("antechamber.log_dir")
475 # Get subdirectories and sort by last modified time
476 dirs = Dir.entries(log_dir).reject! { |x|
477 !FileTest.directory?(x) || x == "." || x == ".."
481 dirs[0..-(nkeep + 1)].each { |d|
486 error_message_box $!.to_s
495 return ambertools_dialog("antechamber")
498 def import_ac(acfile)
499 open(acfile, "r") { |fp|
503 idx = Integer(s[4..11]) - 1
504 charge = Float(s[54..63])
514 def import_frcmod(file)
515 self.md_arena.prepare(true) # Clean up existing parameters
517 open(file, "r") { |fp|
547 name, weight = s.split
548 wtable[name] = Float(weight)
550 types, k, r0, com = s.split(nil, 4)
551 pp = par.bonds.lookup(types, :local, :missing) || par.bonds.insert
552 pp.atom_types = types
557 types, k, a0, com = s.split(nil, 4)
558 pp = par.angles.lookup(types, :local, :missing) || par.angles.insert
559 pp.atom_types = types
564 types, n, k, phi0, period, com = s.split(nil, 6)
565 pp = par.dihedrals.lookup(types, :local, :missing) || par.dihedrals.insert
566 pp.atom_types = types
570 pp.period = Float(period).round
573 types, k, phi0, period, com = s.split(nil, 5)
574 pp = par.impropers.lookup(types, :local, :missing) || par.impropers.insert
575 pp.atom_types = types
579 pp.period = Float(period).round
582 name, r_eq, eps, com = s.split(nil, 4)
583 pp = par.vdws.lookup(name, :local, :missing) || par.vdws.insert
590 pp.weight = wtable[name]
602 if (p = elements.assoc(ap.atomic_number))
605 elements.push [ap.atomic_number, 1]
608 elements.sort_by { |p| p[0] }
613 error_message_box "Molecule is empty"
616 error_message_box "No ESP information is loaded"
619 return unless ambertools_dialog("resp")
620 nc = get_global_settings("antechamber.nc")
621 ante_dir = get_global_settings("antechamber.ante_dir")
623 # Create the temporary directory
624 dname = create_ante_log_dir((self.path ? File.basename(self.path, ".*") : self.name), "rs")
630 eq = search_equivalent_atoms
631 # search for methyl (and methylene??) hydrogens
634 if ap.element == "C" && ap.connects.length == 4
635 hs = ap.connects.select { |n| atoms[n].element == "H" }
638 hs.each { |n| methyl[n] = mc } # methyl hydrogens
639 methyl[ap.index] = -1 # methyl carbon
644 open("resp.input#{i}", "w") { |fp|
645 fp.print " #{self.name} \n"
647 fp.print " ioutput=1, IQOPT=#{i}, nmol=1, ihfree=1, irstrnt=1, qwt=#{i*0.0005},\n"
650 fp.print " #{self.name} \n"
651 fp.printf "%4d%5d\n", nc, self.natoms
655 n = (methyl[idx] ? 0 : eq[idx] + 1)
656 n = 0 if n == idx + 1
658 n = (methyl[idx] ? methyl[idx] + 1 : -1)
659 n = 0 if n == idx + 1
661 fp.printf "%4d%5d\n", ap.atomic_number, n
663 fp.print "\n\n\n\n\n\n"
667 # Export as an antechamber format
669 formula = c.map { |p| Parameter.atoms[p[0]].name + p[1].to_s }.join(" ")
670 open("respgen_in.ac", "w") { |fp|
671 fp.printf("CHARGE %9.2f ( %d )\n", nc, nc)
672 fp.printf("Formula: #{formula}\n")
674 fp.printf("ATOM %6d %-4s%-3s%6d%12.3f%8.3f%8.3f%10.6f%8s\n", ap.index + 1, ap.name, ap.res_name, ap.res_seq, ap.r.x, ap.r.y, ap.r.z, ap.charge, ap.atom_type)
676 bonds.each_with_index { |b, i|
677 fp.printf("BOND%5d%5d%5d%5d %5s%5s\n", i + 1, b[0] + 1, b[1] + 1, 0, atoms[b[0]].name, atoms[b[1]].name)
681 # Create resp input by respgen
682 if !system("#{ante_dir}/respgen -i respgen_in.ac -o resp.input1 -f resp1") \
683 || !system("#{ante_dir}/respgen -i respgen_in.ac -o resp.input2 -f resp2")
684 error_message_box("Cannot run respgen.")
691 a2b = 1.8897259885 # angstrom to bohr
692 open("resp.esp", "w") { |fp|
693 fp.printf("%5d%5d%5d\n", natoms, nelpots, nc)
695 fp.printf(" %16.7E%16.7E%16.7E\n", ap.r.x * a2b, ap.r.y * a2b, ap.r.z * a2b)
699 fp.printf("%16.7E%16.7E%16.7E%16.7E\n", esp, pos.x, pos.y, pos.z)
705 if !system("#{ante_dir}/resp -O -i resp.input1 -o resp.output1 -e resp.esp -t qout_stage1") \
706 || !system("#{ante_dir}/resp -O -i resp.input2 -o resp.output2 -e resp.esp -q qout_stage1 -t qout_stage2")
707 error_message_box("Cannot run resp.")
713 open("punch", "r") { |fp|
715 next unless s =~ /Point charges/ && s =~ /after optimization/
720 break if ary.count == 0
721 if Integer(ary[1]) != atoms[i].atomic_number
722 error_message_box(sprintf("The atom %d has inconsistent atomic number (%d in mol, %d in resp output)", i, atoms[i].atomic_number, ary[1]))
726 atoms[i].charge = Float(ary[3])
738 error_message_box "Molecule is empty"
742 RubyDialog.run("#{name}:GAMESS/RESP", nil, nil) {
744 item(:text, :title=>"Step 1:\nCreate GAMESS input for ESP calculation"),
745 [item(:button, :title=>"Create GAMESS Input...",
747 esp_save = get_global_settings("gamess.esp")
748 set_global_settings("gamess.esp", 1)
749 mol.cmd_create_gamess_input
750 set_global_settings("gamess.esp", esp_save)
754 item(:text, :title=>"Step 2:\nImport GAMESS .dat file"),
755 [item(:button, :title=>"Import GAMESS dat...",
757 fname = RubyDialog.open_panel("Select GAMESS .dat file", nil, "*.dat")
762 errmsg = "Cannot find ESP results in the dat file." if mol.nelpots == 0
764 errmsg = "Error reading the dat file."
767 message_box(errmsg + "\nYou may want to try another .dat file.", "GAMESS Import Error", :ok, :warning)
769 set_attr("resp", :enabled=>true)
775 item(:text, :title=>"Step 3:\nRun RESP for charge fitting"),
776 [item(:button, :title=>"Run RESP...", :tag=>"resp",
779 if get_global_settings("antechamber.log_level") == "latest"
780 mol.clean_ante_log_dir(Integer(get_global_settings("antechamber.log_keep_number")))
787 [item(:button, :title=>"Close", :action=>proc { action(1) }),
791 set_attr("resp", :enabled=>false)
796 def cmd_edit_local_parameter_in_mainview(ptype, names, types, value, params)
797 # The parameters are given as space separated strings
798 # e.g. "1.862 200.000"
802 pen = self.parameter.bonds
805 pen = self.parameter.angles
807 k = ["k", "period", "phi0"]
808 pen = self.parameter.dihedrals
810 k = ["k", "period", "phi0"]
811 pen = self.parameter.impropers
816 hash = RubyDialog.run("Edit local parameter") {
818 item(:text, :title=>"Edit #{ptype} parameter for #{names} (#{types})"),
819 item(:text, :title=>"(Current value = #{value})"),
821 [item(:text, :title=>"types"), {:align=>:center}],
822 [item(:text, :title=>k[0]), {:align=>:center}],
823 [item(:text, :title=>k[1]), {:align=>:center}],
824 (k[2] ? [item(:text, :title=>k[2]), {:align=>:center}] : -1),
826 item(:textfield, :width=>100, :value=>types, :tag=>"types"),
827 item(:textfield, :width=>100, :value=>p[0], :tag=>k[0]),
828 item(:textfield, :width=>100, :value=>p[1], :tag=>k[1]),
829 (k[2] ? item(:textfield, :width=>100, :value=>p[2], :tag=>k[2]) : -1)
834 pref = pen.lookup(hash["types"], :create, :local, :missing, :nobasetype, :nowildcard)
835 raise "Cannot create new #{ptype} parameter" if !pref
836 k.each { |key| pref.set_attr(key, hash[key]) }
837 self.md_arena.prepare(true) # Check parameter only