
# 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>Meshing functions</h3>

## Run tetra in batch mode, using the given <em>tetin</em> and
## <em>uncut_domain</em> files.  The full path names must be given.
## One or more arguments may be given.  These arguments are:
## <ul>
## <li> <b>tradeoff</b> <em>type</em> : the type is either <b>space</b> or
## <b>time</b>
## <li> <b>log</b> <em>logfile</em> : the file to write stdout to
## </ul>

proc ic_run_tetra {tetin uncut_dom args} {
    global uns_cur

    if ![lindex [check_licenses_enabled] 0] {
        error "No med license was obtained"
    }
    
    if [ic_wb2_input_parameter_mode] {
        ic_wb2_update_while_input_parameter_mode
    }
    
    # check for in process
    set in_process 0
    set existing 0
    set quiet 0
    set tempfilename "__temp_surface.tin"
    set args2 ""
    foreach {n v} $args {
        if { $n == "in_process" && $v } {
            set in_process 1
        }
        if { $n == "quiet" && $v != 0} {
            set quiet 1
        }
        if { $n == "use_existing" } {
            set existing 1
        }
        if { $n == "tempfilename" } {
            set tempfilename $v
        } else {
            lappend args2 $n
            lappend args2 $v
        }
    }
    set logfile ""
    if $in_process {
        global uns_cur
        if { $existing } {
            if {!$quiet} {
                mess "Saving tetin file to $tempfilename\n"
            }
            ic_save_tetin $tempfilename 0 0 "" "" 0 $quiet
        }
        ic_unload_mesh $quiet
        ic_uns_create_empty
        set stat 0
        if [catch {eval $uns_cur tetra_mesh $args2} res] {
            set stat 1
            mess "Error: Tetra meshing failed; $res\n" red
        }
        if $existing {
            ic_unload_tetin $quiet
            if {!$quiet} {
                mess "Restoring tetin file from $tempfilename\n"
                }
            ic_load_tetin $tempfilename 0 1 1 $quiet
            ic_rm $tempfilename
        }
        if $stat {
            return 0
        }
        $uns_cur renumber_all_nodes 1 0
        $uns_cur renumber_all_elements 1 0
        domain_after_load_mesh $quiet
        return 1
    } else {
        global ic_application_output_hook

        # This is needed so that remote execution will find the cmd and log files
        set ifile tetra_cmd.tmp
        set fp [open $ifile w]
        if { $tetin != "__no_tetin__" } {
            puts $fp "SurfaceFile      \"$tetin\""
        }
        puts $fp "DomainOutputFile \"$uncut_dom\""
        set gui_output 0
        foreach {n v} $args {
            switch $n {
                tradeoff {
                    if {$v == "time"} {
                        puts $fp "CacheIntersections	1"
                    } else {
                        puts $fp "CacheIntersections	0"
                    }
                }
                log {
                    set logfile $v
                }
                show_progress {
                    if $v {
                        set gui_output 1
                        puts $fp "show_progress 1"
                    }
                }
                default {
                    puts $fp "$n $v"
                }
            }
        }
        puts $fp "OverwriteBoco no"
        close $fp
        give_back_mesher_license
        if $gui_output {
            set ic_application_output_hook check_tetra_progress
            catch { interrupt begin "Tetra Batch" initializing 100 application_terminated no_grab }
        }
        set stat [run_application_batch icemcfd/tetra TETRA_BIN $ifile $logfile 0]
        catch { unset ic_application_output_hook }
        catch { interrupt finish }
        # error return
        if !$stat {
            mess "Error: tetra exited with non-zero status\n" red
            return 0
        }
        if [file exists $uncut_dom] { return 1 }
        mess "Error: tetra didn't create $uncut_dom\n" red
    }
    return 0
}

proc check_tetra_progress {pid line} {
    if [string match @@@* $line] {
        set msg [string trim [string range $line 3 end]]
        set percent [lindex $msg 0]
        set line [lrange $msg 1 end]
        catch {interrupt check $percent 100 $line}
    }
    mess "$line\n" blue
}

## creates an xml file that defines the input parameters for the python fieldmesher controller module.
## returns the xml file name

proc ic_write_fieldmesher_params_xml_file {working_dir input_mesh output_mesh tetin_file meshing_controls\
                                           global_maxsize global_minsize global_growthrate smoothing_level\
                                           use_fluent hexacore inflation last_layer_aspect n_ortho_layers fix_first_layer\
                                           enhance_norm_comp enhance_offset_comp gap_factor max_cap_skew max_cell_skew\
                                           frozen_volumes debug interactive} {
    global is_win env
	
    if $is_win {
        set working_dir [win_shortname $working_dir]
        set input_mesh [win_shortname $input_mesh]
        set output_mesh [win_shortname $output_mesh]
        set tetin_file [win_shortname $tetin_file]
        if {$meshing_controls != ""} {
            set meshing_controls [win_shortname $meshing_controls]
        }
    }

    set xml_file "$working_dir/FieldMesherControllerParameters.xml";
    
    if $debug { mess "DEBUG: xml parameter file = $xml_file\n" }

    set f [open $xml_file w]
    puts $f "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>"
    puts $f "<Ansys.ICEMCFD.FieldMesherController>"
    puts $f "    <IOFilenames>"
    puts $f "        <InputMeshFilename>$input_mesh</InputMeshFilename>"
    puts $f "        <OutputMeshFilename>$output_mesh</OutputMeshFilename>"
    if {$tetin_file != ""} {
        puts $f "        <TetinFilename>$tetin_file</TetinFilename>"
    }
    if {$meshing_controls != ""} {
        puts $f "        <MeshingControlsFilename>$meshing_controls</MeshingControlsFilename>"
    }
    puts $f "    </IOFilenames>"
    puts $f "    <GlobalParameters>"
    puts $f "        <GlobalMaxSize>$global_maxsize</GlobalMaxSize>"
    puts $f "        <GlobalMinSize>$global_minsize</GlobalMinSize>"
    puts $f "        <GlobalGrowthRate>$global_growthrate</GlobalGrowthRate>"
    if {$smoothing_level >= 0 && $smoothing_level <= 2} {
        puts $f "        <SmoothingLevel>$smoothing_level</SmoothingLevel>"
    }
    if $hexacore {
        puts $f "        <UseHexCoreMesher>True</UseHexCoreMesher>"
    }
    if $inflation {
        puts $f "        <DoInflation>True</DoInflation>"
    }
    if {$frozen_volumes != ""} {
        puts $f "        <FrozenMeshNamedCollectionNames>"
		foreach name $frozen_volumes {
			puts $f "        	<String>$name</String>"
		}
        puts $f "        </FrozenMeshNamedCollectionNames>"
    }
    if $use_fluent {
        puts $f "        <UseFluentExecutable>True</UseFluentExecutable>"
    }
    if $debug {
        puts $f "        <KeepTemporaryFiles>True</KeepTemporaryFiles>"
        puts $f "        <EnableLogging>True</EnableLogging>"
    }
    if $interactive {
        puts $f "        <InteractiveMode>True</InteractiveMode>"
    }
    puts $f "    </GlobalParameters>"
    if $inflation {
        puts $f "    <GlobalInflationParameters>"
        if {$last_layer_aspect > 0} {
            puts $f "        <LastLayerAspectRatio>$last_layer_aspect</LastLayerAspectRatio>"
        }
        if {$n_ortho_layers > 0} {
            puts $f "        <NOrthogonalLayers>$n_ortho_layers</NOrthogonalLayers>"
        }
        if {$fix_first_layer} {
            puts $f "        <FixFirstLayer>True</FixFirstLayer>"
        }
        if {$enhance_norm_comp} {
            puts $f "        <EnhanceNormalComputation>True</EnhanceNormalComputation>"
        }
        if {$enhance_offset_comp} {
            puts $f "        <EnhanceOffsetComputation>True</EnhanceOffsetComputation>"
        }
        if {$gap_factor >= 0 && $gap_factor <= 2} {
            puts $f "        <GapFactor>$gap_factor</GapFactor>"
        }
        if {$max_cap_skew >= 0 && $max_cap_skew <= 1} {
            puts $f "        <MaxCapSkewness>$max_cap_skew</MaxCapSkewness>"
        }
        if {$max_cell_skew >= 0 && $max_cell_skew <= 1} {
            puts $f "        <MaxCellSkewness>$max_cell_skew</MaxCellSkewness>"
        }
        puts $f "    </GlobalInflationParameters>"
    }
    puts $f "</Ansys.ICEMCFD.FieldMesherController>"

    close $f
    
    return $xml_file
}

## Run the fieldmesher executable in batch mode, using the given <em>arglist</em> which
## is of the form [list <em>options inputfile outputfile</em>]. The full
## path names for <em>inputfile</em> and <em>outputfile</em> must be given.

proc ic_run_fieldmesher {inpfile outfile args} {
    global is_win env tcl_platform projlib

    set tetin ""
    set custom_python_file ""
    set xml_parameter_file ""
    set expansion_factor 0
    set global_max_size 0
    set smoothing_level 0
    set arglist ""
    set gui_output 0
    set debug 0
    set interactive 0
    set hexacore 0
    set use_fluent 0
    set inflation 0
    set last_layer_aspect 0;
    set n_ortho_layers 0
    set fix_first_layer 0
    set enhance_norm_comp 0
    set enhance_offset_comp 0
    set gap_factor -1
    set max_cap_skew -1
    set max_cell_skew -1
	set frozen_volumes ""

    set current_dir [pwd]
    if $is_win {
        set inpfile [win_shortname $inpfile]
    }
    if ![file exists $inpfile] {
        mess "ERROR: the input mesh $inpfile is not available.\n" red
        return 0
    }
    if $is_win {
        set outpfile [win_shortname $outfile]
    }
    foreach {n v} $args {
        switch $n {
            tetin {set tetin $v}
            tetexpand {set expansion_factor $v}
            verbose {set debug $v}
            tgrid_hexacore {set hexacore $v}
            tgrid_prism {set inflation $v}
            tgrid_last_layer_aspect {set last_layer_aspect $v}
            tgrid_n_ortho_layers {set n_ortho_layers $v}
            tgrid_fix_first_layer {set fix_first_layer $v}
            tgrid_enhance_norm_comp {set enhance_norm_comp $v}
            tgrid_enhance_offset_comp {set enhance_offset_comp $v}
            tgrid_smoothing_level {set smoothing_level $v}
            tgrid_gap_factor {set gap_factor $v}
            tgrid_max_cap_skew {set max_cap_skew $v}
            tgrid_max_cell_skew {set max_cell_skew $v}
            use_fluent {set use_fluent $v}
            tgrid_interactive {set interactive $v}
			tgrid_frozen_parts {set frozen_volumes  $v}
            custom_python_file {set custom_python_file $v}
        }
    }
	
    if  {$tetin != "" && [file pathtype $tetin] == "relative"} {
        if {![file exists $tetin]} {
            set tetin ""
        } else {
            if $is_win {
                set tetin [win_shortname $current_dir/$tetin]
            } else {
                set tetin $current_dir/$tetin
            }
        }
    }

    if  {$tetin == "" && $inflation} {
        mess "WARNING: no geometry is loaded. Pre Inflation is skipped\n" red
        set inflation 0
    }

    if [ic_wb2_input_parameter_mode] {
        ic_wb2_update_while_input_parameter_mode
    }

    set global_max_size 0.0
    if {[ic_geo_is_loaded] || $projlib != ""} {
        foreach {n v} [ic_get_meshing_params global 0] {
            if {$n == "gmax"} {
                set global_max_size $v
                break
            }
        }
    }
    set global_min_size 0.0
    if {[ic_geo_is_loaded] || $projlib != ""} {
        foreach {n v} [ic_get_meshing_params global 0] {
            if {$n == "gnat"} {
                set global_min_size $v
                break
            }
        }
    }
    
    # set system directories
    set ansys_sysdir ""
    set framework_sysdir ""
    set python_sysdir ""
    if $is_win {
        if {$env(PROCESSOR_ARCHITECTURE)=="x86"} {
            set ansys_sysdir intel
            set framework_sysdir Win32
            set python_sysdir win32
        } elseif {$env(PROCESSOR_ARCHITECTURE) =="AMD64"} {
            set ansys_sysdir winx64
            set framework_sysdir Win64
            set python_sysdir winx64
        } else {
            mess "ERROR: environment variable PROCESSOR_ARCHITECTURE should contain either \"x86\" or \"AMD64\"\n" red
            return 0
        }
    } elseif {$tcl_platform(os) == "Linux"} {
        set rr [exec uname -a]
        if {[lsearch $rr "ia64"] >= 0} {
            mess "Operating system $tcl_platform(os) ia64 not supported\n" red
            return 0
        } elseif {[lsearch $rr "x86_64"] >= 0} {
            set ansys_sysdir linx64
            set framework_sysdir Linux64
            set python_sysdir linx64
        } else {
            set ansys_sysdir linia32
            set framework_sysdir Linux32
            set python_sysdir lin32
        }                
    } else {
        mess "Operating system $tcl_platform(os) not supported\n" red
        return 0
    }
    # set up environment
    if ![info exists env(AWP_ROOT)] {
        set env(AWP_ROOT) "$env(ICEM_ACN)/../.."
        if $debug { mess "DEBUG: Setting environment variable AWP_ROOT to $env(ICEM_ACN)\n" }
    }
    if $is_win {
        set env(AWP_ROOT) [win_shortname $env(AWP_ROOT)]
    }

    # check for default installation paths of the fluent/tgrid meshing executables
    set meshing_exe fluent
    if !$use_fluent {
        set meshing_exe tgrid
    }
    if $is_win {
        set meshing_exe_path "$env(AWP_ROOT)/${meshing_exe}/ntbin/win64/${meshing_exe}.exe"
    } elseif {$tcl_platform(os) == "Linux"} {
        set meshing_exe_path "$env(AWP_ROOT)/${meshing_exe}/bin/${meshing_exe}"
    }
    if ![file exists $meshing_exe_path] {
        mess "WARNING: Standart installation path $meshing_exe_path not available.\n         Please check your ANSYS Workbench installation.\n" green
        if $use_fluent {
            mess "         or try to switch to tgrid meshing by runnning ic_run_fieldmesher without the option use_fluent.\n" green
        }
    }

    # create the temporary directory
    if [info exists env(TMPDIR)] {
        set temp_dir $env(TMPDIR)
    } elseif [info exists env(TEMP)] {
        set temp_dir $env(TEMP)
    } elseif [info exists env(TMP)] {
        set temp_dir $env(TMP)
    } else {
        set temp_dir [pwd]
    }
    if $is_win {
        set temp_dir [win_shortname $temp_dir]
    }

    set pid [pid]
    set counter 1
    set working_directory ${temp_dir}/ansys_fieldmesher_${pid}_$counter
    while {[file exists ${working_directory}] == 1} {
        if $debug { mess "DEBUG: temporary directory ${working_directory} already exists\n" }
        set counter [expr $counter + 1]
        set working_directory ${temp_dir}/ansys_fieldmesher_${pid}_$counter
    }
    cmd_mkdir $working_directory
    if $debug { mess "DEBUG: temporary directory is ${working_directory}\n" }

    # write parameter file
    set meshing_controls ""
		
    if [catch {set xml_parameter_file [ic_write_fieldmesher_params_xml_file $working_directory \
                                                $inpfile $outfile $tetin $meshing_controls\
                                                $global_max_size $global_min_size $expansion_factor\
                                                $smoothing_level  $use_fluent $hexacore $inflation\
                                                $last_layer_aspect $n_ortho_layers $fix_first_layer\
                                                $enhance_norm_comp $enhance_offset_comp $gap_factor\
                                                $max_cap_skew $max_cell_skew $frozen_volumes\
                                                $debug $interactive] }] {
        if $debug {
            mess "DEBUG: temporay files kept under $working_directory.\n" blue
        } else {
            catch { cmd_rm_r $working_directory }
        }
        mess "ERROR writing xml parameter file.\n" red
        return 0
    }
    if ![file exists $xml_parameter_file] {
        if $debug {
            mess "DEBUG: temporay files kept under $working_directory.\n" blue
        } else {
            catch { cmd_rm_r $working_directory }
        }
        mess "ERROR: Cannot create xml parameter file $xml_parameter_file. Check permissions.\n" red
        return 0
    }

    set env(ANSYS_ICEMCFD_FIELDMESHER_XMLFILE) $xml_parameter_file

    set orig_path $env(PATH)

    set python_version "2_7_13"
    set cpython_path "$env(AWP_ROOT)/commonfiles/CPython/$python_version"
    set python_configuration ""
    set python_exe "no_exe"
    if $debug {
        set python_configuration "Debug"
        if {[file exists "$cpython_path/$python_sysdir/$python_configuration/python"] && [file exists "$cpython_path/$python_sysdir/$python_configuration/Ansys"]} {
            set python_exe "python_d"
        } else {
            set python_configuration "Release"
            if {[file exists "$cpython_path/$python_sysdir/$python_configuration/python"] && [file exists "$cpython_path/$python_sysdir/$python_configuration/Ansys"]} {
                set python_exe "python"
            }
        }
    }
    if {$python_exe == "no_exe"} {
        set python_configuration "Release"
        if {[file exists "$cpython_path/$python_sysdir/$python_configuration/python"] && [file exists "$cpython_path/$python_sysdir/$python_configuration/Ansys"]} {
            set python_exe "python"
        } else {
            set python_configuration "Debug"
            if {[file exists "$cpython_path/$python_sysdir/$python_configuration/python"] && [file exists "$cpython_path/$python_sysdir/$python_configuration/Ansys"]} {
                set python_exe "python_d"
            }
        }
    }
    if {$python_exe != "no_exe"} {
        set default_python_path "$cpython_path/$python_sysdir/$python_configuration"
    } else {
        set default_python_path "not_set"
    }
    if $debug { mess "DEBUG: default python path: $default_python_path\n" }

    if $is_win {
        set python_path "not_set"
        if [info exists env(PYTHONPATH)] {
            set python_path $env(PYTHONPATH)
            if $debug { mess "DEBUG: python path set by environment variable PYTHONPATH: $python_path\n" }
        }
        if {$python_path == "not_set" || ![file exists "$python_path"]} {
            if {$default_python_path != "not_set"} {
                set python_path "$default_python_path"
                set env(PYTHONPATH) "$python_path"
                if $debug { mess "DEBUG: Setting environment PYTHONPATH variable to $python_path\n" }
            }
        }
        set python_home "not_set"
        if [info exists env(PYTHONHOME)] {
            set python_home $env(PYTHONHOME)
        }
        if {$python_home == "not_set" || ![file exists "$python_home"]} {
            if {$default_python_path != "not_set"} {
                set python_home "$default_python_path/python"
                set env(PYTHONHOME) "$python_home"
                if $debug { mess "DEBUG: Setting environment PYTHONHOME variable to $python_home\n" }
            }
        }
        if {[file exists "$python_home"]} {
            set env(PATH) "$env(PYTHONHOME);$env(AWP_ROOT)/Framework/bin/$framework_sysdir;$env(PATH)"
            if $debug { mess "DEBUG: Adding $env(AWP_ROOT)/Framework/bin/$framework_sysdir to PATH environment variable\n" }
        } else {
            mess "ERROR: PYTHONHOME=$python_home does not exist!\n" red
        }

    } elseif {$tcl_platform(os) == "Linux"} {
        set python_path "not_set"
        if [info exists env(PYTHONPATH)] {
            set python_path $env(PYTHONPATH)
            if $debug { mess "DEBUG: python path set by environment variable PYTHONPATH: $python_path\n" }
        }
        if {$python_path == "not_set" || ![file exists "$python_path"]} {
            if {$default_python_path != "not_set"} {
                set python_path "$default_python_path"
                set env(PYTHONPATH) "$python_path"
                if $debug { mess "DEBUG: Setting environment PYTHONPATH variable to $python_path\n" }
            }
        }

        set ld_lib_path_changed 0
        if {[file exists "$python_path"]} {
            set env(PATH) "$env(PYTHONPATH)/python/bin/:$env(PATH)"
            if $debug { mess "DEBUG: Adding $env(PYTHONPATH)/python/bin/ to PATH environment variable\n" }
            if [info exists env(LD_LIBRARY_PATH)] {
                set orig_ld_lib_path $env(LD_LIBRARY_PATH)
                set ld_lib_path_changed 1
            }
            set env(LD_LIBRARY_PATH) "$env(PYTHONPATH)/python/lib:$env(PYTHONPATH)/sip/lib:$env(PYTHONPATH)/openssl/lib:$env(AWP_ROOT)/Framework/bin/$framework_sysdir:$env(LD_LIBRARY_PATH)"
            if $debug { mess "DEBUG: Adding $env(PYTHONPATH)/python/lib:$env(PYTHONPATH)/sip/lib:$env(PYTHONPATH)/openssl/lib:$env(AWP_ROOT)/Framework/bin/$framework_sysdir to LD_LIBRARY_PATH environment variable\n" }
        } else {
            mess "ERROR: PYTHONPATH=$python_path does not exist!\n" red
        }

        # this is needed only for python 2.7
        #  set env(LD_PRELOAD)"$env(PYTHONPATH)/python/lib/libpython2.7.so" 
    }

    # choose python script
    if {$custom_python_file == ""} {
        set python_template "$env(PYTHONPATH)/Ansys/ICEMCFD/FieldMesherController.py"
    } else {
        set python_template $custom_python_file
    }

    # compose the fieldmesher call command
   if $is_win {
        set fieldmesher_exe "$env(PYTHONHOME)/$python_exe.exe"
        # check the installation
        if ![file exists $fieldmesher_exe] {
            mess "ERROR: $fieldmesher_exe not available. Please check your ANSYS Workbench installation.\n" red
            catch { cmd_rm_r $working_directory }
            return 0
        }
        regsub -all \\\\ $python_template / python_template
        regsub -all \\\\ $fieldmesher_exe / fieldmesher_exe
        if [catch {ic_run_application_exec . "" "$fieldmesher_exe" "\"$python_template\""} r] {
            set env(PATH) $orig_path
            if $debug {
                mess "DEBUG: temporay files kept under $working_directory.\n" blue
            } else {
                catch { cmd_rm_r $working_directory }
            }
            mess "ERROR FieldMesher application failed.\n" red
            return 0
        }
    } else {
        set fieldmesher_exe "$env(AWP_ROOT)/aisol/CommonFiles/$ansys_sysdir/ansys.meshing.fieldmesher.exe"
        # check the installation
        if ![file exists $fieldmesher_exe] {
            mess "ERROR: $fieldmesher_exe not available. Please check your ANSYS Workbench installation.\n" red
            catch { cmd_rm_r $working_directory }
            return 0
        }
        if [catch {ic_run_application_exec . "" "$env(AWP_ROOT)/aisol/.workbench" "-cmd -NoMW $fieldmesher_exe $python_template"} r] {
            set env(PATH) $orig_path
            if $ld_lib_path_changed {
                set env(LD_LIBRARY_PATH) $orig_ld_lib_path
            }
            if $debug {
                mess "DEBUG: temporay files kept under $working_directory.\n" blue
            } else {
                catch { cmd_rm_r $working_directory }
            }
            mess "ERROR FieldMesher application failed.\n" red
            return 0
        }
    }
    # revert to original path variables
    set env(PATH) $orig_path
    if !$is_win {
        if $ld_lib_path_changed {
            set env(LD_LIBRARY_PATH) $orig_ld_lib_path
        }
    }

    # load the fieldmesher's output mesh
    if ![file exists $outfile] {
        mess "ERROR: Import mesh failed\n" red
        if $debug {
            mess "DEBUG: temporay files kept under $working_directory.\n" blue
        } else {
            catch  { cmd_rm_r $working_directory }
        }
        return 0
    }
    ic_uns_load_one_domain $outfile 3 1 {} 2

    if $debug {
        mess "DEBUG: temporay files kept under $working_directory.\n" blue
    } else {
        catch { cmd_rm_r $working_directory }
    }

    return 1
}

