# This code creates the user interface for the mesh editor.
#
# Copyright (c) 1997 Pacific Marketing and Consulting, Inc,
#       and ICEM Systems, GmbH.  All rights reserved.
# Author:  Wayne A. Christopher, wayne@icemcfd.com
# $Revision$ on $Date$

# This file contains special comments of the form ## html ... which are
# used to automatically generate documentation

## <h3>Boundary condition editing and modification functions</h3>

## Load in the boundary condition data from the <i>file</i>.

proc ic_boco_load {file} {
    global famboco_obj batch_mode
    global tdv_default
    
    set mod 1
    if {$famboco_obj == ""} {
	set famboco_obj [family_boco]
	if !$batch_mode { $tdv_default add_image $famboco_obj }
	uns_mesh_attach_family_boco 1
	set mod 0
    }
    mess "Loading family boco data from $file\n"
    busy 1
    $famboco_obj load_family_boco $file
    busy 0
    $famboco_obj configure -filename $file -modified $mod
    
    display_update
}

## Load in the attribute data from the <i>file</i>.

proc ic_boco_load_atr {file} {
    global famboco_obj batch_mode
    global tdv_default
    
    set mod 1
    if {$famboco_obj == ""} {
	set famboco_obj [family_boco]
	if !$batch_mode { $tdv_default add_image $famboco_obj }
	uns_mesh_attach_family_boco 1
	set mod 0
    }
    mess "Loading attribute data from $file\n"
    busy 1
    $famboco_obj load_attributes $file boco_get_arg_names_defaults_types_and_positions \
	"boco_computepos 1"
    busy 0
    $famboco_obj configure -filename $file -modified $mod
    
    display_update
}

# For every bctype,pos return the name, and for every bctype,name the
# default, type, and pos This is only called from the C++ level

proc boco_get_arg_names_defaults_types_and_positions {solname} {
    global famboco_info famboco_arg_name famboco_arg_pos
    global famboco_arg_type famboco_arg_def
    
    if {![info exists famboco_info(name)] || ($famboco_info(name) != $solname)} {
	if [info exists famboco_info(name)] { puts "$famboco_info(name) != $solname" }
	
	famboco_set_solver "" $solname 1
	catch {unset famboco_arg_name}
	catch {unset famboco_arg_pos}
	catch {unset famboco_arg_type}
	catch {unset famboco_arg_def}
	
	foreach b $famboco_info(bctypes) {
	    foreach a $famboco_info(args,$b) {
		if {[lindex $a 0] != "arg"} continue ;# might be a label
		catch {unset ap}
		if [catch {array set ap [lrange $a 1 end]} err] {
		    puts "Error for $b $a: $err"
		    continue
		}
		foreach i {name pos type default} {
		    if ![info exists ap($i)] {
			puts "ERROR: missing $i from $b $a"
			if {$i == "default"} { set ap($i) 0 }
		    }
		}
		set famboco_arg_name($b,$ap(pos)) $ap(name)
		set famboco_arg_pos($b,$ap(name)) $ap(pos)
		set famboco_arg_type($b,$ap(name)) $ap(type)
		set famboco_arg_def($b,$ap(name)) $ap(default)
	    }
	}
    }
}

# This is also called from C++
# It takes a list of name/value pairs from a single BC and puts them in the right order
# based on what's in bcinfo.  It also has to deal with dup_block which is kind of messy

#set bcp_count 0

proc boco_computepos {have_names bctype args} {
    global famboco_info
    
    #puts [list boco_computepos $have_names $bctype $args]
    if $have_names {
	array set argv $args
    } else {
	set i 1
	foreach a $args { set argv($i) $a; incr i }
    }
    
    if {$bctype == "color"} {
	# This is an annoying special case
	if $have_names {
	    return [list [list color int [expr int([lindex $args 1])] 1 0]]
	} else {
	    return [list [list color int [expr int([lindex $args 0])] 1 0]]
	}
    } elseif {![info exists famboco_info(args,$bctype)]} {
	set args2 ""
	set maxi 0
	set i 0
	if $have_names {
	    set args2 ""
	    foreach {n a} $args { lappend args2 $a }
	    set args $args2
	}
	while {[llength $args] && ([lindex $args end] == "")} {
	    set args [lrange $args 0 end-1]
	}

	# This must be a BC for a different kind of solver.  Just write it out somehow.
	set retlist ""
	set i 1
	foreach a $args {
	    if {$a == ""} {
		set type int
		set a -999999
	    } elseif [scan $a %g dummy] {
		if {([string first . $a] >= 0) || ([string first e $a] >= 0) || 
		    ([string first E $a] >= 0)} {
		    set type double
		} else {
		    set type int
		}
	    } else {
		set type string
	    }
	    lappend retlist [list arg$i $type $a $i 0]
	    incr i
	}
	return $retlist
    }
    
    set curpos 0
    set maxpos 0
    foreach a $famboco_info(args,$bctype) {
	catch {unset ap}
	array set ap [lrange $a 1 end]
	incr curpos
	if [info exists ap(pos)] {
	    set p $ap(pos)
	} else {
	    set p $curpos
	}
	set atpos($p) $a
	if {$p > $maxpos} { set maxpos $p }
    }
    #parray atpos
    set retlist ""
    for {set p 1} {$p <= $maxpos} {incr p} {
	if ![info exists atpos($p)] {
	    lappend retlist [list dummy int 0 $p 0]
	    continue
	}
	
	catch {unset ap}
	array set ap [lrange $atpos($p) 1 end]
	
	set name $ap(name)
	if $have_names {
	    set key $name
	} else {
	    set key $p
	}
	
	if [info exists ap(dup_block)] {
	    #puts "p $p max $maxpos"
	    # How many successive args get duplicated
	    set na $ap(dup_block)
	    
	    # How many copies of them?
	    if ![info exists argv($key)] {
		set nc 0
	    } else {
		set nc $argv($key)
	    }
	    
	    # Move all the ones above this out of the way if the block is duped more than
	    # once
	    set totnew [expr $na * ($nc - 1)]
	    if $totnew {
		if {$totnew > 0} {
		    for {set p2 $maxpos} {$p2 > $p + $na} {incr p2 -1} {
			if [info exists atpos($p2)] {
			    set atpos([expr $p2 + $totnew]) $atpos($p2)
			    unset atpos($p2)
			}
		    }
		} else {
		    for {set p2 [expr $p + 1]} {$p2 <= $maxpos + $totnew} {incr p2} {
			set p2n [expr $p2 - $totnew]
			if [info exists atpos($p2n)] {
			    set atpos($p2) $atpos($p2n)
			    #puts "set atpos($p2) \$atpos($p2n)"
			    unset atpos($p2n)
			} else {
			    catch {unset atpos($p2)}
			}
		    }			
		}
		incr maxpos $totnew
		#puts "incr maxpos $totnew"
	    }
	    if {$totnew >= 0} {
		for {set a 1} {$a <= $na} {incr a} {
		    set apsave [lrange $atpos([expr $p + $a]) 1 end]
		    for {set n 0} {$n < $nc} {incr n} {
			catch {unset ap2}
			array set ap2 $apsave
			set ap2(pos) [expr $p + $n * $na + $a]
			append ap2(name) _$n
			set atpos($ap2(pos)) [concat arg [array get ap2]]
		    }
		}
	    }
	}
	if [info exists argv($key)] {
	    set v $argv($key)
	} else {
	    set v $ap(default)
	}
	
	set isset [expr {[info exists ap(is_set)] && $ap(is_set)}]
	
	switch $ap(type) {
	    int - integer {
		if {$v == ""} {
		    # should have been an int_blank?
		    lappend retlist [list $name string $v $p $isset]
		} else {
		    lappend retlist [list $name int [expr int($v)] $p $isset]
		}
	    }
	    double - float {
		lappend retlist [list $name double $v $p $isset]
	    }
	    default {
		lappend retlist [list $name string $v $p $isset]
	    }
	}
    }
    #global bcp_count
    #if {![expr [incr bcp_count] % 100]} { puts $bcp_count }
    #puts $args
    #puts $retlist
    return $retlist
}

## Save the current boco data to the given file name.

proc ic_boco_save {file} {
    global whoami famboco_obj famboco_info
    
    if ![ic_boco_is_loaded] {
        ic_empty_boco
    }
    
    set ss [$famboco_obj get -solver]
    if {$ss != ""} {
       if {![info exists famboco_info(name)] || ($famboco_info(name) != $ss)} {
	        famboco_set_solver "" $ss
       }
    }
    
    set comment "$whoami by [cmd_whoami] on [cmd_date]"
    busy 1
    $famboco_obj save_family_boco $file $comment 0
    busy 0
    mess "Saved family boco data to $file\n"
    $famboco_obj configure -modified 0
    famboco_annotate_family_boco $file
}

## Save a new format bc (.atr) file.

proc ic_boco_save_atr {file {ss ""}} {
    global whoami famboco_obj famboco_info famboco_arg_name_pos famboco_arg_set
    global version
    
    if ![ic_boco_is_loaded] {
        ic_empty_boco
    }
    
    if {$ss == ""} { set ss [$famboco_obj get -solver] }
    if {$ss == ""} {
        set ss Nastran
        #mess "Setting solver to Nastran since it was not set\n"
        return
    }
    if {![info exists famboco_info(name)] || ($famboco_info(name) != $ss)} {
        famboco_set_solver "" $ss
    }
    
    set comment "Written by $whoami $version, user [cmd_whoami], [cmd_date]"
    busy 1
    $famboco_obj save_attributes $file $comment $ss "boco_computepos 0"
    busy 0
    mess "Saved attribute data to $file\n"
    $famboco_obj configure -modified 0
}

