5 # Created by Toshi Nagata on 2008/06/28.
6 # Copyright 2008 Toshi Nagata. All rights reserved.
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation version 2 of the License.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
19 def cmd_assign_residue
24 atoms = sel.inspect.sub!("IntGroup[", "").sub!("]", "")
25 hash = Dialog.run("Assign Residue") {
27 item(:text, :title=>"New residue name/number\n(like \"RES.1\")\nfor atoms #{atoms}"),
28 item(:textfield, :width=>120, :tag=>"residue"))
31 residue = hash["residue"]
32 assign_residue(sel, residue)
36 def cmd_offset_residue
41 atoms = sel.inspect.sub!("IntGroup[", "").sub!("]", "")
42 hash = Dialog.run("Offset Residues") {
44 item(:text, :title=>"Offset residue number:\nfor atoms #{atoms}"),
45 item(:textfield, :width=>120, :tag=>"offset"))
48 offset = hash["offset"].to_i
49 offset_residue(sel, offset)
53 def cmd_sort_by_residue
58 sorted = sel.sort_by { |i| [self.atoms[i].res_seq, i] }
61 (0...natoms).each { |i|
65 break if j >= sorted.length
70 self.renumber_atoms(ary)
76 hash = Dialog.run("Delete Frames") {
78 item(:text, :title=>"Start"),
79 item(:textfield, :width=>120, :tag=>"start", :value=>"0"),
80 item(:text, :title=>"End"),
81 item(:textfield, :width=>120, :tag=>"end", :value=>(n - 1).to_s),
82 item(:text, :title=>"Keeping frames every..."),
84 item(:text, :title=>"Step"),
85 item(:textfield, :width=>120, :tag=>"step", :value=>"0"))
88 sframe = Integer(hash["start"])
89 eframe = Integer(hash["end"])
90 step = Integer(hash["step"])
91 return if sframe > eframe
92 eframe = n - 1 if eframe >= n
93 fgroup = IntGroup[sframe..eframe]
95 while sframe <= eframe
100 remove_frames(fgroup)
104 def cmd_reverse_frames
107 hash = Dialog.run("Reverse Frames") {
109 item(:text, :title=>"Start"),
110 item(:textfield, :width=>120, :tag=>"start", :value=>"0"),
111 item(:text, :title=>"End"),
112 item(:textfield, :width=>120, :tag=>"end", :value=>(n - 1).to_s))
114 if hash[:status] == 0
115 sframe = Integer(hash["start"])
116 eframe = Integer(hash["end"])
117 return if sframe > eframe
118 eframe = n - 1 if eframe >= n
119 frames = (0...sframe).to_a + (sframe..eframe).to_a.reverse + ((eframe + 1)...n).to_a
120 reorder_frames(frames)
124 def cmd_concat_frames
127 if Molecule.list.length == 1
128 message_box("No other molecule.", "", :ok)
131 ls = (0...Molecule.list.length).select { |i|
133 if m == self || m.natoms != self.natoms
136 if nil != (0...m.natoms).find { |n| m.atoms[n].atomic_number != self.atoms[n].atomic_number }
144 message_box("No molecule has the same atomic sequence as the present one.", "", :ok)
148 label_hash = Hash.new # Check if documents with the same name are present
149 ls.each_with_index { |i, idx| # i is an index to Molecule, idx is an index to ls
152 idx_h = label_hash[label] # If non-nil, then duplicate name
153 label_hash[label] = idx
155 if labels[idx_h] == label
156 labels[idx_h] = label + " (" + Molecule[ls[idx_h]].dir + ")"
158 label = label + " (" + Molecule[i].dir + ")"
162 nf = Molecule[ls[0]].nframes
163 hash = Dialog.run("Concatenate Frames") {
165 item(:text, :title=>"From Molecule:"),
166 item(:popup, :subitems=>labels, :tag=>"mol", :width=>240,
167 :action=>lambda { |it|
168 nf = Molecule[ls[it[:value]]].nframes
169 set_attr("start_title", :title=>"Start (0-#{nf})")
170 set_attr("end_title", :title=>"End (0-#{nf})")
172 item(:radio, :title=>"All Frames", :tag=>"all_frames", :value=>1,
173 :action=>lambda { |it|
174 flag = (it[:value] == 0)
175 set_attr("start_frame", :enabled=>flag)
176 set_attr("end_frame", :enabled=>flag)
178 item(:radio, :title=>"Select Frames", :tag=>"select_frames",
179 :action=>lambda { |it|
180 flag = (it[:value] != 0)
181 set_attr("start_frame", :enabled=>flag)
182 set_attr("end_frame", :enabled=>flag)
184 item(:text, :title=>"Start (0-#{nf})", :tag=>"start_title"),
185 item(:textfield, :value=>"0", :tag=>"start_frame", :enabled=>false),
186 item(:text, :title=>"End (0-#{nf})", :tag=>"end_title"),
187 item(:textfield, :value=>"0", :tag=>"end_frame", :enabled=>false))
188 radio_group("all_frames", "select_frames")
190 if hash[:status] == 0
191 idx_h = Integer(hash["mol"])
192 m = Molecule[ls[idx_h]]
193 f = m.frame # Save the current frame number
195 if hash["all_frame"] != 0
199 f1 = Integer(hash["start_frame"])
200 f2 = Integer(hash["end_frame"])
202 f1 = nf - 1 if f1 > nf - 1
204 f2 = nf - 1 if f2 > nf - 1
209 a = (f2..f1).to_a.reverse
211 prop = m.property_names
217 self.atoms[i].r = m.atoms[i].r
220 self.set_property(pr, m.get_property(pr))
226 def cmd_extra_properties
228 get_count = lambda { |it| mol.nframes }
229 get_value = lambda { |it, row, col|
233 sprintf("%.8f", mol.get_property(col - 1, row)) rescue ""
236 mol.open_auxiliary_window("Extra Props", :resizable=>true, :has_close_box=>true) {
238 @on_document_modified = lambda { |m|
239 if (n = m.property_names) != names
241 col = [["frame", 40]] + names.map { |nn| [nn, 120] }
242 item_with_tag("table")[:columns] = col
244 item_with_tag("table")[:refresh] = true
247 item(:table, :width=>320, :height=>300, :flex=>[0,0,0,0,1,1], :tag=>"table",
248 :on_count=>get_count,
249 :on_get_value=>get_value),
252 set_min_size(320, 200)
253 # listen(mol, "documentModified", on_document_modified)
254 # listen(mol, "documentWillClose", lambda { |m| close } )
255 @on_document_modified.call(mol)
264 wave_min = lambda { m = 1e8; wave.each { |x| if x != 0.0 && x < m; m = x; end }; m }
265 wave_max = lambda { m = -1e8; wave.each { |x| if x != 0.0 && x > m; m = x; end }; m }
266 d = open_auxiliary_window("Energy", :resizable=>true, :has_close_box=>true) {
267 graph_item = nil # Forward declaration
269 draw_graph = lambda { |it|
271 f = graph_item[:frame]
272 draw_rectangle(0, 0, f[2], f[3])
275 draw_line(16, 0, 16, height + 12, width + 20, height + 12)
280 h = (h == 0.0 ? 1.0 : height / h)
287 w = Float(width) / (c - 1)
291 # Skip the points that have exactly 0.0 value
292 wave.each_with_index { |d, i|
295 a.push(height - (d - min) * h + 12)
297 if d == 0.0 || i == wave.length - 1
298 # End of this curve fragment
304 # If wave has only one point, then draw a horizontal line
308 # Otherwise, draw a zero-length line
313 lines.push(a) # Store this line fragment
321 brush(:color=>[0.2, 0.2, 1.0])
329 yy = height - (y - min) * h + 12
330 draw_ellipse(cur * w + 16, height - (y - min) * h + 12, 6)
332 @on_document_modified = lambda { |m|
336 wave = [mol.get_property("energy", 0)] * 2
338 wave = (0...mol.nframes).map { |i| mol.get_property("energy", i) }
340 f = graph_item[:frame]
341 item_with_tag("energy")[:title] = sprintf("Energy = %.10f hartree, Frame = %d", mol.get_property("energy", cur), cur)
342 graph_item.refresh_rect([0, 0, f[2], f[3]])
345 item(:text, :title=>"Energy = ", :tag=>"energy"),
346 item(:view, :frame=>[0, 0, 320, 240], :tag=>"graph", :on_paint=>draw_graph, :flex=>[0,0,0,0,1,1]),
347 # item(:button, :title=>"Update", :action=>doc_modified, :align=>:center, :flex=>[1,1,1,0,0,0]),
348 # item(:button, :title=>"Close", :action=>proc { hide }, :align=>:center, :flex=>[1,1,1,0,0,0]),
351 graph_item = item_with_tag("graph")
355 @on_document_modified.call(mol)
361 def cmd_create_surface
362 if @surface_dialog_attr == nil
363 @surface_dialog_attr = Hash.new
364 @surface_dialog_attr["hidden"] = -1
366 surface_dialog_attr = @surface_dialog_attr
368 mol.open_auxiliary_window("MO Surface", :resizable=>true, :has_close_box=>true) {
369 tags = ["mo_ao", "mo", "color", "opacity", "threshold", "expand", "grid"]
370 motype = mol.get_mo_info(:type)
371 alpha = mol.get_mo_info(:alpha)
372 beta = mol.get_mo_info(:beta)
373 ncomps = mol.get_mo_info(:ncomps)
376 coltable = [[0,0,1], [1,0,0], [0,1,0], [1,1,0], [0,1,1], [1,0,1], [0,0,0]]
377 mo_ao_items = ["Molecular Orbitals", "Atomic Orbitals"]
378 mo_ao_keys = ["MO", "AO"]
379 if (nbo = mol.instance_eval { @nbo }) != nil
380 nbo.keys.each { |key|
381 if key[0..2] == "AO/"
385 name2 = "Natural Atomic Orbitals"
387 name2 = "Natural Bond Orbitals"
389 name2 = "Natural Hybrid Orbitals"
391 name2 = "Natural Localized Molecular Orbitals"
393 name2 = "Pre-orthogonal Natural Atomic Orbitals"
395 name2 = "Pre-orthogonal Natural Hybrid Orbitals"
397 name2 = "Pre-orthogonal Natural Bond Orbitals"
399 mo_ao_items.push(name2)
400 mo_ao_keys.push(key2)
404 mult = (motype == "UHF" ? 2 : 1)
405 mo_menu = ["1000 (-00.00000000)"] # Dummy entry; create later
411 a_idx, s_idx, label = mol.get_gaussian_component_info(i)
412 if a_idx_old != a_idx
415 n = mol.atoms[a_idx].name
419 tabvals.push([a, n, label, a_idx])
421 on_update_attr = lambda {
423 surface_dialog_attr[tag] = item_with_tag(tag)[:value]
425 it = item_with_tag("hide")
426 case surface_dialog_attr["hidden"]
438 on_get_value = lambda { |it, row, col|
444 coeffs = mol.get_mo_coefficients(mo_index)
446 coeffs = (0...ncomps).map { |i| (i == mo_index ? 1.0 : 0.0) }
448 coeffs = nbo["AO/" + mo_ao_keys[mo_ao]].column(mo_index).to_a[0]
451 sprintf("%.6f", coeffs[row])
454 h = {"mo"=>nil, "color"=>nil, "opacity"=>nil, "threshold"=>nil, "expand"=>nil, "grid"=>nil}
456 on_action = lambda { |it|
459 if tag == "color" || tag == "opacity"
460 opac = value("opacity").to_f
461 opac = 0.0 if opac < 0.0
462 opac = 1.0 if opac > 1.0
464 color = coltable[col] + [opac]
465 color0 = [1,1,1,opac]
466 mol.set_surface_attr(:color=>color, :color0=>color0)
468 elsif tag == "threshold"
469 thres = it[:value].to_f
470 thres = 0.001 if thres >= 0.0 && thres < 0.001
471 thres = -0.001 if thres <= 0.0 && thres > -0.001
472 mol.set_surface_attr(:thres=>thres)
475 should_update = false
478 if val && h[key] != val
483 item_with_tag("update")[:enabled] = should_update
487 on_mo_action = lambda { |it|
491 mo_index = (mo / 2) + (mo % 2 == 1 ? ncomps : 0) + 1
492 if mo_index <= alpha || (mo_index > ncomps && mo_index <= ncomps + beta)
499 if mo_index <= alpha && mo_index <= beta
501 elsif mo_index <= alpha || mo_index <= beta
514 col = (occ_new == 0 ? 1 : (occ_new == -1 ? 2 : 0))
515 set_value("color", col)
519 item_with_tag("table")[:refresh] = true
523 on_set_action = lambda { |it|
524 if mo_ao != it[:value]
527 mo_menu = (1..(ncomps * mult)).map { |n|
531 c1 = (i2 == 0 ? "B" : "A")
532 c2 = (i1 > (i2 == 0 ? beta : alpha) ? "*" : "")
537 if i1 > beta && i1 > alpha
539 elsif i1 > beta || i1 > alpha
545 en = mol.get_mo_energy(i1 + (i2 == 0 ? ncomps : 0))
546 sprintf("%d%s%s (%.8f)", i1, c1, c2, en)
551 mo_menu[i] = sprintf("AO%d: %s (%s)", i + 1, tabvals[i][2], mol.atoms[tabvals[i][3]].name)
555 key = mo_ao_keys[mo_ao]
556 labels = nbo[key + "_L"]
557 if labels == nil && key[0] == ?P
558 labels = nbo[key[1..-1] + "_L"]
561 lab = sprintf("%s%d", key, i + 1)
563 lab += ":" + labels[i]
568 it0 = item_with_tag("mo")
569 it0[:subitems] = mo_menu
571 h["mo"] = nil # "Update" button is forced to be enabled
572 on_mo_action.call(it0)
576 on_update = lambda { |it|
580 opac = h["opacity"].to_f
581 opac = 0.0 if opac < 0.0
582 opac = 1.0 if opac > 1.0
583 color = coltable[h["color"]] + [opac]
584 color0 = [1, 1, 1, opac]
585 thres = h["threshold"].to_f
586 thres = 0.001 if thres >= 0.0 && thres < 0.001
587 thres = -0.001 if thres <= 0.0 && thres > -0.001
588 expand = h["expand"].to_f
589 expand = 0.01 if expand < 0.01
590 expand = 10.0 if expand > 10.0
591 grid = h["grid"].to_i
599 mol.set_mo_coefficients(0, 0.0, coeffs)
601 if it[:tag] == "create_cube"
602 basename = File.basename(mol.path || mol.name, ".*")
603 fname1 = Dialog.save_panel("Cube file name", mol.dir || Dir.pwd, basename + ".cube", "Gaussian cube file (*.cube)|*.cube")
605 mol.cubegen(fname1, idx, grid, true)
608 mol.create_surface(idx, :npoints=>grid, :color=>color, :thres=>thres, :expand=>expand, :color0=>color0)
611 surface_dialog_attr["hidden"] = 0
617 item(:text, :title=>"Orbital Set"),
618 item(:popup, :tag=>"mo_ao", :subitems=>mo_ao_items, :action=>on_set_action),
619 item(:text, :title=>"Select"),
620 item(:popup, :tag=>"mo", :subitems=>mo_menu, :action=>on_mo_action)),
622 item(:text, :title=>"Color"),
623 item(:popup, :tag=>"color", :subitems=>["blue", "red", "green", "yellow", "cyan", "magenta", "black"], :action=>on_action),
624 item(:text, :title=>"Opacity"),
625 item(:textfield, :tag=>"opacity", :width=>80, :value=>"0.8", :action=>on_action),
626 item(:text, :title=>"Threshold"),
627 item(:textfield, :tag=>"threshold", :width=>80, :value=>"0.05", :action=>on_action),
628 item(:text, :title=>"Box Limit"),
629 item(:textfield, :tag=>"expand", :width=>80, :value=>"1.0", :action=>on_action)),
631 item(:text, :title=>"Number of Grid Points"),
632 item(:textfield, :tag=>"grid", :width=>120, :value=>"512000", :action=>on_action)),
633 item(:table, :width=>300, :height=>300, :tag=>"table",
634 :columns=>[["Atom", 60], ["Name", 60], ["Label", 60], ["Coeff", 120]],
635 :on_count=> lambda { |it| tabvals.count },
636 :on_get_value=>on_get_value,
637 :flex=>[0,0,0,0,1,1]),
639 item(:button, :tag=>"update", :title=>"Update", :action=>on_update),
640 item(:button, :tag=>"clear", :title=>"Clear", :action=>lambda { |it|
642 item_with_tag("update")[:enabled] = true
643 surface_dialog_attr["hidden"] = -1
646 item(:button, :tag=>"hide", :title=>"Hide", :action=>lambda { |it|
647 case surface_dialog_attr["hidden"]
649 surface_dialog_attr["hidden"] = 1
652 surface_dialog_attr["hidden"] = 0
657 item(:button, :tag=>"create_cube", :title=>"Create Cube", :action=>on_update),
658 :flex=>[0,1,0,0,0,0]),
662 if (val = surface_dialog_attr[tag]) != nil
663 item_with_tag(tag)[:value] = val
666 mo_idx = surface_dialog_attr["mo"]
668 on_set_action.call(item_with_tag("mo_ao"))
669 item_with_tag("mo")[:value] = surface_dialog_attr["mo"] = mo_idx
671 set_min_size(size[0], 250)
672 item_with_tag("table")[:refresh] = true
677 def ask_graphic_export_scale
678 scale = get_global_settings("global.export_graphic_scale")
679 scale = (scale ? scale.to_i - 1 : 3)
680 bg_color = get_global_settings("global.export_background_color")
681 bg_color = (bg_color ? bg_color.to_i + 1 : 0)
682 hash = Dialog.run("Set Export Properties") {
684 item(:text, :title=>"Resolution:"),
685 item(:popup, :subitems=>["Screen", "Screenx2", "Screenx3", "Screenx4", "Screenx5"], :tag=>"resolution", :value=>scale),
686 item(:text, :title=>"Background color:"),
687 item(:popup, :subitems=>["Same as Screen", "Transparent", "Black", "White"], :tag=>"bg_color", :value=>bg_color))
689 if hash[:status] == 0
690 set_global_settings("global.export_graphic_scale", (hash["resolution"] + 1).to_s)
691 set_global_settings("global.export_background_color", (hash["bg_color"] - 1).to_s)
700 $test_dialog = Dialog.new("Test") { item(:text, :title=>"test"); show }
707 sdir = get_global_settings("global.scratch_dir")
709 p = Dialog.open_panel("Please select scratch directory", sdir, nil, true)
712 error_message_box("Please avoid path containing a white space.\n" + p.sub(/ /, "<!> <!>"))
716 set_global_settings("global.scratch_dir", p)
726 register_menu("Assign residue...", :cmd_assign_residue, :non_empty)
727 register_menu("Offset residue...", :cmd_offset_residue, :non_empty)
728 register_menu("Sort by residue", :cmd_sort_by_residue, :non_empty)
729 register_menu("", "")
730 register_menu("Delete Frames...", :cmd_delete_frames, lambda { |m| m && m.nframes > 1 } )
731 register_menu("Reverse Frames...", :cmd_reverse_frames, lambda { |m| m && m.nframes > 1 } )
732 register_menu("Concatenate Frames...", :cmd_concat_frames, lambda { |m| m && m.nframes > 1 } )
733 register_menu("", "")
734 register_menu("Show Energy Window...", :cmd_show_energy, lambda { |m| m && m.property_names.include?("energy") } )
735 register_menu("Show MO Surface...", :cmd_create_surface, lambda { |m| m && m.get_mo_info(:type) != nil } )
736 #register_menu("cmd test", :cmd_test)