## Run tri2tet in batch mode, using the given <em>arglist</em> which
## is of the form [list <em>options inputfile outputfile</em>]. The full
## path names for <em>inputfile</em> and <em>outputfile</em> must be given.

proc ic_run_tri2tet {inpfile outfile args} {
    global projlib ic_application_output_hook
    set pfile ""
    set tetin ""
    set arglist ""
    set gui_output 0
    set error_subset ""
    set progenv TRI2TET_BIN
    set run_tg_tri2tet 0

    foreach {n v} $args {
        switch $n {
            tetin {set tetin $v}
            family {lappend arglist -f$v}
            tetexpand {lappend arglist -t$v}
            safety {lappend arglist -s$v}
            keepall {if $v {lappend arglist -k}}
            estelem {lappend arglist -e$v}
            output {lappend arglist -o$v}
            offfile {lappend arglist -W$v}
            verbose {if $v {lappend arglist -v}}
#            bgmesh {if $v {lappend arglist -b}}
#            bgfile {lappend arglist -S$v}
            fillholes {if $v {lappend arglist -h}}
            subset {lappend arglist -r$v}
            errors {set error_subset $v}
            progenv {set progenv $v}
            singlefamily {if $v {lappend arglist -1}}
            internalwall {
                foreach fam $v {
                    lappend arglist -i$fam
                }
            }
            show_progress {
                if $v {
                    lappend arglist -G
                    set gui_output 1
                }
            }
            use_tg_tgrid_af {if $v {lappend arglist -a}}
            use_tg_tri2tet { set run_tg_tri2tet $v }
            
        }
    }

    if {$tetin == ""} {
        if {[ic_geo_is_loaded]} {
        
            if [ic_wb2_input_parameter_mode] {
                ic_wb2_update_while_input_parameter_mode
            }
        
            set pfile [cmd_tempfile tri2tet_parm .tmp]
            set fp [open $pfile w]
            foreach {n v} [ic_get_meshing_params global 0] {
                if {$n == "gmax"} {
                    puts $fp "define_model $v"
                    break
                }
            }
            foreach name [ic_geo_get_objects density] {
                set np [ic_geo_num_nodes density $name]
                set def "define_density_poly npnts $np"
                foreach {n v} [ic_get_meshing_params density $name] {
                    switch $n {
                        emax {append def " size $v"}
                        ewid {append def " width $v"}
                        erat {append def " size_ratio $v"}
                    }
                }
                puts $fp $def
                for {set n 0} {$n < $np} {incr n} {
                    puts $fp [ic_geo_get_node density $name $n]
                }
            }
            foreach name [ic_geo_get_objects material] {
                if {$name != "ORFN"} {
                    set loc [ic_geo_get_material_location $name]
                    set fam [ic_geo_get_family material $name]
                    puts $fp "material_point $loc family $fam"
                }
            }
            foreach fam [ic_geo_list_families] {
                if {[catch {ic_geo_get_family_param $fam internal_wall} iw] || $iw == ""} {
                    set iw 0
                }
                if {$iw} {
                    puts $fp "define_family $fam internal_wall 1"
                }
            }
            close $fp
            lappend arglist -p$pfile
        }
    } else {
        if {$tetin == "__no_tetin__"} {
            if {$projlib != ""} {
            
                if [ic_wb2_input_parameter_mode] {
                    ic_wb2_update_while_input_parameter_mode
                }
            
                foreach {n v} [ic_get_meshing_params global 0] {
                    if {$n == "gmax"} {
                        lappend arglist -g$v
                        break
                    }
                }
            }
        } else {
            lappend arglist -p$tetin
        }
    }

    lappend arglist $inpfile $outfile
    set logfile tri2tet_log.tmp
    catch {file delete $logfile}

    give_back_mesher_license

    if $gui_output {
        set ic_application_output_hook check_tri2tet_progress
    }
    if $run_tg_tri2tet {
        set ok [run_application_batch_exec_direct icemcfd/tg_tri2tet $progenv $arglist $logfile]
    } else {
        set ok [run_application_batch_exec_direct icemcfd/tri2tet $progenv $arglist $logfile]
    }
    catch {unset ic_application_output_hook}
    catch {interrupt finish}

    if {$pfile != ""} {catch {file delete $pfile}}
    if {$ok < 0} {
        mess "tri2tet was terminated\n" red
        set ok 0
    } elseif !$ok {
        mess "Error: tri2tet exited with a non-zero status\n" red
    } elseif [catch {open $logfile r} fp] {
        mess "Error: $fp\n" red
        set ok 0
    } else {
        set ok 0
        while {[gets $fp line] >= 0} {
            if {$line == "status 0" || [string match "renaming temporary file to *" $line]} {
                set ok 1
                break
            }
        }
        catch {close $fp}
        if {!$ok} {
            mess "Error: tri2tet terminated prematurely\n" red
        }
    }
    if {$ok && ![file exists $outfile]} {
        mess "Error: tri2tet didn't create $outfile\n" red
        set ok 0
    }

    if {$error_subset != ""} {
        if [ic_uns_subset_exists $error_subset] {
            ic_uns_subset_clear $error_subset
        }
        set errors {}
        if {!$ok && ![catch {open $logfile r} fp]} {
            while {[gets $fp line] >= 0} {
                if {[lindex $line 0] == "@#"} {
                    eval lappend errors [lrange $line 1 end]
                }
            }
            catch {close $fp}
        }
        if {$errors != {}} {
            if ![ic_uns_subset_exists $error_subset] {
                ic_uns_subset_create $error_subset
            }
            foreach pos $errors {
                catch {ic_uns_subset_add_near_pos $error_subset $pos}
            }
            mess "Subset $error_subset created.\n"
        }
    }

    catch {file delete $logfile}

    return $ok
}

proc check_tri2tet_progress {pid line} {
    if [string match @* $line] {
        set code [lindex $line 0]
        if {$code != "@#"} {
            if {$code == "@@"} {
                catch {interrupt begin "Tetrahedra from Existing Mesh" \
                       initializing 100 application_terminated no_grab}
            } else {
                set percent [string range $code 1 end]
                set msg [lrange $line 1 end]
                catch {interrupt check $percent 100 $msg}
            }
        }
    } else {
    mess "$line\n" blue
    }
}

## Run advancing front mesher in batch mode, using the given <em>arglist</em>
## which is of the form [list <em>options inputfile outputfile</em>]. The full
## path names for <em>inputfile</em> and <em>outputfile</em> must be given.

proc ic_run_afmesh {inpfile outfile args} {
    global projlib ic_application_output_hook
    global env

    set pfile ""
    set geometry 0
    set prism ""
    set arglist ""
    set gui_output 0
    set error_subset ""
    set progenv AFMESH_BIN

    foreach {n v} $args {
        switch $n {
            geometry {set geometry $v}
            surface {if $v {lappend arglist -surface}}
            prism {set prism $v}
            family {lappend arglist -family $v}
	        expand - tetexpand {lappend arglist -expand $v}
	        verbose {if $v {lappend arglist -verbose}}
            keepfiles {if $v {lappend arglist -keep}}
            delaunay {if $v {lappend arglist -delaunay}}
            proximity {if $v {lappend arglist -proximity}}
            topo - topology {if $v {lappend arglist -topo}}
            join - merge {if $v {lappend arglist -join}}
	        tol - tolerance {lappend arglist -tol $v}
	        errors {set error_subset $v}
            progenv {set progenv $v}
	        show_progress {
		        if $v {
		            set gui_output 1
		        }
	        }
	    }
    }

    if {$geometry} {
        lappend arglist -tetin
        if {$prism != ""} {lappend arglist -inflate $prism}
    } else {
        lappend arglist -uns
	    if {[ic_geo_is_loaded]} {
	        if [ic_wb2_input_parameter_mode] {
                ic_wb2_update_while_input_parameter_mode
            }
	        set pfile [cmd_tempfile afmesh_parm .tmp]
	        set fp [open $pfile w]
            array set g [ic_get_meshing_params global 0]
            puts $fp "define_model $g(gmax) reference_size 1 \
                natural_size $g(gnat) natural_size_refinement $g(gnatref) \
                n_cells_in_gap $g(gcgap)"
	        foreach name [ic_geo_get_objects density] {
		        set np [ic_geo_num_nodes density $name]
		        set def "define_density_poly npnts $np"
		        foreach {n v} [ic_get_meshing_params density $name] {
		            switch $n {
			            emax {append def " size $v"}
			            ewid {append def " width $v"}
			            erat {append def " size_ratio $v"}
		            }
		        }
		        puts $fp $def
		        for {set n 0} {$n < $np} {incr n} {
		            puts $fp [ic_geo_get_node density $name $n]
		        }
	        }
	        foreach name [ic_geo_get_objects material] {
		        if {$name != "ORFN"} {
		            set loc [ic_geo_get_material_location $name]
		            set fam [ic_geo_get_family material $name]
		            puts $fp "material_point $loc family $fam"
		        }
	        }
	        close $fp
	        lappend arglist -mesh $pfile
	    }
    }

    lappend arglist $inpfile $outfile
    set logfile afmesh_cmd.log
    catch {file delete $logfile}

    give_back_mesher_license

    set ok [run_application_batch_exec_direct icemcfd/afmesh $progenv $arglist $logfile]

    if {$pfile != ""} {catch {file delete $pfile}}
    if {$ok < 0} {
	    mess "afmesh was terminated\n" red
	    set ok 0
    } elseif !$ok {
        mess "Error: afmesh exited with a non-zero status\n" red
    } elseif [catch {open $logfile r} fp] {
	    mess "Error: $fp\n" red
	    set ok 0
    } else {
	    set errors ""
        set nerror 0
	    while {[gets $fp line] >= 0} {
            if {[string match {*ATAL_ERROR_START} $line]} {
                set nerror 1
            } elseif {[string match {*ATAL_ERROR_END} $line]} {
                set nerror 0
            } elseif {[string match {ERROR:*} $line]} {
                append errors "[string range $line 6 end]\n"
            } else {
                if {$nerror} {append errors "$line\n"}
            }
	    }
        catch {close $fp}
        if {$errors != ""} {
	        mess "Error: afmesh failed\n" red
	        set ok 0
	    }
    }
    if {$ok && ![file exists $outfile]} {
	    mess "Error: afmesh didn't create $outfile\n" red
	    set ok 0
    }

    return $ok
}

## Run tgrid mesher in batch mode, using the given <em>arglist</em>
## which is of the form [list <em>options inputfile outputfile</em>]. The full
## path names for <em>inputfile</em> and <em>outputfile</em> must be given.

