The following section is of purely practical nature. It contains examples of the most common functions from the areas CustomLib deals with.

Example no. 1: Using functions based on Interpolated Data

The linear interpolation uses two or more pairs of input data points that approximate a function. To define functions based on interpolated data, use the function dialog box, which can be opened from the data tree automatically clicking on the button Σ as follows,

An example of the <value/> node field in the .spd file is the following:

<value n="disc_load" pn="Discrete load" min_two_pnts="1" single_value="1" function="[loads_function_time]" function_func="" state="normal" v="1.0">

<function>

<functionVariable n="interpolator_func" pn="Interpolation function" variable="x" units="s">

<value n="point" pn="Point" v="0.0,0.0"/>

<value n="point" pn="Point" v="10.0,1.0"/>

<value n="point" pn="Point" v="1.0e9,1.0"/>

</functionVariable>

</function>

</value>

<proc n="loads_function_time" args="">

return [cmas2d_CustomLIB::chk_loads_function_time $domNode]

</proc>

proc cmas2d_CustomLIB::chk_loads_function_time { domNode } {

set loads [list [list scalar]]

lappend loads [list interpolator_func x x T]

return [join $loads ,]

}

Notice that both <function/> and <functionVariable/> nodes are required to define an interpolation function.

Note: The nodes of type <value n="point"/> can be omitted to start with an empty table of input data points.

Example no. 2: Using functions based on Interpolated Data, methods for entering data

It is possible to select automatically the method for entering data (Single value, Linear ramp or Equation). For functions of one variable, you can select between the following interpolation methods by default:

  • Single value: The linear interpolation uses two or more pairs of input data points that approximate a function.
  • Linear ramp: Calculates two or more 'Linear ramp' pairs of input data points that approximate a function.
  • Equation: Calculates two or more pairs of input data points that satisfy a given equation.

Use the function dialog box, which can be opened from the data tree clicking on the button Σ as follows,

<value n="Factor" pn="Factor" v="1.0" help="This factor, that can be a number or a formula, multiplies the vector load" function="[loads_function Punctual_Load]" function_func="CompassFEM::function_loads"/>

<proc n="loads_function" args="load_name">

return [chk_loads_function $domNode $load_name]

</proc>

proc function_loads { ftype what n pn frame domNode funcNode \

units_var ov_var } {

switch $ftype {

sinusoidal_load {

return [function_loads_sinusoidal $what $n $pn $frame $domNode \

$funcNode $units_var $ov_var]

}

custom_editor {

return [function_custom_editor $what $n $pn $frame $domNode \

$funcNode $units_var $ov_var]

}

}

}

proc chk_loads_function {domNode load_name} {

set loads [list [list scalar]]

lappend loads [list sinusoidal_load t]

return [join $loads ,]

}

proc function_custom_editor { what n pn f domNode funcNode \

units_var ov_var } {

# Create your own child window implemented in Tcl/Tk..

}