proc unused__ic_boco_save_atr {file {ss ""}} {
    global whoami famboco_obj famboco_info famboco_arg_name_pos famboco_arg_set
    global version
    
    if ![ic_boco_is_loaded] {
	ic_empty_boco
    }
    
    # Basically we need to know what the position is for every bctype,argname pair
    if {$ss == ""} { set ss [$famboco_obj get -solver] }
    if {$ss == ""} { mess "Not saving atr file - solver is not set\n"; return }
    if {![info exists famboco_info(name)] || ($famboco_info(name) != $ss)} {
	famboco_set_solver "" $ss
    }
    catch {unset famboco_arg_name_pos}
    foreach b $famboco_info(bctypes) {
	foreach a $famboco_info(args,$b) {
	    #puts "$b $a"
	    catch {unset ap}
	    if [catch {array set ap [lrange $a 1 end]} err] {
		puts "Error for $b $a: $err"
		continue
	    }
	    set famboco_arg_name_pos($b,$ap(name)) $ap(pos)
	    set famboco_arg_name_pos($b,$ap(pos)) $ap(name)
	    if [info exists ap(is_set)] {
		set famboco_arg_set($b) $ap(name)
	    }
	}
    }
    
    set comment "Written by $whoami $version, user [cmd_whoami], [cmd_date]"
    busy 1
    $famboco_obj save_attributes $file $comment $ss famboco_arg_name_pos famboco_arg_set
    busy 0
    mess "Saved attribute data to $file\n"
    $famboco_obj configure -modified 0
}

## Has a family boco file been loaded?

proc ic_boco_is_loaded {} {
    global famboco_obj
    
    return [expr {$famboco_obj != ""}]
}
set dont_record_command(ic_boco_is_loaded) 1

## See if the current boco data has been modified since the last save.

proc ic_boco_is_modified {} {
    global famboco_obj
    return [expr {($famboco_obj != "") && [$famboco_obj get -modified]}]
}
set dont_record_command(ic_boco_is_modified) 1

## Force the current boco data to be dirty.

proc ic_boco_set_modified {} {
    global famboco_obj
    if {$famboco_obj != ""} { $famboco_obj configure -modified 1 }
}

## Unload the current family boco data.

proc ic_boco_unload {} {
    global famboco_obj
    if {$famboco_obj != ""} {
	$famboco_obj clear ""
	uns_mesh_attach_family_boco 0
	$famboco_obj delete
	set famboco_obj ""
    }
}

## Create an empty boco database.

proc ic_empty_boco {} {
    global famboco_obj batch_mode
    global tdv_default projlib
    
    if {$projlib == ""} {
	ic_empty_tetin
	ic_geo_set_modified 0
    }

    if {$famboco_obj == ""} {
	set famboco_obj [family_boco]
	if !$batch_mode { $tdv_default add_image $famboco_obj }
	uns_mesh_attach_family_boco 1
    }
}

## Set the default solver for an existing boco database. If
## no solver is given, then it returns the current solver setting.

proc ic_boco_solver {{solver ""}} {
    global famboco_obj
    
    if ![ic_boco_is_loaded] { return "" }
    if {"" == $solver} {
	set solver [$famboco_obj get -solver]
	#if {"" != $solver} { mess "$solver defined as solver in boco file\n" }
    } else {
	$famboco_obj configure -solver $solver
    }
    return $solver
}

## Get the boundary conditions associated with the given family.  They are
## returned as a list, each element is one boco.  Each boco element is a list
## where the first element is 0 or 1 whether the data is active or not,
## the second is the type, such as WALL or INT, and the rest
## are the parameters.  For information about what the available types and
## parameters are, see the <b>Solver parameters and boundary condition
## data</b> section for the solver you are interested in.
## If the bctypes argument is given then only the matching bc's are returned.

proc ic_boco_get {fam {bctypes ""} {not_bctypes ""}} {
    global famboco_obj
    
    if {$famboco_obj == ""} { ic_empty_boco }
    set bcs [$famboco_obj get_fam_bocos $fam]
    if {$bctypes == ""} { return $bcs }
    set bcs2 ""
    foreach bc $bcs {
	set bt [lindex $bc 1]
	set ok 0
	foreach bctype $bctypes {
	    if [string match $bctype $bt] {
		set ok 1
		foreach nbt $not_bctypes {
		    if {$bt == $nbt} { set ok 0; break }
		}
		break
	    }
	}
	if $ok { lappend bcs2 $bc }
    }
    return $bcs2
}
set dont_record_command(ic_boco_get) 1

## This is the same as <b>ic_boco_get</b> (for backward compatibility only).

proc ic_get_boco {fam} { return [ic_boco_get $fam] }

## Set the boundary conditions associated with the given family.  They must be
## given as a list, each element is one boco.  Each boco element is a list
## where the first element is 0 or 1 whether the data is active or not,
## the second is the type, such as WALL or INT, and the rest
## are the parameters.    For information about what the available types and
## parameters are, see the <b>Solver parameters and boundary condition
## data</b> section for the solver you are interested in.

proc ic_boco_set {fam bocos} {
    global famboco_obj
    
    if {$famboco_obj == ""} { ic_empty_boco }
    set res [$famboco_obj set_fam_bocos $fam $bocos]
    return $res
}

## Just append some bc's

proc ic_boco_append {fam nbcs} {
    global famboco_obj
    
    if {$famboco_obj == ""} { ic_empty_boco }
    set bcs [ic_boco_get $fam]
    ic_boco_set $fam [concat $bcs $nbcs]
}

## Change one bc

proc ic_boco_modify {fam obc nbc} {
    global famboco_obj
    
    if {$famboco_obj == ""} { ic_empty_boco }
    set nbcs ""
    foreach bc [ic_boco_get $fam] {
	set same 1
	foreach b1 $bc b2 $obc {
	    # The strings might be formatted differently but be the same list
	    if {$b1 != $b2} { set same 0; break }
	}
	if $same { set bc $nbc }
	lappend nbcs $bc
    }
    ic_boco_set $fam $nbcs
}

## Delete bc's with a given set of types from a family

proc ic_boco_delete_bctypes {fam bctypes} {
    global famboco_obj
    
    if {$famboco_obj == ""} return
    
    set nbcs ""
    foreach bc [ic_boco_get $fam] {
	if [lmember $bctypes [lindex $bc 1]] continue
	lappend nbcs $bc
    }
    ic_boco_set $fam $nbcs
}

## This is the same as <b>ic_boco_get</b> (for backward compatibility only).

proc ic_set_boco {fam bocos} { return [ic_boco_set $fam $bocos] }

## Delete all bc's on all families of a given type.

proc ic_boco_delete_all_bctype {bctype} {
    foreach f [ic_boco_list_families] {
	set nb ""
	foreach b [ic_boco_get $f] {
	    if {[lindex $b 1] != $bctype} { lappend nb $b }
	}
	ic_boco_set $f $nb
    }
}

## Return a list of bctype famlist bctype famlist ... pairs
## If which is "" then all bc's are returned, otherwise only those that show up in the list
## A family could possibly appear twice in the famlist if it has the bc twice

proc ic_boco_bctypes_used_by_families {{which ""}} {
    # This is the slow way - it can be done faster if necessary in C++
    foreach f [ic_boco_list_families] {
	foreach b [ic_boco_get $f] {
	    set t [lindex $b 1]
	    lappend found($t) $f
	}
    }
    if {$which == ""} { set which [array names found] }
    set ret ""
    foreach t $which {
	if [info exists found($t)] {
	    lappend ret $t $found($t)
	}
    }
    return $ret
}
set dont_record_command(ic_boco_bctypes_used_by_families) 1

## List all families and bc's that have a bc of a given type.  If bctype
## is a list then it will match the second, etc argument.  Glob characters
## are allowed here.
## If exclude_fam_pattern is non-"" skip famillies that match that pattern.
## Generally this will be *:* for use with ai_env

proc ic_boco_list_with_bctype {bcargs {exclude_fam_pattern ""}} {
    set bctype [lindex $bcargs 0]
    set ret ""
    foreach f [ic_boco_list_families] {
	if {($exclude_fam_pattern != "") && 
	    [string match $exclude_fam_pattern $f]} continue
	set nb ""
	foreach b [ic_boco_get $f] {
	    if {[lindex $b 1] == $bctype} {
		set ok 1
		foreach t1 [lrange $bcargs 1 end] v1 [lrange $b 2 end] {
		    if {$t1 == ""} break
		    if ![string match $t1 $v1] { set ok 0 }
		}
		if $ok {
		    lappend ret [list $f $b]
		}
	    }
	}
    }
    return $ret
}
set dont_record_command(ic_boco_list_with_bctype) 1

## List the families by bctype.

proc ic_boco_classify_by_bctype {bctype {exclude_fam_pattern ""}} {
    foreach f [ic_boco_list_families] {
	if {($exclude_fam_pattern != "") && 
	    [string match $exclude_fam_pattern $f]} continue
	set nb ""
	foreach b [ic_boco_get $f] {
	    if {[lindex $b 1] == $bctype} {
		set vals [lindex $b 2]
		lappend ret($vals) $f
	    }
	}
    }
    return [array get ret]
}
set dont_record_command(ic_boco_classify_by_bctype) 1

## A convenience function that just returns the group names with a specific 
## set of bc args
## For example ic_boco_groups_with_bctype [list FORCE_NAME ff1]

proc unused__ic_boco_groups_with_bctype {bcargs {exclude *:*}} {
    set ret ""
    foreach bb [ic_boco_list_with_bctype $bcargs $exclude] {
	lappend ret [lindex $bb 0]
    }
    return $ret
}

