source: trunk/gui/apps/simsim.in @ 1612

Last change on this file since 1612 was 1612, checked in by dkearney, 14 years ago

updated to handle phases and choices. changed up random number generator to make it more reliable

  • Property svn:executable set to *
File size: 23.4 KB
Line 
1#!/bin/sh
2# ======================================================================
3#  AUTHOR:  Derrick S. Kearney, Purdue University
4#  Copyright (c) 2004-2008  Purdue Research Foundation
5#
6#  See the file "license.terms" for information on usage and
7#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
8# ======================================================================
9#\
10RAPPTURE_INSTALL_DIR=@prefix@ ; \
11. $RAPPTURE_INSTALL_DIR/bin/rappture.env ; \
12exec wish "$0" $*
13
14package require Rappture
15package require RapptureGUI
16
17proc defaultHandler {child} {
18    set childchildList {}
19    set units [$child get "units"]
20    # this is for nanowire
21    if {[string is integer -strict $units]} {
22        set units ""
23    }
24    set defaultVal [$child get "default"]
25    if {"" != $defaultVal} {
26        if {"" != $units} {
27            set defaultVal [Rappture::Units::convert $defaultVal \
28                            -context $units -to $units -units on]
29        }
30        $child put "current" $defaultVal
31    } else {
32        if {"components" != [$child element -as type]} {
33            set childchildList [$child children]
34        } elseif {"parameters" != [$child element -as type]} {
35            set childchildList [$child children]
36        } else {
37            set childchildList [$child children]
38        }
39    }
40    return $childchildList
41}
42
43proc numberHandler {child} {
44    set value ""
45    set units [$child get "units"]
46    set min [$child get "min"]
47    set max [$child get "max"]
48
49    if {"" != $min} {
50        if {"" != $units} {
51            # check to see if the user added units to their min value
52            # apps like mosfet need this check.
53            set min [Rappture::Units::convert $min \
54                        -context $units -to $units -units off]
55            if {[string is double -strict $min]} {
56                # pass
57            }
58        }
59    }
60
61    if {"" != $max} {
62        if {"" != $units} {
63            # check to see if the user added units to their min value
64            # apps like mosfet need this check.
65            set max [Rappture::Units::convert $max \
66                        -context $units -to $units -units off]
67            if {[string is double -strict $max]} {
68                # pass
69            }
70        }
71    }
72
73    if {"yes" == [$child get "simset"]} {
74        $child remove "simset"
75    } else {
76        if { ("" != $min) && ("" != $max) } {
77            set value [random $min $max]
78            $child put current $value$units
79        } else {
80            defaultHandler $child
81        }
82    }
83}
84
85proc integerHandler {child} {
86    set value ""
87    set units [$child get "units"]
88    set min [$child get "min"]
89    set max [$child get "max"]
90
91    # nanowire needs this because they set units == 0 and 1 ???
92    if {[string is integer -strict $units]} {
93        set units ""
94    }
95
96    if {"" != $min} {
97        if {"" != $units} {
98            # check to see if the user added units to their min value
99            # apps like mosfet need this check.
100            set min [Rappture::Units::convert $min \
101                        -context $units -to $units -units off]
102            if {[string is integer -strict $min]} {
103                # pass
104            }
105        }
106    }
107
108    if {"" != $max} {
109        if {"" != $units} {
110            # check to see if the user added units to their min value
111            # apps like mosfet need this check.
112            set max [Rappture::Units::convert $max \
113                        -context $units -to $units -units off]
114            if {[string is integer -strict $max]} {
115                # pass
116            }
117        }
118    }
119
120    if {"yes" == [$child get "simset"]} {
121        $child remove "simset"
122    } else {
123        if { ("" != $min) && ("" != $max) } {
124            set value [randomInt $min $max]
125            $child put current $value$units
126        } else {
127            defaultHandler $child
128        }
129    }
130
131}
132
133proc booleanHandler {child} {
134    if {"yes" == [$child get "simset"]} {
135        $child remove "simset"
136    } else {
137        set value [expr {int(rand()*2)}]
138        if {$value == 1} {
139            set value "yes"
140        } else {
141            set value "no"
142        }
143        $child put "current" $value
144    }
145}
146
147proc loaderHandler {child toolDir} {
148
149    set exDir [file join $toolDir "examples"]
150    if {! [file isdirectory $exDir]} {
151        # in this case we should try to keep processing, not exit
152        puts "could not find examples directory: $exDir"
153        exit 0
154    }
155
156    set exPathExp [$child get example]
157    set fpath [file join $exDir $exPathExp]
158    set exFileList [glob -nocomplain $fpath]
159
160    if {0 == [llength $exFileList]} {
161        puts "while searching examples directory: $exDir"
162        puts "could not open find files matching regex: $exPathExp"
163        set defaultEx [$child get "default"]
164        if {[file exists [file join $exDir $defaultEx]]} {
165            lappend exFileList $defaultEx
166            puts "using default example file"
167        } else {
168            puts "default example file does not exists, exiting"
169            exit 0;
170        }
171    }
172
173    set importExFileIdx [expr {int(rand()*[llength $exFileList])}]
174    set importExFile [lindex $exFileList $importExFileIdx]
175
176    if {! [file exists $importExFile]} {
177        # importExFile does not exist
178    }
179
180    set exlib [Rappture::library $importExFile]
181
182    # get the about.label from the file
183    # if about.label does not exist, use the file name
184    set importExLabel [$exlib get about.label]
185    if {"" != $importExLabel} {
186        set currentVal $importExLabel
187    } else {
188        set currentVal [file tail $importExFile]
189    }
190
191    $child put "current" $currentVal
192
193    set exlibChildList [::Rappture::entities -as object $exlib "input"]
194    return $exlibChildList
195}
196
197proc groupHandler {child} {
198    return [$child children -as object]
199}
200
201proc choiceHandler {valType child} {
202    if {"yes" == [$child get "simset"]} {
203        $child remove "simset"
204    } else {
205        set optList [$child children -as object -type option]
206        set value ""
207        if {"random" == $valType} {
208            set optIdx [expr {int(rand()*[llength $optList])}]
209            set optLib [lindex $optList $optIdx]
210            set value [$optLib get value]
211        } elseif {"default" == $valType} {
212            set defaultVal [$child get default]
213            foreach optLib $optList {
214                set label [$optLib get about.label]
215                set valTag [$optLib get value]
216                if {($defaultVal == $label) || ($defaultVal == $valTag)} {
217                    set value $valTag
218                    break
219                }
220            }
221        }
222
223        if {"" == $value} {
224            set value [$optLib get about.label]
225        }
226        $child put "current" $value
227    }
228}
229
230proc defaultize {xmlobj toolDir} {
231    set childList [$xmlobj children -as object input]
232
233    while {[llength $childList]} {
234        set child [lrange $childList 0 0]
235        set childList [lreplace $childList 0 0]
236
237        switch -- [$child element -as type] {
238            number      { defaultHandler $child }
239            integer     { defaultHandler $child }
240            boolean     { defaultHandler $child }
241            string      { defaultHandler $child }
242            choice      { choiceHandler default $child }
243            loader      { loaderHandler  $child $toolDir }
244            structure   { defaultHandler $child }
245            group       { set cclist [groupHandler $child]
246                          set childList [concat $childList $cclist] }
247            phase       { set cclist [groupHandler $child]
248                          set childList [concat $childList $cclist] }
249            default     { defaultHandler $child }
250        }
251    }
252}
253
254proc randomize {presetArr xmlobj toolDir} {
255    upvar $presetArr presets
256    set childList [$xmlobj children -as object input]
257
258    while {[llength $childList]} {
259
260        set child [lrange $childList 0 0]
261        set childList [lreplace $childList 0 0]
262
263        set cpath [cleanPath [$child element -as path]]
264
265        set ppath [$child parent -as path]
266        set cPresets [array get presets $cpath*]
267
268        foreach {cPresetsPath cPresetsVal} $cPresets {
269            set cutIdx [expr {[string length $cpath] + 1}]
270            set iPath [string range $cPresetsPath $cutIdx end]
271
272            # apply the preset value and remove from preset array
273            $child put $iPath $cPresetsVal
274            unset presets($cPresetsPath)
275
276            # if the value was set on a current node, then set a preset flag
277            if {"current" == $iPath} {
278                $child put simset "yes"
279            }
280        }
281
282        switch -- [$child element -as type] {
283            number    { numberHandler  $child }
284            integer   { integerHandler $child }
285            boolean   { booleanHandler $child }
286            string    { defaultHandler $child }
287            choice    { choiceHandler random  $child }
288            loader    {
289                set cpath [$child element -as path]
290                set ccList [loaderHandler $child $toolDir]
291                foreach cc $ccList {
292                    set ccpath [$cc element -as path]
293                    # even though the loader might have been returned in ccList
294                    # do not add the loader back to the childList or you might
295                    # get an infinite loop
296                    if {$cpath != $ccpath} {
297                        set ccpath [cleanPath $ccpath]
298                        $xmlobj copy $ccpath from $cc ""
299                        lappend childList [$xmlobj element -as object $ccpath]
300                    }
301                }
302            }
303            structure { defaultHandler $child }
304            group     {
305                set ccList [groupHandler $child]
306                set childList [concat $childList $ccList]
307            }
308            phase     {
309                set ccList [groupHandler $child]
310                set childList [concat $childList $ccList]
311            }
312            default   { defaultHandler $child }
313        }
314    }
315}
316
317proc random {m M} {
318    return [expr {$m+(rand()*($M-$m))}]
319}
320
321proc randomInt {m M} {
322    return [expr {int(rand()*($M-$m+1)+$m)}]
323}
324
325proc cleanPath { path } {
326    if {"." == [string index $path 0]} {
327        # this is because tcl's library module (element -as path)
328        # returns a crazy dot in the 0th position
329        set path [string range $path 1 end]
330    }
331    return $path
332}
333
334proc parsePathVal {listVar returnVar} {
335    upvar $listVar params
336    upvar $returnVar presetArr
337    catch {unset presetArr}
338
339    # initialize variables
340    set pathValStr ""
341    set match "junk"
342
343    while {"" != $match} {
344        set match ""
345        set path ""
346        set val ""
347        set val2 ""
348        set val3 ""
349        set val4 ""
350        set pathValStr [string trimleft [join $params " "]]
351
352        # search the params for:
353        # 1) xml path
354        # 2) followed by = sign
355        # 3) followed by a starting " sign
356        # 4) followed by any text (including spaces)
357        # 5) followed by an ending " sign
358        # ex: input.number(temperature).current="400K"
359        # ex: input.number(temperature).current=400K
360        # ex: input.string(blahh).current="hi momma, i love you!"
361        #    \"([^\"]+)\"
362        #    ((\"([^\"]+)\")|(:([^:]+):)|(\'([^\']+)\')|([^\s]+))
363        regexp -expanded {
364            (
365                [a-zA-Z0-9]+(\([a-zA-Z0-9._]+\))?
366                (\.[a-zA-Z0-9]+(\([a-zA-Z0-9._]+\))?)*
367            )
368            =
369            ((\"([^\"]+)\")|(\'([^\']+)\')|([^\s]+))
370        } $pathValStr match path junk junk junk val junk val2 junk val3 val4
371
372
373        if {"" != $match} {
374            # remove the matching element from orphaned params list
375            foreach p [split $match] {
376                set paramsIdx [lsearch -exact $params $p]
377                set params [lreplace $params $paramsIdx $paramsIdx]
378            }
379
380            if {("" != $val2) && ("" == $val3) && ("" == $val4)} {
381                set val $val2
382            } elseif {("" == $val2) && ("" != $val3) && ("" == $val4)} {
383                set val $val3
384            } elseif {("" == $val2) && ("" == $val3) && ("" != $val4)} {
385                set val $val4
386            }
387
388            # add the name and value to our preset array
389            set presetArr($path) $val
390        }
391    }
392}
393
394proc printHelp {} {
395    set help {
396simsim [OPTIONS] [CONST]
397
398simulator simulator - simulate simulation
399
400OPTIONS:
401 -tool <path>        - use the tool.xml file specified at <path>
402 -values <valtype>   - the type of values used to populate driver file.
403                       valid <valtype>'s include:
404                       "default" - replace <current> values with <default>'s,
405                       "current" - use <current> values (ie do nothing),
406                       "random" - generate random values for <current>.
407 -driver <path>      - write a driver file to <path>.
408 -compare <path>     - compare the results with the run.xml
409                       file at <path>.
410 -nosim              - no simulation.
411 -help               - print this help menu.
412
413CONST:
414 when -values is set to random, constant values can be set for
415 specific inputs. the general form for constant values is:
416
417   xmlpath(id)=value
418
419 where xmlpath is the path of the element in the xml tree,
420 id is the id of the xml node and value is the constant value
421 you want to place in the element. the following the constant
422 will set input.number(Ef).current to the value "2eV":
423
424   input.number(Ef)=2eV
425
426EXAMPLES:
427simulate using ./tool.xml, default values, no comparisons or driver
428    simsim
429
430simulate using ./tool.xml, random values, no comparisons or driver
431    simsim -values random
432
433from ./tool.xml, create a driver file named "mydriverfile.xml"
434with default values
435    simsim -nosim -driver mydriverfile.xml
436
437from ./tool.xml, create a driver file named "mydriverfile.xml"
438with random values
439    simsim -values random -nosim -driver mydriverfile.xml
440
441from ./tool.xml, simulate with random values but set
442input.number(Ef) to "2eV"
443    simsim -values random input.number(Ef).current=2eV
444
445run a simulation using the current values from driver.xml,
446"-values current" is only useful if you are asking
447simsim to run the simulation and you provide a driver file.
448    simsim -tool driver.xml -values current
449
450compare two xml file, don't do a simulation, don't print a driver.xml
451    simsim -tool driver.xml -compare previousrun.xml -nosim
452
453run a simulation and compare the result to previousrun.xml
454    simsim -compare previousrun.xml
455
456}
457    puts $help
458    exit 0
459}
460
461proc diffs {xmlobj1 xmlobj2} {
462    set rlist ""
463
464    # query the values for all entities in both objects
465    set thisv [concat [Rappture::entities $xmlobj1 "input"] [Rappture::entities $xmlobj1 "output"]]
466    set otherv [concat [Rappture::entities $xmlobj2 "input"] [Rappture::entities $xmlobj2 "output"]]
467
468    # scan through values for this object and compare against other one
469    foreach path $thisv {
470        set i [lsearch -exact $otherv $path]
471        if {$i < 0} {
472            foreach {raw norm} [value $xmlobj1 $path] break
473            lappend rlist - $path $raw ""
474        } else {
475            foreach {traw tnorm} [value $xmlobj1 $path] break
476            foreach {oraw onorm} [value $xmlobj2 $path] break
477            if {![string equal $tnorm $onorm]} {
478                lappend rlist c $path $traw $oraw
479            }
480            set otherv [lreplace $otherv $i $i]
481        }
482    }
483
484    #add any values left over in the other object
485    foreach path $otherv {
486        foreach {oraw onorm} [Rappture::LibraryObj::value $xmlobj2 $path] break
487        lappend rlist + $path "" $oraw
488    }
489    return $rlist
490}
491
492proc value {libobj path} {
493    switch -- [$libobj element -as type $path] {
494        structure {
495            set raw $path
496            # try to find a label to represent the structure
497            set val [$libobj get $path.about.label]
498            if {"" == $val} {
499                set val [$libobj get $path.current.about.label]
500            }
501            if {"" == $val} {
502                if {[$libobj element $path.current] != ""} {
503                    set comps [$libobj children $path.current.components]
504                    set val "<structure> with [llength $comps] components"
505                } else {
506                    set val "<structure>"
507                }
508            }
509            return [list $raw $val]
510        }
511        number {
512            # get the usual value...
513            set raw ""
514            if {"" != [$libobj element $path.current]} {
515                set raw [$libobj get $path.current]
516            } elseif {"" != [$libobj element $path.default]} {
517                set raw [$libobj get $path.default]
518            }
519            if {"" != $raw} {
520                set val $raw
521                # then normalize to default units
522                set units [$libobj get $path.units]
523                if {"" != $units} {
524                    set val [Rappture::Units::convert $val \
525                        -context $units -to $units -units off]
526                }
527            }
528            return [list $raw $val]
529        }
530        curve {
531            set raw ""
532            if {"" != [$libobj element $path.component.xy]} {
533                set raw [$libobj get $path.component.xy]
534            }
535            return [list $raw $raw]
536        }
537        log {
538            set raw ""
539            if {"" != [$libobj element]} {
540                set raw [$libobj get]
541            }
542            return [list $raw $raw]
543        }
544        cloud {
545            set raw ""
546            if {"" != [$libobj element $path.points]} {
547                set raw [$libobj get $path.points]
548            }
549            return [list $raw $raw]
550        }
551        field {
552            set raw ""
553            if {"" != [$libobj element $path.component.values]} {
554                set raw [$libobj get $path.component.values]
555            }
556            return [list $raw $raw]
557        }
558
559
560    }
561
562    # for all other types, get the value (current, or maybe default)
563    set raw ""
564    if {"" != [$libobj element $path.current]} {
565        set raw [$libobj get $path.current]
566    } elseif {"" != [$libobj element $path.default]} {
567        set raw [$libobj get $path.default]
568    }
569    return [list $raw $raw]
570}
571
572proc compare {orig result} {
573    if {"" == $orig} {
574        return
575    }
576    if {"" == $result} {
577        return
578    }
579
580    if {![Rappture::library isvalid $orig]} {
581        set origObj [Rappture::library $orig]
582        if {![Rappture::library isvalid $origObj]} {
583            puts "cannot create Rappture library from $orig\n"
584            return
585        }
586    } else {
587        set origObj $orig
588    }
589
590    if {![Rappture::library isvalid $result]} {
591        set resultObj [Rappture::library $result]
592        if {![Rappture::library isvalid $resultObj]} {
593            puts "cannot create Rappture library from $result\n"
594            return
595        }
596    } else {
597        set resultObj $result
598    }
599
600    foreach {op vpath oldval newval} [diffs $origObj $resultObj] {
601        puts "$op $vpath $oldval $newval"
602    }
603}
604
605proc parseOptions {listVar returnVar} {
606    upvar $listVar argv
607    upvar $returnVar params
608
609    # parse command line arguments
610    set argc [llength $argv]
611    for {set i 0} {$i < $argc} {incr i} {
612        set opt [lindex $argv $i]
613        if {("-t" == $opt) || ("-tool" == $opt) || ("--tool" == $opt)} {
614            if {[expr {$i + 1}] < $argc} {
615                incr i
616                set params(--tool) [lindex $argv $i]
617                # need to check to see if file exists, if not raise error
618            } else {
619                printHelp
620            }
621        } elseif {  ("-d" == $opt)      ||
622                    ("-driver" == $opt) ||
623                    ("--driver" == $opt)    } {
624            if {[expr {$i + 1}] < $argc} {
625                incr i
626                set params(--driver) [lindex $argv $i]
627                # need to check to see if file exists, if not raise error
628            } else {
629                printHelp
630            }
631        } elseif {  ("-v" == $opt)      ||
632                    ("-values" == $opt) ||
633                    ("--values" == $opt)    } {
634            if {[expr {$i + 1}] < $argc} {
635                incr i
636                set valuesFlag [lindex $argv $i]
637                if {("default" == $valuesFlag) ||
638                    ("current" == $valuesFlag) ||
639                    ("random" == $valuesFlag)  } {
640                    set params(--values) $valuesFlag
641                } else {
642                    printHelp
643                }
644            } else {
645                printHelp
646            }
647        } elseif {  ("-c" == $opt)      ||
648                    ("-compare" == $opt)||
649                    ("--compare" == $opt)   } {
650            if {[expr {$i + 1}] < $argc} {
651                incr i
652                set params(--compare) [lindex $argv $i]
653                # need to check to see if file exists, if not raise error
654            } else {
655                printHelp
656            }
657        } elseif {  ("-n" == $opt)      ||
658                    ("-nosim" == $opt)  ||
659                    ("--nosim" == $opt)     } {
660            set params(--nosim) true
661        } elseif {  ("-h" == $opt)      ||
662                    ("-help" == $opt)   ||
663                    ("--help" == $opt)      } {
664            printHelp
665        } else {
666            # place all extra params in the params array
667            lappend params(oParams) $opt
668        }
669    }
670}
671
672proc main {argv} {
673    # set default values
674    array set presets []
675    set i 0
676
677    array set params {
678        --compare ""
679        --values default
680        --nosim false
681        --driver ""
682        --tool "./tool.xml"
683        oParams {}
684    }
685
686    parseOptions argv params
687
688    # keep the wish window from popping up
689    wm withdraw .
690
691    # parse out path=val combinations from the list of orphaned parameters
692    parsePathVal params(oParams) presets
693    if {0 != [llength $params(oParams)]} {
694        puts "Could not understand the following parameters"
695        puts "params(oParams) = $params(oParams)"
696        puts "length params(oParams) = [llength $params(oParams)]"
697    }
698
699    set err ""
700    if {! [file exists $params(--tool)]} {
701        append err "\ntool file \""
702        append err $params(--tool)
703        append err "\" does not exist, use -t option\n"
704        puts $err
705        printHelp
706    }
707
708    set xmlobj [Rappture::library $params(--tool)]
709    set installdir [file dirname $params(--tool)]
710
711    # need a better way to do this,
712    # maybe just take xmldiff functionality out of simsim
713    if {(!$params(--nosim)) || ("" != $params(--driver))} {
714        switch -- $params(--values) {
715            random      { randomize presets $xmlobj $installdir }
716            current     { }
717            default     { defaultize $xmlobj $installdir }
718        }
719    }
720
721    if {"" != $params(--driver)} {
722        set fid [open $params(--driver) w]
723        puts $fid {<?xml version="1.0"?>}
724        puts $fid [$xmlobj xml]
725        close $fid
726    }
727
728    if {$params(--nosim)} {
729        if {"" != $params(--compare)} {
730            compare $xmlobj $params(--compare)
731        }
732        exit 0
733    }
734
735    set tool [Rappture::Tool ::#auto $xmlobj $installdir]
736
737    # read the run.xml file.
738    # from analyzer.tcl:
739
740    set result ""
741    # execute the job
742    foreach {status result} [eval $tool run] break
743
744    # read back the result from run.xml
745    if {$status == 0 && $result != "ABORT"} {
746        if {[Rappture::library isvalid $result]} {
747            # do comparison if user chose to compare with other results
748            if {"" != $params(--compare)} {
749                compare $params(--compare) $result
750            }
751        } else {
752            set status 1
753            puts "Can't find result file in output.\nDid you call Rappture::result in your simulator?"
754        }
755    } else {
756        puts $result
757    }
758}
759
760main $argv
761exit 0
Note: See TracBrowser for help on using the repository browser.