proc ic_run_tgrid {inpfile outfile args} {
    global projlib ic_application_output_hook

    set pfile ""
    set tetin ""
    set spacing ""
    set arglist ""
    set progenv TGRID_BIN

    foreach {n v} $args {
	    switch $n {
	        family {lappend arglist -f$v}
            spacing {set spacing $v}
            tetin {set tetin $v}
            tetexpand - expand {lappend arglist -t$v}
            bgmesh {if $v {lappend arglist -b}}
            bgfile {lappend arglist -S$v}
	        keepall {if $v {lappend arglist -k}}
	        verbose {if $v {lappend arglist -v}}
            progenv {set progenv $v}
            use_af {if $v {lappend arglist -a}}
	    }
    }

    if {$tetin == ""} {
	if {[ic_geo_is_loaded]} {
	
	    if [ic_wb2_input_parameter_mode] {
	        ic_wb2_update_while_input_parameter_mode
        }
	
	    set pfile [cmd_tempfile tgrid_parm .tmp]
	    set fp [open $pfile w]
	    foreach {n v} [ic_get_meshing_params global 0] {
		if {$n == "gmax"} {
		    puts $fp "define_model $v"
		    break
		}
	    }
	    foreach name [ic_geo_get_objects density] {
		set np [ic_geo_num_nodes density $name]
		set def "define_density_poly npnts $np"
		foreach {n v} [ic_get_meshing_params density $name] {
		    switch $n {
			emax {append def " size $v"}
			ewid {append def " width $v"}
			erat {append def " size_ratio $v"}
		    }
		}
		puts $fp $def
		for {set n 0} {$n < $np} {incr n} {
		    puts $fp [ic_geo_get_node density $name $n]
		}
	    }
	    foreach name [ic_geo_get_objects material] {
		if {$name != "ORFN"} {
		    set loc [ic_geo_get_material_location $name]
		    set fam [ic_geo_get_family material $name]
		    puts $fp "material_point $loc family $fam"
		}
	    }
	    close $fp
	    lappend arglist -p$pfile
	} else {
            if {$spacing == "" && $projlib != ""} {
            
                if [ic_wb2_input_parameter_mode] {
                    ic_wb2_update_while_input_parameter_mode
                }
                
		        foreach {n v} [ic_get_meshing_params global 0] {
		            if {$n == "gmax"} {
			            set spacing $v
			            break
		            }
		        }
	        }
        }
    } else {
	    lappend arglist -p$tetin
    }
    if {$spacing != ""} {lappend arglist -g$spacing}

    lappend arglist $inpfile $outfile
    set logfile tgrid_cmd.log
    catch {file delete $logfile}

    give_back_mesher_license

    set ok [run_application_batch_exec_direct icemcfd/tgrid $progenv \
	$arglist $logfile]

    if {$pfile != ""} {catch {file delete $pfile}}
    if {$ok < 0} {
	mess "tgrid was terminated\n" red
	set ok 0
    } elseif !$ok {
	mess "Error: tgrid exited with a non-zero status\n" red
    } elseif [catch {open $logfile r} fp] {
	mess "Error: $fp\n" red
	set ok 0
    } else {
	set last ""
	while {[gets $fp line] >= 0} {
	    set last $line
	}
	catch {close $fp}
	set line [string trim $last]
	if {$line != "status 0"} {
	    mess "Error: tgrid terminated prematurely\n" red
	    set ok 0
	}
    }
    if {$ok && ![file exists $outfile]} {
	mess "Error: tgrid didn't create $outfile\n" red
	set ok 0
    }

    return $ok
}

## Run the cutter, using an existing <em>tetin</em> file and
## <em>uncut_domain</em> produced by tetra.
## One or more arguments may be given.  These arguments are:
## <ul>
## <li> <b>saveall</b> <em>on</em> : if <em>on</em> is 1 (the default) then
## all elements will be saved, otherwise selected families
## <li> <b>savefams</b> <em>f1 f2 ...</em> : the families to save if the
## <b>saveall</b> option was 0
## <li> <b>which</b> <em>type</em> : either <b>volume</b> (the default) or
## <b>surface</b>
## <li> <b>fix_nonman</b> <em>on</em> : if <em>on</em> is 1 then fix
## non-manifold vertices, otherwise not
## <li> <b>autovol</b> <em>0 | 1</em> : turn off (or on) automatic
## assignement of a material to unassigned tetrahedra. (D = 0)
## <li> <b>log</b> <em>logfile</em> : the file to write stdout to
## </ul>

proc ic_run_cutter {tetin uncut_dom cut_dom args} {
    global projlib is_win

    if ![lindex [check_licenses_enabled] 0] {
	error "No med license was obtained"
    }

#    if {$projlib == ""} {
##	error "Can't run cutter unless you load a tetin file first"
#    }

    set saveall 1; set savefams ""; set which volume; set fix_nonman 0
    set autovol 0; set incorfn 0; set logfile ""

    foreach {n v} $args {
	switch $n {
	    saveall {
		set saveall $v
	    }
	    savefams {
		set savefams $v
	    }
	    which {
		set which $v
	    }
	    fix_nonman {
		set fix_nonman $v
	    }
	    autovol {
		set autovol $v
	    }
	    log {
		set logfile $v
	    }
	    default {
		mess "Warning: ignored unknown option $n $v\n"
	    }
	}
    }

    set ifile [cmd_tempfile cutter_cmd .tmp]
    set fp [open $ifile w]
    puts $fp "flood_fill"

    set fams ""
    if $saveall {
	foreach  f [ get_famnames ] {
		if { $f != "ORFN" } {
			lappend fams $f
		}
	}
    } else {
	set fams $savefams
    }
    if $fix_nonman {
	puts $fp "fix_non_manifold"
    }
    if {$which == "surface"} {
	puts $fp "save only_surface $fams"
    } else {
	puts $fp "save $fams"
    }
    puts $fp "quit"
    close $fp

    if $is_win {
	set prog icemcfd/cutter_batch
    } else {
	set prog icemcfd/cutter
    }

    if $autovol {
	set opts autovol
    } else {
	set opts ""
    }

    give_back_mesher_license

    set ret [run_application_batch $prog CUTTER_BIN \
		 "\"$uncut_dom\" \"$tetin\" \"$cut_dom\" $opts <$ifile" \
		 $logfile]
    cmd_rm $ifile
    return $ret
}

##
## Runs the prism mesher, using an existing <em>tetin</em> file and
## domain file <em>cut_dom</em> produced by cutter.
## New domain file <em>prism_dom</em> will be created.
## The parameter <em>tetin</em> is mandatory. If no geometry file should be used,
## the first argument has to be empty quotes:
## <ul>
## <b>ic_run_prism</b> "" <em>cut_dom</em> <em>args</em>
## <ul>
## One or more arguments may be given.
## Arguments, default values [...], and optional values /.../ are listed below:
## <ul>
## <li> <b>layers</b> [3]
## <li> <b>height</b> [0.2]
## <li> <b>ratio</b>  [1.0]
##
## All parts will be used for prism as a default!
## User can define list of parts:
##   <li> <b>family</b> {fam_name fam_name ...}
## Or user can specify the list with different height and/or ratio for selected parts:
## In both cases parameters height and ratio will be used as defaults.
##   <li> <b>family</b> {{fam_name height 0.35} {fam_name height 0.3 ratio 1.1} ...}
##
## <li> <b>n_triangle_smoothing_steps</b> [5]
## <li> <b>n_tetra_smoothing_steps</b> [10]
## <li> <b>ortho_weight</b> [0.5]
## <li> <b>total_height</b> []
## <li> <b>max_aspect_ratio</b> []
## <li> <b>prism_height_limit</b> []
## <li> <b>triangle_quality</b> [inscribed_ratio] /laplace height_over_base skewness
##						   min_angle max_angle inscribed_area/
## <li> <b>max_prism_angle</b> [180]
## <li> <b>fillet</b> [0.1]
## <li> <b>law</b> [exponential] /linear / wb-exponential
## <li> <b>into_orphan</b> 0
## <li> <b>new_volume</b> []
## <li> <b>side_family</b> []
## <li> <b>top_family</b> []
## <li> <b>smoothing_steps</b> [6]
## <li> <b>min_prism_quality</b> [0.01]
## <li> <b>max_binary_tetin</b> [0]
## <li> <b>use_prism_v10</b> [0]
## </ul>
## <p>
## Example of ic_run_prism usage:
## ic_run_prism $tetin $domain $new_domain layers 5 height 0.33 family {TOP {WALLS height
##									  0.44} BOTTOM}
##
## Note: using the "use_prism_v10 1" argument starts the old prism 10.0 version instead of the current version 

proc ic_run_prism {tetin cut_dom prism_dom args} {
    global projlib is_win env

    # For all hex mesh, prism doesn't work well, hence run bfcart inflation 
    set nhexas [ic_count_elements HEXA_8]
    set ntetras [ic_count_elements TETRA_4]
    set npyras [ic_count_elements PYRA_5]
    set nmixed [expr $ntetras + $npyras]
    if {$nhexas > 0 && $nmixed == 0} {
	    set stat [ic_hexcart_bfcart_inflation]
	    return $stat
    }

    # Don't do give_back_mesher_license because this needs a prism and we never grab that
    if ![lindex [check_licenses_enabled] 0] {
	    error "No med license was obtained"
	    return 0
    }

    if {![file exists $tetin]} {
        mess "Warning: tetin file does not exist. No geometry will be passed to prism\n"
        set tetin ""
    }
    if {$tetin == ""} {
        mess "Running prism with automatically generated tetin\n"
    }
    if {$cut_dom == "" || ![file exists $cut_dom]} {
	    error "Mesh input file not found"
	    return 0
    }
    if {$prism_dom == ""} {
	    error "Prism mesh output file not given"
	    return 0
    }

    set logfile ""
    set paramfile ""
    set numfams 0

    # check args prism_params file
    set iparam [lsearch -exact $args params]
    if {$iparam >= 0} {
        incr iparam
        set paramfile [lindex $args $iparam]
    }
    if {$paramfile == ""} {
        set paramfile [cmd_tempfile prism_params .tmp]
        set iparam 0
    }

    set logicals {into_orphan init_dir_from_prev delete_base_triangles do_checks do_not_allow_sticking print_memory_usage graphics}

    # Prism input file
    if {$is_win} {
        # shortname in case of file name with spaces
        if {[llength $tetin] > 1} {
            set tetin [win_shortname $tetin]
        }
        if {[llength $cut_dom] > 1} {
            set cut_dom [win_shortname $cut_dom]
        }  
        if {[llength $prism_dom] > 1} {
            set prism_dom [win_shortname $prism_dom]
        }
    }

    if [ic_wb2_input_parameter_mode] {
        ic_wb2_update_while_input_parameter_mode
    }

    set use_v10 0
    set idx_v10 [lsearch -exact $args use_prism_v10]
    if {($idx_v10 >= 0) && ([lindex $args [expr $idx_v10 + 1]] == "1")} {
        set use_v10 1
        if {$tetin != ""} {
            mess "Saving geometry as tetin version 10.0 file for prism v10\n"
            ic_save_tetin $tetin 0 0 {} {} 1 0 1
        }
    }

    set fileId [open $paramfile "w"]
    if {$tetin != ""} {
        puts $fileId "surface_file $tetin"
    }
    puts $fileId "input_file $cut_dom"
    puts $fileId "output_file $prism_dom"

    foreach {n v} $args {
        if $use_v10 {
            if $is_win {
                if {$n == "stair_step" || $n == "blayer_2d" || $n == "use_existing_quad_layers" || $n == "interpolate_heights" || $n == "stop_columns" || $n == "incremental_write" || $n == "verbosity_level"} {
                    mess "Ignoring $n option for prism v10\n"
                    continue
                }
            } else {
                if {$n == "blayer_2d" || $n == "use_existing_quad_layers" || $n == "interpolate_heights" || $n == "incremental_write" || $n == "verbosity_level"} {
                    mess "Ignoring $n option for prism v10\n"
                    continue
                }
            }
        }
        if {$n == "log"} {
            set logfile $v
        } elseif {$n == "params"} {
            # skip this
        } elseif {$n == "use_prism_v10"} {
            # skip this
        } elseif {[lsearch $logicals $n] >= 0} {
            if {$v == "1"} {
                puts $fileId $n
            }
        } else {
            if {$v != ""} {
                if {$n == "family"} {
                    if [ic_wb2_input_parameter_mode] {
                        set p [lindex $v 0]
                        if [ic_wb2_is_input_part_param_set prism $p] {
                            if {[ic_geo_get_family_param $p prism] == 1}  {
                                set new $p
                                foreach "nn vv" [lrange $v 1 end] {
                                    set vvv [ic_wb2_get_input_prism_part_value $nn $p] 
                                    if {$vvv != ""} {
                                        lappend new $nn $vvv
                                    } else {
                                        lappend new $nn $vv
                                    }
                                }
                                set v $new
                                incr numfams
                            }
                        } else {
                            incr numfams
                        }
                    } else {
                        incr numfams
                    }
                }
                puts $fileId "$n $v"
                set already_defined($n) $v
            }
        }
    }

    # set the global values - prism doesn't read these yet
    foreach {n v} [ic_get_meshing_params prism 0] {
        # if already set by args (higher priority), ignore the param
        if [info exists already_defined($n)] continue
        if $use_v10 {
            if $is_win {
                if {$n == "stair_step" || $n == "blayer_2d" || $n == "use_existing_quad_layers" || $n == "interpolate_heights" || $n == "stop_columns" || $n == "incremental_write" || $n == "verbosity_level"} {
                    mess "Ignoring $n option for prism v10\n"
                    continue
                }
            } else {
                if {$n == "blayer_2d" || $n == "use_existing_quad_layers" || $n == "interpolate_heights" || $n == "incremental_write" || $n == "verbosity_level"} {
                    mess "Ignoring $n option for prism v10\n"
                    continue
                }
            }
        }
        puts $fileId "$n $v"
    }


    # if no families given, get them from geometry
    if !$numfams {
        foreach fam [ic_geo_get_prism_families] {
            puts $fileId "family $fam"
            incr numfams
        }
    }

    close $fileId

    if {!$numfams} {
        if {$iparam == 0} {
            if [file exists $ifile] {
                cmd_rm $ifile
            }
        }
        error "No parts specified for prism meshing"
        return 0
    }

    if $use_v10 {
        if $is_win {
            if {![info exists env(ANSYS71_DIR)] && [info exists env(ANSYS170_DIR)]} {
                set env(ANSYS71_DIR) $env(ANSYS170_DIR)
            }
        }
    	set ok [run_application_batch_exec_direct icemcfd/prism_v10 PRISM_BIN $paramfile $logfile]
    } else {
    	set ok [run_application_batch_exec_direct icemcfd/prism PRISM_BIN $paramfile $logfile]
    }	
    if {$iparam == 0} {
        if [file exists $ifile] {
            cmd_rm $ifile
        }
    }

    if {$ok < 0} {
	    mess "prism was terminated\n" red
	    set ok 0
    } elseif !$ok {
	    mess "Error: prism exited with a non-zero status\n" red
    } elseif [catch {open $logfile r} fp] {
	    mess "Error: $fp\n" red
	    set ok 0
    } else {
	    set ok 0
	    while {[gets $fp line] >= 0} {
            if {$line == "prism finished"} {
                set ok 1
                break
            }
	    }
	    catch {close $fp}
	    if !$ok {
	        mess "Error: prism terminated prematurely\n" red
	    }
    }
    if {$ok && ![file exists $prism_dom]} {
        mess "Error: prism didn't create $prism_dom\n" red
        set ok 0
    }
    return $ok
}

## Run the hexa mesher in batch mode, using the given <em>tetin</em> file
## and <em>blocking</em>, which may be "" to start with no blocking, and
## <em>replay</em> file which will be used as the standard input.  The project
## directory should be given as <em>proj_dir</em>.

proc ic_run_hexa {tetin blocking replay  { proj_dir "" } { domains_dir "" } {  mesh_dir "" } } {
    global batch_mode
    
    if { $proj_dir == "" } {
	    set proj_dir [ file dirname $tetin ]
	    if { [ file tail $proj_dir ] == "mesh" } {
		    set proj_dir [ file dirname $proj_dir ]
	    }
    }
    if ![lindex [check_licenses_enabled] 0] {
        error "No med license was obtained"
    }
    if { $domains_dir == "" } {
        set domains_dir $proj_dir/domains
    }
    if { $mesh_dir == "" } {
        set mesh_dir $proj_dir/mesh
    }
    give_back_mesher_license
    if {$blocking != ""} {
	    set stat [run_application_batch icemcfd/hexa HEXA_BIN \
			"\"$proj_dir\" \"$blocking\" -tetin \"$tetin\" \
			-i \"$replay\" -ignore_errors \
			-domains_dir \"$domains_dir\" \
			-mesh_dir \"$mesh_dir\"" ]
    } else {
	    set stat [run_application_batch icemcfd/hexa HEXA_BIN \
			"\"$proj_dir\" -tetin \"$tetin\" \
			-i \"$replay\" -ignore_errors \
			-domains_dir \"$domains_dir\" \
			-mesh_dir \"$mesh_dir\""]
    }
    if { $stat == 0 && $batch_mode } {
        error "HEXA failed"
    }
    return $stat
}