## Get all the possible values for an arg at a specific position for
## a list of bctypes.  This is used to get a list of load sets for ai*env

proc ic_boco_list_bcargs_for_bctypes {types pos} {
    foreach t $types {
	foreach bc [ic_boco_list_with_bctype $t *:*] {
	    set found([lindex [lindex $bc 1] $pos]) 1
	}
    }
    return [array names found]
}
set dont_record_command(ic_boco_list_bcargs_for_bctypes) 1

## Return the BCs of the types t1s, which are in families whose BC of type t2
## has value v2.  (I.e. t1s might be {BR BT}, t2 might be CONSTRAINT_NAME,
## and v2 might be "foo 123".  Used for ai*env.  Note that this automatically
## *excludes* families with : in them.

proc unused__ic_boco_get_with_matching_name {t1s t2 v2} {
    set matches [ic_boco_list_with_bctype [list $t2 $v2] *:*]
    foreach m $matches {
	set bcs [ic_boco_get [lindex $m 0]]
	foreach b $bcs {
	    if [lmember $t1s [lindex $b 1]] {
		set found($b) 1
	    }
	}
    }
    return [array names found]
}

## Get an unused name for a given bc type.

proc unused__ic_boco_get_unused_bcarg {bctype prefix} {
    set matches [ic_boco_list_with_bctype [list $bctype] *:*]
    foreach m $matches {
	set fullname [lindex [lindex $m 1] 2]
	set found([lindex [split $fullname /] end]) 1
    }
    for {set i 0} {[info exists found($prefix$i)]} {incr i} {}
    return $prefix$i
}

## Get an unused name with a given prefix.  Avoid the ones given in the second arg.

proc ic_boco_get_unused_group {prefix {avoid ""}} {
    foreach a $avoid { set found($a) 1 }
    foreach f [ic_boco_list_families] { set found($f) 1 }
    for {set i 0} {[info exists found($prefix$i)]} {incr i} {}
    return $prefix$i
}
set dont_record_command(ic_boco_get_unused_group) 1

## Finds all families that have a bctype named t2 with a value v2 and
## replaces the existing bcs that have types in newbcs with the new ones.
## (I.e. t1s might be {{1 BR 1 2 3} ...}, t2 might be CONSTRAINT_NAME,
## and v2 might be "foo 123".  Used for ai*env.  Note that this automatically
## *includes* families with : in them.
## If a value is given for replace_these it should be a list of bctypes to
## get rid of, otherwise the types are taken from newbcs.
## Returns the families that were so modified.

proc unused__ic_boco_replace_with_matching_name {newbcs t2 v2 {replace_these ""}} {
    set matches [ic_boco_list_with_bctype [list $t2 $v2] ""]

    if {$replace_these != ""} {
	foreach r $replace_these { set grot($r) 1 }
    } else {
	foreach m $newbcs { set grot([lindex $m 1]) 1 }
    }
    foreach m $matches {
	set f [lindex $m 0]
	set bcs [ic_boco_get $f]
	#puts "f $f, bcs $bcs"
	set updated ""
	foreach bc $bcs {
	    if ![info exists grot([lindex $bc 1])] {
		lappend updated $bc
	    }
	}
	foreach bc $newbcs {
	    lappend updated $bc
	}
	ic_boco_set $f $updated
	set modified($f) 1
    }
    return [array names modified]
}

## Same as above but does it for all families in a part.

proc ic_boco_replace_in_part {newbcs part {replace_these ""}} {
    if {$replace_these != ""} {
	foreach r $replace_these { set grot($r) 1 }
    } else {
	foreach m $newbcs { set grot([lindex $m 1]) 1 }
    }
    foreach f [ic_geo_list_families_in_part $part] {
	set bcs [ic_boco_get $f]
	#puts "f $f, bcs $bcs"
	set updated ""
	foreach bc $bcs {
	    if ![info exists grot([lindex $bc 1])] {
		lappend updated $bc
	    }
	}
	foreach bc $newbcs {
	    lappend updated $bc
	}
	ic_boco_set $f $updated
	set modified($f) 1
    }
    return [array names modified]
}

## Same as above but does it for all families in a group.

proc ic_boco_replace_in_group {newbcs group {replace_these ""}} {
    if {$replace_these != ""} {
	foreach r $replace_these { set grot($r) 1 }
    } else {
	foreach m $newbcs { set grot([lindex $m 1]) 1 }
    }
    foreach f [ic_geo_list_families_with_group $group] {
       set updated ""
       foreach g [split $f :] {
	set bcs [ic_boco_get $g]
	foreach bc $bcs {
	    if ![info exists grot([lindex $bc 1])] {
		lappend updated $bc
	    }
	}
	if {$g != $group} {
	   foreach bc $bcs {
	      if [info exists grot([lindex $bc 1])] {
	         lappend updated $bc
	     }
	   }
	} else {
	   foreach bc $newbcs {
	       lappend updated $bc
	   }
	}
       }
       ic_boco_set $f $updated
       set modified($f) 1
    }
    return [array names modified]
}

## Add some bcs to a family.

proc ic_boco_add {fam bocos} {
    set obcs [ic_boco_get $fam]
    ic_boco_set $fam [concat $obcs $bocos]
}

## Replace some bc's for a family

proc ic_boco_replace {fam bcarg newbcs} {
    global famboco_obj
    
    set rbcs $newbcs
    foreach bc [$famboco_obj get_fam_bocos $fam]  {
	if {[lindex $bc 1] != $bcarg} { lappend rbcs $bc }
    }
    $famboco_obj set_fam_bocos $fam $rbcs
}

## Return the current default family_boco file which was loaded (if any).

proc ic_boco_get_for_tetin {} {
    error "this function should not be called"
    global famboco_obj
    if {$famboco_obj == ""} { return "" }
    return [$famboco_obj get -filename]
}
set dont_record_command(ic_geo_get_famboco) 1

## Reload the family boco file which has already been loaded (if any).

proc ic_boco_reload {} {
    error "this function should not be called"
    global famboco_obj
    if {$famboco_obj != ""} {
	$famboco_obj clear ""
	$famboco_obj load_family_boco [$famboco_obj get -filename]
    }
}

## List the current families which have boundary conditions associated
## with them.  A pattern may be given that limits the families to those
## matching the pattern, as part of a :-separated list.
## Pat may also be !: which means no : characters

proc ic_boco_list_families {{pat ""}} {
    global famboco_obj projlib
    
    if {$famboco_obj == ""} {
	if {$projlib != ""} {
	    return [ic_geo_list_families]
	} else {
	    return [ic_uns_list_families]
	}
    }
    set fams [$famboco_obj list_families $pat]
    return $fams
    
    the old slow way
    puts "saw [llength $fams]"
    if {$pat != ""} {
	set fams2 ""
	set ngot 0
	set nseen 0
	puts "looking for $pat"
	foreach f $fams {
	    if [string match *:$pat:* ___:$f:___] {
		lappend fams2 $f
		incr ngot
		if {!($ngot % 1000)} { puts "got $ngot" }
	    }
	    incr nseen
	    if {!($nseen % 1000)} { puts "saw $nseen" }
	}
	set fams $fams2
    }
    return $fams
}
set dont_record_command(ic_boco_list_families) 1

## Change the <b>family_boco</b> file that is associated with the tetin file.

proc ic_boco_list_families_only {} {
    global famboco_obj
    
    if {$famboco_obj == ""} {
	return ""
    }
    set ret ""
    set fams [$famboco_obj list_families ""]
    foreach f $fams {
       if {[ic_boco_get $f] != ""} {lappend ret $f}
    }
    return $ret
}
set dont_record_command(ic_boco_list_families_only) 1

proc ic_boco_set_for_tetin {newboco} {
    error "this function should not be called"
    # It is not clear why this is useful
    global projlib
    $projlib set_famboco_file $newboco
#    $projlib configure -boco_modified 0
}

## What is the dimension of this family?  Warning: see the next function.
## If there is more than one type of element in the family then the return value
## will be "mixed" unless the "all_dim" parameter is set to 1

proc ic_boco_fam_dim {fam {all_dim 0}} {
    global projlib uns_cur uns_families_have_dimension
    
    if {$projlib != ""} {
	set pid [$projlib lookup_family_pid $fam]
	if {$pid != ""} {
	    foreach {type dim} {material 3 surface 2 curve 1 point 0} {
		if {[$projlib get_objects_in_family $type $pid] != ""} {
		    set found($dim) 1
		}
	    }
	}
    }
    
    if {$uns_cur != ""} {
	if ![info exists uns_families_have_dimension] {
	    foreach {f d} [$uns_cur list_families_and_dimensions_and_sides] {
		set uns_families_have_dimension($f) $d
	    }
	}
	if [info exists uns_families_have_dimension($fam)] {
	    foreach dim $uns_families_have_dimension($fam) {
		set found($dim) 1
	    }
	}
    }
    if {$all_dim || ([array size found] == 1)} {
	return [array names found]
    } else {
	set two 0; set other 0
	foreach d [array names found] {
	    if [string match 2* $d] {
		incr two
	    } else {
		incr other
	    }
	}
	if {$two && !$other} {
	    return 2
	} else {
	    return mixed
	}
    }
}
set dont_record_command(ic_boco_fam_dim) 1

## What are the dimensions of this part?

proc ic_boco_parts_dim {parts} {
    foreach part $parts {
	foreach f [ic_geo_list_families_in_part $part] {
	    foreach d [ic_boco_fam_dim $f 1] {
		set got($d) 1
	    }
	}
    }
    return [array names got]
}
set dont_record_command(ic_boco_part_dim) 1

