a formula with natural logarithm in tcl - tcl

This formula will be coded in tcl.
Y= Intercept + Slope1*(X – X0) + (Slope2 – Slope1)*Delta*ln(1+exp((X-X0)/Delta))
I searched that ln() in this formula can be coded using log() in tcl.
However, I cannot get the correct output in tcl.
Here is what I coded, could you please take a look? Any comments? Thank you.
set Intercept 0.7416
set Slope1 52.42
set x0 0.01491
set Slope2 0.2533
set Delta 0.002275
set y_frac [expr {
$Intercept + $Slope1*($x/$tend - $x0/$tend)
+ ($Slope2-$Slope1) * $Delta * log10(1.0 + exp(($x/$tend - $x0/$tend)/$Delta))
}]
set y [expr {$y_frac *100.0}] ​

The log() function computes the natural logarithm of its argument (just as the exp() function is its inverse, computing ex). That means your expression becomes (with the simplest of transformations) just:
set Y [expr {
$Intercept + $Slope1*($X – $X0) + ($Slope2 – $Slope1)*$Delta*log(1+exp(($X-$X0)/$Delta))
}]
I'd refactor the expression a little, as deltas are natural things to extract:
set dX [expr { $X - $X0 }]
set dSlope [expr { $Slope2 - $Slope1 }]
set Y [expr { $Intercept + $Slope1*$dX + $dSlope*$Delta*log(1+exp($dX/$Delta)) }]
At this point, it's probably easier to define a procedure to encapsulate this.
proc computeY {y} {
global Intercept Slope1 Slope2 Delta x0
set dX [expr { $x - $x0 }]
set dSlope [expr { $Slope2 - $Slope1 }]
set Y [expr { $Intercept + $Slope1*$dX + $dSlope*$Delta*log(1+exp($dX/$Delta)) }]
return [expr { $Y * 100.0 }]; # Your final rescaling
}

Related

Does TCL containg built in functions of type get_bits and set_bits?

The get_bits will return specific bits of a value and set_bits will set specific bits of a value to a specified value. Does TCL contain such functions built in or should they be written by the user?
The binary scan command does come close to the get_bits function but is not the same thing.
There's no specific function for getting or setting a particular bit. We can make them.
proc get_bit {value bit} {
expr {($value & (1 << $bit)) != 0}
}
proc set_bit {varName bit {value 1}} {
upvar 1 $varName var
if {$value} {
set var [expr {$var | (1 << $bit)}]
} else {
set var [expr {$var & ~(1 << $bit)}]
}
}
Those will work with integer values of any width; you're not restricted to 32 bits or 64 bits.
# Lots of bits!
set x 123456789012345678901234567890
# Fetch a particular bit
puts [get_bit $x 17]
# Set a bit to 1
set_bit x 78
puts "x = $x"
# Set a bit to 0
set_bit x 75 0
puts "x = $x"

Assign random number to node in TCL script for ns-2: ERROR variable is array

I am trying to run the following tcl script but getting an error
can't set "val": variable is array
while executing
"set val [random_int $upper_limit]"
Here is my code,Please any help
proc random_int { upper_limit } {
global myrand
set myrand [expr int(rand() * $upper_limit + 1)]
return $myrand
}
set upper_limit 21
set val [random_int $upper_limit]
$ns at 0.6 "[$node($val) set ragent_] malicious"
Your current main problem is that there's an existing use of the val as an array; Tcl's variables can't simultaneously be scalars and arrays. The most expedient fix is to change the name of the variable, perhaps to value.
set value [random_int $upper_limit]
$ns at 0.6 "[$node($value) set ragent_] malicious"
Apart from that, your random number generator could be a bit sharper code. It probably doesn't need to access any global variables, and it really should have the expression put in braces (for a bunch of reasons including both speed and safety). Here's the trimmed/tuned version:
proc random_int { upper_limit } {
expr { int(rand() * $upper_limit + 1) }
}
Occasionally, I write such procedures slightly differently, like this:
proc random_int { upper_limit } {expr {
int(rand() * $upper_limit + 1)
}}
It's semantically identical, but it makes it clearer what the author is really thinking about.

Why does not work using while for looping process