## Run frontal in batch mode, using the given <em>tetin</em> file.
## The full path name must be given.
## One or more arguments may be given.  These arguments are:
## <ul>
## <li> <b>dir</b> <em>xyz</em> : the <em>xyz</em> argument is a list of
## 3 numbers that determines what direction we are looking
## <li> <b>tol</b> <em>tol</em> : the tolerance to use
## <li> <b>curve</b> <em>curvefile</em> : the curve file to write
## </ul>

proc ic_run_frontal { args} {
    global projlib

    if ![ic_geo_num_objects surface] {
	user_error "No surface geometry is available"
	return
    }

    set dir ""
    set tol ""
    set curve ""
    foreach {n v} $args {
	switch $n {
	    dir   { set dir [coords_dir_into_global $v] }
	    tol   { set tol $v }
	    curve { set curve $v }
	    default {
		mess "Warning: ignored unknown option $n $v\n"
	    }
	}
    }
    if {$tol == ""} { tol = .01 }

    set args2 $tol
    if { $dir != "" } {
	append args2 " -dir $dir"
    }

    set area [lindex [ eval $projlib compute_frontal_area $args2 ] 0]
    mess "Frontal area: $area\n"
    update_bsp_consistency
}

## Kill the current running application.

proc ic_application_terminate {} {
    global application_running
    if [info exists application_running] application_terminated
}

## Free surface meshing by the extended splitting line algorithm.
## The arguments come in keyword value pairs. Possible options are:
## <ul>
## <li><b>name</b> <em>value</em> : Name of the mapping
## <li><b>q</b> <em>1/2/3/4/5</em> :
## <ul>
##  <li>1 = loop(s) from loop entities (default)
##  <li>11 = loop(s) from surface entities
##  <li>2 = loop from bar elements
##  <li>3 = loop from subset of bar elements
##  <li>4 = single loop from surface elements
##  <li>5 = single loop from surface elements, replace old
##        elements if new elements have higher quality,
##        work only on free surface nodes
##  <li>-1 = loop(s) from loop entities, create bar elements only
## </ul>
## <li><b>h</b> <em>1/2/3/4</em> :
##     Ignore holes if 1, mesh only holes if 2, mesh
##     hull of surface elements if 3, or mesh gaps of surface elements if 4.
##     The default is 0.
## <li><b>element</b> <em>1/2/3/4/5</em> : If set to 4, every
##     loop must have an even number of nodes on the edges in order
##     to form an all-quadrilateral QUAD_4 mesh. This constrain
##     will be automatically satisfied by creating an even number of
##     nodes on each curve, or it will be dropped if the creation of
##     one (option 1) or several (option 2) TRI_3 transition triangles
##     is accepted. The meshing scheme can also generate TRI_3
##     triangle surface meshes (option 3), TRI_3 STL-like facets
##     (option 5), structured QUAD_4 grids (option 6), and TRI_3 split
##     STL facets (option 7). The default is 4.
## <li><b>inner</b> <em>value</em> : Preferred interior element size
##     if > 0.  Take from surface parameters if -1.  The default is 0.
## <li><b>offset</b> <em>n</em> : Create n layers of offset elements.
##     The default is 0.  Argument "curve_family" can be used to limit
##     offsetting to this family.  A negative value will be only applied
##     to curves with a given height parameter.
## <li><b>offset_h</b> <em>value</em> : Relative value for the offset
##     height. The default is 1.  Please use -1 to apply the curve
##     parameters.
## <li><b>dev</b> <em>value</em> : Refine mesh up to three times to
##     reduce surface deviation. Value defines the maximum surface
##     deviation if not 0. The default is 0.
## <li><b>proj</b> <em>0/1</em> : Project the new nodes onto the CAD
##     surfaces if 1, or onto an an isoparametric quadratic
##     interpolation surface if 2.  The default is 0.
## <li><b>iters</b> <em>maximum</em> : The mesh quality can be improved
##     if additional smoothing steps are performed. With this option set,
##     a pure Laplacian smoothing is used in which each interior node is
##     moved to the centroid of its neighbors. In typical cases smoothing
##     will converge after about six steps. The default maximum number
##     of steps is 20.  No complains about inaccuracy if negative.
## <li><b>conver</b> <em>value</em> : The smoothing accuracy
##     defines a convergence value which is related to the
##     element sizes. The default value is 0.025.
##     Uses an activity control for better performance if negative.
## <li><b>b_smooth</b> <em>0/1</em> : Smooth boundaries if 1. The default is 0.
## <li><b>improvement</b> <em>0/1/2/3</em> : Allow advanced improvement
##     operations (node and element elimination, diagonal swapping,
##     element merging and splitting, etc.) if set. The default is 0.
## <li><b>violate</b> <em>value</em> : Allow a node to move off the
##     surface by this value. The default is 0.
## <li><b>merge_tol</b> <em>value</em> : Merge tolerance
##     related to element size.  The default is 0.1.
## <li><b>ele_tol</b> <em>value</em> : The default element tolerance
##     is 0. Set this violate geometry value to remove tiny elements,
##     holes and loops.  Given bunching will be protected if < 0.
## <li><b>boundary_tol</b> <em>value</em> : The default boundary tolerance
##     is -1.  The mesher then applies above ele_tol on the boundary.  Set
##     this value in case you want to have an independent violate geometry
##     tolerance for the boundary elements.
## <li><b>geo_tol</b> <em>value</em> : The default looping tolerance
##     is 0, which means that curves are only connected via identical
##     endpoints. Set this value to enable free surface meshing
##     to calculate the loop topology of inexact curves.
## <li><b>bunch</b> <em>0/1</em> : Use node bunching from BAR_2 elements
##     on curves if 1. The default is 0.
## <li><b>curve_family</b> <em>name</em> : Use curves from this family
##     to define a virtual loop.
## <li><b>curve_family_2</b> <em>name</em> : Use also curves from this
##     family to define a virtual loop but never associate BAR_2
##     elements to these curves.
## <li><b>error_family</b> <em>name</em> : Move failed loops to this
##     family.
## <li><b>offset_element_family</b> <em>name</em> : Move offset elements
##     to this family.
## <li><b>material</b> <em>name</em> : Label new elements with this
##     family name.
## <li><b>time_max</b> <em>limit</em> : Stop the mesher if time limit
##     has been exceeded.  Default is infinite seconds.
## <li><b>ele_max</b> <em>limit</em> : Stop the mesher if the element
##     limit has been exceeded.  Default is infinite number of elements.
## <li><b>try_twice</b> <em>0/1/-n</em> :  Try simple triangulation if 1
##     and meshing fails.  First try simple triangulation and then retry
##     without holes if 2.  The default is 1.  A negative value n
##     limits simple triangulation to loops with number of nodes <= n.
##     (for element type 2 and 3 only)
## <li><b>loops</b> <em>names</em> :  List of selected loops.
##     The default is to mesh all loops.
## <li><b>block</b> <em>value</em> : Create a structured mesh instead
##     of an unstructured up to this block quality if value > 0. The
##     default value is 0.  A suitable choice for hybrid meshes would
##     be 0.2.
## <li><b>adjust_nodes</b> <em>value</em> : Recompute number of nodes on
##     edges such that all structured loops can be map meshed if set.
##     Smooth nodes if > 1.  The default value is 0.
## <li><b>adjust_nodes_max</b> <em>value</em> : Relative limit
##     for node adjustment.  The default value is 0.5 so that only
##     those edges are adjusted where a maximum of 50% of the nodes
##     need to be removed.
## <li><b>ignore_tp</b> <em>0/1</em> : Ignore location of topological
##     curve end points if set.  The default value is 0.
## <li><b>board</b> <em>0/1</em> : Run improved meshing scheme for
##     boards with many holes if set.  The default value is 0.
## <li><b>complete_edges</b> <em>0/1/2</em> : Complete missing edges
##     in boundary loop if 1.  2 allows more than one loop from bar
##     elements (q = 2 or 3).  Broken loops are ignored.  The default
##     value is 0.
## <li><b>geo_id</b> <em>0/1</em> : Create elements with family name
##     of nearest surface if set.  The default value is 0.
## <li><b>enn</b> <em>0/1</em> : Estimated number of nodes if set.
##     The default value is 0.
## <li><b>dormant</b> <em>0/1</em> : Merge loops at dormant curves if set.
##     The default value is 0.
## <li><b>pattern</b> <em>value</em> : Improve element pattern up to this
##     angle if > 0.  The default value is 150 degree.
## <li><b>simple_offset</b> <em>0/1</em> : Do simple normal offsetting if set.
##     The default value is 0.
## <li><b>four</b> <em>0/1</em> :  Force four-quad patterns at prescribed points if set.
##     The default value is 0.
## <li><b>max_area</b> <em>value</em> : Maximum element area if set.  The default is 0.
## <li><b>max_length</b> <em>value</em> : Maximum edge length if set.  The default is 0.
## <li><b>min_angle</b> <em>value</em> : Minimum angle if set.  The default is 0.
## <li><b>max_nodes</b> <em>value</em> : Maximum number of nodes if set.
##     The default is 0.
## <li><b>max_elements</b> <em>value</em> : Maximum number of elements if set.
##     The default is 0.
## <li><b>n_threads</b> <em>value</em> : Number of threads.  The default is 0.
## <li><b>freeb</b> <em>0/1</em> :  Allow free bunching for patch independent
##     surfaces if set.  The default is 0.
## <li><b>snorm</b> <em>0/1</em> :  Orient elements to surface normals
##     if set.  The default is 0.
## <li><b>shape</b> <em>0/1</em> :  Element shape function, 0 is linear
##     and 1 is quadratic.  The default is 0.
## </ul>
proc ic_quad {args} {
    global uns_cur uns_type_visible projlib batch_mode
    #
    if ![lindex [check_licenses_enabled] 0] {
        error "No med license was obtained."
    }
    set name ""
    set q 1
    set h 0
    set element 4
    set inner 0
    set proj 0
    set iters 20
    set conver 0.025
    set b_smooth 0
    set improvement 0
    set violate 0
    set merge_tol 0.1
    set geo_tol 0
    set ele_tol 0
    set bunch 0
    set curve_family ""
    set curve_family_2 ""
    set error_family ""
    set offset_element_family ""
    set material ""
    set offset 0
    set offset_h 1.0
    set dev 0
    set time_max 0
    set ele_max 0
    set err 0
    set debug 0
    set surf 0
    set try_twice 1
    set loops ""
    set block 0
    set make_consist 0
    set complete_edges 0
    set boundary_tol -1
    set adjust_nodes 0
    set adjust_nodes_max 0.5
    set ignore_tp 0
    set board 0
    set geo_id 0
    set enn 0
    set dormant 0
    set pattern 150
    set no_undo 0
    set simple_offset 0
    set from_surf 0
    set four 0
    set max_area 0
    set max_length 0
    set min_angle 0
    set max_nodes 0
    set max_elements 0
    set n_threads 0
    set breakpoint 0
    set freeb 0
    set snorm 0
    set shape 0
    foreach {n v} $args {
        switch $n {
            name - q - h - element - proj - iters - conver - improvement - violate - \
            geo_tol - ele_tol - bunch - curve_family - curve_family_2 - error_family - \
            offset_element_family - material - offset - offset_h - dev - time_max - \
            ele_max - try_twice - loops - block - make_consist - complete_edges - \
            inner - boundary_tol - adjust_nodes - adjust_nodes_max - merge_tol - \
            ignore_tp - board - geo_id - enn - dormant - pattern - no_undo - \
            from_surf - simple_offset - b_smooth - four - max_length - max_area - \
            min_angle - max_nodes - max_elements - n_threads - breakpoint - freeb - \
            snorm - shape - debug {
                set $n $v
            }
            default {error "Unknown Quad meshing option $n $v."}
        }
    }
    if {![ic_geo_is_loaded] && $q == "1"} {
        ic_mess "No loops found.\n" red
        return 2
    }
    set loop_numbers ""
    if {$from_surf} {
        set type surface
    } else {
        set type loop
    }
    foreach ln $loops {
        if {[ic_geo_check_objects_exist $type $ln] == ""} {
            ic_mess "No $type named \"$ln\".\n" red
        } else {
            lappend loop_numbers [$projlib get_number_by_name $type $ln]
        }
    }
    if ![ic_uns_is_loaded] {ic_uns_create_empty}
    ensure_unstruct_mesh_exists
    if {$name == ""} {
        set map [$uns_cur get_map visible]
    } else {
        set map [$uns_cur get_map $name]
        if {$q == "3"} {
            set map [uns_get_subset $name]
        }
    }
    if {[$map element_info] == "" && $loops == ""} {
        set map [$uns_cur get_map all]
    }
    #   The quad code will modify the map, by promoting the type, so we'd better
    #   make a temporary copy and get rid of it when quad is done.
    set tmp_map [uns_map -mesh $uns_cur]
    $tmp_map copy_from $map 0
    if {[lsearch [$tmp_map element_info] VERTEX] < 0 && $loops == ""} {
        set the_nodes ""
        foreach e [$tmp_map element_info] {
            set t [lindex $e 2]
            if {$t == "BAR_2"} {
                lappend the_nodes [lindex $e 3]
            }
        }
        set uni_pairs [quad_uniquify_pairs $the_nodes]
        foreach n $uni_pairs {   
            $tmp_map add_nodes_by_number $n 0
        }
    }
    if {$proj == "2"} {
        set surf 1
        set proj 0
    }
    if [ic_geo_is_loaded] {
        set p $proj
    } else {
        set p 0
    }
    # reset all previous calls for busy to enable user interrupt in the gui
    busy reset
    if {!$no_undo} {ic_newop}
    if {!$no_undo} {start_new_elems}
    if [catch {
        $tmp_map quad $q $h $element $p $iters $conver $b_smooth $improvement $violate \
        $bunch $loop_numbers $merge_tol $geo_tol $ele_tol $curve_family $curve_family_2 \
        $error_family $material $offset $offset_h $offset_element_family $dev \
        $time_max $ele_max $surf $try_twice $block $make_consist $complete_edges \
        $inner $boundary_tol $adjust_nodes $adjust_nodes_max $ignore_tp $board $geo_id \
        $enn $dormant $pattern $simple_offset $four $max_length $max_area $min_angle \
        $max_nodes $max_elements $n_threads $breakpoint $freeb $snorm $shape $debug
        } res] {
        ic_mess "$res\n" red
        set err 4
    }
    if {!$no_undo} {finish_new_elems}
    $tmp_map delete
    display_quad_results
    busy 0
    return $err
}

## Free surface meshing from geometry.  This option creates and deletes it's own loops
## used in the Quad mesher.  Provided for backward compatibility.  Please use ic_quad2
## instead.
## <ul>
## <li><b>what</b> : Can be "surfaces" (creates a loop per surface),
##     "native_surfaces (directly meshes each surface),
##     "connected_surfaces" (creates a loop per connected surfaces),
##     "surface_group" (list of surfaces, where the list of surfaces becomes one loop),
##     "curves" (list of curves becomes a loop),
##     or "curves_only" (list of curves to be meshed with bar elements).
## <li><b>entities</b> : List as described in "what".
## <li><b>element</b> : Please see ic_quad description for element.
## <li><b>proj</b> : Please see ic_quad description for proj.
## <li><b>iters</b> : Please see ic_quad description for iters.
## <li><b>conver</b> : Please see ic_quad description for conver.
## <li><b>b_smooth</b> : Please see ic_quad description for b_smooth.
## <li><b>tol</b> : Please see ic_quad description for geo_tol.
## <li><b>ele_tol</b> : Please see ic_quad description for ele_tol.
## <li><b>dev</b> : Please see ic_quad description for dev.
## <li><b>improvement</b> : Please see ic_quad description for improvement.
## <li><b>block</b> : Please see ic_quad description for block.
## <li><b>bunch</b> : Please see ic_quad description for bunch.
## <li><b>violate</b> : Please see ic_quad description for violate.
## <li><b>adjust_nodes</b> : Please see ic_quad description for adjust_nodes.
## <li><b>adjust_nodes_max</b> : Please see ic_quad description for adjust_nodes_max.
## <li><b>max_point_dist</b> : For backward compatibility.
## <li><b>try_harder</b> <em>0/3</em> :  Try harder if > 0 and meshing fails
##     (0 = just run the Quad mesher,
##      1 = try simple triangulation (for element type 2 and 3 only),
##      2 = try 1 and retry without merging at dormant curves,
##      3 = try 1 and/or Tetra mesher, and then retry 2).  The default is 0.
## <li><b>even_if_dormant</b> <em>0/1</em> :  Use curves even if they are dormant.
##     The default is 1.
## <li><b>offset</b> <em>n</em> : Create offset elements on curves with
##     a given height parameter if -1.  This is the default.
## <li><b>pattern</b> <em>value</em> :  Improve element pattern up to this
##     angle if > 0.  The default value is 150 degree.
## <li><b>big</b> <em>0/1</em> :  Undo/redo will be available for each meshing step if 0.
##     Just one undo/redo is stored for 1. The default value is 1.
## <li><b>board</b> <em>0/1</em> : Run improved meshing scheme for
##     boards with many holes if set.  The default value is 0.
## <li><b>remove_old</b> <em>-1/0/1</em> : Remove old elements if 1, don't remove
##     any elements if 0, use an automatic approach if -1.  The default value is -1.
## <li><b>inner</b> : Please see ic_quad description for inner.
## <li><b>simple_offset</b> : Please see ic_quad description for simple_offset.
## </ul>
proc ic_quad_from_geometry {what entities {element 4} {proj 0} {iters 20} {conver 0.025} \
    {tol 0.1} {ele_tol 0} {dev 0} {improvement 0} {block 0} {bunch 1} {violate 0} \
    {debug 0} {adjust_nodes 0} {adjust_nodes_max 0.5} {max_point_dist 10} {try_harder 0} \
    {even_if_dormant 1} {offset -1} {error_subset "Failed_surfaces"} {pattern 150} \
    {big 1} {board 0} {remove_old -1} {inner 0} {simple_offset 0} {split_tri 0}} {
    return [ic_quad2 what $what entities $entities element $element proj $proj \
        iters $iters conver $conver geo_tol $tol ele_tol $ele_tol dev $dev \
        improvement $improvement block $block bunch $bunch violate $violate \
        debug $debug adjust_nodes $adjust_nodes adjust_nodes_max $adjust_nodes_max \
        max_point_dist $max_point_dist try_harder $try_harder \
        even_if_dormant $even_if_dormant offset $offset error_subset $error_subset \
        pattern $pattern big $big board $board remove_old $remove_old inner $inner \
        simple_offset $simple_offset split_tri $split_tri]
}