## Warning: hack ...  If you call ic_boco_fam_dim, then you change something,
## and then you call it again, you will get the old data.  This is because
## the list_families_and_dimensions_and_sides function is too slow so the
## results have to be cached.  As a temporary workaround, call 
## ic_boco_reset_fam_dim to clear out the old data.

proc ic_boco_reset_fam_dim {} {
    global uns_families_have_dimension
    catch {unset uns_families_have_dimension}
}
    
## Delete all the BC icons that currently exist.

proc ic_boco_clear_icons {} {
    global famboco_obj
    if {$famboco_obj != ""} {
	$famboco_obj clear_bc_icons
    }
}

## Rename a family - this has to move all the icons

proc ic_boco_rename_family {old new} {
    global famboco_obj
    if {$famboco_obj != ""} {
	$famboco_obj change_family_bc_icons $old $new
    }
}

## Create a new BC icon for a given family.  The arguments are:
## <ul>
## <li> <b>type</b> : what kind of b.c. this is, such as "temperature",
## "pressure", etc.  If another icon of the same type and family is already
## there it will be deleted.  In general this could be the bctype of the bc
## that this icon represents.
## <li> <b>fam</b> : the family name.
## <li> <b>icontype</b> : the shape of the icon to draw.
## These are listed below.
## <li> <b>locations</b> : the XYZ locations of where the icons should be
## drawn.  If this list is empty then they will be automatically computed.
## This list can also be a geometry type, "geometry" or "unsmap".
## That restricts the automatic computation to that object.
## <li> <b>scale</b> : a size scale for the icons.  Default is 1.0
## <li> <b>maxnum</b> : the maximum number of icons to draw.  If there are
## more nodes or elements than this value then just one icon will be drawn
## at the centroid of the family.  0 means no limits.
## <li> <b>color</b> : the color to use for the icon.
## <li> <b>params</b> : depends on the <b>icontype</b> value as described
## below.
## </ul>
## The different icon shape types are as follows.
## <ul>
## <li> <b>arrow</b> : draw an arrow.  The params are:
## <ul>
## <li> <b>direction</b> : an XYZ triple that gives the arrow's direction
## <li> <b>nheads_end</b> : how many heads to draw at the end.  If this value
## is 0 then no head will be drawn.  If it's -1 then a X will be drawn.
## <li> <b>open_end</b> : should the heads be open, if arrows are drawn, and
## if nheads is -1 then this is the linewidth to use when drawing the X
## <li> <b>nheads_start</b> (optional) : how many heads to draw at the start
## <li> <b>open_start</b> (optional) : should the heads be open at the start.
## </ul>
## <li> <b>circle</b> : draw a circle.  The params are:
## <ul>
## <li> <b>pixels</b> : the size of the circle
## <li> <b>open</b> : if 0 the circle is filled, if 1 it's wireframe
## </ul>
## <li> <b>triangle</b> : draw a triangle (like a constraint).  The params are:
## <ul>
## <li> <b>pixels</b> : the size of the triangle
## <li> <b>open</b> : if 0 the triangle is filled, if 1 it's wireframe
## <li> <b>tail</b> : draw a tail on it
## </ul>
## <li> <b>helix</b> : draw a spring-like helix.  The params are:
## <ul>
## <li> <b>ntwists</b> : the number of twists
## <li> <b>radius</b> : the radius as a factor of the length
## </ul>
## </ul>

proc ic_boco_add_icon {type fam icontype locations scale maxnum color label params vis only_dims} {
    global famboco_obj uns_cur projlib batch_mode

    if $batch_mode return
    
    if {$famboco_obj == ""} { ic_empty_boco }
    
#    puts [list ic_boco_add_icon $type $fam $icontype $locations $scale $maxnum $color $label $params $vis $only_dims]
    
    set only_some 0
    foreach t {unsmap point curve surface material body geometry geometry_or_mesh} {
	    if ![string compare $t $locations] {
	        set only_$t 1
	        set only_some 1
	        set locations ""
	    } else {
	        set only_$t 0
	    }
    }
    if $only_geometry_or_mesh {
	    if [ic_geo_count_in_family all $fam] {
	        set only_geometry 1
	    } else {
	        set only_unsmap 1
	    }
    }
    if $only_geometry {
	    foreach t {point curve surface material body} { set only_$t 1 }
    }
    set on_vertices 0
    if {$locations == ""} {
	    if {($uns_cur != "") && (!$only_some || $only_unsmap) && ([$uns_cur lookup_family_pid $fam] != "")} {
	        set tmap [uns_map -mesh $uns_cur -list_type 2 -name tmp_boco_map]
	        set types ""
	        if {($only_dims == "") || [lmember $only_dims 0]} {
		        lappend types NODE
	        }
	        if {($only_dims == "") || [lmember $only_dims 1]} {
		        lappend types BAR_2 LINE_2 LINE_3 BAR_3 
	        }
	        if {($only_dims == "") || [lmember $only_dims 2]} {
		        lappend types TRI_3 QUAD_4 TRI_6 QUAD_8 QUAD_9
	        }
	        if {($only_dims == "") || [lmember $only_dims 3]} {
		        # this will not happen
	        }
	        $tmap add_family_type $fam $types 0
	        if [string match element_prop* $type] {
		        if {($icontype == "helix") || ($icontype == "orientation")} { #FIXME
		            set locations [$tmap get_element_vertex_positions $maxnum 1]
		            set on_vertices 1
		        } else {
		            set locations [$tmap get_element_centroid_positions $maxnum]
		        }
	        } else {
		        set dups 0
		        set locations [$tmap get_element_vertex_positions $maxnum $dups]
		            set on_vertices 1
	            }
	            $tmap delete
	    } elseif {($projlib != "") && (!$only_some || !$only_unsmap)} {
	        set pid [$projlib lookup_family_pid $fam]
	        set types ""
	        if {($only_dims == "") || [lmember $only_dims 0]} {
		        lappend types point
	        }
	        if {($only_dims == "") || [lmember $only_dims 1]} {
		        lappend types curve
	        }
	        if {($only_dims == "") || [lmember $only_dims 2]} {
		        lappend types surface
	        }
	        foreach gtype $types {
		        foreach gnum [$projlib get_objects_in_family $gtype $pid] {
		            if {!$only_some || [set only_$gtype]} {
			            if {($icontype == "helix") || ($icontype == "orientation")} { # FIXME
			                if {$gtype != "curve"} { error "can't put a helix on a non-curve" }
			                if ![catch {gk_get_crv_pos 0 $gnum} loc] {
			                    eval lappend locations $loc
			                }
			                if ![catch {gk_get_crv_pos 1 $gnum} loc] {
			                    eval lappend locations $loc
			                }
			                # eval lappend locations [$projlib curve_end_points $gnum]
			            } else {
			                lappend locations [lindex [$projlib text_point $gtype $gnum] 0]
			            }
		            }
		        }
	        }
	    } else {
	        # not a problem since these can often be property groups with no
	        # stuff in them
	        #mess "Warning: can't draw BC icon for $fam - no data\n"
#	        return
	    }
    }
    if {$locations == ""} {
	    puts "no locations"
	    return
    }
    puts "[llength $locations] locations on_v $on_vertices"
    # temporarily broken
    # set locations unsmesh
    # puts [list $icontype locations $locations]
    if {$color == "family_color"} {
	    set color [family_color_for_name $fam]
    }
    set csystem [ic_csystem_get_current]
    if {$vis == ""} { set vis 1 }
    eval $famboco_obj add_bc_icon $icontype \"$fam\" \"$type\" [list $locations] $on_vertices $scale $color $csystem [list " $label"] $vis $vis $params
}

# ------------------------------------------------------------------------------

## Smarter wrapper around the below fns

proc ic_boco_apply_attr_symbol {group bctype setname icontype parsed params labels vis lvis} {
#    puts [list ic_boco_apply_attr_symbol $group $bctype $setname $icontype $parsed $params $labels $vis $lvis]
    array set par $parsed
    set setval ""
    if {$setname == ""} { set setval $par($setname) }
    
    # Find the old symbols and delete them
    foreach id [ic_boco_list_attr_symbols $bctype $group $setval] {
	ic_boco_delete_attr_symbol $id
    }
    set icontype1 [extract_option $icontype par]
    set params1 [extract_option $params par]
    set labels1 [extract_option $labels par]
    foreach {p n} $parsed { lappend params1 [list $p $n] }

    return [ic_boco_add_attr_symbol $bctype $group $icontype1 $setval \
		$params1 $labels1 $vis $lvis]
}

proc extract_option {vals pvar} {
    if {[lindex $vals 0] != "?"} { return $vals }
    upvar 1 $pvar var
    foreach {pred val} [lrange $vals 1 end] {
	if {$pred == "default"} { return $val }
	set ok 1
	foreach pred1 $pred {
	    set v1 [lindex $pred1 0]
	    if {[lsearch [lrange $pred1 1 end] $var($v1)] < 0} { set ok 0; break }
	}
	if $ok { return $val }
    }
    error "no match found in predicate list"
}

## Easier way to set visibility by group and bctype

proc ic_boco_set_attr_symbol_group_visible {group bctype vis lvis} {
    foreach id [ic_boco_list_attr_symbols $bctype $group ""] {
        ic_boco_modify_attr_symbol_visible $id $vis $lvis
    }
}

## The new version which can pull out distributed values from nodes and elements
## type is the bctype this corresponds to
## group is the group or part this attaches to
## icontype is arrow, helix, etc
## set controls how new symbols replace old ones
## params is a list of {name value expr} tuples
##      expr is a list of bcfield names that get multiplied together
## label is a list of text, paramname, text, paramname .. that gets concatenated
## vis is a flag saying whether it should be initially visible or not
## lvis is 1 if the label should be drawn once, 0 not, 2 on all points
## Return value is a symbol id that can be used in the functions below.