proc function_loads_sinusoidal { what n pn f domNode funcNode \

units_var ov_var } {

global ProblemTypePriv

switch $what {

create {

set help [= "This is a a sinusoidal load that depends on the time"]

set f1 [ttk::frame $f.f1]

set idx 0

foreach i [list amplitude circular_frequency phase_angle initial_time end_time] \

n [list [= "Amplitude (%s)" A] \

[= "Frequency (%s)" f] [= "Phase angle (%s)" \u3a6] \

[= "Initial time (%s)" t0] [= "End time (%s)" t1]] \

m [list - Frequency Rotation T T] \

u [list - 1/s rad s s] \

v [list 1.0 1.0 0.0 0.0 0.0] {

ttk::label $f1.l$idx -text $n:

gid_groups_conds::register_popup_help $f1.l$idx $help

if { $m ne "-" } {

set ProblemTypePriv($i) $v

set ProblemTypePriv(${i}_units $u

gid_groups_conds::entry_units $f1.sw$idx \

-unit_magnitude $m \

-value_variable ProblemTypePriv($i) \

-units_variable ProblemTypePriv(${i}_units)

} else {

ttk::entry $f1.sw$idx -textvariable ProblemTypePriv($i)

set ProblemTypePriv($i) $v

}

grid $f1.l$idx $f1.sw$idx -sticky w -padx 2 -pady 2

grid configure $f1.sw$idx -sticky ew

incr idx

}

if { ![info exists ProblemTypePriv(l_sin_img)] } {

set ProblemTypePriv(l_sin_img) [image create photo \

-file [file join $ProblemTypePriv(problemtypedir) images \

sinusoidal_load.gif]]

}

ttk::label $f1.l -text "f(t)=A\u00b7sin(2\u00b7\u03C0\u00b7f\u00b7t+\u03a6)" \

-relief solid -padding 3 -width 20 \

-anchor w

grid $f1.l -sticky w -padx 6 -pady 6

label $f.image -image $ProblemTypePriv(l_sin_img) -bd 1 -relief solid

grid $f1 $f.image -sticky nw -padx 8 -pady 2

grid configure $f.image -sticky new

grid columnconfigure $f 1 -weight 1

grid rowconfigure $f 0 -weight 1

if { $funcNode ne "" } {

set xp {functionVariable[@variable="t" and

@n="sinusoidal_load"]}

set fvarNode [$funcNode selectNodes $xp]

} else { set fvarNode "" }

if { $fvarNode ne "" } {

foreach i [list amplitude circular_frequency phase_angle initial_time end_time] \

pn [list [= "Amplitude (%s)" A] \

[= "Frequency (%s)" f] [= "Phase angle (%s)" \u3a6] \

[= "Initial time (%s)" t0] [= "End time (%s)" t1]] \

m [list - Frequency Rotation T T] {

set xp [format_xpath {string(value[@n=%s]/@v)} $i]

set ProblemTypePriv($i) [$fvarNode selectNodes $xp]

if { $m ne "-" } {

set xp [format_xpath {string(value[@n=%s]/@units)} $i]

set ProblemTypePriv(${i}_units) \

[gid_groups_conds::units_to_nice_units [$fvarNode selectNodes $xp]]

}

}

}

return [= "Sinusoidal load"]

}

apply {

set idx 0

set f1 $f.f1

foreach i [list amplitude circular_frequency phase_angle initial_time end_time] \

pn [list [= "Amplitude (%s)" A] \

[= "Frequency (%s)" f] [= "Phase angle (%s)" \u3a6] \

[= "Initial time (%s)" t0] [= "End time (%s)" t1]] \

m [list - Rotation Rotation T T] {

if { ![string is double -strict $ProblemTypePriv($i)] } {

if { $m ne "-" } { set e $f1.sw$idx.e } else { set e $f1.sw$idx }

tk::TabToWindow $e

error [= "Value %s is not OK" $pn]

}

incr idx

}

set xp {functionVariable[@variable="t"]}

if { [$funcNode selectNodes $xp] eq "" } {

set fvarNode [$funcNode appendChildTag functionVariable]

} else {

set fvarNode [$funcNode selectNodes $xp]

foreach i [$fvarNode childNodes] { $i delete }

}

$fvarNode setAttribute n sinusoidal_load pn [= "Sinusoidal load"] \

variable "t"

foreach i [list amplitude circular_frequency phase_angle initial_time end_time] \

pn [list [= "Amplitude (%s)" A] \

[= "Frequency (%s)" f] [= "Phase angle (%s)" \u3a6] \

[= "Initial time (%s)" t0] [= "End time (%s)" t1]] \

m [list - Frequency Rotation T T] {

set v [$fvarNode appendChildTag value [list attributes() \

n $i pn $pn v $ProblemTypePriv($i)]]

if { $m ne "-" } {

set newunits [gid_groups_conds::nice_units_to_units \

$ProblemTypePriv(${i}_units)]

$v setAttribute unit_magnitude $m \

units $newunits

}

}

}

}

}

Example no. 3: Editable multiline text for entering mathematical expressions

The following example allows to enter a numerical or math expression, such as a formula, into an editable multiline text box. CustomLib will display automatically the resulting math expression in the data tree. Moreover, the user will be able to overwrite this expression with a different formula using Ramdebugger editor. The 'Edit' button opens Ramdebugger, the graphical debugger for the scripting lenguage Tcl-Tk.

The <condition/> node field in the .spd file is as follows,

<condition n="custom_constraints" pn="Custom constraints" ov="point,line,surface,volume" state="normal" ov_default="surface" ovm="" help="A constraint defined by using the TCL extension language">

<dependencies node="value[@n='over_what']" att1="v" v1="node" att2="state" v2="disabled" condition="@ov='point'"/>

<dependencies node="value[@n='over_what']" att1="state" v1="normal" condition="@ov='line' or @ov='surface' or @ov='volume'"/>

<value n="units" pn="Units set" v="N-m-kg" values="N-m-kg,N-cm-kg,N-mm-kg"/>

<value n="tcl_code" pn="TCL code" v="" fieldtype="long text"/>

<value n="over_what" pn="Over" v="node" values="node,element" dict="node,node,element,element" editable="0" help="Condition will be applied to either over every selected element or every selected node"/>

<edit_command n="edit_custom_data" pn="Edit" proc="edit_custom_data constraints" edit_type="callback"/>

</condition>

<proc n="edit_custom_data" args="custom_type callback">

custom_data_edit $domNode $dict $custom_type $callback

</proc>

proc custom_data_edit { domNode dict custom_type callback } {

if { ![interp exists ramdebugger] } { interp create ramdebugger }

ramdebugger eval [list set argv [list -noprefs -rgeometry 600x500+300+300 \

-onlytext]]

package require RamDebugger

if { [dict exists $dict tcl_code] } {

set data [string trim [dict get $dict tcl_code]]

} else {

set xp {string(value[@n="tcl_code"]/@v)}

set data [string trim [$domNode selectNodes $xp]]

}

if { $data eq "" } {

set data [format "# %s\n# %s\n" [= "Enter TCL code to define a load"] \

[= "Use menu 'Custom data' for help"]]

}

interp alias ramdebugger EditEndInRamDebugger "" CompassFEM::custom_data_edit_update \

$callback

ramdebugger eval [list RamDebugger::OpenFileSaveHandler "*custom data*" \

$data EditEndInRamDebugger]

set menu ""

set file [file join $::ProblemTypePriv(problemtypedir) scripts custom_bc_examples.txt]

set fin [open $file r]

set data [read $fin]

close $fin

set elms ""

set rex {\s*#\s*type:\s*(\S.*\S)\s+name:\s*(\S.*\S)\s*$}

foreach "idxsL idxsType idxsName" [regexp -inline -all -line -indices $rex $data] {

if { $elms ne "" } {

lset elms end end [expr {[lindex $idxsL 0]-1}]

}

set type [eval [list string range $data] $idxsType]

set name [eval [list string range $data] $idxsName]

lappend elms [list $type $name [lindex $idxsL 0] ""]

}

if { $elms ne "" } {

lset elms end end [expr {[string length $data]-1}]

}

set subMenus ""

foreach i $elms {

foreach "type name idx1 idx2" $i break

lappend subMenus [list command "$name ($type)" {} \

[= "View example '%s' (%s)" $name $type] "" \

-command [list open_new_window_show $idx1 $idx2]]

}

lappend subMenus [list separator]

set idx2 [expr {[string length $data]-1}]

lappend subMenus [list command [= "All examples"] {} \

[= "View all examples"] "" \

-command [list open_new_window_show 0 $idx2]]

lappend menu [list cascad [= Examples] {} examples 0 $subMenus]

set cmd {

set ip [RamDebugger::OpenFileInNewWindow -ask_for_file 0]

set fin [open %FILE r]

set data [read $fin]

close $fin

set d [string range $data $idx1 $idx2]

$ip eval [list $::RamDebugger::text insert insert $d]

$ip eval [list RamDebugger::MarkAsNotModified]

}

set cmd [string map [list %FILE [list $file]] $cmd]

ramdebugger eval [list proc open_new_window_show [list idx1 idx2] $cmd]

lappend menu [list separator]

foreach "n nargs" [list coords 2 conec 1 nnode 0 elem_num 0 elem_normal 1 \

epsilon 0 facLoad 0] {

set name [= "Insert command '%s'" $n]

set cmd {

set t $::RamDebugger::text

$t insert insert [list %N%]

$t insert insert "("

$t insert insert [string repeat , %NARGS%]

$t insert insert ")"

$t mark set insert "insert-[expr {%NARGS%+1}]c"

}

set cmd [string map [list %N% $n %NARGS% $nargs] \

$cmd]

lappend menu [list command $name "" "" "" -command $cmd]

}

lappend menu [list separator]

foreach "n nfull" [list \

add_to_load_vector "add_to_load_vector ?-substitute? ?-local? nodenum loadvector" \

addload "addload ?-local? pressure|triangular_pressure|punctual_load args"] {

set name [= "Insert command '%s'" $n]

set cmd {

$::RamDebugger::text insert insert [list %NFULL%]

}

set cmd [string map [list %NFULL% $nfull] $cmd]

lappend menu [list command $name "" "" "" -command $cmd]

}

switch $custom_type {

constraints { set title [= "Constraints data"] }

properties { set title [= "Properties data"] }

loads { set title [= "Loads data"] }

default { set title [= "Custom data"] }

}

ramdebugger eval [list RamDebugger::AddCustomFileTypeMenu $title $menu]

}

Example no. 4: Create your own custom editor window for entering mathematical expressions

The user can create any number of custom windows in the user interface. A child window is opened automatically to display information to the user and/or get information from the user. This is a great way to add custom windows for your own purposes.

The Function Editor window can be opened by locating in the data tree the field to modify, and choosing the edit button (e.g. Σ).

This will open the Function Editor window and load the current expression into it, which can be empty.

The <value/> node field in the .spd file is as follows,

<value n="Density" pn="Density" v="" unit_magnitude="M/L^3" units="kg/m^3" help="Density of the fluid" function="[loads_function editor]" pn_function="Density" function_func="function_loads"/>

proc function_loads { ftype what n pn frame domNode funcNode \

units_var ov_var } {

switch $ftype {

editor {

return [function_editor $what $n $pn $frame $domNode \

$funcNode $units_var $ov_var]

}

}

}

proc function_editor { what n pn f domNode funcNode \

units_var ov_var } {

# Create your own child window implemented in Tcl/Tk.

}

<proc n="loads_function" args="load_name">

return [chk_loads_function $domNode $load_name]

</proc>

proc chk_loads_function { domNode load_name } {

set loads [list [list scalar]]

if { [lsearch "editor" $load_name] != -1 } {

lappend loads [list editor ""]

return [join $loads ,]

}

return [join $loads ,]

}