proc ic_get_ignore_size {} {
    global ignore_size
    return $ignore_size
}
set dont_record_command(ic_get_ignore_size) 1

## Free surface meshing from geometry.
proc ic_quad2 {args} {
    global batch_mode projlib ignore_size
    #
    set what "surfaces"
    set entities ""
    set element 2
    set proj 1
    set iters -20
    set conver 0.025
    set b_smooth 0
    set geo_tol -1
    set ele_tol 0
    set dev 0
    set improvement 1
    set block 0.2
    set bunch 0
    set violate 0
    set debug 0
    set adjust_nodes 0
    set adjust_nodes_max 0.5
    set max_point_dist 0
    set try_harder 0
    set max_simple_triangulation 0
    set even_if_dormant 1
    set merge_dormant 1
    set smoothdormant 0
    set offset -1
    set error_subset "Failed_surfaces"
    set pattern 150
    set big 1
    set board 0
    set remove_old -1
    set inner 0
    set max_area 0
    set max_length 0
    set min_angle 0
    set max_nodes 0
    set max_elements 0
    set simple_offset 0
    set allow_free_surfaces 1
    set enn 0
    set ignore_size 0
    set from_surf 0
    set time_max 0
    set ele_max 0
    set split_tri 0
    set four 0
    set n_threads 0
    set material ""
    set breakpoint 0
    set freeb 0
    set snorm 0
    set shape 0
    
    if [ic_wb2_input_parameter_mode] {
        ic_wb2_update_while_input_parameter_mode
    }
    
    foreach {n v} $args {
        switch $n {
            what - entities - element - proj - iters - conver - geo_tol - ele_tol - \
            dev - improvement - block - bunch - violate - debug - adjust_nodes - \
            adjust_nodes_max - max_point_dist - try_harder - even_if_dormant - offset - \
            error_subset - pattern - big - board - remove_old - inner - simple_offset - \
            enn - allow_free_surfaces - b_smooth - time_max - ele_max - split_tri - \
            four - merge_dormant - max_area - max_length - min_angle - max_nodes - \
            max_elements - n_threads - smoothdormant - material - breakpoint - \
            max_simple_triangulation - freeb - snorm - shape {
                if [ic_wb2_input_parameter_mode] {
                    switch $n {
                        ele_tol {
                            if [ic_wb2_is_input_meshing_param_set ign_size] {
                                set vv [ic_get_meshing_param_value surface_global ign_size]
                                if {$vv != "___NA___"} { set v $vv }
                            }
                        }
                    }
                }
                set $n $v
            }
            default {error "Unknown Quad meshing option $n $v."}
        }
    }
    if {$geo_tol == -1} {
        if {$what != "curves_only"} {
            set geo_tol 0
        }
        #
        # geo_tol = -1 = automatic tiny geometry tolerance for pure curve meshing only
        #
        if {$debug > 1} {
            ic_mess "geo_tol = $geo_tol\n" red
        }
    }
    if {$entities == ""} {
        if {$what == "surfaces" || $what == "native_surfaces"} {
            set entities [ic_geo_get_objects surface]
        } elseif {$what == "curves"} {
            set entities [ic_geo_get_objects curve]
        } elseif {$what == "loops"} {
            set entities [ic_geo_get_objects loop]
        }
    }
    set error_family ""
    set q 1
    set offset_h -1.0
    set curve_family ""
    set offset_element_family ""
    if {[ic_geo_subset_exists $error_subset] != 0} {
        ic_geo_subset_delete $error_subset
    }
    set n_entities [llength $entities]
    if {$entities == ""} {
        set loops [ic_geo_get_objects loop]
        if {$loops == ""} {
            mess "No entities defined\n" red
            busy 0
            return 1
        } else {
            if {$what == "curves_only"} {
                mess "Meshing curves only.\n" red
                set q -1
            }
            set what "loops"
            set n_loops [llength $loops]
            if {$n_loops == 1} {
                mess "No entities defined, but found classic loop.\n" red
            } else {
                mess "No entities defined, but found $n_loops classic loops.\n" red
            }
            set try_harder 1
            set max_point_dist 0
            if {$remove_old == -1} {
                set remove_old 1
            }
        }
    } else {
        set n_curve    [llength [ic_geo_get_objects curve   "" $even_if_dormant]]
        set n_surface  [llength [ic_geo_get_objects surface "" $even_if_dormant]]
        if {$debug > 1} {
            mess "$n_entities entities for method \"$what\": \"$entities\"\n" red
            mess "n_curve = $n_curve\n" red
            mess "n_surface = $n_surface\n" red
        }
        if {$remove_old == -1 && ($what == "curves" || (($what == "surfaces" || $what == "native_surfaces") && $n_surface == $n_entities))} {
            set remove_old 1
            if {$debug > 0} {
                mess "Remove all old elements.\n" red
            }
        }
    }
    if {$what != "loops"} {
        set error_family "IC_QUAD_FROM_GEOMETRY"
        set i 0
        while {[ic_uns_check_family $error_family]} {
            incr i
            #
            # Create unique error family name.
            #
            set error_family "IC_QUAD_FROM_GEOMETRY$i"
        }
        ic_uns_new_family $error_family
        if {$debug > 1} {
            mess "Error part = \"$error_family\"\n" red
        }
    }
    #
    # Create Loops.
    #
    if ![ic_uns_is_loaded] {ic_uns_create_empty}
    busy 1
    ic_quad_undo "start" $big
    set boundary_curves ""
    set mid_curves ""
    set loops ""
    set surfList ""
    set degen_curve ""
    if {$what == "curves"} {
        set boundary_curves $entities
        set fam [ic_geo_get_family curve [lindex $entities 0]]
        if {!$big} {ic_undo_suspend}
        set loops [ic_geo_create_loop "" $fam names $entities 0 ""]
        if {!$big} {ic_undo_resume}
    } elseif {$what == "curves_only"} {
        set q -1
        set element 1
        set proj 1
        set even_if_dormant 0
        set merge_dormant 0
        if {!$big} {ic_undo_suspend}
        foreach c $entities {
            if {$material == ""} {
                set fam [ic_geo_get_family curve $c]
            } else {
                set fam $material
            }
            lappend loops [ic_geo_create_loop "" $fam names $c 0 ""]
        }
        if {!$big} {ic_undo_resume}
    } elseif {$what == "native_surfaces"} {
        #
        # loop(s) from surface entities
        #
        set q 11
        set surfList $entities
        set loops $entities
    } elseif {$what != "loops"} {
        set surfList $entities
        set flag_error 0
        set max_flag 3
        if {$n_entities <= (2 * $max_flag)} {
            set max_flag $n_entities
        }
        set ta [ tdv_active 0 ]
        #
        # Gather up the shadow curves foreach family in advance
        # but only if connectors exist on the model.
        #
        set model_has_directives [ic_meshdirect_model_has_directives]
        if $model_has_directives {
            foreach f [ic_geo_list_families] {
                set shadow_curves_in_srf_fam($f) ""
                foreach crv [ic_geo_get_objects curve $f] {
                    if [ic_meshdirect_entity_is_shadow_entity $crv] {
                        lappend shadow_curves_in_srf_fam($f) $crv
                    }
                }
                set shadow_points_in_srf_fam($f) ""
                foreach pnt [ic_geo_get_objects point $f] {
                    if [ic_meshdirect_entity_is_shadow_entity $pnt] {
                        lappend shadow_points_in_srf_fam($f) $pnt
                    }
                }
            }
        }
        # this command should support multiple surfaces grouped
        #   -->surface could be multiple surfs
        foreach surface $entities {
            set surf [lindex $surface 0]
            set surface_num [llength $surface]
            set fam [ic_geo_get_family surface $surf]
            set curves [ic_geo_boundary surface $surf 0 $even_if_dormant 1]
            if {$curves == ""} {
                incr flag_error
                if {$debug >= 0} {
                    if {$flag_error <= $max_flag} {
                        mess "No curves are attached to surface \"$surf\".\n" red
                    } elseif {$flag_error == ($max_flag + 1)} {
                        mess "...\n" red
                    }
                }
                continue
            } elseif {$debug > 1} {
                mess "Surface \"$surf\" has curves \"$curves\".\n" red
            }
            #
            # For a given surface, add the shadow points and curves from its family
            # to loop but only if connectors exist on the model.
            #
            if $model_has_directives {
                set surf_shadow_curves $shadow_curves_in_srf_fam($f)
                set surf_shadow_points $shadow_points_in_srf_fam($f)
            } else {
                set surf_shadow_curves ""
                set surf_shadow_points ""
            }
            append surf_shadow_points \
                [ic_geo_surface_get_objects $surf "embedded_points"]
            if {$debug > 1} {
                mess "Surface \"$surf\" has points \"$surf_shadow_points\".\n" red
            }
            if {$what == "surfaces"} {
                # if surface is only one surface build one loop per surface,
                # else group surfaces the latter is needed for batch mesher
                if {!$big} {ic_undo_suspend}
                if {$surface_num == 1} {
                    set loop [ic_geo_create_loop $fam.$surf $fam \
                        names $curves 0 $surf "" $surf_shadow_points]
                    set n_degen_curve [llength $surf_shadow_points]
                    if {$n_degen_curve} {
                        set all_curves [ic_geo_get_objects curve]
                        set n_all_curves [llength $all_curves]
                        set coff [expr $n_all_curves - $n_degen_curve]
                        while {$coff < $n_all_curves} {
                            lappend degen_curve [lindex $all_curves $coff]
                            incr coff
                        }
                        if {$debug > 1} {
                            mess "$n_degen_curve orphan curves: $degen_curve\n" red
                        }
                    }
                } else {
                    set loop [scrib_lib_quad_create_loop_for_group $fam $surface]
                }
                if {!$big} {ic_undo_resume}
                lappend loops $loop
            }
            if {$what == "surface_group" || $remove_old == -1} {
                #
                # Find internal curves between two attached surfaces to remove bar
                # elements.  Also used for oneLoop option.  Only boundaries should
                # be used for loop.
                #
                foreach crv $curves {
                    if {[lsearch $boundary_curves $crv] == -1} {
                        lappend boundary_curves $crv
                    } else {
                        lappend mid_curves $crv
                    }
                }
            }
        }
        tdv_active $ta
        if {$flag_error == $n_entities} {
            mess "No curves are available.  You may run the Build Topology function.\n" red
            if {!$allow_free_surfaces} {
                ic_quad_undo "finish" $big
                busy 0
                return 2
            } else {
                #
                # loop(s) from surface entities
                #
                set q 11
                set surfList $entities
                set loops $entities
                set what "native_surfaces"
                mess "Now automatically build topology and mesh native surfaces.\n"
                set btol -1
                set parameters [ic_set_meshing_params global 4711]
                foreach {name val} $parameters {
                    if {$name == "gmax"} {
                        if {$debug > 1} {
                            mess "global maximum element size = $val\n"
                            mess "ignore size = $ele_tol\n"
                        }
                        if {!$ele_tol && $val > 0} {
                            set ele_tol [expr $val * 0.3]
                            mess "Adjusting Ignore Size to $ele_tol\n" red
                            set ignore_size $ele_tol
                        }
                        if {$ele_tol} {
                            set btol [expr $ele_tol / 30.0]
                            mess "Using a Build Topology tolerance of $btol\n" red
                        }
                        break
                    }
                }
                set suspend 0
                if $big {
                    ic_undo_resume
                    set suspend 1
                }
                set trim_srfs 0
                set concat_crvs 1
                set quiet 1
                if {$debug > 0} {
                    set quiet 0
                }
                ic_geo_build_topo_on_srfs $entities "" $btol $trim_srfs \
                    $concat_crvs $quiet
                if $suspend {
                    ic_undo_suspend
                }
                set parameters [ic_set_meshing_params global 4711]
                foreach {name val} $parameters {
                    if {$name == "gmax"} {
                        if {$debug > 1} {
                            mess "global maximum element size = $val\n"
                        }
                        if {!$ele_tol && $val > 0} {
                            set ele_tol [expr $val * 0.1]
                            mess "Adjusting ignore size to $ele_tol\n" red
                        }
                        break
                    }
                }
            }
        }
    }
    if {$what == "surface_group"} {
        foreach crv $mid_curves {
            set place [lsearch $boundary_curves $crv]
            set boundary_curves [lreplace $boundary_curves $place $place]
        }
        if {$boundary_curves == ""} {
            mess "Could not find boundary curves\n" blue
            ic_quad_undo "finish" $big
            busy 0
            return 3
        }
        if {!$big} {ic_undo_suspend}
        set loops [ic_geo_create_loop "" $fam names $boundary_curves 0 $surfList ""]
        if {!$big} {ic_undo_resume}
    }
    #
    # Remove mesh if already exist for surfaces and curves.
    #
    if {$what != "curves_only"} {
        if {$remove_old == 1} {
            ic_delete_elements type QUAD_4 no_undo 1
            ic_delete_elements type QUAD_8 no_undo 1
            ic_delete_elements type TRI_3 no_undo 1
            ic_delete_elements type TRI_6 no_undo 1
            if {!$bunch} {
                ic_delete_elements type BAR_2 no_undo 1
                ic_delete_elements type BAR_3 no_undo 1
            }
            ic_delete_elements type NODE no_undo 1
            if {$debug > 1} {
                mess "Old elements removed.\n" red
            }
        } elseif {$remove_old == -1} {
            set delmaps ""
            if {!$bunch} {
                #  We should also delete the node elements!?
                foreach crv $mid_curves {
                    lappend delmaps [ic_uns_get_attached_elements curve $crv]
                }
            }
            foreach surf $surfList {
                set map [ic_uns_get_attached_elements surface $surf]
                set map_parts [ic_uns_subset_list_families $map]
                set surf_part [ic_geo_get_part surface $surf]
                if {[ic_uns_subset_count_elements $map] && $map_parts != "" && "$map_parts" != "$surf_part"} {
                    set map2 [ic_uns_subset_create "" 0]
                    ic_uns_subset_make_families $map2 $surf_part
                    ic_uns_uniqify $map2
                    #  Delete all attached elements except the node elements
                    ic_uns_subset_copy $map $map2 "1 2 3"
                    ic_uns_subset_delete $map2 
                }
                if {[ic_uns_subset_count_elements $map] && "[ic_uns_subset_list_families $map]" == "$surf_part"} {
                    lappend delmaps $map
                }
            }
            if {$what == "surface_group"} {
                set incsurfs ""
                if [info exists surf_added] {unset surf_added}
                foreach crv $boundary_curves {
                    set surfs [ic_geo_incident curve $crv]
                    foreach surf $surfs {
                        if ![info exists surf_added($surf)] {
                            set surf_added($surf) 1
                        } else {
                            incr surf_added($surf)
                        }
                    }
                }
                foreach surf [array names surf_added] {
                    if {$surf_added($surf) == [llength $boundary_curves]} {
                        lappend delmaps [ic_uns_get_attached_elements surface $surf]
                    }
                }
            }
            foreach elmap $delmaps {
                ic_delete_elements subset $elmap no_undo 1
                ic_uns_subset_delete $elmap
            }
            if {$debug > 0} {
                mess "Old elements removed using automatic approach.\n" red
            }
        }
    }
    #
    # Add unconnected points to the loops if "what" is "surface" or "surface_group"
    #
    if {$max_point_dist > 0 &&
        (($what == "surface_group") || ($what == "surfaces"))} {
        if {$debug > 1} {
            mess "max_point_dist = \"$max_point_dist\"\n" red
        }    
        set pt_list [scrib_lib_geo_get_unattached_points ""]
        scrib_lib_quad_add_attached_points_to_loops $pt_list $max_point_dist
    }
    #
    # Now mesh.
    #
    if {$element == 5} {
        set try_harder 0
    }
    if {$element == 3 || $element == 5 || $element == 7 || $element == 8} {
        set use_block 0
    } else {
        set use_block $block
    }
    set err 0
    if {$try_harder == 3} {
        #
        # Let's use Tetra within the Quad mesher
        #
        set try_twice 4
        set try_harder 2
    } elseif {$try_harder > 0} {
        if {$max_simple_triangulation} {
            set try_twice -$max_simple_triangulation
        } else {
            set try_twice 2
        }
        if {$split_tri} {
            #
            # For testing, you can set try_twice to 3.  It will
            # then split STL-like triangulations by aspect ratio.
            #
            set try_twice 3
        }
    } else {
        set try_twice 0
    }
    if {$what == "native_surfaces"} {
        set from_surf 1
    }
    set dormant $merge_dormant
    if !$even_if_dormant {
        set dormant 0
    }
    if {!$dormant && $smoothdormant} {
        set dormant 2
    }
    set args [list q $q loops $loops element $element proj $proj \
        iters $iters conver $conver b_smooth $b_smooth block $use_block bunch $bunch \
        ele_tol $ele_tol geo_tol $geo_tol improvement $improvement \
        violate $violate dev $dev error_family $error_family \
        curve_family $curve_family offset $offset offset_h $offset_h \
        offset_element_family $offset_element_family debug $debug \
        adjust_nodes $adjust_nodes adjust_nodes_max $adjust_nodes_max \
        try_twice $try_twice dormant $dormant pattern $pattern \
        no_undo 1 board $board inner $inner simple_offset $simple_offset \
        enn $enn from_surf $from_surf time_max $time_max ele_max $ele_max four $four \
        max_length $max_length max_area $max_area min_angle $min_angle \
        max_nodes $max_nodes max_elements $max_elements n_threads $n_threads \
        material $material breakpoint $breakpoint freeb $freeb snorm $snorm \
        shape $shape]
    if {$debug > 1} {
        mess "ic_quad $args\n" red
    }
    set err [eval ic_quad $args]
    #
    # This step will return what loops/surfaces failed, for the user.
    #
    if {$what == "surfaces"} {
        set failed_loops [ic_geo_get_objects loop $error_family]
        if {$failed_loops != ""} {
            if {$try_harder > 1 && $even_if_dormant && $merge_dormant} {
                #
                #   First retry without merging at dormant curves.
                #
                set err 0
                set dormant_loops ""
                foreach loop $failed_loops {
                    set curves [ic_geo_boundary loop $loop 0 $even_if_dormant 1]
                    set do 0
                    foreach c $curves {
                        if [ic_geo_get_dormant_entity curve $c] {
                            set do 1
                            break
                        }
                    }
                    if $do {
                        ic_geo_set_family loop $fam names $loop 0
                        lappend dormant_loops $loop
                    }
                }
                set argsd [list loops $failed_loops element $element proj $proj \
                    iters $iters conver $conver block $use_block bunch 1 \
                    ele_tol $ele_tol geo_tol $geo_tol improvement $improvement \
                    violate $violate dev $dev error_family $error_family \
                    curve_family $curve_family offset $offset offset_h $offset_h \
                    offset_element_family $offset_element_family debug $debug \
                    adjust_nodes $adjust_nodes adjust_nodes_max $adjust_nodes_max \
                    try_twice $try_twice pattern $pattern no_undo 1]
                if {$dormant_loops != ""} {
                    if {$debug >= 0} {
                        mess "Failed loops with dormant curves are: $dormant_loops\n" red
                    } else {
                        mess "[llength $dormant_loops] failed loops\n" red
                    }
                    if {$debug > 1} {
                        mess "ic_quad $argsd\n" red
                    }
                    if [catch {eval ic_quad $argsd} res ] {
                        mess "$res\n" red
                        set err 4
                    }
                 }
                set failed_loops [ic_geo_get_objects loop $error_family]
            } elseif {$debug > 1} {
                mess "Failed loops are: $failed_loops\n" red
            }
            if {$failed_loops != ""} {
                set err 4
                set failed_surfs ""
                set subset_surfs ""
                foreach loop $failed_loops {
                    set surfs [ic_geo_incident loop $loop]
                    set failed_surfs [concat $failed_surfs $surfs]
                    set subset_surfs [concat $subset_surfs surface $surfs]
                }
                if {$debug >= 0} {
                    mess "Failed surfs are: $failed_surfs\n" red
                } else {
                    mess "[llength $failed_surfs] failed surfaces\n" red
                }
                if !$batch_mode {
                    ic_geo_subset_add_items $error_subset $subset_surfs
                    ic_geo_subset_visible $error_subset 1
                    aie_subset_put_in_tree geom $error_subset
                }
                if {$debug == 1 && !$batch_mode} {
                    scrib_lib_exec_med_display_update
                    if {[ tk_dialog .fail_quad "Quad failed" \
                        "Do you want to continue trying to fix loops?" \
                        "" -1 Yes No ]}  {set failed_loops "" ; set err 0}
                }
            }
            set ii 0
            while {$try_harder > 2 && $failed_loops != ""} {
                incr ii
                if {$ii > 4} {
                    mess "Tried to mesh with partial method $ii times.  Giving up." red
                    break
                }
                if {$debug > 0} {
                    mess "Partial step $ii\n" red
                }
                #
                # --> check to see if failed loops are due to folded surfaces or group.
                # If so we will try to resolve once with breaking 
                # loop of group, or splitting folded surface.
                #
                # check if loop is a group of surfaces
                # -->this may not even be supported yet
                foreach lp $failed_loops {
                    set surfs [ic_geo_incident loop $lp]
                    if {[llength $surfs] > 1} {
                        if {!$big} {ic_undo_suspend}
                        ic_delete_geometry loop names $lp
                        set fam [ic_geo_get_family surface [lindex $surfs 0]]
                        scrib_lib_quad_create_loops_for_each_surface $fam $surfs
                        set place [lsearch $failed_loops $lp]
                        set failed_loops [lreplace $failed_loops $place $place]
                        foreach s $surfs {
                            set place [lsearch $failed_surfs $s]
                            set failed_surfs [lreplace $failed_surfs $place $place]
                        }
                        set l [lsearch $args loops]
                        if {$l > -1} {set args [lreplace $args $l [expr $l + 1]]}
                        lappend args loops [ic_geo_get_objects loop]
                        scrib_lib_quad_add_attached_points_to_loops $pt_list $max_point_dist
                        if {!$big} {ic_undo_resume}
                    }
                }
                if {($failed_loops == "") && ($ii < 2)} {
                    ic_unload_mesh
                    if {$debug > 1} {
                        mess "ic_quad $args\n" red
                    }
                    if [catch {eval ic_quad $args} res ] {
                        mess "$res\n" red
                        set err 4
                    }
                    set failed_loops [ic_geo_get_objects loop $error_family]
                    if {$failed_loops == ""} {
                        mess "Was able to mesh all surfaces\n"
                        set err 0
                        break
                    } else {
                        set failed_surfs ""
                        set subset_surfs ""
                        foreach l $failed_loops {
                            set surfs [ic_geo_incident loop $l]
                            set failed_surfs [concat $failed_surfs $surfs]
                            set subset_surfs [concat $subset_surfs surface $surfs]
                        }
                        mess "Failed surfs are: $failed_surfs\n" red
                        if !$batch_mode {
                            ic_geo_subset_add_items $error_subset $subset_surfs
                            aie_subset_put_in_tree geom $error_subset
                        }
                    }
                }
                if {$debug > 1} {
                    mess "scrib_lib_tetra_partial_mesh $failed_surfs $failed_loops "\
                        "$args \"\" $debug\n" red
                }
                set failed_loops [scrib_lib_tetra_partial_mesh $failed_surfs \
                    $failed_loops $args "" $debug]
                if {$debug > 1} {
                    mess "Failed_loops are: $failed_loops\n"
                }
                if {$failed_loops != ""} {
                    set err 1
                    set failed_surf_list ""
                    set subset_surfs ""
                    foreach l $failed_loops {
                        lappend failed_surf_list [ic_geo_incident loop $l]
                    }
                    foreach s $failed_surf_list {
                        set failed_surfs [concat $failed_surfs $s]
                        set subset_surfs [concat $subset_surfs surface $s]
                    }
                    mess "New failed surfs are: $failed_surf_list\n" red
                    mess "Failed surfs are: $failed_surfs\n" red
                    if !$batch_mode {
                        ic_geo_subset_add_items $error_subset $subset_surfs
                        aie_subset_put_in_tree geom $error_subset
                    }
                    if {$debug == 1} {
                        if {[ tk_dialog .fail_quad "Quad failed ($ii times)" \
                            "Do you want to continue trying to fix loops?" \
                            "" -1 Yes No ]}  {
                            set failed_loops ""
                            set err 0
                            #aie_create_initial_assemblies
                        }
                    }
                } else {
                    mess "Was able to mesh all surfaces\n"
                    set err 0
                    break
                }
            }
        }
        set loops [ic_geo_get_objects loop]
    }
    if {$what != "loops"} {
        #
        # Clean up loops.
        #
        if {$what != "native_surfaces"} {
            if {!$big} {ic_undo_suspend}
            ic_delete_geometry loop names $loops
            if {!$big} {ic_undo_resume}
            if {$degen_curve != ""} {
                # These orphan "degen_curve" curves are from pj_new_face_surface.
                if {$debug > 1} {
                    mess "Now delete orphan curves \"$degen_curve\".\n"
                }
                define_geom_objects2 curve $degen_curve
                foreach crv $degen_curve {
                    if {[ic_geo_check_objects_exist curve $crv] != ""} {
                        ic_delete_geometry curve names $crv
                    }
                }
            }
        }
        #
        # Delete elements in error family if any.
        #
        if {$debug <= 0} {
            if [ic_uns_check_family $error_family] {
                ic_delete_elements family $error_family no_undo 1
            }
            ic_uns_delete_family $error_family
            if {$debug < -1} {
                ic_delete_disconnected_vertices 1
            } else {
                ic_delete_disconnected_vertices 0
            }
        }
    }
    ic_quad_undo "finish" $big
    busy 0
    return $err
}