proc ic_boco_add_attr_symbol {bctype group icontype set params label vis lvis} {
    global famboco_obj uns_cur projlib batch_mode

    if $batch_mode return
    
    if {$famboco_obj == ""} { ic_empty_boco }
    
    if {$vis == ""} { set vis 1 }
    
    $famboco_obj configure -projlib $projlib -unsmesh $uns_cur
    return [$famboco_obj add_attr_symbol $bctype $group $icontype $set $params $label $vis $lvis]
}

## Delete a symbol

proc ic_boco_delete_attr_symbol {id} {
    global famboco_obj

    if {$famboco_obj != ""} {
	$famboco_obj delete_attr_symbol $id
    }
}

## List symbols

proc ic_boco_list_attr_symbols {{bctype ""} {group ""} {set ""}} {
    global famboco_obj

    if {$famboco_obj != ""} {
	return [$famboco_obj list_attr_symbols $bctype $group $set]
    }
}
set dont_record_command(ic_boco_list_attr_symbols) 1

## Modify params

proc ic_boco_modify_attr_symbol_params {id params} {
    global famboco_obj

    puts [list ic_boco_modify_attr_symbol_params $id $params]
    
    if {$famboco_obj != ""} {
	$famboco_obj modify_attr_symbol_params $id $params
    }
}

## Modify label

proc ic_boco_modify_attr_symbol_label {id label} {
    global famboco_obj

    puts [list ic_boco_modify_attr_symbol_label $id $label]
    
    if {$famboco_obj != ""} {
	$famboco_obj modify_attr_symbol_label $id $label
    }
}

## Modify vis

proc ic_boco_modify_attr_symbol_visible {id vis lvis} {
    global famboco_obj

    if {$famboco_obj != ""} {
        $famboco_obj modify_attr_symbol_visible $id $vis $lvis
    }
}

## Get visibility flags - icon and label

proc ic_boco_get_attr_symbol_visible {id} {
    global famboco_obj

    if {$famboco_obj != ""} {
	return [$famboco_obj get_attr_symbol_visible $id]
    } else {
	return ""
    }
}
set dont_record_command(ic_boco_get_attr_symbol_visible) 1

# ------------------------------------------------------------------------------

## Create a trivial (no boundary conditions) family_boco file with
## filename <e>bocofilename</e> for the loaded unstruct mesh. This
## function returns the filename created.

proc ic_boco_create_trivial {{bocofilename "./family_boco_trivial"}} {
    
    if ![ic_uns_is_loaded] {
	mess "cannot create trival family_boco: no mesh exists\n" red
	return ""
    }
    
    set maxpid 0
    foreach ff [ic_uns_list_families pids] {
        set name [lindex $ff 0]
        set pid [lindex $ff 1]
        set name_for($pid) $name
        if {$pid > $maxpid} { set maxpid $pid }
    }
    
    set fp [open $bocofilename w]
    for {set pid 1} {$pid <= $maxpid} {incr pid} {
        if [info exists name_for($pid)] {
	    set name $name_for($pid)
        } else {
	    set name FAM$pid
        }
        puts $fp "\"$name\""
    }

    close $fp
    return $bocofilename
}

# -----------------------------------------------------------------------
# This is the new stuff for use with AI*Env

## Make sure a part name exists

proc ic_boco_ensure_part_exists {nfam} {
    global projlib uns_cur
    
    ic_geo_new_family $nfam
    set p [$projlib lookup_family_pid $nfam]
    # modified $p to "-1" since it was overwriting other families --jka
    if {$uns_cur != ""} { $uns_cur create_family $nfam -1 }
    ic_boco_set_part_color $nfam
}

## Apply a list of BCs to either a set of geometrical entities or a
## mesh subset.  This does all the fancy group stuff.
## Objects is a list of type, names, ...  The types can be surface, curve,
## point, material, subset, or unsmap.
## Families are composed of "groups" separated by commas.  Each group is
## also a family whether it's used or not.  Groups are named G0, G1, etc,
## where the prefix G could be anything such as CN for constraints.
## This function returns a list of the new family names that have been created.
## WARNING: if you pass a unsmap in as one of the objects parameters, this 
## function will delete it ...
## The nfam argument is the name of a group to use, instead of checking to see
## if a new one should be created

proc ic_boco_set_for_objects {objects bocos pre nfam} {
    global projlib famboco_obj uns_cur
    
    set verbose 0
    
    if $verbose { puts [list ic_boco_set_for_objects $objects $bocos $pre $nfam] }
    
    set unsmaps_given ""
    
    if {$famboco_obj == ""} ic_empty_boco
    
    create_empty_tetin_if_needed
    
    # If the family is given just use that (making sure it exists)
    set make_first 0
    if {$nfam != ""} {
	ic_boco_ensure_part_exists $nfam
	$famboco_obj set_fam_bocos $nfam $bocos
	set make_first 1
    } else {
	# First see if we have a family with exactly these bc's already
	set nfam [$famboco_obj search_fam_bocos $bocos 1]
	if $verbose { puts "$famboco_obj search_fam_bocos $bocos 1 -> $nfam" }
	if {$nfam == ""} {
	    for {set i 0} 1 {incr i} {
		set nfam $pre$i
		if {($projlib != "") && ([$projlib lookup_family_pid $nfam] != "")} continue
		if {($uns_cur != "") && ([$uns_cur lookup_family_pid $nfam] != "")} continue
		break
	    }
	    ic_boco_ensure_part_exists $nfam
	    $famboco_obj set_fam_bocos $nfam $bocos
	    if $verbose { puts "Created new group $nfam with bc's $bocos" }
	} else {
	    if $verbose { puts "Reused existing group $nfam" }
	}
    }
    
    if {$objects == ""} { return $nfam }
    # Figure out the families we're dealing with here
    foreach {type1 objs} $objects {
	foreach obj $objs {
	    set type $type1
	    if {$type == "surf"} { set type surface } ;# sloppy...
	    # This is a bit of a hack since even though we say we have
	    # curves, for example, we might have a unsmap in the list which has
	    # 1-d elements in it.  At this point all we can do is recognize the
	    # object name.
	    if {[string match uns_s* $obj] && ([$uns_cur get_map $obj] != "")} {
		set type unsmap
		if $verbose { puts "$obj has [[$uns_cur get_map $obj] count]" }
	    }
	    switch $type {
		surface - curve - point - material - body {
		    set num [$projlib get_number_by_name $type $obj]
		    set fam [$projlib get_family $type $num]
		    # Exclude the families that already have this group
		    if [string match *:$nfam:* :$fam:] continue
		    lappend found($fam) $type $num $obj
		}
		unsmap {
		    # Here we need to make a new unsmap for each family that we
		    # find.
		    set map [$uns_cur get_map $obj]
		    if $verbose { puts "count [$map count], fams [$map list_families]" }
		    
		    # If there are only nodes here and not NODE elements we
		    # need to fix that.
		    if {[lindex [$map count] 1] > 0} {
			ic_uns_create_element NODE ORFN $obj 0 $obj 1
		    }
		    
		    foreach fam [$map list_families] {
			if {[string match *:$nfam:* :$fam:] && [ic_boco_get $fam] != ""} continue
			if ![info exists family_maps($fam)] {
			    set fmap [uns_map -mesh $uns_cur]
			    set family_maps($fam) $fmap
			    lappend found($fam) unsmap $fmap $obj
			}
			if ![$family_maps($fam) add_with_family $fam $map] {
			    error "no elements found in $map with fam $fam"
			}
		    }
		    lappend unsmaps_given $map
		}
		subset {
		    # Grab all the things that are in the subset.
		    if 0 {
			foreach fam [ic_boco_list_families $obj] {
			    if [string match *:$nfam:* :$fam:] continue
			    foreach {t1 n1} [ic_geo_get_objects all $fam] {
				set num [$projlib get_number_by_name $t1 $n1]
				lappend found($fam) $t1 $num $n1
			    }
			    set mapn [ic_uns_subset_create_family_type $fam __all__]
			    if {$mapn != ""} {
				set map [$uns_cur get_map $mapn]
				set family_maps($fam) $map
				$map promote_type 2
				lappend found($fam) unsmap $map $obj
			    }
			}
		    }
		    foreach {gtype gname} [ic_geo_subset_get_items $obj] {
			set fam [ic_geo_get_family $gtype $gname]
			set gnum [$projlib get_number $gtype $gname]
			lappend found($fam) $gtype $gnum $gname
		    }
		    if [ic_uns_subset_exists $obj] {
			set smap [$uns_cur get_map $obj]
			
			# This might be a nodes-only subset.  If so create the node
			# elements now.
			if [lindex [$smap count] 1] {
			    $uns_cur create_family NODES -1
			    $smap create_element NODE NODES $smap 0 1 0 0
			    aie_new_families_or_elem_types
			}
			foreach fam [ic_uns_subset_families_used $obj 0] {
			    set map [uns_map -mesh $uns_cur]
			    lappend unsmaps_given $map
			    lappend found($fam) unsmap $map $obj
			    $map copy_only_fam $smap $fam
			}
		    }
		}
		part {
		    foreach {gtype gname} [ic_geo_objects_in_parts "" $obj] {
			set fam [ic_geo_get_family $gtype $gname]
			set gnum [$projlib get_number $gtype $gname]
			lappend found($fam) $gtype $gnum $gname
		    }
		    foreach f [ic_geo_list_families_in_part $obj] {
			set mapn [ic_uns_subset_create_family_type $f __all__]
			if {$mapn != ""} {
			    set map [$uns_cur get_map $mapn]
			    lappend unsmaps_given $map
			    lappend found($f) unsmap $map $obj
			}
		    }
		}
		default {
		    error "unknown type $type"
		}
	    }
	}
    }
    if {$verbose && [array exists found]} { parray found }
    set pa [tdv_active 0]
    set oldfams [array names found]
    set newfams ""
    foreach oldfam $oldfams {
	set ogroups [split $oldfam :]
	set partname [lindex $ogroups 0]
	set ogroups [lrange $ogroups 1 end]
	if ![string match *:$nfam:* :$oldfam:] {
             set ngroups [concat $ogroups [list $nfam]]
         } else {
             set ngroups $ogroups
         }
	if !$make_first { set ngroups [lsort $ngroups] }
	set ngroups [concat [list $partname] $ngroups]
	set ngroups [ldelete $ngroups ORFN]
	set newfam [join $ngroups :]
	set vis [get_fam_visible $oldfam]
	if {[$projlib lookup_family_pid $newfam] == "" || [ic_geo_family_is_empty $newfam]} {
	    ic_geo_new_family $newfam
	    gui_change_visibility family $newfam $vis
	    set p [$projlib lookup_family_pid $newfam]
	    if {$uns_cur != ""} { $uns_cur create_family $newfam -1 }
	    set bocos ""
	    foreach g $ngroups {
		set bocos [concat $bocos [$famboco_obj get_fam_bocos $g]]
	    }
	    $famboco_obj set_fam_bocos $newfam $bocos
	    ic_boco_set_part_color $newfam
	    if $verbose { puts "Created new family $newfam with bc's $bocos" }
	    
	    lappend newfams $newfam
	    
	} else {
	    if $verbose { puts "Reused existing family $newfam" }
	    
	    lappend newfams $newfam
	}
	
	# Now put all the things from that family into the new one.
	set ng 0; set ne 0
	
	foreach {type num objname} $found($oldfam) {
	    switch $type {
		material - body {
		    ic_geo_set_family $type $newfam numbers $num 0
		    incr ng
		}
		surface - curve - point {
		    ic_geo_set_family $type $newfam numbers $num 0
		    incr ng
		    
		    # Also change the elements beneath this object.
		    if {$uns_cur != ""} {
			if $verbose { puts "ic_uns_change_family_if_project all $oldfam $newfam $type $objname" }
			incr ne [ic_uns_change_family_if_project all \
				     $oldfam $newfam $type $objname]
		    }
		}
		unsmap {
		    # Create uns families if it doesn't exist already.
		    ic_uns_new_family $newfam
		    $num set_family $newfam
		    incr ne [lindex [$num count] 0]
		}
	    }
	}
	if $ng {
	    if $verbose { puts "Moved $ng geometry object[expr {($ng == 1) ? "" : "s"}] to $newfam" }
	}
	if $ne {
	    if $verbose { puts "Moved $ne mesh element[expr {($ng == 1) ? "" : "s"}] to $newfam" }
	}
	
	
	# Is this a non-atomic family that is now empty?  If so delete it
	# .....
    }
    
    if [info exists family_maps] {
	foreach m [array names family_maps] {
	    $family_maps($m) delete
	}
	update_uns_visible
	ic_uns_families_changed
    }
    foreach uns $unsmaps_given { catch {$uns delete} }
    tdv_active $pa
    return $newfams
}

