source: trunk/gui/scripts/energyLevels.tcl @ 8

Last change on this file since 8 was 8, checked in by mmc, 16 years ago

Updated the code to the latest Rappture XML conventions, and fixed
up the moleculeViewer and energyLevels viewer. Everything works
properly now with the new app-huckel project. You can load up
the molecule viewer, rotate molecules, and view their energy levels.

File size: 16.8 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: EnergyLevels - visualizer for discrete energy levels
3#
4#  This widget is a simple visualizer for a set of quantized energy
5#  levels, as you might find for a molecule or a quantum well.  It
6#  takes the Rappture XML representation for a <table> and extracts
7#  values from the "energy" column, then plots those energies on a
8#  graph.
9# ======================================================================
10#  AUTHOR:  Michael McLennan, Purdue University
11#  Copyright (c) 2004  Purdue Research Foundation, West Lafayette, IN
12# ======================================================================
13package require Itk
14package require BLT
15
16option add *EnergyLevels.width 4i widgetDefault
17option add *EnergyLevels.height 4i widgetDefault
18option add *EnergyLevels.levelColor blue widgetDefault
19option add *EnergyLevels.levelTextForeground blue widgetDefault
20option add *EnergyLevels.levelTextBackground #d9d9d9 widgetDefault
21
22option add *EnergyLevels.font \
23    -*-helvetica-medium-r-normal-*-*-120-* widgetDefault
24
25option add *EnergyLevels.detailFont \
26    -*-helvetica-medium-r-normal-*-*-100-* widgetDefault
27
28itcl::class Rappture::EnergyLevels {
29    inherit itk::Widget
30
31    itk_option define -layout layout Layout ""
32    itk_option define -output output Output ""
33    itk_option define -levelcolor levelColor LevelColor ""
34    itk_option define -leveltextforeground levelTextForeground Foreground ""
35    itk_option define -leveltextbackground levelTextBackground Background ""
36
37    constructor {args} { # defined below }
38    destructor { # defined below }
39
40    protected method _render {}
41    protected method _adjust {what val}
42    protected method _getColumn {name}
43    protected method _getUnits {name}
44    protected method _getMidPt {elist pos}
45}
46
47itk::usual EnergyLevels {
48}
49
50# ----------------------------------------------------------------------
51# CONSTRUCTOR
52# ----------------------------------------------------------------------
53itcl::body Rappture::EnergyLevels::constructor {args} {
54    itk_option add hull.width hull.height
55    pack propagate $itk_component(hull) no
56
57    #
58    # Add label for the title.
59    #
60    itk_component add title {
61        label $itk_interior.title
62    }
63    pack $itk_component(title) -side top
64
65    #
66    # Add labels showing level stats at bottom.
67    #
68    itk_component add stats {
69        frame $itk_interior.stats
70    }
71    pack $itk_component(stats) -side bottom -fill x
72
73    itk_component add numvall {
74        label $itk_component(stats).numvall -text "Number of valence electrons:"
75    }
76    grid $itk_component(numvall) -row 0 -column 0 -sticky e
77
78    itk_component add numvalv {
79        label $itk_component(stats).numvalv
80    }
81    grid $itk_component(numvalv) -row 0 -column 1 -sticky w
82
83    itk_component add numbasl {
84        label $itk_component(stats).numbasl -text "Number of basis functions:"
85    }
86    grid $itk_component(numbasl) -row 1 -column 0 -sticky e
87
88    itk_component add numbasv {
89        label $itk_component(stats).numbasv
90    }
91    grid $itk_component(numbasv) -row 1 -column 1 -sticky w
92
93
94    itk_component add cntls {
95        frame $itk_interior.cntls
96    }
97    pack $itk_component(cntls) -side right -fill y
98    grid rowconfigure $itk_component(cntls) 0 -weight 1
99    grid rowconfigure $itk_component(cntls) 1 -minsize 10
100    grid rowconfigure $itk_component(cntls) 2 -weight 1
101
102    #
103    # Add MORE/FEWER levels control for TOP of graph
104    #
105    itk_component add upperE {
106        frame $itk_component(cntls).upperE
107    }
108
109    itk_component add upperEmore {
110        label $itk_component(upperE).morel -text "More"
111    } {
112        usual
113        rename -font -detailfont detailFont Font
114    }
115    pack $itk_component(upperEmore) -side top
116
117    itk_component add upperEfewer {
118        label $itk_component(upperE).fewerl -text "Fewer"
119    } {
120        usual
121        rename -font -detailfont detailFont Font
122    }
123    pack $itk_component(upperEfewer) -side bottom
124
125    itk_component add upperEcntl {
126        scale $itk_component(upperE).cntl -orient vertical -showvalue 0 \
127            -command [itcl::code $this _adjust upper]
128    }
129    pack $itk_component(upperEcntl) -side top -fill y
130
131    #
132    # Add MORE/FEWER levels control for BOTTOM of graph
133    #
134    itk_component add lowerE {
135        frame $itk_component(cntls).lowerE
136    }
137
138    itk_component add lowerEmore {
139        label $itk_component(lowerE).morel -text "More"
140    } {
141        usual
142        rename -font -detailfont detailFont Font
143    }
144    pack $itk_component(lowerEmore) -side bottom
145
146    itk_component add lowerEfewer {
147        label $itk_component(lowerE).fewerl -text "Fewer"
148    } {
149        usual
150        rename -font -detailfont detailFont Font
151    }
152    pack $itk_component(lowerEfewer) -side top
153
154    itk_component add lowerEcntl {
155        scale $itk_component(lowerE).cntl -orient vertical -showvalue 0 \
156            -command [itcl::code $this _adjust lower]
157    }
158    pack $itk_component(lowerEcntl) -side top -fill y
159
160    #
161    # Add graph showing levels
162    #
163    itk_component add graph {
164        blt::graph $itk_interior.graph \
165            -highlightthickness 0 -plotpadx 0 -plotpady 0 \
166            -width 3i -height 3i
167    } {
168        keep -background -foreground -cursor -font
169    }
170    pack $itk_component(graph) -expand yes -fill both
171    $itk_component(graph) legend configure -hide yes
172
173    eval itk_initialize $args
174}
175
176# ----------------------------------------------------------------------
177# DESTRUCTOR
178# ----------------------------------------------------------------------
179itcl::body Rappture::EnergyLevels::destructor {} {
180}
181
182# ----------------------------------------------------------------------
183# USAGE: _render
184#
185# Used internally to load a list of energy levels from a <table> within
186# the -output XML object.  The -layout object indicates how information
187# should be extracted from the table.  The <layout> should have an
188# <energies> tag and perhaps a <labels> tag, which indicates the table
189# and the column within the table containing the energies.
190# ----------------------------------------------------------------------
191itcl::body Rappture::EnergyLevels::_render {} {
192    #
193    # Clear any information shown in the graph.
194    #
195    set graph $itk_component(graph)
196    eval $graph element delete [$graph element names]
197    eval $graph marker delete [$graph marker names]
198
199    #
200    # Plug in the title from the layout
201    #
202    set title ""
203    if {$itk_option(-layout) != ""} {
204        set title [$itk_option(-layout) get label]
205    }
206    if {"" != $title} {
207        pack $itk_component(title) -side top -before $itk_component(stats)
208        $itk_component(title) configure -text $title
209    } else {
210        pack forget $itk_component(title)
211    }
212
213    #
214    # Look through the layout and figure out what to extract
215    # from the table.
216    #
217    set elist [_getColumn energies]
218    if {[llength $elist] == 0} {
219        return
220    }
221    set units [_getUnits energies]
222
223    set llist [_getColumn names]
224    if {[llength $llist] == 0} {
225        # no labels? then invent some
226        set i 0
227        foreach name $elist {
228            lappend llist "E$i"
229            incr i
230        }
231    }
232
233    #
234    # Update the graph to show the current set of levels.
235    #
236    set n 0
237    set nlumo 0
238    set emax ""
239    set emin ""
240    set ehomo ""
241    set elumo ""
242    foreach eval $elist lval $llist {
243        if {$lval == "HOMO"} {
244            set ehomo $eval
245            set lval "HOMO = $eval $units"
246            set nlumo [expr {$n+1}]
247        } elseif {$lval == "LUMO" || $n == $nlumo} {
248            set elumo $eval
249            set lval "LUMO = $eval $units"
250        }
251
252        set elem "elem[incr n]"
253        $graph element create $elem \
254            -xdata {0 1} -ydata [list $eval $eval] \
255            -color $itk_option(-levelcolor) -symbol "" -linewidth 2
256
257        $graph marker create text -coords [list 0.5 $eval] \
258            -text $lval -anchor c \
259            -foreground $itk_option(-leveltextforeground) \
260            -background $itk_option(-leveltextbackground)
261
262        if {$emax == ""} {
263            set emax $eval
264            set emin $eval
265        } else {
266            if {$eval > $emax} {set emax $eval}
267            if {$eval < $emin} {set emin $eval}
268        }
269    }
270    $graph xaxis configure -min 0 -max 1 -showticks off -linewidth 0
271    if {$units != ""} {
272        $graph yaxis configure -title "Energy ($units)"
273    } else {
274        $graph yaxis configure -title "Energy"
275    }
276
277    # bump the limits so they are big enough to show labels
278    set fnt $itk_option(-font)
279    set h [expr {0.5*([font metrics $fnt -linespace] + 5)}]
280    set emin [expr {$emin-($emax-$emin)*$h/150.0}]
281    set emax [expr {$emax+($emax-$emin)*$h/150.0}]
282    $graph yaxis configure -min $emin -max $emax
283
284    # fill in the stats at the bottom
285    $itk_component(numvalv) configure -text $nlumo
286    $itk_component(numbasv) configure -text $n
287
288    #
289    # If we found HOMO/LUMO levels, then add the band gap at
290    # that point.  Also, fix the controls for energy range.
291    #
292    if {$ehomo != "" && $elumo != ""} {
293        set id [$graph marker create line \
294            -coords [list 0.2 $elumo 0.2 $ehomo]]
295        $graph marker after $id
296
297        set egap [expr {$elumo-$ehomo}]
298        set emid [expr {0.5*($ehomo+$elumo)}]
299        $graph marker create text \
300            -coords [list 0.21 $emid] -background "" \
301            -text "Eg = [format %.2g $egap] $units" -anchor w
302
303        # fix the limits for the lower scale
304        set elim [_getMidPt $elist [expr {$nlumo-1}]]
305        if {"" != $elim} {
306            $itk_component(lowerEcntl) configure -from $elim -to $emin \
307                -resolution [expr {0.02*($elim-$emin)}]
308            grid $itk_component(lowerE) -row 2 -column 0 -sticky ns
309
310            set e0 [_getMidPt $elist [expr {$nlumo-3}]]
311            if {"" != $e0} {
312                $itk_component(lowerEcntl) set $e0
313            } else {
314                $itk_component(lowerEcntl) set $elim
315            }
316        } else {
317            grid forget $itk_component(lowerE)
318        }
319
320        # fix the limits for the upper scale
321        set elim [_getMidPt $elist [expr {$nlumo+1}]]
322        if {"" != $elim} {
323            $itk_component(upperEcntl) configure -from $emax -to $elim \
324                -resolution [expr {0.02*($emax-$elim)}]
325            grid $itk_component(upperE) -row 0 -column 0 -sticky ns
326
327            set e0 [_getMidPt $elist [expr {$nlumo+3}]]
328            if {"" != $e0} {
329                $itk_component(upperEcntl) set $e0
330            } else {
331                $itk_component(upperEcntl) set $elim
332            }
333        } else {
334            grid forget $itk_component(upperE)
335        }
336    } else {
337        grid forget $itk_component(upperE)
338        grid forget $itk_component(lowerE)
339    }
340}
341
342# ----------------------------------------------------------------------
343# USAGE: _adjust <what> <val>
344#
345# Used internally to adjust the upper/lower limits of the graph
346# as the user drags the slider from "More" to "Fewer".  Sets
347# the specified limit to the given value.
348# ----------------------------------------------------------------------
349itcl::body Rappture::EnergyLevels::_adjust {what val} {
350    switch -- $what {
351        upper {
352            $itk_component(graph) yaxis configure -max $val
353        }
354        lower {
355            $itk_component(graph) yaxis configure -min $val
356        }
357        default {
358            error "bad limit \"$what\": should be upper or lower"
359        }
360    }
361}
362
363# ----------------------------------------------------------------------
364# USAGE: _getColumn <name>
365#
366# Used internally to load a list of energy levels from a <table> within
367# the -output XML object.  The -layout object indicates how information
368# should be extracted from the table.  The <layout> should have an
369# <energies> tag and perhaps a <labels> tag, which indicates the table
370# and the column within the table containing the energies.
371# ----------------------------------------------------------------------
372itcl::body Rappture::EnergyLevels::_getColumn {name} {
373    if {$itk_option(-layout) == "" || $itk_option(-output) == ""} {
374        return
375    }
376
377    #
378    # Figure out which column in which table contains the data.
379    # Then, find that table and extract the column.  Figure out
380    # the position of the column from the list of all column names.
381    #
382    set table [$itk_option(-layout) get $name.table]
383    set col [$itk_option(-layout) get $name.column]
384
385    set clist ""
386    foreach c [$itk_option(-output) children -type column $table] {
387        lappend clist [$itk_option(-output) get $table.$c.label]
388    }
389    set ipos [lsearch $clist $col]
390    if {$ipos < 0} {
391        return  ;# can't find data -- bail out!
392    }
393
394    set units [$itk_option(-output) get $table.column$ipos.units]
395
396    set rlist ""
397    foreach line [split [$itk_option(-output) get $table.data] "\n"] {
398        if {"" != [string trim $line]} {
399            set val [lindex $line $ipos]
400
401            if {$units != ""} {
402                set val [Rappture::Units::convert $val \
403                    -context $units -to $units -units off]
404            }
405            lappend rlist $val
406        }
407    }
408    return $rlist
409}
410
411# ----------------------------------------------------------------------
412# USAGE: _getUnits <name>
413#
414# Used internally to extract the units from a <table> within the
415# -output XML object.  The -layout object indicates how information
416# should be extracted from the table.  The <layout> should have an
417# <energies> tag and perhaps a <labels> tag, which indicates the table
418# and the column within the table containing the units.
419# ----------------------------------------------------------------------
420itcl::body Rappture::EnergyLevels::_getUnits {name} {
421    if {$itk_option(-layout) == "" || $itk_option(-output) == ""} {
422        return
423    }
424
425    #
426    # Figure out which column in which table contains the data.
427    # Then, find that table and extract the column.  Figure out
428    # the position of the column from the list of all column names.
429    #
430    set table [$itk_option(-layout) get $name.table]
431    set col [$itk_option(-layout) get $name.column]
432
433    set clist ""
434    foreach c [$itk_option(-output) children -type column $table] {
435        lappend clist [$itk_option(-output) get $table.$c.label]
436    }
437    set ipos [lsearch $clist $col]
438    if {$ipos < 0} {
439        return  ;# can't find data -- bail out!
440    }
441
442    return [$itk_option(-output) get $table.column$ipos.units]
443}
444
445# ----------------------------------------------------------------------
446# USAGE: _getMidPt <elist> <pos>
447#
448# Used internally to compute the midpoint between two energy levels
449# at <pos> and <pos-1> in the <elist>.  Returns a number representing
450# the mid-point (average value) or "" if the levels involved do
451# no exist in <elist>.
452# ----------------------------------------------------------------------
453itcl::body Rappture::EnergyLevels::_getMidPt {elist pos} {
454    if {$pos < [llength $elist] && $pos > 1} {
455        set e1 [lindex $elist $pos]
456        set e0 [lindex $elist [expr {$pos-1}]]
457        return [expr {0.5*($e0+$e1)}]
458    }
459    return ""
460}
461
462# ----------------------------------------------------------------------
463# OPTION: -layout
464# ----------------------------------------------------------------------
465itcl::configbody Rappture::EnergyLevels::layout {
466    if {$itk_option(-layout) != ""
467          && ![Rappture::library isvalid $itk_option(-layout)]} {
468        error "bad value \"$itk_option(-layout)\": should be Rappture::library object"
469    }
470    after idle [itcl::code $this _render]
471}
472
473# ----------------------------------------------------------------------
474# OPTION: -output
475# ----------------------------------------------------------------------
476itcl::configbody Rappture::EnergyLevels::output {
477    if {$itk_option(-output) != ""
478          && ![Rappture::library isvalid $itk_option(-output)]} {
479        error "bad value \"$itk_option(-output)\": should be Rappture::library object"
480    }
481    after cancel [itcl::code $this _render]
482    after idle [itcl::code $this _render]
483}
484
485# ----------------------------------------------------------------------
486# OPTION: -levelColor
487# ----------------------------------------------------------------------
488itcl::configbody Rappture::EnergyLevels::levelcolor {
489    after cancel [itcl::code $this _render]
490    after idle [itcl::code $this _render]
491}
492
493# ----------------------------------------------------------------------
494# OPTION: -leveltextforeground
495# ----------------------------------------------------------------------
496itcl::configbody Rappture::EnergyLevels::leveltextforeground {
497    after cancel [itcl::code $this _render]
498    after idle [itcl::code $this _render]
499}
500
501# ----------------------------------------------------------------------
502# OPTION: -leveltextbackground
503# ----------------------------------------------------------------------
504itcl::configbody Rappture::EnergyLevels::leveltextbackground {
505    after cancel [itcl::code $this _render]
506    after idle [itcl::code $this _render]
507}
Note: See TracBrowser for help on using the repository browser.