Why my script does not work. I am using while for looping process.
Hope anyone could help me for my case. The script as below;
Set global
global xmin xmax ymin ymax xVer yVer x1 y1 Count
Set paramaters
set xmin 0
set xmax 51
set ymin 0
set ymax 51
set x1 2
set y1 2
set xVer 2
set yVer 2
set Count 1
set goToVer "n"
Do looping process
while {$x1 > $xmin && $x1 < $xmax && $y1 > $ymin && $y1 < $ymax} {
# For horizontal axis
while {$x1 > $xmin && $x1 < $xmax} {
set azi [expr (45+90)]
set dip 0
set length 2
set dist [expr (cos($dip) * $length)]
set x1 [expr ($x1 + (sin($azimuth) * $dist))]
set y1 [expr ($y1 + (cos($azimuth) * $dist))]
set goToVer "y"
incr Count
}
# For vertical axis
if {$goToVer == "y"} {
set azi 45
set dip 0
set length 5
set dist [expr (cos($dip) * $length)]
set x1 [expr ($xVer + (sin($azimuth) * $dist))]
set y1 [expr ($yVer + (cos($azimuth) * $dist))]
set xVer $x1
set yVer $y1
incr Count
}
}
Thanks in advance!
I don't know what the problem is, but there are some things that we can do to make everything better. First step: let's ry factoring out the coordinate conversion code itself into a little procedure (I've fixed the braces around the expressions too):
proc convert {x y length azimuth dip} {
set dist [expr {cos($dip) * $length}]
set x1 [expr {$x + sin($azimuth) * $dist}]
set y1 [expr {$y + cos($azimuth) * $dist}]
return [list $x1 $y1]
}
while {$x1 > $xmin && $x1 < $xmax && $y1 > $ymin && $y1 < $ymax} {
# For horizontal axis
while {$x1 > $xmin && $x1 < $xmax} {
set azi [expr (45+90)]
set dip 0
set length 2
lassign [convert $x1 $y1 $length $azi $dip] x1 y1
set goToVer "y"
incr Count
}
# For vertical axis
if {$goToVer == "y"} {
set azi 45
set dip 0
set length 5
lassign [convert $xVer $yVer $length $azi $dip] x1 y1
incr Count
}
}
Next, the value of azi in the inner loop is suspicious; it looks like it is in degrees but Tcl's trigonometry functions (like those in most other programming languages) take their argument in radians. Multiply it by π/180°.
Finally, the logic of the loops is weird. I'm not saying it is wrong… but I really find it hard to comprehend what you'd use looping like that for. To loop a pair of coordinates over some space using equal steps on the axes, you use for loops with integer iterator variables and then apply a conversion to get your floating point coordinates (this is best because it limits cumulative errors):
set azi [expr {(45 + 90) * 3.1415927/180}]
set dip 0
set length 2
for {set x $xmin} {$x <= $xmax} {incr x} {
for {set y $ymin} {$y <= $ymax} {incr y} {
set dist [expr {cos($dip) * $length}]
set x1 [expr {$x + sin($azimuth) * $dist}]
set y1 [expr {$y + cos($azimuth) * $dist}]
# I assume you want to do something with $x1,$y1 here…
}
}
Alternatively, you could use regular spacing in polar coordinates, or any other regular scheme; it's just that good code exploits regularity and you're strongly recommended to work that way if you can. But that might not be what you were trying to do at all. Your code is confusing in its intent.
Which brings me to your actual bugs, which appear to revolve around state management. The logic with the goToVer was confused, BTW, and that might've been the problem you were having. You were setting it in the inner loop, but from that point on it was always set. I recommend not doing things like that as it is quite difficult to debug (there are cases where it can make sense, but it doesn't look like you're doing them) and instead sticking to regular grids, but they can work. I'm guessing that you are missing a reset of the variable to 0 at some point in the outer loop, probably just before the inner loop starts.

conversion of and operation having a function from c# to tcl

how to do the and operation given as one line statement in tcl in tcl where pcieDeviceControlRegister is a function given as in the code:
code:
pcieDeviceControlRegister = cfgSpace.pcieDeviceControlRegister & (~((uint)0xF));
Reference for pcieDeviceControlRegister function is :
public uint pcieDeviceControlRegister
{
get
{
if (pcieCapabilityOffset != 0)
return (ReadDW((int)(pcieCapabilityOffset + 8) / 4, 0xF)) & 0xFFFF;
else
return 0;
}
set
{
if (pcieCapabilityOffset != 0)
{
uint val = ReadDW((int)(pcieCapabilityOffset + 8) / 4, 0xF)& 0xFFFF0000;
val |= value;
// write should be done with byte enables !!!
WriteDW((int)(pcieCapabilityOffset + 8) / 4, val, 0xF);
}
}
}
You'll have to arrange for the mapping of ReadDW and WriteDW into Tcl, probably by writing a little C or C++ code that makes commands (with the same names) that do those operations. I'm assuming that you've already done that. (SWIG can generate the glue code if you need it.)
Then, we define a command like this:
proc pcieDeviceControlRegister {{newValue ""}} {
global pcieCapabilityOffset
# Filter the bogus setup case early; if this is really an error case though,
# it is better to actually throw an error instead of struggling on badly.
if {$pcieCapabilityOffset == 0} {
return 0
# error "PCIE capability offset is zero"
}
set offset [expr {($pcieCapabilityOffset + 8) / 4}]
if {$newValue eq ""} {
# This is a read operation
return [expr {[ReadDW $offset 0xF] & 0xFFFF}]
} else {
# This is a write operation
set val [expr {[ReadDW $offset 0xF] & 0xFFFF0000}]
# Note that we do the bit filtering HERE
set val [expr {$val | ($newValue & 0xFFFF)}]
WriteDW $offset $val 0xF
return
}
}
With that, which you should be able to see is a pretty simple translation of the C# property code (with a bit of minor refactoring), you can then write your calling code like this:
pcieDeviceControlRegister [expr {[pcieDeviceControlRegister] & ~0xF}]
With Tcl, you don't write casts to different types of integers: Tcl just has numbers (which are theoretically of infinite width) so instead you need to do a few more bit masks in key places.
The conversion of the above code to a method on an object is left as an exercise. It doesn't change very much…

tcl formatting floating point with fixed precision

# the unit of period is picosecond
set period 625000.0
set period_sec [format %3.6g [expr $period * 1e-12]]
puts $period_sec
result: 6.25e-07
Is there a way to force tcl to get results like 625e-09
Assuming that you want to format it to the nearest exponent, you could use a proc which formats it like this:
proc fix_sci {n} {
# Not a sci-fmt number with negative exponent
if {![string match "*e-*" $n]} {return $n}
# The set of exponents
set a 9
set b 12
# Grab the number (I called it 'front') and the exponent (called 'exp')
regexp -- {(-?[0-9.]+)e-0*([0-9]+)} $n - front exp
# Check which set of exponent is closer to the exponent of the number
if {[expr {abs($exp-$a)}] < [expr {abs($exp-$b)}]} {
# If it's the first, get the difference and adjust 'front'
set dif [expr {$exp-$a}]
set front [expr {$front/(10.0**$dif)}]
set exp $a
} else {
# If it's the first, get the difference and adjust 'front'
set dif [expr {$exp-$b}]
set front [expr {$front/(10.0**$dif)}]
set exp $b
}
# Return the formatted numbers, front in max 3 digits and exponent in 2 digits
return [format %3ge-%.2d $front $exp]
}
Note that your original code returns 6.25e-007 (3 digits in the exponent).
If you need to change the rule or rounding the exponent, you will have to change the if part (i.e. [expr {abs($exp-$a)}] < [expr {abs($exp-$b)}]). For example $exp >= $a could be used to format if the exponent is 9 or below.
ideone demo of above code for 'closest' exponent.
For Tcl versions before 8.5, use pow(10.0,$dif) instead of 10.0**$dif
I do not think there is anything in the format command that will help you directly. However, if you consider a slight variation on the format code, then it may be a lot easier to get what you want (with a bit of string manipulation):
format %#3.6g $number
gives a number like: 6.25000e-007
This can be parsed more easily:
Extract the exponent
Determine the number of positions to shift the decimal point
Shift it and replace the exponent
It is not entirely straightforward, I am afraid, but it should be doable. Wiki page http://wiki.tcl.tk/5000 may give you some inspiration.