## Apply a non-solver specific icon to a particular family.

proc ic_boco_apply_generic_icon {fam obtype ctype params vis {only_dims ""}} {
    if {[lindex $params 0] == "+"} {
	    # this means the parameters are given by name
    }
    switch $ctype {
	    constraint {
	        ic_boco_add_icon constraint $fam triangle $obtype 1.0 0 yellow "" "10 1 0" $vis $only_dims
	    }
	    impdisp {
	        ic_boco_add_icon impdisp $fam triangle $obtype 1.0 0 yellow "" "10 1 1" $vis $only_dims
	    }
	    force {
	        if {[lindex $params 0] || [lindex $params 1] || [lindex $params 2]} {
		        ic_boco_add_icon force $fam arrow $obtype 1.0 0 cyan "" [concat [lrange $params 0 2] [lindex $params 6] [lindex $params 7] 1 1 0 0] $vis $only_dims
	        }
	        if {[lindex $params 3] || [lindex $params 4] || [lindex $params 5]} {
		        ic_boco_add_icon force $fam arc_arrow $obtype 1.0 0 orange "" [concat [lrange $params 3 5] [lindex $params 6] [lindex $params 7] 1 0 0 0] $vis $only_dims
	        }
	    }
	    directional_pressure {
	        ic_boco_add_icon pressure $fam arrow $obtype 1.0 0 green "" [concat [lrange $params 0 4] 1 1 0 0] $vis $only_dims
	    }
	    pressure {
	        ic_boco_add_icon pressure $fam circle $obtype 1.0 0 green "" "5 1" $vis $only_dims
	    }
	    temperature {
	        ic_boco_add_icon temperature $fam circle $obtype 1.0 0 red "" "5 0" $vis $only_dims
	    }
	    rigid_bar {
	        ic_boco_add_icon element_prop_1d $fam circle $obtype 1.0 0 red "" "3 1" $vis $only_dims
	    }
	    spring {
	        ic_boco_add_icon element_prop_1d $fam helix $obtype 1.0 0 yellow "" "5 .1" $vis $only_dims
	    }
	    damper {
	        ic_boco_add_icon element_prop_1d $fam circle $obtype 1.0 0 red "" "3 0" $vis $only_dims
	    }
	    velocity {
	        ic_boco_add_icon velocity $fam arrow $obtype 1.0 0 yellow "" [concat [lrange $params 0 2] 0 30 1 1 0 0] $vis $only_dims
	    }
	    orientation {
	        ic_boco_add_icon element_prop_1d $fam orientation $obtype 1.0 0 white "" $params $vis $only_dims
	    }
	    mpcdisp {
	        set color "yellow"
	        if {[lindex $params 0] == 0} {set color "red"}             
	        if {[lindex $params 0] != "-1"} {
	            ic_boco_add_icon mpcdisp $fam circle $obtype 1.0 0 $color "" "5 1" $vis $only_dims
	        }
	    }
	    singlecontact {
	        ic_boco_add_icon singlecontact $fam triangle $obtype 1.0 0 red "" "10 0 0" $vis $only_dims
	    }
	    rigidwall {
	        ic_boco_add_icon rigidwall $fam circle $obtype 1.0 0 cyan "" "5 0" $vis $only_dims
	    }
    }
}

## Apply a solver specific icon to a particular family.

proc ic_boco_apply_solver_icon {fam bctype icontype iconparams vis {only_dims ""} {bcargs ""}} {
    #puts [list ic_boco_apply_solver_icon $fam $bctype $icontype $iconparams $vis $only_dims $bcargs]
    set locs geometry_or_mesh
    set color white
    set scale 1.0
    set maxnum 0
    set label $bctype
    set lcs 0
    
    array set pvals {
	triangle {pixels open tail}
	arrow {n1 n2 n3 lcs npixels nheads open nheads2 open2}
	circle {pixels open}
	helix {twists radius}
    }
    
    foreach {n v} $iconparams {
	set $n [boco_arg_subst $v $iconparams $bcargs]
    }
    set params ""
    foreach n $pvals($icontype) { lappend params [set $n] }
    ic_boco_add_icon $bctype $fam $icontype $locs $scale $maxnum $color $label \
	$params $vis $only_dims
}

# Do this in a subroutine to avoid namespace pollution
# Also this deals with the ? stuff

proc boco_arg_subst {__val __iconparams __bcargs} {
    foreach {__n __v} $__iconparams { set $__n $__v }
    foreach {__n __v} $__bcargs { set $__n $__v }
    if {[string index $__val 0] == "?"} {
	foreach {__p __v} [lrange $__val 1 end] {
	    set __ok 1
	    puts "p is $__p"
	    foreach __p1 $__p {
		set __ok1 0
		set __have [set [lindex $__p1 0]]
		puts "is $__have in [lrange $__p1 1 end]"
		foreach __want [lrange $__p1 1 end] {
		    if {$__have == $__want} { set __ok1 1; break }
		}
		if !$__ok1 { set __ok 0; break }
	    }
	    if $__ok {
		set __val1 $__v
		break
	    }
	}
	set __val $__val1
    }
    
    return [subst $__val]
}

## Apply a non-solver specific icon to a particular family.

proc ic_boco_apply_generic_icon_to_group {group ctype params vis {only_dims ""}} {
    set params_old $params
    foreach fam [ic_boco_list_families] {
	if [string match *:$group:* :$fam:] {
	    if {$ctype == "mpcdisp" && [lindex $params_old 0] == "-1"} {
	       set param_name "Nastran:MPC:DEP"
	       set unsmap  [ic_uns_subset_create]
	       ic_uns_subset_make_families $unsmap $fam
	       set vals [ic_uns_bcfield_get $param_name $unsmap 0]
                 if {[llength $vals] > 0} {
                    if {[lsearch $vals "1"] >= 0} {
                       set params [list 0]
                    } else {
                       set params [list 1]
                    }
                } else {
                   set params [list -1]
                }
	       ic_uns_subset_delete $unsmap
	    }
	    ic_boco_apply_generic_icon $fam geometry_or_mesh $ctype $params $vis $only_dims
	}
    }
}

## Apply a solver specific icon to a particular group

proc ic_boco_apply_solver_icon_to_group {group bctype icontype iconparams vis {bcargs ""}} {
    foreach fam [ic_boco_list_families] {
	if [string match *:$group:* :$fam:] {
	    ic_boco_apply_solver_icon $fam $bctype $icontype $iconparams $vis "" $bcargs
	}
    }
}

