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 name2 = "Atomic Hybrid Orbitals"
401 name2 = "Localized Hybrid Orbitals"
403 name2 = "Localized Property-optimized Orbitals"
405 name2 = "Chemist's Localized Property-optimized Orbitals"
407 mo_ao_items.push(name2)
408 mo_ao_keys.push(key2)
412 mult = (motype == "UHF" ? 2 : 1)
413 mo_menu = ["1000 (-00.00000000)"] # Dummy entry; create later
419 a_idx, s_idx, label = mol.get_gaussian_component_info(i)
420 if a_idx_old != a_idx
423 n = mol.atoms[a_idx].name
427 tabvals.push([a, n, label, a_idx])
429 on_update_attr = lambda {
431 surface_dialog_attr[tag] = item_with_tag(tag)[:value]
433 it = item_with_tag("hide")
434 case surface_dialog_attr["hidden"]
446 on_get_value = lambda { |it, row, col|
452 coeffs = mol.get_mo_coefficients(mo_index)
454 coeffs = (0...ncomps).map { |i| (i == mo_index ? 1.0 : 0.0) }
456 coeffs = nbo["AO/" + mo_ao_keys[mo_ao]].column(mo_index).to_a[0]
459 sprintf("%.6f", coeffs[row])
462 h = {"mo"=>nil, "color"=>nil, "opacity"=>nil, "threshold"=>nil, "expand"=>nil, "grid"=>nil}
464 on_action = lambda { |it|
467 if tag == "color" || tag == "opacity"
468 opac = value("opacity").to_f
469 opac = 0.0 if opac < 0.0
470 opac = 1.0 if opac > 1.0
472 color = coltable[col] + [opac]
473 color0 = [1,1,1,opac]
474 mol.set_surface_attr(:color=>color, :color0=>color0)
476 elsif tag == "threshold"
477 thres = it[:value].to_f
478 thres = 0.001 if thres >= 0.0 && thres < 0.001
479 thres = -0.001 if thres <= 0.0 && thres > -0.001
480 mol.set_surface_attr(:thres=>thres)
483 should_update = false
486 if val && h[key] != val
491 item_with_tag("update")[:enabled] = should_update
495 on_mo_action = lambda { |it|
499 mo_index = (mo / 2) + (mo % 2 == 1 ? ncomps : 0) + 1
500 if mo_index <= alpha || (mo_index > ncomps && mo_index <= ncomps + beta)
507 if mo_index <= alpha && mo_index <= beta
509 elsif mo_index <= alpha || mo_index <= beta
522 col = (occ_new == 0 ? 1 : (occ_new == -1 ? 2 : 0))
523 set_value("color", col)
527 item_with_tag("table")[:refresh] = true
531 on_set_action = lambda { |it|
532 if mo_ao != it[:value]
535 mo_menu = (1..(ncomps * mult)).map { |n|
539 c1 = (i2 == 0 ? "B" : "A")
540 c2 = (i1 > (i2 == 0 ? beta : alpha) ? "*" : "")
545 if i1 > beta && i1 > alpha
547 elsif i1 > beta || i1 > alpha
553 en = mol.get_mo_energy(i1 + (i2 == 0 ? ncomps : 0))
554 sprintf("%d%s%s (%.8f)", i1, c1, c2, en)
559 mo_menu[i] = sprintf("AO%d: %s (%s)", i + 1, tabvals[i][2], mol.atoms[tabvals[i][3]].name)
563 key = mo_ao_keys[mo_ao]
564 labels = nbo[key + "_L"]
565 if labels == nil && key[0] == ?P
566 labels = nbo[key[1..-1] + "_L"]
569 lab = sprintf("%s%d", key, i + 1)
571 lab += ":" + labels[i]
576 it0 = item_with_tag("mo")
577 it0[:subitems] = mo_menu
579 h["mo"] = nil # "Update" button is forced to be enabled
580 on_mo_action.call(it0)
584 on_update = lambda { |it|
588 opac = h["opacity"].to_f
589 opac = 0.0 if opac < 0.0
590 opac = 1.0 if opac > 1.0
591 color = coltable[h["color"]] + [opac]
592 color0 = [1, 1, 1, opac]
593 thres = h["threshold"].to_f
594 thres = 0.001 if thres >= 0.0 && thres < 0.001
595 thres = -0.001 if thres <= 0.0 && thres > -0.001
596 expand = h["expand"].to_f
597 expand = 0.01 if expand < 0.01
598 expand = 10.0 if expand > 10.0
599 grid = h["grid"].to_i
607 mol.set_mo_coefficients(0, 0.0, coeffs)
609 if it[:tag] == "create_cube"
610 basename = File.basename(mol.path || mol.name, ".*")
611 fname1 = Dialog.save_panel("Cube file name", mol.dir || Dir.pwd, basename + ".cube", "Gaussian cube file (*.cube)|*.cube")
613 mol.cubegen(fname1, idx, grid, true)
616 mol.create_surface(idx, :npoints=>grid, :color=>color, :thres=>thres, :expand=>expand, :color0=>color0)
619 surface_dialog_attr["hidden"] = 0
625 item(:text, :title=>"Orbital Set"),
626 item(:popup, :tag=>"mo_ao", :subitems=>mo_ao_items, :action=>on_set_action),
627 item(:text, :title=>"Select"),
628 item(:popup, :tag=>"mo", :subitems=>mo_menu, :action=>on_mo_action)),
630 item(:text, :title=>"Color"),
631 item(:popup, :tag=>"color", :subitems=>["blue", "red", "green", "yellow", "cyan", "magenta", "black"], :action=>on_action),
632 item(:text, :title=>"Opacity"),
633 item(:textfield, :tag=>"opacity", :width=>80, :value=>"0.8", :action=>on_action),
634 item(:text, :title=>"Threshold"),
635 item(:textfield, :tag=>"threshold", :width=>80, :value=>"0.05", :action=>on_action),
636 item(:text, :title=>"Box Limit"),
637 item(:textfield, :tag=>"expand", :width=>80, :value=>"1.0", :action=>on_action)),
639 item(:text, :title=>"Number of Grid Points"),
640 item(:textfield, :tag=>"grid", :width=>120, :value=>"512000", :action=>on_action)),
641 item(:table, :width=>300, :height=>300, :tag=>"table",
642 :columns=>[["Atom", 60], ["Name", 60], ["Label", 60], ["Coeff", 120]],
643 :on_count=> lambda { |it| tabvals.count },
644 :on_get_value=>on_get_value,
645 :flex=>[0,0,0,0,1,1]),
647 item(:button, :tag=>"update", :title=>"Update", :action=>on_update),
648 item(:button, :tag=>"clear", :title=>"Clear", :action=>lambda { |it|
650 item_with_tag("update")[:enabled] = true
651 surface_dialog_attr["hidden"] = -1
654 item(:button, :tag=>"hide", :title=>"Hide", :action=>lambda { |it|
655 case surface_dialog_attr["hidden"]
657 surface_dialog_attr["hidden"] = 1
660 surface_dialog_attr["hidden"] = 0
665 item(:button, :tag=>"create_cube", :title=>"Create Cube", :action=>on_update),
666 :flex=>[0,1,0,0,0,0]),
670 if (val = surface_dialog_attr[tag]) != nil
671 item_with_tag(tag)[:value] = val
674 mo_idx = surface_dialog_attr["mo"]
676 on_set_action.call(item_with_tag("mo_ao"))
677 item_with_tag("mo")[:value] = surface_dialog_attr["mo"] = mo_idx
679 set_min_size(size[0], 250)
680 item_with_tag("table")[:refresh] = true
685 def ask_graphic_export_scale
686 scale = get_global_settings("global.export_graphic_scale")
687 scale = (scale ? scale.to_i - 1 : 3)
688 bg_color = get_global_settings("global.export_background_color")
689 bg_color = (bg_color ? bg_color.to_i + 1 : 0)
690 hash = Dialog.run("Set Export Properties") {
692 item(:text, :title=>"Resolution:"),
693 item(:popup, :subitems=>["Screen", "Screenx2", "Screenx3", "Screenx4", "Screenx5"], :tag=>"resolution", :value=>scale),
694 item(:text, :title=>"Background color:"),
695 item(:popup, :subitems=>["Same as Screen", "Transparent", "Black", "White"], :tag=>"bg_color", :value=>bg_color))
697 if hash[:status] == 0
698 set_global_settings("global.export_graphic_scale", (hash["resolution"] + 1).to_s)
699 set_global_settings("global.export_background_color", (hash["bg_color"] - 1).to_s)
708 $test_dialog = Dialog.new("Test") { item(:text, :title=>"test"); show }
715 sdir = get_global_settings("global.scratch_dir")
717 p = Dialog.open_panel("Please select scratch directory", sdir, nil, true)
720 error_message_box("Please avoid path containing a white space.\n" + p.sub(/ /, "<!> <!>"))
724 set_global_settings("global.scratch_dir", p)
734 register_menu("Assign residue...", :cmd_assign_residue, :non_empty)
735 register_menu("Offset residue...", :cmd_offset_residue, :non_empty)
736 register_menu("Sort by residue", :cmd_sort_by_residue, :non_empty)
737 register_menu("", "")
738 register_menu("Delete Frames...", :cmd_delete_frames, lambda { |m| m && m.nframes > 1 } )
739 register_menu("Reverse Frames...", :cmd_reverse_frames, lambda { |m| m && m.nframes > 1 } )
740 register_menu("Concatenate Frames...", :cmd_concat_frames, lambda { |m| m && m.nframes > 1 } )
741 register_menu("", "")
742 register_menu("Show Energy Window...", :cmd_show_energy, lambda { |m| m && m.property_names.include?("energy") } )
743 register_menu("Show MO Surface...", :cmd_create_surface, lambda { |m| m && m.get_mo_info(:type) != nil } )
744 #register_menu("cmd test", :cmd_test)