Changeset 6021 for trunk/lang/tcl/scripts/units.tcl
- Timestamp:
- Feb 18, 2016, 4:13:14 PM (9 years ago)
- Location:
- trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk
- Property svn:mergeinfo changed
-
trunk/lang/tcl/scripts/units.tcl
r3362 r6021 1 # -*- mode: tcl; indent-tabs-mode: nil -*- 1 2 # ---------------------------------------------------------------------- 2 3 # COMPONENT: units - mechanism for converting numbers with units … … 60 61 } 61 62 63 64 # ---------------------------------------------------------------------- 65 # USAGE: mcheck_range value {min ""} {max ""} 66 # 67 # Checks a value or PDF to determine if is is in a required range. 68 # Automatically does unit conversion if necessary. 69 # Returns value if OK. Error if out-of-range 70 # Examples: 71 # [mcheck_range "gaussian 0C 1C" 200K 500K] returns 1 72 # [mcheck_range "uniform 100 200" 150 250] returns 0 73 # [mcheck_range 100 0 200] returns 1 74 # ---------------------------------------------------------------------- 75 76 proc Rappture::Units::_check_range {value min max units} { 77 # puts "_check_range $value min=$min max=$max units=$units" 78 # make sure the value has units 79 if {$units != ""} { 80 set value [Rappture::Units::convert $value -context $units] 81 # for comparisons, remove units 82 set nv [Rappture::Units::convert $value -context $units -units off] 83 # get the units for the value 84 set newunits [Rappture::Units::Search::for $value] 85 } else { 86 set nv $value 87 } 88 89 if {"" != $min} { 90 if {"" != $units} { 91 # compute the minimum in the new units 92 set minv [Rappture::Units::convert $min -to $newunits -context $units -units off] 93 # same, but include units for printing 94 set convMinVal [Rappture::Units::convert $min -to $newunits -context $units] 95 } else { 96 set minv $min 97 set convMinVal $min 98 } 99 if {$nv < $minv} { 100 error "Minimum value allowed here is $convMinVal" 101 } 102 } 103 if {"" != $max} { 104 if {"" != $units} { 105 # compute the maximum in the new units 106 set maxv [Rappture::Units::convert $max -to $newunits -context $units -units off] 107 # same, but include units for printing 108 set convMaxVal [Rappture::Units::convert $max -to $newunits -context $units ] 109 } else { 110 set maxv $max 111 set convMaxVal $max 112 } 113 if {$nv > $maxv} { 114 error "Maximum value allowed here is $convMaxVal" 115 } 116 } 117 return $value 118 } 119 120 proc Rappture::Units::mcheck_range {value {min ""} {max ""} {units ""}} { 121 # puts "mcheck_range $value min=$min max=$max units=$units" 122 123 switch -- [lindex $value 0] { 124 normal - gaussian { 125 # get the mean 126 set mean [_check_range [lindex $value 1] $min $max $units] 127 if {$units == ""} { 128 set dev [lindex $value 2] 129 set ndev $dev 130 } else { 131 set dev [Rappture::Units::convert [lindex $value 2] -context $units] 132 set ndev [Rappture::Units::convert $dev -units off] 133 } 134 if {$ndev <= 0} { 135 error "Deviation must be positive." 136 } 137 return [list gaussian $mean $dev] 138 } 139 uniform { 140 set min [_check_range [lindex $value 1] $min $max $units] 141 set max [_check_range [lindex $value 2] $min $max $units] 142 return [list uniform $min $max] 143 } 144 exact { 145 return [_check_range [lindex $value 1] $min $max $units] 146 } 147 default { 148 return [_check_range [lindex $value 0] $min $max $units] 149 } 150 } 151 } 152 153 # ---------------------------------------------------------------------- 154 # USAGE: mconvert value ?-context units? ?-to units? ?-units on/off? 155 # 156 # This version of convert() converts multiple values. Used when the 157 # value could be a range or probability density function (PDF). 158 # Examples: 159 # gaussian 100k 1k 160 # uniform 0eV 10eV 161 # 42 162 # exact 42 163 # ---------------------------------------------------------------------- 164 165 proc Rappture::Units::mconvert {value args} { 166 # puts "mconvert $value : $args" 167 array set opts { 168 -context "" 169 -to "" 170 -units "on" 171 } 172 173 set value [split $value] 174 175 switch -- [lindex $value 0] { 176 normal - gaussian { 177 set valtype gaussian 178 set vals [lrange $value 1 2] 179 set convtype {0 1} 180 } 181 uniform { 182 set valtype uniform 183 set vals [lrange $value 1 2] 184 set convtype {0 0} 185 } 186 exact { 187 set valtype "" 188 set vals [lindex $value 1] 189 set convtype {0} 190 } 191 default { 192 set valtype "" 193 set vals $value 194 set convtype {0} 195 } 196 } 197 198 foreach {key val} $args { 199 if {![info exists opts($key)]} { 200 error "bad option \"$key\": should be [join [lsort [array names opts]] {, }]" 201 } 202 set opts($key) $val 203 } 204 205 set newval $valtype 206 foreach val $vals ctype $convtype { 207 if {$ctype == 1} { 208 # This code handles unit conversion for deltas (changes). 209 # For example, if we want a standard deviation of 10C converted 210 # to Kelvin, that is 10K, NOT a standard deviation of 283.15K. 211 set units [Rappture::Units::Search::for $val] 212 set base [eval Rappture::Units::convert 0$units $args -units off] 213 set new [eval Rappture::Units::convert $val $args -units off] 214 set delta [expr $new - $base] 215 set val $delta$opts(-to) 216 } 217 # tcl 8.5 allows us to do this: 218 # lappend newval [Rappture::Units::convert $val {*}$args] 219 # but we are using tcl8.4 so we use eval :^( 220 lappend newval [eval Rappture::Units::convert $val $args] 221 } 222 return $newval 223 } 224 62 225 # ---------------------------------------------------------------------- 63 226 # USAGE: convert value ?-context units? ?-to units? ?-units on/off? … … 69 232 # current system. 70 233 # ---------------------------------------------------------------------- 71 proc Rappture::Units::convert {value args} { 72 array set opts { 73 -context "" 74 -to "" 75 -units "on" 76 } 77 foreach {key val} $args { 78 if {![info exists opts($key)]} { 79 error "bad option \"$key\": should be [join [lsort [array names opts]] {, }]" 80 } 81 set opts($key) $val 82 } 83 84 # 85 # Parse the value into the number part and the units part. 86 # 87 set value [string trim $value] 88 if {![regexp {^([-+]?[0-9]+\.?([0-9]+)?([eEdD][-+]?[0-9]+)?) *(/?[a-zA-Z]+[0-9]*)?$} $value match number dummy1 dummy2 units]} { 89 set mesg "bad value \"$value\": should be real number with units" 90 if {$opts(-context) != ""} { 91 append mesg " of [Rappture::Units::description $opts(-context)]" 92 } 93 error $mesg 94 } 95 if {$units == ""} { 96 set units $opts(-context) 97 } 98 99 # 100 # Try to find the object representing the current system of units. 101 # 102 set units [Rappture::Units::System::regularize $units] 103 set oldsys [Rappture::Units::System::for $units] 104 if {$oldsys == ""} { 105 set mesg "value \"$value\" has unrecognized units" 106 if {$opts(-context) != ""} { 107 append mesg ".\nShould be units of [Rappture::Units::description $opts(-context)]" 108 } 109 error $mesg 110 } 111 112 # 113 # Convert the number to the new system of units. 114 # 115 if {$opts(-to) == ""} { 116 # no units -- return the number as is 117 return "$number$units" 118 } 119 return [$oldsys convert "$number$units" $opts(-to) $opts(-units)] 120 } 234 # proc Rappture::Units::convert {value args} {} 235 # Actual implementation is in rappture/lang/tcl/src/RpUnitsTclInterface.cc. 236 121 237 122 238 # ---------------------------------------------------------------------- … … 127 243 # along with a list of all compatible systems. 128 244 # ---------------------------------------------------------------------- 129 proc Rappture::Units::description {units} { 130 set sys [Rappture::Units::System::for $units] 131 if {$sys == ""} { 132 return "" 133 } 134 set mesg [$sys cget -type] 135 set ulist [Rappture::Units::System::all $units] 136 if {"" != $ulist} { 137 append mesg " ([join $ulist {, }])" 138 } 139 return $mesg 140 } 245 # proc Rappture::Units::description {units} {} 246 # Actual implementation is in rappture/lang/tcl/src/RpUnitsTclInterface.cc. 247 141 248 142 249 # ---------------------------------------------------------------------- … … 153 260 private variable _system "" ;# this system of units 154 261 155 public proc for {units} 156 public proc all {units} 262 # These are in rappture/lang/tcl/src/RpUnitsTclInterface.cc. 263 # public proc for {units} 264 # public proc all {units} 265 157 266 public proc regularize {units} 158 267 … … 360 469 # if there is no system that matches the units string. 361 470 # ---------------------------------------------------------------------- 362 itcl::body Rappture::Units::System::for {units} { 363 # 364 # See if the units are a recognized system. If not, then try to 365 # extract any metric prefix and see if what's left is a recognized 366 # system. If all else fails, see if we can find a system without 367 # the exact capitalization. The user might say "25c" instead of 368 # "25C". Try to allow that. 369 # 370 if {[info exists _base($units)]} { 371 return $_base($units) 372 } else { 373 set orig $units 374 if {[regexp {^(/?)[cCmMuUnNpPfFaAkKgGtT](.+)$} $units match slash tail]} { 375 set base "$slash$tail" 376 if {[info exists _base($base)]} { 377 set sys $_base($base) 378 if {[$sys cget -metric]} { 379 return $sys 380 } 381 } 382 383 # check the base part for improper capitalization below... 384 set units $base 385 } 386 387 set matching "" 388 foreach u [array names _base] { 389 if {[string equal -nocase $u $units]} { 390 lappend matching $_base($u) 391 } 392 } 393 if {[llength $matching] == 1} { 394 set sys [lindex $matching 0] 395 # 396 # If we got rid of a metric prefix above, make sure 397 # that the system is metric. If not, then we don't 398 # have a match. 399 # 400 if {[string equal $units $orig] || [$sys cget -metric]} { 401 return $sys 402 } 403 } 404 } 405 return "" 406 } 471 # itcl::body Rappture::Units::System::for {units} {} 472 # Actual implementation is in rappture/lang/tcl/src/RpUnitsTclInterface.cc. 473 407 474 408 475 # ---------------------------------------------------------------------- … … 413 480 # relationships that lead to the same base system. 414 481 # ---------------------------------------------------------------------- 415 itcl::body Rappture::Units::System::all {units} { 416 set sys [Rappture::Units::System::for $units] 417 if {$sys == ""} { 418 return "" 419 } 420 421 if {"" != [$sys cget -basis]} { 422 set basis [lindex [$sys cget -basis] 0] 423 } else { 424 set basis $units 425 } 426 427 set ulist $basis 428 foreach u [array names _base] { 429 set obj $_base($u) 430 set b [lindex [$obj cget -basis] 0] 431 if {$b == $basis} { 432 lappend ulist $u 433 } 434 } 435 return $ulist 436 } 482 # itcl::body Rappture::Units::System::all {units} {} 483 # Actual implementation is in rappture/lang/tcl/src/RpUnitsTclInterface.cc. 484 437 485 438 486 # ----------------------------------------------------------------------
Note: See TracChangeset
for help on using the changeset viewer.