## Apply a solver specific icon to a particular part

proc ic_boco_apply_solver_icon_to_part {part bctype icontype iconparams vis {bcargs ""}} {
    foreach fam [ic_geo_list_families_in_part $part] {
	ic_boco_apply_solver_icon $fam $bctype $icontype $iconparams $vis "" $bcargs
    }
}

## Set the icon visibility.  bcargs are something like FORCE_NAME F1

proc unused__ic_boco_set_visible {bcargs bctypes on} {
    global famboco_obj
    
    if {$famboco_obj == ""} return
    
    foreach match [ic_boco_list_with_bctype $bcargs] {
	foreach bctype $bctypes {
	    $famboco_obj set_fam_visible [lindex $match 0] $bctype $on
	}
    }
}

## Remove the icons for a particular family/bctype combo

proc ic_boco_delete_icons_for_bctypes {fam bctypes} {
    global famboco_obj
    
    if {$famboco_obj == ""} return
    foreach bc $bctypes { $famboco_obj delete_bc_icon $fam $bc }
}

## Set the icon visibility by family.  If fams is empty then this means all families

proc ic_boco_set_family_visible {fams bctypes on} {
    global famboco_obj

    if {$famboco_obj == ""} return
    if {$fams == ""} { set fams [list ""] }
    foreach fam $fams {
	foreach bctype $bctypes {
	    if {$bctype == "mpcdisp"} {
                 if {$on} {
	          set unsmap  [ic_uns_subset_create]
                   if {$unsmap != ""} {
	             ic_uns_subset_make_families $unsmap $fam
	             ic_uns_subset_visible $unsmap $on
                   }
                } else {
	          set subs [ic_uns_subset_list_visible]
	          set pos [lsearch $subs "Selected"]
	          if { $pos >= 0} {
	             set subs [lreplace $subs $pos $pos]
	          }
	          foreach sub $subs {
	             set subfams [ic_uns_subset_list_families $sub]
	             if {[lsearch $subfams $fam] >= 0} {ic_uns_subset_visible $sub 0}
	          }
                }
	    }
	    $famboco_obj set_fam_visible $fam $bctype $on
	}
    }
}

## Remove the given objects from all groups that have a bc with a specified
## name

proc ic_boco_remove_objects_from_groups {objlist bcargs} {
    global projlib famboco_obj uns_cur
    
    if {$famboco_obj == ""} return
    if {$projlib == ""} { ic_empty_tetin }
    
    set rem_fams ORFN ;# we never want to stay in ORFN if we can avoid it
    foreach f [ic_boco_list_with_bctype $bcargs *:*] {
	lappend rem_fams [lindex $f 0]
    }
    #puts "REM $rem_fams"
    set ng 0; set ne 0
    foreach {type1 objs} $objlist {
	#puts "TYPE IS $type1 OBJS are $objs"
	
	foreach obj $objs {
	    set type $type1

	    # This is a bit of a hack since even though we say we have
	    # curves, for example, we might have a unsmap in the list which has
	    # 1-d elements in it.  At this point all we can do is recognize the
	    # object name.
	    if {[string match uns_s* $obj] && ([$uns_cur get_map $obj] != "")} {
		set type unsmap
	    }
	
	    switch $type {
		surface - curve - point - material - body {
		    set num [$projlib get_number_by_name $type $obj]
		    set fam [$projlib get_family $type $num]
		    set nfam ""
		    foreach g [split $fam :] {
			#if ![string match $prefix* $g] {
			#    lappend nfam $g
			#}
			if ![lmember $rem_fams $g] {
			    lappend nfam $g
			}
		    }
		    puts "num $num fam $fam"
		    set newfam [join $nfam :]
		    if {$newfam == ""} { set newfam ORFN }
		    if {$newfam != $fam} {
			lappend found($newfam) $type $num $obj $fam
		    }
		}
		
		unsmap {
		    # Here we need to make a new unsmap for each family that we
		    # find.
		    set map [$uns_cur get_map $obj]
		    foreach fam [$map list_families] {
			set nfam ""
			foreach g [split $fam :] {
			#    if ![string match $prefix* $g] {
			#	lappend nfam $g
			#    }
			    if ![lmember $rem_fams $g] {
				lappend nfam $g
			    }
			}
			set newfam [join $nfam :]
			if {$newfam == ""} { set newfam ORFN }
			if {$newfam != $fam} {
			    if ![info exists family_maps($newfam)] {
				set fmap [uns_map -mesh $uns_cur]
				set family_maps($newfam) $fmap
				lappend found($newfam) unsmap $fmap $obj $fam
			    }
			    if ![$family_maps($newfam) add_with_family $fam $map] {
				error "no elements found in $map with fam $fam"
			    }
			}
		    }
		}
		subset {
		}
		default {
		    error "unknown type $type"
		}
	    }
	}
    }
    
    set newfams ""
    #puts "FOUND [array names found]"    
    foreach newfam [array names found] {
	if {[$projlib lookup_family_pid $newfam] == ""} {
	    ic_geo_new_family $newfam
	    set p [$projlib lookup_family_pid $newfam]
	    if {$uns_cur != ""} { $uns_cur create_family $newfam -1 }
	    set bocos ""
	    foreach g [split $newfam :] {
		set bocos [concat $bocos [$famboco_obj get_fam_bocos $g]]
	    }
	    $famboco_obj set_fam_bocos $newfam $bocos
	    ic_boco_set_part_color $newfam
	    #mess "Created new family $newfam with bc's $bocos\n"
	    
	    lappend newfams $newfam
	    
	} else {
	    #mess "Reused existing family $newfam\n"
	}
	
	# Just to be safe
	if {$uns_cur != ""} {
	    $uns_cur create_family $newfam -1
	}
	
	# Now put all the things from that family into the new one.
	set ng 0; set ne 0
	catch {unset olds}
	foreach {type num objname oldfam} $found($newfam) {
	    switch $type {
		material - body {
		    ic_geo_set_family $type $newfam numbers $num 0
		    incr ng
		}
		surface - curve - point {
		    ic_geo_set_family $type $newfam numbers $num 0
		    incr ng
		    
		    # Also change the elements beneath this object.
		    if {$uns_cur != ""} {
			incr ne [ic_uns_change_family_if_project all \
				     $oldfam $newfam $type $objname]
		    }
		}
		unsmap {
		    $num set_family $newfam
		    incr ne [lindex [$num count] 0]
		}
	    }
	    set olds($oldfam) 1
	}
	if $ng {
	    #mess "Removing from [array names olds] : moved $ng geometry object[expr {($ng == 1) ? "" : "s"}] to $newfam\n"
	}
	if $ne {
	    #mess "Removing from [array names olds] : moved $ne mesh element[expr {($ng == 1) ? "" : "s"}] to $newfam\n"
	}
	
	# Move the icons over
	# ********************************************
	
	# Is this a non-atomic family that is now empty?  If so delete it
	# .....
    }
    
    if [info exists family_maps] {
	foreach m [array names family_maps] {
	    $family_maps($m) delete
	}
	update_uns_visible
	ic_uns_families_changed
    }
    return $newfams
}

## Add the parts and subsets to the APPLY_TO_PARTS and APPLY_TO_SUBSETS pseudo-bc's on
## the named group

proc ic_boco_add_parts_and_subsets_to_group {name parts_or_subsets} {
    global famboco_obj
    puts [list ic_boco_add_parts_and_subsets_to_group $name $parts_or_subsets]
    set bcs [$famboco_obj get_fam_bocos $name]
    set bcs2 ""
    set app_parts [list 1 APPLY_TO_PARTS]
    set app_subs [list 1 APPLY_TO_SUBSETS]
    foreach b $bcs {
	switch [lindex $b 1] {
	    APPLY_TO_PARTS { set app_parts $b }
	    APPLY_TO_SUBSETS { set app_subs $b }
	    default { lappend bcs2 $b }
	}
    }
    foreach {t n} $parts_or_subsets {
	switch $t {
	    part { lappend app_parts $n }
	    subset { lappend app_subs $n }
	    default { error "bad type $t in list: $parts_or_subsets" }
	}
    }
    if {[llength $app_parts] > 2} { lappend bcs2 $app_parts }
    if {[llength $app_subs] > 2} { lappend bcs2 $app_subs }
    
    $famboco_obj set_fam_bocos $name $bcs2
}

## This routine is called when objects are moved into and out of parts and
## subsets.

proc ic_boco_what_groups_go_with_part_or_subset {type name} {
    global famboco_obj
    
    if {$famboco_obj == ""} { return "" }
    
    set app_groups ""
    foreach group [$famboco_obj list_families !:] {
	set bcs [$famboco_obj get_fam_bocos $group]
	foreach bc $bcs {
	    switch [lindex $bc 1] {
		APPLY_TO_PARTS {
		    if {($type == "part") && [lmember [lrange $bc 2 end] $name]} {
			lappend app_groups $group
		    }
		}
		APPLY_TO_SUBSETS {
		    if {($type == "subset") && [lmember [lrange $bc 2 end] $name]} {
			lappend app_groups $group
		    }
		}
	    }
	}
    }
    return $app_groups
}

## Utility function to move stuff from one part to another

proc ic_boco_change_part {oldfam newpart} {
    set oldpart [lindex [split $oldfam :] 0]
    
    foreach g [lrange [split $oldfam :] 1 end] {
	set curg($g) 1
    }
    foreach g [ic_boco_what_groups_go_with_part_or_subset part $oldpart] {
	mess "Taking objects out of group $g\n"
	catch {unset curg($g)}
    }
    foreach g [ic_boco_what_groups_go_with_part_or_subset part $newpart] {
	mess "Adding objects into group $g\n"
	set curg($g) 1
    }
    set newfam [join [concat [list $newpart] [array names curg]] :]
    return $newfam
}