## Set Quad's debug level.
proc ic_quad_debug {{db 0}} {
    return [quad_debug $db]
}

## Free surface meshing from elements.
## <ul>
## <li><b>name</b> : Name of the mapping.
## <li><b>element</b> : If set to 4, every
##     loop must have an even number of nodes on the edges in order
##     to form an all-quadrilateral QUAD_4 mesh. This constrain
##     can be dropped if the creation of
##     one (option 1) or several (option 2) TRI_3 transition triangles
##     is accepted. The meshing scheme can also generate TRI_3
##     triangle surface meshes (option 3) and TRI_3 STL-like facets
##     (option 5). The default is 4.
## <li><b>proj</b> : Project the new nodes onto the CAD
##     surfaces if 1.  The default is 0.
## <li><b>remesh</b> :  Ignore holes if 1, mesh only holes if 2, mesh
##     hull of surface elements if 3, or mesh gaps of surface elements if 4.
##     The default is 0.
## <li><b>iters</b> : The mesh quality can be improved
##     if additional smoothing steps are performed. With this option set,
##     a pure Laplacian smoothing is used in which each interior node is
##     moved to the centroid of its neighbors. In typical cases smoothing
##     will converge after about six steps. The default maximum number
##     of steps is 20.
## <li><b>conver</b> : The smoothing accuracy
##     defines a convergence value which is related to the
##     element sizes. The default value is 0.025.
## <li><b>tol</b> : Merge tolerance related to element size.  The default is 0.1.
## <li><b>ele_tol</b> : The default element tolerance
##     is 0. Set this violate geometry value to remove tiny elements
##     or holes.
## <li><b>dev</b> : Refine mesh up to three times to
##     reduce surface deviation. Value defines the maximum surface
##     deviation if not 0. The default is 0.
## <li><b>db</b> : Do some checking and printing if > 0, print less
##     messages if < 0.  Default is 0.
## <li><b>make_consistent</b> : Make volume mesh consistent if set. The
##     default value is 0.
## <li><b>offset</b> : Create n layers of offset elements.
##     The default is 0.
## <li><b>offset_h</b> : Relative value for the offset
##     height. The default is 1.
## <li><b>keep_fixed_nodes</b> : Keep fixed nodes if set. The default is 0.
## <li><b>inner</b> : Preferred interior element size
##     if > 0. The default is 0.
## <li><b>material</b> <em>name</em> : Label new elements with this
##     family name.
## <li><b>geo_id</b> <em>0/1</em> : Create elements with family name
##     of nearest surface if set.  The default value is 0.
## <li><b>simple_offset</b> <em>0/1</em> :  Do simple normal offsetting if set.
##     The default value is 0.
## </ul>
proc ic_quad_from_elements {name {element 4} {proj 0} {remesh 0} {iters 20} \
    {conver 0.025} {tol 0.1} {ele_tol 0} {dev 0} {db 0} {make_consistent 0} \
    {offset 0} {offset_h 1} {keep_fixed_nodes 0} {inner 0} {material ""} {geo_id 0} \
    {simple_offset 0}} {

    if ![lindex [check_licenses_enabled] 0] {
        error "No med license was obtained."
    }
    if {$material == "-inherited"} {
        set material ""
    }
    set map [uns_get_subset $name]
    if {"" == $map} {
        if {"" != $name} {
            set map $name
        } else {
            mess "couldn't get map $name from uns or as given\n"
            return
        }
    }
    if {$db > 0} {mess "name = $name, map = $map\n" red}
    set q -4
    if {$keep_fixed_nodes} {
        set q 4
    }
    ic_newop
    start_new_elems
    busy 1
    set new_elems ""
    if [catch {
        $map quad_from_elements $q $element $proj $remesh $iters \
            $conver $tol $ele_tol $dev $db $make_consistent $offset $offset_h \
            $inner $material $geo_id $simple_offset
    } res] {
        mess "$res\n" red
    } else {
        set new_elems $res
    }
    finish_new_elems
    display_quad_results
    busy 0
    return $new_elems
}

## Quad from bars.

proc ic_quad_from_bars {name {element 4} {proj 0} {iters 20} {conver 0.025} \
    {improvement 0} {surf 0} {complete 0} {ele_tol 0} {dev 0} {db 0} \
    {make_consistent 0}} {

    if ![lindex [check_licenses_enabled] 0] {
        error "No med license was obtained."
    }
    set ta [tdv_active 0]
    ic_newop
    start_new_elems
    busy 1
    set map [uns_get_subset $name]
    if {"" == $map} {
        if {"" != $name} {
            set map $name
        } else {
            mess "couldn't get map $name from uns or as given\n"
	    tdv_active $ta
            return 1
        }
    }
    set ret ""
    set new_elems ""
    if [catch {
        $map quad_from_bars $element $proj $iters $conver $improvement $surf \
            $complete $ele_tol $dev $db $make_consistent
    } res] {
        mess "$res\n" red
        set ret error
    } else {
        set new_elems $res
    }
    finish_new_elems
    display_quad_results
    busy 0
    tdv_active $ta
    if {$new_elems != ""} {
        return $new_elems
    } else {
        return $ret
    }
}

## Given a surface name <em>sname</em> create a quad mesh from the existing
## edge mesh on its boundary. Example usage:
##     ic_uns_quad_mesh_surface_from_edge_mesh E43457
proc ic_quad_mesh_surface_from_edge_mesh {sname} {

    set bnd_map [ic_uns_subset_surface_boundary $sname]
    if {"" == $bnd_map} {
        mess "Error: could not get a boundary edge map for surface \"$sname\"\n" red
        return 0
    }
    set stat [ic_quad name $bnd_map q 3 element 1 proj 0 iters 20 conver 0.025 \
        improvement 1 complete_edges 0 ele_tol 0 dev 0 offset 0 \
        offset_h 1.0 inner 0 boundary_tol 0 debug 0]
    ic_uns_subset_delete $bnd_map
    return $stat
}

## Quad from loops.
proc ic_quad_from_loops {name element proj iters conver improvement bunch loopnames \
    tol etol fam dev db {violate 0} {offset 0} {offset_h 1} \
    {offset_element_family ""} {block 0} {inner 0} {boundary_tol -1} {adjust_nodes 0} \
    {board 0} {ignore_tp 0} {adjust_nodes_max 1} {enn 0} {dormant 0} {pattern 0} \
    {b_smooth 0}} {
    global uns_cur projlib uns_type_visible batch_mode

    if ![lindex [check_licenses_enabled] 0] {
        error "No med license was obtained."
    }
    set loops ""
    foreach ln $loopnames {
        lappend loops [$projlib get_number_by_name loop $ln]
    }
    ic_newop
    start_new_elems
    busy 1
    set map [uns_get_subset $name]
    if {"" == $map} {
        if {"" != $name} {
            set map $name
        } else {
            mess "couldn't get map $name from uns or as given\n"
            return
        }
    }
    if {$improvement > 0} {
        set try_twice 1
    } else {
        set try_twice 0
    }
    if [catch {
        $map quad 1 0 $element $proj $iters \
            $conver $b_smooth $improvement $violate $bunch $loops 0.0 $tol $etol \
            $fam "" "" "" $offset $offset_h $offset_element_family \
            $dev 0 0 0 $try_twice $block 0 0 $inner $boundary_tol \
            $adjust_nodes $adjust_nodes_max $ignore_tp $board 0 $enn \
            $dormant $pattern 0 0 0 0 0 0 0 0 0 0 0 0 $db
    } res] {
        mess "$res\n" red
    }
    finish_new_elems
    display_quad_results
    busy 0
}

