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", "showbox", "reverse", "basis"]
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], [1,1,1]]
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 name2 = "Atomic Hybrid Orbitals"
401 name2 = "Lewis Hybrid Orbitals"
403 name2 = "Pre-orthogonal Lewis Hybrid Orbitals"
405 name2 = "Localized Property-optimized Orbitals"
407 name2 = "Chemist's Localized Property-optimized Orbitals"
409 mo_ao_items.push(name2)
410 mo_ao_keys.push(key2)
414 mult = (motype == "UHF" ? 2 : 1)
415 mo_menu = ["1000 (-00.00000000)"] # Dummy entry; create later
417 coeffs_matrix = nil # Coefficient matrix for showing surface
418 coeffs = nil # Coefficient array for showing surface
419 table_coeffs_matrix = nil # Coefficient matrix for table (based on "basis")
422 on_update_attr = lambda {
424 surface_dialog_attr[tag] = item_with_tag(tag)[:value]
426 it = item_with_tag("hide")
427 case surface_dialog_attr["hidden"]
439 update_mo_labels = lambda { |mo_ao_index|
442 m = (1..(ncomps * mult)).map { |n|
446 c1 = (i2 == 0 ? "B" : "A")
447 c2 = (i1 > (i2 == 0 ? beta : alpha) ? "*" : "")
452 if i1 > beta && i1 > alpha
454 elsif i1 > beta || i1 > alpha
460 en = mol.get_mo_energy(i1 + (i2 == 0 ? ncomps : 0))
461 sprintf("%d%s%s (%.8f)", i1, c1, c2, en)
463 elsif mo_ao_index == 1
466 m[i] = sprintf("%d: %s (%s)", i + 1, tabvals[i][2], mol.atoms[tabvals[i][3]].name)
470 key = mo_ao_keys[mo_ao_index]
471 labels = nbo[key + "_L"]
472 if labels == nil && key[0] == ?P
473 labels = nbo[key[1..-1] + "_L"]
477 lab = sprintf("%d: %s", i + 1, labels[i])
479 lab = sprintf("%s%d", key, i + 1)
486 on_get_value = lambda { |it, row, col|
488 if !table_coeffs_matrix || !coeffs_matrix
494 a_idx, s_idx, label = mol.get_gaussian_component_info(i)
495 if a_idx_old != a_idx
498 n = mol.atoms[a_idx].name
502 tabvals.push([a, n, label, a_idx])
505 labels = update_mo_labels.call(bs + 1)
508 label = label.gsub(/^\d+: /, "")
509 tabvals.push([(i + 1).to_s, "", label, i])
512 # Update coeffs_matrix
514 # coeffs_matrix = AO/(orbitals_to_display)
519 m.push(mol.get_mo_coefficients(i * mult + j + 1))
522 coeffs_matrix = LAMatrix.new(m) # Matrix AO/MO
524 coeffs_matrix = LAMatrix.identity(ncomps) # Matrix AO/AO (identity)
526 coeffs_matrix = nbo["AO/" + mo_ao_keys[mo_ao]]
531 m2 = LAMatrix.identity(ncomps)
533 m2 = nbo["AO/" + mo_ao_keys[bs + 1]]
535 # (basis)/(orbitals_to_display)
536 table_coeffs_matrix = m2.inverse * coeffs_matrix
538 # Update coefficient array
540 coeffs = coeffs_matrix.column(mo_index).to_a[0]
545 sprintf("%.6f", table_coeffs_matrix[mo_index, row])
548 $stderr.write(e.to_s + "\n")
549 $stderr.write(e.backtrace.inspect + "\n")
552 h = {"mo"=>nil, "color"=>nil, "opacity"=>nil, "threshold"=>nil, "expand"=>nil, "grid"=>nil, "reverse"=>nil, "basis"=>nil }
554 on_action = lambda { |it|
557 if tag == "color" || tag == "opacity"
558 opac = value("opacity").to_f
559 opac = 0.0 if opac < 0.0
560 opac = 1.0 if opac > 1.0
562 color = coltable[col] + [opac]
563 color0 = [1,1,1,opac]
564 mol.set_surface_attr(:color=>color, :color0=>color0)
566 elsif tag == "threshold" || tag == "reverse"
567 thres = item_with_tag("threshold")[:value].to_f
568 thres = 0.001 if thres >= 0.0 && thres < 0.001
569 thres = -0.001 if thres <= 0.0 && thres > -0.001
570 if item_with_tag("reverse")[:value] == 1
573 mol.set_surface_attr(:thres=>thres)
575 elsif tag == "showbox"
576 mol.set_surface_attr(:showbox=>value.to_i)
579 should_update = false
582 if val && h[key] != val
587 item_with_tag("update")[:enabled] = should_update
591 on_mo_action = lambda { |it|
595 mo_index = (mo / 2) + (mo % 2 == 1 ? ncomps : 0)
596 if mo_index < alpha || (mo_index >= ncomps && mo_index < ncomps + beta)
603 if mo_index < alpha && mo_index < beta
605 elsif mo_index < alpha || mo_index < beta
615 if mo_menu[mo] =~ /(\(ryd)|(\(RY)|(\(NB)|(\*)/
625 col = (occ_new == 0 ? 1 : (occ_new == -1 ? 2 : 0))
626 set_value("color", col)
630 item_with_tag("table")[:refresh] = true
634 on_set_action = lambda { |it|
635 if mo_ao != it[:value]
637 mo_menu = update_mo_labels.call(mo_ao)
638 it0 = item_with_tag("mo")
640 it0[:subitems] = mo_menu
641 it0[:value] = val # Keep the mo number invariant
642 h["mo"] = nil # "Update" button is forced to be enabled
644 coeffs_matrix = nil # Matrix needs update
645 on_mo_action.call(it0)
649 on_basis_action = lambda { |it|
650 if h["basis"] != value("basis")
651 table_coeffs_matrix = nil
652 item_with_tag("table")[:refresh] = true
655 on_update = lambda { |it|
659 opac = h["opacity"].to_f
660 opac = 0.0 if opac < 0.0
661 opac = 1.0 if opac > 1.0
662 color = coltable[h["color"]] + [opac]
663 color0 = [1, 1, 1, opac]
664 thres = h["threshold"].to_f
665 thres = 0.001 if thres >= 0.0 && thres < 0.001
666 thres = -0.001 if thres <= 0.0 && thres > -0.001
670 expand = h["expand"].to_f
671 expand = 0.01 if expand < 0.01
672 expand = 10.0 if expand > 10.0
673 grid = h["grid"].to_i
681 mol.set_mo_coefficients(0, 0.0, coeffs)
683 if it[:tag] == "create_cube"
684 basename = File.basename(mol.path || mol.name, ".*")
685 fname1 = Dialog.save_panel("Cube file name", mol.dir || Dir.pwd, basename + ".cube", "Gaussian cube file (*.cube)|*.cube")
687 mol.cubegen(fname1, idx, grid, true)
690 mol.create_surface(idx, :npoints=>grid, :color=>color, :thres=>thres, :expand=>expand, :color0=>color0)
693 surface_dialog_attr["hidden"] = 0
699 item(:text, :title=>"Orbital Set"),
700 item(:popup, :tag=>"mo_ao", :subitems=>mo_ao_items, :action=>on_set_action),
701 item(:text, :title=>"Select"),
702 item(:popup, :tag=>"mo", :subitems=>mo_menu, :action=>on_mo_action)),
704 item(:text, :title=>"Color"),
705 item(:popup, :tag=>"color", :subitems=>["blue", "red", "green", "yellow", "cyan", "magenta", "black", "white"], :action=>on_action),
706 item(:text, :title=>"Opacity"),
707 item(:textfield, :tag=>"opacity", :width=>80, :value=>"0.8", :action=>on_action),
708 item(:text, :title=>"Threshold"),
709 item(:textfield, :tag=>"threshold", :width=>80, :value=>"0.05", :action=>on_action),
710 item(:checkbox, :tag=>"reverse", :title=>"Reverse Phase", :action=>on_action),
713 item(:text, :title=>"Number of Grid Points"),
714 item(:textfield, :tag=>"grid", :width=>80, :value=>"512000", :action=>on_action),
715 item(:text, :title=>"Expand box"),
716 item(:textfield, :tag=>"expand", :width=>80, :value=>"1.0", :action=>on_action),
717 item(:checkbox, :tag=>"showbox", :title=>"Show box", :action=>on_action),
719 # table coefficients are based on "basis" (AO, NAO, etc.)
720 # MO is not allowed as basis, so the "basis" menu begins with "AO"
722 item(:text, :title=>"Coeffs based on:"),
723 item(:popup, :tag=>"basis", :subitems=>mo_ao_keys[1..-1], :value=>0, :action=>on_basis_action)),
724 item(:table, :width=>380, :height=>300, :tag=>"table",
725 :columns=>[["Atom", 40], ["Name", 60], ["Label", 140], ["Coeff", 120]],
726 :on_count=> lambda { |it| ncomps },
727 :on_get_value=>on_get_value,
728 :flex=>[0,0,0,0,1,1]),
730 item(:button, :tag=>"update", :title=>"Update", :action=>on_update),
731 item(:button, :tag=>"clear", :title=>"Clear", :action=>lambda { |it|
733 item_with_tag("update")[:enabled] = true
734 surface_dialog_attr["hidden"] = -1
737 item(:button, :tag=>"hide", :title=>"Hide", :action=>lambda { |it|
738 case surface_dialog_attr["hidden"]
740 surface_dialog_attr["hidden"] = 1
743 surface_dialog_attr["hidden"] = 0
748 item(:button, :tag=>"create_cube", :title=>"Create Cube", :action=>on_update),
749 :flex=>[0,1,0,0,0,0]),
753 if (val = surface_dialog_attr[tag]) != nil
754 item_with_tag(tag)[:value] = val
757 mo_idx = surface_dialog_attr["mo"]
759 on_set_action.call(item_with_tag("mo_ao"))
760 item_with_tag("mo")[:value] = surface_dialog_attr["mo"] = mo_idx
762 set_min_size(size[0], 250)
763 item_with_tag("table")[:refresh] = true
768 def ask_graphic_export_scale
769 scale = get_global_settings("global.export_graphic_scale")
770 scale = (scale ? scale.to_i - 1 : 3)
771 bg_color = get_global_settings("global.export_background_color")
772 bg_color = (bg_color ? bg_color.to_i + 1 : 0)
773 hash = Dialog.run("Set Export Properties") {
775 item(:text, :title=>"Resolution:"),
776 item(:popup, :subitems=>["Screen", "Screenx2", "Screenx3", "Screenx4", "Screenx5"], :tag=>"resolution", :value=>scale),
777 item(:text, :title=>"Background color:"),
778 item(:popup, :subitems=>["Same as Screen", "Transparent", "Black", "White"], :tag=>"bg_color", :value=>bg_color))
780 if hash[:status] == 0
781 set_global_settings("global.export_graphic_scale", (hash["resolution"] + 1).to_s)
782 set_global_settings("global.export_background_color", (hash["bg_color"] - 1).to_s)
791 $test_dialog = Dialog.new("Test") { item(:text, :title=>"test"); show }
798 sdir = get_global_settings("global.scratch_dir")
800 p = Dialog.open_panel("Please select scratch directory", sdir, nil, true)
803 error_message_box("Please avoid path containing a white space.\n" + p.sub(/ /, "<!> <!>"))
807 set_global_settings("global.scratch_dir", p)
817 register_menu("Assign residue...", :cmd_assign_residue, :non_empty)
818 register_menu("Offset residue...", :cmd_offset_residue, :non_empty)
819 register_menu("Sort by residue", :cmd_sort_by_residue, :non_empty)
820 register_menu("", "")
821 register_menu("Delete Frames...", :cmd_delete_frames, lambda { |m| m && m.nframes > 1 } )
822 register_menu("Reverse Frames...", :cmd_reverse_frames, lambda { |m| m && m.nframes > 1 } )
823 register_menu("Concatenate Frames...", :cmd_concat_frames, lambda { |m| m && m.nframes > 1 } )
824 register_menu("", "")
825 register_menu("Show Energy Window...", :cmd_show_energy, lambda { |m| m && m.property_names.include?("energy") } )
826 register_menu("Show MO Surface...", :cmd_create_surface, lambda { |m| m && m.get_mo_info(:type) != nil } )
827 #register_menu("cmd test", :cmd_test)