## For a given family, if you move something that family into or out of
## a subset, what should the new family be?

proc ic_boco_change_subset {oldfam subset add} {
    set glist [split $oldfam :]
    foreach g [ic_boco_what_groups_go_with_part_or_subset subset $subset] {
	if $add {
	    if ![lmember $glist $g] {
		lappend glist $g
		mess "Putting objects into group $g\n"
	    }
	} else {
	    if [lmember $glist $g] {
		set glist [ldelete $glist $g]
		mess "Removing objects from group $g\n"
	    }
	}
    }
    return [join $glist :]
}

## For every icon on family oldfam, remove the locations of the objects.  For every
## icon on newfam, add the locations.

proc ic_boco_add_or_remove_bc_icons {on_fam add objects} {
    global famboco_obj projlib
    
    #VG 07/12/06: Wayne, check/replace next line
     if {$famboco_obj == ""} return  
    
    #puts [list ic_boco_add_or_remove_bc_icons $on_fam $add $objects]
    
    foreach ic [$famboco_obj list_all_bc_icons] {
	if {[lindex $ic 0] == $on_fam} {
	    #puts "IC = $ic"
	    set loctype [lindex $ic 5]
	    set icontype [lindex $ic 6]
	    set locations ""
	    foreach {type obj} $objects {
		switch $type {
		    unsmap {
			if {$loctype == "vertex"} {
			    set locations [concat $locations [$obj get_element_vertex_positions 0 1]]
			} else {
			    set locations [concat $locations [$obj get_element_centroid_positions 0]]
			}
		    }
		    point - surface - body - material {
			set gnum [$projlib get_number $type $obj]
			lappend locations [lindex [$projlib text_point $type $gnum] 0]
		    }
		    curve {
			set gnum [$projlib get_number $type $obj]
			if {($icontype == "helix") || ($icontype == "orientation")} { #FIXME
			    if ![catch {gk_get_crv_pos 0 $gnum} loc] {
			       eval lappend locations $loc
			    }
			    if ![catch {gk_get_crv_pos 1 $gnum} loc] {
			       eval lappend locations $loc
			    }
			    #set locations [concat $locations [$projlib curve_end_points $gnum]]
			} else {
			    lappend locations [lindex [$projlib text_point $type $gnum] 0]
			}
		    }
		}
	    }
	    if {$locations != ""} {
		#puts "LOCATIONS $locations"
		set nch [eval $famboco_obj add_or_remove_bc_icon_locations_to_family $on_fam \
			     [lindex $ic 1] $add $locations]
		#puts "$nch icons added or removed"
	    }
	}
    }
}

## Replace a certain arg in the bc's for a given set of families with a different
## value.

proc ic_boco_replace_arg {fams replace_types replace_pos replace_val} {
    foreach f $fams {
	set nbc ""
	foreach bc [ic_boco_get $f] {
	    if {[lsearch $replace_types [lindex $bc 1]] >= 0} {
		set bc [lreplace $bc $replace_pos $replace_pos $replace_val]
	    }
	    lappend nbc $bc
	}
	ic_boco_set $f $nbc
    }
}

## Remove everything from groups that have a specific bctype and set
## of arguments, and delete those groups.  Note that this will never get
## called with part groups ...

proc ic_boco_delete_group_with_bcs {bcargs} {
    global projlib famboco_obj uns_cur
    
    if {$famboco_obj == ""} return
    
    create_empty_tetin_if_needed
    
    foreach result [ic_boco_list_with_bctype $bcargs *:*] {
	set group [lindex $result 0]
	if {[llength $group] != 1} continue
	
	ic_boco_delete_group $group
    }
}

## List all the bcicons

proc ic_boco_list_icons {{fam ""} {type ""}} {
    global famboco_obj
    return [$famboco_obj list_bc_icons $fam $type]
}
set dont_record_command(ic_boco_list_icons) 1

## Remove everything from groups and delete those groups

proc ic_boco_delete_group {group} {
    global projlib famboco_obj uns_cur

    if {$uns_cur != ""} {
	set allmap [$uns_cur get_map all]
    }
    
    # First see what icons are on this singleton group
    set del_bctypes [$famboco_obj list_bc_icons $group ""]
    
    set delf ""
    foreach fam [$projlib list_families 0] {
	set pid [$projlib lookup_family_pid $fam]
	set groups [split $fam :]
	if ![lmember $groups $group] continue
	
	set newfam [join [ldelete $groups $group] :]
	if {$newfam != ""} {
	    $projlib new_family $newfam
	    set newpid [$projlib lookup_family_pid $newfam]
	    foreach {type num} [$projlib get_objects_in_family "" $pid] {
		$projlib set_family $type $num $newpid
			set bsp [$projlib get_object $type $num]
			if {$bsp == ""} continue
			$bsp configure -family $newfam
	    }
	    
	    if {$uns_cur != ""} {
		set fmap [uns_map -mesh $uns_cur]
		if [$fmap add_with_family $fam $allmap] {
		    $fmap set_family $newfam
		}
		$fmap delete
	    }
	}
	$projlib delete_family $fam
	$famboco_obj clear $fam
	$famboco_obj move_bc_icons $fam $newfam
	foreach del $del_bctypes {
	    $famboco_obj delete_bc_icon $newfam $del
	}
	lappend delf $fam
    }
    
    if {$uns_cur != ""} {
	update_uns_visible
	ic_uns_families_changed
    }
    return $delf
}
    
## Clear the bc's and icons from one family

proc ic_boco_clear_family {fam} {
    global famboco_obj
    if {$famboco_obj == ""} return
    $famboco_obj clear $fam
    $famboco_obj delete_bc_icon $fam ""
}

## Look for a group inside this family that is a part name, and define the 
## color of the family based on the value of the name.  This is specific
## to AI*Env.  Note that it takes only the last component of the part name
## so that things don't shift around when you put them in assemblies.

proc ic_boco_set_part_color {famname {update_uns 1}} {
    global batch_mode

    if $batch_mode return
    set name [lindex [split $famname :] 0]
    set name [lindex [split $name /] end]
    set col [family_color_for_name $name]
    set_family_color_for_name $famname $col
    if $update_uns { ic_uns_families_changed }
}

## Set nastran-type coordinate systems

proc ic_boco_nastran_csystem {what args} {
    global famboco_obj
    if {$famboco_obj == ""} return
    
    switch $what {
	reset {
	    $famboco_obj reset_nastran_csystems
	}
	set {
	    eval $famboco_obj set_nastran_csystem $args
	}
    }
}

## Clean up the set of bc's a bit.  Keep the singleton groups if
## keep_groups is 1

proc ic_boco_delete_unused {keep_groups} {
    global famboco_obj projlib uns_cur

    if {$famboco_obj == ""} { return 0 }
    
    set nd 0
    foreach single {0 1} {
	if {$keep_groups && $single} continue
	if { $famboco_obj == "" } continue
	foreach fam [$famboco_obj list_families ""] {
	    if {$fam == "ORFN"} continue
	    if {$single != ![string match *:* $fam]} continue
	    if [info exists keep_me($fam)] continue
	    if {[ic_geo_count_in_family all [list $fam]] || \
		    [ic_uns_count_in_families [list $fam]] || \
		    [ic_meshdirect_directive_uses_family $fam]} {
		if !$single {
		    foreach g [split $fam :] {
			set keep_me($g) 1
		    }
		}
		continue
	    }
	    
	    if {$projlib != ""} {
		$projlib delete_family $fam 
		# the delete_family call doesn't set this flag so let's do it 
		$projlib configure -modified 1
	    }
	    if {$uns_cur != ""} { $uns_cur delete_family $fam }
	    $famboco_obj configure -modified 1
	    
	    #mess "Delete $fam\n"
	    incr nd
	}
    }
    return $nd
}

## List the families that have no mesh or geometry in them.

proc ic_boco_list_unused {} {
    global famboco_obj projlib uns_cur
    
    if {$famboco_obj == ""} { return "" }
    
    # This is slow, need to speed it up ********************
    
    set ret ""
    foreach fam [$famboco_obj list_families ""] {
	if [ic_geo_count_in_family all [list $fam]] continue
	if [ic_uns_count_in_families [list $fam]] continue
	if [ic_meshdirect_directive_uses_family $fam] continue
	lappend ret $fam
    }
    return $ret
}
set dont_record_command(ic_boco_list_unused) 1

## Return the list of fams belonging to the given <em>partname</em>

proc ic_boco_get_fams_of_part {partname} {
    set ret ""
    foreach f [ic_geo_list_families] {
	if {[lindex [split $f :] 0] == $partname} {
	    lappend ret $f
	}
    }
    return $ret
}
set dont_record_command(ic_boco_get_fams_of_part) 1

## Return the part to which the given family <em>famname</em> belongs.

proc ic_boco_get_part_of_fam {famname} {    
    return [lindex [split $famname :] 0]
}
set dont_record_command(ic_boco_get_part_of_fam) 1

##

proc ic_solver_mesh_info {solver} {
    global env solver_mesh_info
    if ![info exists solver_mesh_info1] {
	if {[info exists env(ICEM_ACN)] && \
	    [file exists $env(ICEM_ACN)/icemcfd/output-interfaces/solver_table.tcl]} {
	     source $env(ICEM_ACN)/icemcfd/output-interfaces/solver_table.tcl
	}
    }
	
    if [info exists solver_mesh_info([lindex $solver 0])] {
	mess "Solver $solver supports $solver_mesh_info([lindex $solver 0]).\n"
    } 
    return ""
}