## Smooth surface elements.  By default, any free surface node is moved to the centroid
## of its neighbors (Laplacian method).

proc ic_smooth_quad {name {proj 0} {iters 20} {conver 0.025} {improvement 0} \
    {type 4} {w 0} {b_smooth 0} {debug 0} {n_threads 0} {unique 0} {diagnostic ""} \
    {max_aspect_ratio -1} {w_transition 0} {w_first 0} {h_first 0} {untangle 1}} {
    global uns_cur

    if ![lindex [check_licenses_enabled] 0] {
        error "No med license was obtained."
    }
    set map [uns_get_subset $name]
    if {"" == $map} {
        if {"" != $name} {
            set map $name
        } else {
            mess "couldn't get map $name from uns or as given\n"
            return
        }
    }
    busy 1
    ic_newop
    start_new_elems
    if [catch {
        $map smooth_quad $proj $iters $conver $improvement $type $w $b_smooth \
        $n_threads $unique $diagnostic $max_aspect_ratio $w_transition $w_first \
        $h_first $debug $untangle
    } res] {
        mess "$res\n" red
    }
    finish_new_elems
    busy 0
}

## Smooth volume elements.  By default, any free surface node is moved to the centroid
## of its neighbors (Laplacian method).

proc ic_smooth_hexa {name {proj 0} {iters 20} {conver 0.025} {w 0} {debug 0} \
    {n_threads 0} {unique 0} {diagnostic ""}} {
    global uns_cur

    set map [uns_get_subset $name]
    if {"" == $map} {
        if {"" != $name} {
            set map $name
        } else {
            mess "couldn't get map $name from uns or as given\n"
            return
        }
    }
    busy 1
    ic_newop
    start_new_elems
    if [catch {
        $map smooth_hexa $proj $iters $conver $w $n_threads $unique $diagnostic $debug
    } res] {
        mess "$res\n" red
    }
    finish_new_elems
    busy 0
}

proc ic_mesh_curve {name} {
    global uns_cur

    set fam [ic_geo_get_family curve $name]
    $uns create_family $fam -1
    set num [$projlib get_number_by_name curve $name]
    eval $uns_cur import_bars [$projlib export_bars $num]
}

## Remesh edges.

proc ic_remesh_edges {name tol merge_ends {mtol 0} {db 0}} {
    global uns_cur

    if ![lindex [check_licenses_enabled] 0] {
        error "No med license was obtained."
    }
    set map [uns_get_subset $name]
    if {"" == $map} {
        if {"" != $name} {
            set map $name
        } else {
            mess "couldn't get map $name from uns or as given\n"
            return
        }
    }
    busy 1
    ic_newop
    start_new_elems
    if [catch {
        $map remesh_edges $tol $merge_ends $db
    } res] {
        mess "$res\n" red
    } else {
        #  this additional node merging should not be necessary
        #$map merge_nodes_tol $mtol 0 "0 0 0" "0 0 0" 0 0 0
    }
    finish_new_elems
    busy 0
}

## Remesh bad elements.

proc ic_remesh_bad_elements {name {element 3} {proj 0} {db 0} {all_dim 0} \
    {max_steps -1} {quality 1} {angle 0} {ele_tol 0} {inner 0} \
    {diagnostic "Custom quality"} {material ""} {aggressive 0}} {

    if ![lindex [check_licenses_enabled] 0] {
        error "No med license was obtained."
    }
    set map [uns_get_subset $name]
    if {"" == $map} {
        if {"" != $name} {
            set map $name
        } else {
            mess "couldn't get map $name from uns or as given\n"
            return 1
        }
    }
    if {$db > 0} {mess "name = $name, map = $map\n" red}
    ic_newop
    start_new_elems
    set new_elems ""
    busy 1
    if [catch {
        $map remesh_bad_elements $element $proj $db $all_dim $max_steps 120 20 0 \
            $quality $angle $ele_tol $inner $diagnostic $material $aggressive
    } res] {
        mess "$res\n" red
    } else {
        set new_elems $res
    }
    finish_new_elems
    display_quad_results
    busy 0
    if {$new_elems != ""} {
        return $new_elems
    } else {
        return 0
    }
}

## Remesh regions of a surface mesh by a diagnostic.
## It will return 1 if it did stuff, 0 if it was already ok.

proc ic_quad_remesh_bad_elements_by_quality {fams types metric {quality_limit 0.6} \
    {eltype 2} {ignore_dim 0} } {

    if ![ic_uns_is_loaded] {
	mess "cannot remesh bad elements by quality without a mesh loaded!\n" red
	return -1
    }
    mess "\nRemeshing bad elements by quality metric $metric...\n"
    set tmpmap [ic_uns_subset_create [ic_uns_subset_unused_name remesh_bad_elements_by_quality]]
    ic_uns_subset_add_families_and_types $tmpmap $fams $types
    # --> G E T   M E T R I C S
    set quality_limit [expr $quality_limit * 1.0]
    set stat [ic_uns_metric $tmpmap $metric]
    if ![lindex $stat 0] { return -1 }
    set badmap [ic_uns_subset_create [ic_uns_subset_unused_name bsm_quad_remesh_by_quality]]
    ic_uns_set_with_diagnostic $badmap $tmpmap [list [list -1000 $quality_limit]]
    ic_uns_subset_delete $tmpmap
    set nbads [ic_uns_subset_count_elements $badmap]
    if $nbads {
	mess "found $nbads elements which need to be remeshed\n"
	# watch out for all_dim of 1---it has to be 0 elsewhere!! --jka
	if [catch { ic_remesh_bad_elements $badmap $eltype 1 0 $ignore_dim 20 } res] {
	    mess "\n$res\n"
	}
	mess "...done\n"
    } else {
	mess "...all element qualities acceptable!\n"
    }
    ic_uns_subset_delete $badmap
    return 1
}

## Remesh regions of a surface mesh by a diagnostic.
## It will return 1 if it did stuff, 0 if it was already ok.

proc ic_quad_remesh_by_diagnostic {diagnostic fams types nlayers {eltype 2} \
    {ignore_dim 0} {custom_metric_weights ""}} {

    if ![ic_uns_is_loaded] {
	mess "cannot remesh bad elements by diagnostic without a mesh loaded!\n" red
	return 0
    }
    #mess "Remeshing $nlayers layers around elements in families $fams with $diagnostic problem...\n"
    set name [ic_uns_subset_unused_name "bsm_remesh_by_$diagnostic"]
    set badmap [ic_uns_subset_create $name]
    ic_uns_diagnostic diag_type $diagnostic disp_subset $badmap fams $fams
    set nbads [ic_uns_subset_count_elements $badmap]
    if $nbads {
	mess "found $nbads elements  with $diagnostic problem which need to be remeshed\n"
	ic_uns_subset_add_layer $badmap $nlayers $fams $types	
	if {"" != $custom_metric_weights} {
	    ic_uns_set_metric_weights $custom_metric_weights 1
	    set stat [ic_uns_metric $badmap "Custom quality"]
	}
	if [catch { ic_remesh_bad_elements $badmap $eltype 1 0 $ignore_dim 20 } res] {
	    mess "\n$res\n" red
	}
	mess "...done\n"
    } else {
	mess "...found no $diagnostic problems in the mesh.\n"
	
    }
    ic_uns_subset_delete $badmap
    return $nbads
}

## Enforce vertex in surface mesh.
## The map should include just one node element to provide the vertex
## to be enforced, or the vertex number must be >= 0.

proc ic_enforce_vertex {name {element 3} {proj 0} {db 0} {all_dim 0} {vertex -1} \
    {make_consistent 0}} {

    if ![lindex [check_licenses_enabled] 0] {
        error "No med license was obtained."
    }
    set map [uns_get_subset $name]
    if {"" == $map} {
        if {"" != $name} {
            set map $name
        } else {
            mess "couldn't get map $name from uns or as given\n"
            return 1
        }
    }
    ic_newop
    set new_elems ""
    start_new_elems
    busy 1
    if [catch {
        $map enforce_vertex $element $proj $db $all_dim $vertex $make_consistent
    } res] {
        mess "$res\n" red
    } else {
        set new_elems $res
    }
    finish_new_elems
    display_quad_results
    busy 0
    if {$new_elems != ""} {
        return $new_elems
    } else {
        return "$res"
    }
}

## Change tri to quad elements (new scheme).

proc ic_change_tri_quad_2 {name {proj 0} {db 0} {all_dim 0} {max_steps -1} \
    {skew 160} {warp 20} {quadrization 0} {diagnostic "Angle error"}} {

    if ![lindex [check_licenses_enabled] 0] {
        error "No med license was obtained."
    }
    set map [uns_get_subset $name]
    if {"" == $map} {
        if {"" != $name} {
            set map $name
        } else {
            mess "couldn't get map $name from uns or as given\n"
            return 1
        }
    }
    if {$db > 0} {mess "name = $name, map = $map\n" red}
    ic_newop
    set new_elems ""
    start_new_elems
    busy 1
    if [catch {
        $map remesh_bad_elements 34 $proj $db $all_dim $max_steps \
            $skew $warp $quadrization 0.8 0 0 0 $diagnostic "" 0
    } res] {
        mess "$res\n" red
    } else {
        set new_elems $res
    }
    finish_new_elems
    display_quad_results
    busy 0
    if {$new_elems != ""} {
        return $new_elems
    } else {
        return "$res"
    }
}

## Offset quad layers on the loaded surface mesh. <em>how</em> can be one of
## { curve_family , curve, map } while <em>which</em> would be a curve_family,
## curve_names (list), or map_name respectively. If <em>n_layers</em> is not specified,
## it will try getting the value from the tetin parameters.

proc ic_quad_offset_curve_layers {how which {n_layers ""} {db 0}} {

    if ![ic_geo_is_loaded] {
        error "Cannot offset quad layers without geometry loaded"
    }
    if ![ic_uns_is_loaded] {
        error "Cannot offset quad layers without mesh loaded"
    }
    if {2 != [ic_uns_dimension]} {
       return 0
    }
    
    if [ic_wb2_input_parameter_mode] {
        ic_wb2_update_while_input_parameter_mode
    }
    
    # this is the tetin param that we're dealing with
    set const_param "n_quad_layers"
    set map_created_here 0
    set maplist ""
    switch -- $how {
        curve_family {
            set family_curves [ic_geo_get_objects curve $which]
            foreach c $family_curves {
                set tmpmap [ic_uns_get_attached_elements curve $c]
                ic_uns_subset_add_layer $tmpmap 3 "all" "surf"
                lappend maplist $tmpmap
            }
            set map_created_here 1
            set family $which
        }
        curve {
            foreach c $which {
                set tmpmap [ic_uns_get_attached_elements curve $c]
                ic_uns_subset_add_layer $tmpmap 3 "all" "surf"
                lappend maplist $tmpmap
            }
            set map_created_here 1
            set family ""
        }
        map - subset {
            set maplist $maplist
            set family ""
        }
        default {
            error "Unexpected method of specifying elements for offsetting quad layers. \
                Must be one of \{curve_family, curve, map\}"
        }
    }
    if {"" == $maplist} {
        error "Could not get map with which to offset quad layers"
    }
    # if not specified, get the $const_param from the tetin file on family
    if {("" == $n_layers) && ("" != $family)} {
        set n_layers [ic_geo_get_family_param $family $const_param]
    }
    if {("" == $n_layers) || (0 >= $n_layers)} {
        mess "Warning: given value for n_quad_layers ($n_layers) would not have any effect\n" red
        if $map_created_here {
            foreach map $maplist {
                ic_uns_subset_delete $map
            }
        }
        return 0
    }
    # ok, do the quad stuff
    set element 1
    set proj 1
    set remesh 0
    set iters 20
    set conver 0.025
    set tol 0.1
    set ele_tol 0
    set dev 0
    set make_consistent 0
    set offset $n_layers ;  # this is why we're here.
    set offset_h 1
    set material ""
    foreach map $maplist {
        set stat [ic_quad_from_elements $map $element $proj $remesh $iters $conver \
            $tol $ele_tol $dev $db $make_consistent $offset $offset_h 0 0 $material 0]
        ic_uns_subset_delete $map
    }
    return 1
}

# for backward compat
proc ic_run_meshdirect { idomain tetin {odomain ""} args } {
    return [ic_meshdirect_standalone_exec $idomain $tetin $odomain $args]
}
# for backward compat
proc ic_run_meshdirect_obj {types args} {
    return [ic_meshdirect_object_exec $types $args]
}

proc ic_mapped_mesh { min_len asp_ratio len_ratio fams } {
	global uns_cur
	if { $uns_cur == "" } {
		ic_uns_create_empty
	}
	set replay_file "__temp_hexa_replay.hxa"
	set unstr_dom "__temp_mapped_mesh.uns"
	set tetin "__temp_tetin.tin"
	ic_save_tetin $tetin
	mess "$uns_cur mapped_mesh $min_len $asp_ratio $len_ratio "
	mess "$replay_file $unstr_dom $fams\n"
	eval $uns_cur mapped_mesh $min_len $asp_ratio $len_ratio $replay_file $unstr_dom $fams
	ic_run_hexa $tetin "" $replay_file ./ ./ ./
	ic_uns_load $unstr_dom
}

proc ic_delete_bad_tets {} {
	global uns_cur
	set map [ $uns_cur get_map All ]
	eval $map "delete_tets_and_pyramids"
}


proc ic_remesh_tets { } {
	global uns_cur

	if { [ ic_count_elements TETRA_4 ] > 0 } {
	    set fams [ ic_uns_list_families ]
	    foreach f $fams {
	    	set map [ ic_uns_subset_create_family_type $f TETRA_4 1 ]
		set nt [ ic_uns_subset_count_elements $map ]
		ic_uns_subset_delete $map
		if { $nt > 0 } break
	    }
	    if { $nt > 0 } {
		mess "Part $f has $nt tets\n"
		ic_save_unstruct "before_remesh.uns" 1
# write out the mesh without TETRA_4 and PYRA_5 and some PENTA_6's
		ic_delete_bad_tets
		ic_save_unstruct "missing_middle.uns" 1
		ic_rm "with_middle.uns"

		ic_unload_mesh
		ic_run_tri2tet "missing_middle.uns" "with_middle.uns" family $f
		if [ file exists "with_middle.uns" ] {
		    ic_load_unstruct "with_middle.uns"
		    set map [ $uns_cur get_map All ]

		    mess "Creating pyramid connections between tets and hexas\n"
		    eval $map "create_pyramids"

# fix up volume orientations
		    set nf [$map check_volume_orient 1 NULL]
		} else {
		    ic_load_unstruct "before_remesh.uns"
		}
		ic_rm "with_middle.uns"
		ic_rm "before_remesh.uns"
	    }
	}
}

proc ic_create_pyramids {{args ""}} {
    global uns_cur

    if {$uns_cur == ""} {return 0}

    set smooth 0
    set n_smoothing_steps 30
    set chk_vol_orient 0
    set collapse_apexes 0
    set freeze_sfmesh 0

    foreach {n v} $args {
        switch $n {
            smooth - n_smoothing_steps - chk_vol_orient - collapse_apexes - freeze_sfmesh {
                set $n $v
            }
            default {
                mess "Warning: Unknown Option $n\n" red
            }
        }
    }

    set npyr [ic_count_elements PYRA_5]
    set map [$uns_cur get_map All]
    set cmd create_pyramids
    if {$smooth == 0} {
        append cmd " -no_smooth"
        set n_smoothing_steps 0
    }
    if $n_smoothing_steps {
        append cmd " -n_smoothing_steps $n_smoothing_steps"
    }
    if $chk_vol_orient {
        append cmd " -chk_vol_orient"
    }
    if $collapse_apexes {
        append cmd " -collapse_apexes"
    }
    if $freeze_sfmesh {
        append cmd " -freeze_sfmesh"
    }
    eval $map $cmd
    set npyr [expr [ic_count_elements PYRA_5] - $npyr]
    if {$npyr > 0} {
	    mess "Created $npyr pryamid connections between quad faces and tets\n"
	    return $npyr
    }
    return 0
}

## cartesian hexa mesher
proc ic_mesh_hexcart {args} {
    global env batch_mode

    set tetin ""
    set uns ""
    set minsize 0
    set outfile "hexcart.uns"
    set out ""
    set cartfile ""
    set maxref -1
    set gridfile ""
    set bgmeshfile ""
    set uniform 0
    set conformal 0
    set nlayers 0
    set type 0
    set start ""
    set bbox ""
    set pure_cart 0
    set xcut ""
    set ycut ""
    set zcut ""
    set cart_uns 0
    set geowrap 0
    set symx 0
    set symy 0
    set symz 0
    set volfrac -1.0
    set fill_holes 0
    set bodyfit 0
    set volfam ""
    set marching_cubes 0
    set split_degen 0
    set create_pyramids 0
    set proj_factor ""
    set mtype ""
    set shrinkwrap 0
    set nsmooth -1
    set nuntangle -1
    set csys ""
    set tratio 0
    set aspect_ratio ""
    set write_vof 0
    set create_faces 0
    set create_edges 0
    set feature_split 0
    set kpt_tol 0.0
    set inflate 0
    set infproj 0
    set show_progress 0
    foreach {n v} $args {
	switch $n {
	    tetin - uns - bbox - minsize - outfile - out - maxref - gridfile 
	    - cartfile - bgmeshfile - uniform - conformal - start - aspect_ratio - type - nlayers 
	    - pure_cart - geowrap - cart_uns - xcut - ycut - zcut 
	    - symx - symy - symz - volfrac - fill_holes - bodyfit 
	    - volfam - marching_cubes - split_degen - create_pyramids 
	    - proj_factor - mtype - shrinkwrap - nsmooth - nuntangle - csys - tratio 
	    - write_vof - create_faces - create_edges - feature_split - inflate 
	    - infproj - kpt_tol {
		set $n $v
	    }
	    default {
		mess "Warning: Unknown Option $n\n" red
	    }
	}
    }
    
    # Convert the tetin file to cart3d trifile format.
    set prog "icemcfd/hexcart"

    set argv ""
    if {$tetin != ""} {
	append argv " -tetin \"$tetin\""
    }
    if {$uns != ""} {
	append argv " -uns \"$uns\""
    }    
    if {$out != ""} {
	set outfile $out
    }
    append argv " -out \"$outfile\""

    if {$gridfile != ""} {
	append argv " -grid \"$gridfile\""
    }

    if $feature_split {
	append argv " -feature_split"
    }

    if {$cartfile != ""} {
	append argv " -cart \"$cartfile\""
    }

    if {$bgmeshfile != ""} {
	append argv " -bg_mesh \"$bgmeshfile\""
    }

    if {$maxref > -1} {
	append argv " -ref $maxref"
    } elseif {$minsize >= 0} {
	append argv " -minsize $minsize"
    }

    if {$bbox != ""} {
	append argv " -bbox $bbox"
    }

    if {$start != ""} {
	append argv " -start $start"
    }

    if {$aspect_ratio != ""} {
	append argv " -aspect_ratio $aspect_ratio"
    }

    if $uniform {
	append argv " -uniform"
    }
    if $tratio {
	append argv " -tratio $tratio"
    }


    if $conformal {
	append argv " -conformal"
    }

    if $nlayers {
	append argv " -lay $nlayers"
    }

    if $type {
	append argv " -type $type"
    }

    if $bodyfit {
	append argv " -bodyfit"
    }

    if {$csys != ""} {
	append argv " -csys $csys"
    }

    if $pure_cart {
	append argv " -pure_cart"
    }

    if {$volfrac >= 0.0} {
	append argv " -volfrac $volfrac"
	if $write_vof {
	    append argv " -write_vof"
	}
    }

    if $geowrap {
	append argv " -geowrap"
    }

    if $cart_uns {	
	append argv " -cart_uns"
    } else {
	if {$xcut != ""} {
	    append argv " -xcut $xcut"
	}
	
	if {$ycut != ""} {
	    append argv " -ycut $ycut"
	}
	
	if {$zcut != ""} {
	    append argv " -zcut $zcut"
	}
    }

    if $symx {
	append argv " -symx"
    }
    if $symy {
	append argv " -symy"
    }
    if $symz {
	append argv " -symz"
    }

    if $fill_holes {
	append argv " -fill_holes"
    }

    if $marching_cubes {
	append argv " -marching_cubes"
    }

    if $create_pyramids {
	append argv " -create_pyramids"
    }
    if $split_degen {
	append argv " -split_degen"
    }

    if {$proj_factor != ""} {
	append argv " -proj $proj_factor"
    }

    if {$volfam != ""} {
	append argv " -volfam $volfam"
    }

    if $shrinkwrap {
	append argv " -shrinkwrap"
    }
    if {$nsmooth > -1} {
	append argv " -nsmooth $nsmooth"
    }
    if {$nuntangle > -1} {
	append argv " -nuntangle $nuntangle"
    }
    if {$mtype != ""} {
	append argv " -mtype $mtype"
    }
    if $create_faces {
	append argv " -create_faces"
    }
    if $create_edges {
	append argv " -create_edges"
    }

    if $inflate {
	append argv " -inflate $inflate"
    }

    if $infproj {
	append argv " -infproj"
    }

    if !$batch_mode {
	set show_progress 1
	append argv " -show_progress"
    }

    if {$kpt_tol > 0.0 } {
	append argv " -kpt_tol $kpt_tol"
    }
    cmd_rm $outfile
    
    #release advgeom license if checked out
    eval give_back_advgeom_license
    ic_uns_mesher_license 0

    cmd_rm $outfile
    #set stat [run_application_batch $prog HEXCART_BIN $argv]

    global ic_application_output_hook
    
    if {$show_progress} {
	set ic_application_output_hook check_hexcart_progress
	catch {interrupt begin "Hexcart Batch" \
		   initializing 100 application_terminated no_grab}
    }
    set stat [run_application_batch \
		  icemcfd/hexcart HEXCART_BIN $argv]
    catch {unset ic_application_output_hook}
    catch {interrupt finish}
    
    # error return
    if !$stat {
	    mess "Error: hexcart exited\n" red
    }

    if ![file exists $outfile] {
	    mess "Cartesian mesher failed to create mesh\n" red
	    set stat 0
    }

    return $stat
}

proc check_hexcart_progress {pid line} {
    if [string match @@@* $line] {
	set msg [string trim [string range $line 3 end]]
	set percent [lindex $msg 0]
	set line [lrange $msg 1 end]
	catch {interrupt check $percent 100 $line}
    }
    mess "$line\n" blue
}

proc ic_uns_cart3d_shrinkwrap {tetin_file minsize {proj_fact 0.3} {nsmooth 5} {fam ENVELOPE}} {
    global env
    
    # Convert the tetin file to cart3d trifile format.
    if [info exists env(TET2CART3D_BIN)] {
	set prog $env(TET2CART3D_BIN)
    } else {
	set prog $env(ICEM_ACN)/icemcfd/output-interfaces/tet2cart3d
    }
    
    busy 1
    set c3dfile_prefix [file root [file tail $tetin_file]]
    set c3dtri_file "${c3dfile_prefix}_c3d.i.tri"
    set argv "$tetin_file $c3dtri_file 0"
    
    cmd_rm $c3dtri_file
    mess "Coverting surfaces to cart3D format...\n" blue
    set status [ic_run_application_exec . "" $prog $argv]
    if ![file exists $c3dtri_file] {
	mess "Error: Coversion to Cart3D Tri file Failed\n" red
	busy 0
	return 0
    }

    mess "Computing inputs to cart3D mesher...\n" blue
    #set bbox [ic_geo_bounding_box all]
    set bbox ""
    if {$bbox != ""} {
	set bmin [lindex $bbox 0]
	set bmax [lindex $bbox 1]
    } else {
	set inputs [ic_mesh_cart3d_auto_inputs $c3dtri_file \
			max_ref 1 density_poly 0]    
	set bmin "[lindex $inputs 3] [lindex $inputs 4] [lindex $inputs 5]"
	set bmax "[lindex $inputs 6] [lindex $inputs 7] [lindex $inputs 8]"
    }

    #compute the the max_ref for the given min size
    set lmax 0.0
    foreach i {0 1 2} {
	set l [expr [lindex $bmax $i] -  [lindex $bmin $i]]
	if {$l > $lmax} {
	    set lmax $l
	}
    }

    #set mesh bbox as little bigger than bbox
    set bbmin ""; set bbmax ""
    foreach i {0 1 2} {
	lappend bbmin [expr [lindex $bmin $i] - 0.05 * $lmax]
	lappend bbmax [expr [lindex $bmax $i] + 0.05 * $lmax]
    }

    set nstart 5
    set maxref [expr log($lmax/(4.0*$minsize)) / log(2.0)]
    set maxref [expr int($maxref+0.5)]    
    busy 0

    set c3dmesh_file ${c3dfile_prefix}_c3d.dom
    cmd_rm $c3dmesh_file

    mess "Running cart3D mesher...\n" blue
    set status [ic_mesh_cart3d "" $c3dfile_prefix \
		    trifile      $c3dtri_file \
		    box_min      $bbmin \
		    box_max      $bbmax \
		    min_surfref  $maxref \
		    max_ref      $maxref \
		    unload_mesh  0]

    if [file exists $c3dmesh_file] {
	ic_load_unstruct ${c3dfile_prefix}_c3d.dom
	mess "Creating staircase envelope...\n" blue
	ic_uns_create_staircase_envelope
	mess "Smoothing the envelope...\n" blue
	set lamda 0.63
	set mu -0.65
	ic_uns_smooth_staircase_envelope $lamda $mu $proj_fact $nsmooth 2
	ic_change_family_elements family ENVELOPE newfam $fam
    }
    mess "cartesian shrink wrap done\n" blue
}

## run cartesian shrinkwrap
proc ic_hexcart_shrinkwrap {tetin_file minsize {proj_fact 0.3} {nsmooth 5} {fam inherited} {force_load 0} {mtype 0} {batch 1}} {
    global uns_cur batch_mode

    mess "Running hexcart mesher...\n" blue    

    # to force create stdmesh
    ic_unload_mesh
    ic_uns_create_empty
    if {$fam != "inherited" && (![ic_geo_check_family $fam])} {
        ic_geo_new_family $fam
    }
    
    if [ic_wb2_input_parameter_mode] {
        ic_wb2_update_while_input_parameter_mode
        if [ic_wb2_is_input_meshing_param_set gmax] {
            set v [ic_get_meshing_param_value global gmax]
            if {$v != "___NA___"} { set minsize $v }
        }
        if [ic_wb2_is_input_meshing_param_set shrinkwrap_projfactor] {
            set v [ic_get_meshing_param_value surface_global shrinkwrap_projfactor]
            if {$v != "___NA___"} { set proj_fact $v }
        }
        if [ic_wb2_is_input_meshing_param_set shrinkwrap_nsmooth] {
            set v [ic_get_meshing_param_value surface_global shrinkwrap_nsmooth]
            if {$v != "___NA___"} { set nsmooth $v }
        }
    }
    
    set hexcart_file [file root [file tail $tetin_file]]_cart.uns        
    if $batch {
	    set args "tetin \"$tetin_file\" shrinkwrap 1 minsize $minsize out \"$hexcart_file\" nsmooth $nsmooth proj_factor $proj_fact mtype $mtype nlayers 1"
	    eval ic_mesh_hexcart $args
	    ic_load_unstruct \"$hexcart_file\"
    } else {
	    set args "-tetin \"$tetin_file\" -shrinkwrap -minsize $minsize -nsmooth $nsmooth -proj $proj_fact -cleanup -mtype $mtype -lay 1"
	    if $force_load {
	        append args " -force_load"
	    }
	    if [catch {eval $uns_cur cart_shrinkwrap $args} res] {
	        mess "$res\n"
	        mess "Error: cartesian shrinkwrap failed\n" red
	        return 1
	    }
    }
    
    $uns_cur renumber_all_nodes 1 0
    $uns_cur renumber_all_elements 1 0
    
    domain_after_load_mesh
    
    if {$fam != "inherited"} {
	    ic_uns_change_family all $fam
    }
    update_uns_visible
    $uns_cur configure -modified 1

    if !$batch_mode {
	    aie_create_initial_assemblies
    }
    mess "Cartesian shrink wrap done\n" blue    
}

## create body-fitted cartesian mesh
## if there are more than one part then run part by part
proc ic_hexcart_body_fitted {tetin_file minsize parts args} {
    global uns_cur
    global batch_mode

    # to force create stdmesh
    ic_unload_mesh
 
    set nruns [llength $parts]
    if {$parts == ""} {
        set parts LIVE
	set nruns 1
    }
        
    for {set i 0} {$i < $nruns} {incr i} {
	set fam [lindex $parts $i]
	mess "Running hexcart mesher for Part $fam...\n" blue
	if {$nruns == 1} {
	    set hexcart_file [file root [file tail $tetin_file]]_BFCmesh.uns
	} else {
	    set hexcart_file [file root [file tail $tetin_file]]_${fam}_BFCmesh.uns
	}

	set targs "tetin \"$tetin_file\" bodyfit 1 minsize $minsize outfile \"$hexcart_file\" nlayers 1"
	
	if {$fam != "inherited"} {
	    if ![ic_geo_check_family $fam] {
		ic_geo_new_family $fam	    
	    }
	    append targs " volfam $fam"
	}
	append targs " $args"

	eval ic_mesh_hexcart $targs
	ic_load_unstruct \"$hexcart_file\"
	domain_after_load_mesh

	update_uns_visible
	$uns_cur configure -modified 1

	if !$batch_mode {
	    aie_create_initial_assemblies
	}
    }
    mess "Body-Fitted cartesian mesh generation finished\n" blue
}

## run bfcart based post inflation for all hex mesh
proc ic_hexcart_bfcart_inflation {} {
    global uns_cur
 
    set infproj 0
    array set mpars {}

    foreach {n v} [ic_get_meshing_params variable 0] {
        set mpars($n) [join $v]
    }
    if [info exists mpars(cart_infproj)] {
	set infproj $mpars(cart_infproj)
    }
    
    set args "-inflate 1 -cleanup"
    if $infproj {
	append args " -infproj"
    }

    mess "create_bfcart_inflation $args"
    set stat 0
    if [catch {eval $uns_cur create_bfcart_inflation $args} res] {
	mess "Error: inflation on bfcart mesh failed\n" red
	set stat 1
    }

    ic_uns_mesher_license 1

    update_uns_visible
    $uns_cur configure -modified 1
    return $stat
}

## Run Hex-Dominant mesher
# returns 1 for error
proc ic_mesh_hexdom {family {remesh 0}} {
    global uns_cur


# give back mesher lic    
    ic_uns_mesher_license 0
    set family [string toupper $family]

    if [ catch { ic_uns_orient outwards } res ] {
	ic_mess "error orienting normals: $res\n" red
	return 1
    }
    if { $res } {
	ic_undo_clear
	ic_mess "error orienting normals, \
		perhaps there is more than one region\n"
	if [ catch { $uns_cur hex_dom $family } res ] {
		ic_mess "Hex Dominant meshing failed\n" red
	    return 1
	}
    } else {

# write out everything except the TETRA_4's and PYRA_5's
	ic_save_unstruct __hexdom_surface.uns 1 "" \
	"NODE BAR_2 TRI_3 QUAD_4 PENTA_6 HEXA_8"

	ic_run_application_batch . icemcfd hex_dom "" \
	    [list __hexdom_surface.uns  __hexdom_volume.uns $family]
	ic_rm __hexdom_surface.uns
	if [file exists  __hexdom_volume.uns] {
	    ic_unload_mesh
	    ic_load_unstruct __hexdom_volume.uns
	    ic_rm __hexdom_volume.uns
	} else {
		mess "Error: couldn't generate proper mesh\n" red
		return 1
	}
    }
# remesh the center
    if $remesh {
	ic_remesh_tets
    }
    return 0
}

## Runs mapped based mesher
## type is type of mesh desired options are "All Quad", "Quad w/one Tri", "Quad Dominant", "All Tri", "Tri (STL like)"
## Default is "Quad Dominant"
## surfs is surfaces to mesh, default is all surfaces
## min_edge is minimum edge length allowed in mesh; default is 0

proc ic_run_mapped_based_mesher {{type "Quad Dominant"} {surfs ""} {min_edge 0} {mode ""} {no_merge 1} {version 100}} {

    if {$surfs == ""} { set surfs [ic_geo_get_objects surface ""] }
    if {$min_edge == ""} { set min_edge 0 }
    ic_geo_new_family MESH
    ic_boco_set_part_color MESH
    if {$mode == ""} {
        if {$type == "All Tri" || $type == "Tri (STL like)"} {
	        set mode "-unstruct"
        } else {
	        set mode "-mixed"
        }
    }
    switch $type {
	    "All Quad"	{ set type2 all_quads }
	    "Quad w/one Tri"  { set type2 one_tri }
	    "Quad Dominant"   { set type2 several_tris }
	    "All Tri"	 { set type2 all_tris }
	    "Tri (STL like)"  { set type2 stl }
	    default {
	        mess "Quad meshing can't deal with type $type ... using Quad Dominant\n" red
	        set type2 all_quads
	    }
    }
    
    if [ic_wb2_input_parameter_mode] {
        ic_wb2_update_while_input_parameter_mode
        if [ic_wb2_is_input_meshing_param_set ign_size] {
            set v [ic_get_meshing_param_value surface_global ign_size]
            if {$v != "___NA___"} { set min_edge $v }
        }
    }

    set ret ""    
    set fam MESH
    set outfile hex_temp.uns
    cmd_rm $outfile
    set cmd "ic_hex_create_auto_surface_mesh -out $outfile -fam $fam -mode $mode -type $type2 -min_edge $min_edge -version $version"
    if $no_merge { append cmd " -respect_non_dormant" } 
    append cmd " -surfaces $surfs"
    set res [eval $cmd]
    if { $res != "" } {
	    mess "Error: $res\n" red
	    foreach r $res {
	        set rr [lindex $r end]
	        if [llength [ic_geo_check_objects_exist surface $rr]] {lappend ret $rr}
	    }
    }
    if ![file exists $outfile] {
	    mess "Error: mapped based meshing failed\n" red
	    return $ret
    }
    ic_load_unstruct $outfile
    return $ret
}




