
# Extract all the appropriate faces from the volume mesh.
#
# Copyright (c) 1993, 1994 Pacific Marketing and Consulting
# Author:  Wayne A. Christopher, wayne@pmac.com
#          Pacific Marketing and Consulting, Berkeley, California
#

proc reduce_mesh_groups {full reduced} {
    global ${full}_group ${reduced}_group ${full}_unstruct ${full}_element_set
    global grid_mapping pid_for_key key_for_pid solve_solver grid_type
    global dont_remove_faces_between_solids writing_ff_grid
    global remove_vface_quads_between_fluid grid_cutter_surface 
    global grid_tetra_surface full_element_map
    global object_cant_be_inside_solid want_node_elements
    global cylinder_inside_same_as_outside used_count app_has_can_touch app_has_special_priority
    
    set verbose 1
    
    set tet_rad [expr {[info exists writing_ff_grid] && \
			   ($grid_type == "tetra")}]
  
    # Fix the two-part cylinders with internal radius that we use in icepak3.x
    # but not in 4.x
    foreach m [array names grid_mapping] {
	set obj [lindex $m 0]
	set part [lindex $m 1]
	if {$part != "body1"} continue
	set bsh [$obj getval body_shape]
	if {($bsh == "") || ([$bsh get -shtype] != "cyl")} continue
	
	set oc [lindex $grid_mapping($obj) 0]
	set hc [lindex $grid_mapping($obj) 1]
	set grid_mapping($obj) $oc
	unset grid_mapping($m)
	unset grid_mapping($hc)
	
	# Get rid of the top and bottom, unless of course they are also
 	# used by some other group
	foreach side {1 2} {
	    set vn "${full}_group($hc $side)"
	    if [info exists $vn] {
		[set $vn] delete
		unset $vn
	    }
	}
	
	# Move the inside elements to the fluid family (i.e. delete the group)
	set vn "${full}_group($hc 0)"
	if [info exists $vn] {
	    [set $vn] delete
	    unset $vn
	}
	
	# Move the side elements to the enclosing one
	set vn "${full}_group($hc 3)"
	if [info exists $vn] {
	    set "${full}_group($oc 4)" [set $vn]
	    unset $vn
	}
    }
    
    if [info exists cylinder_inside_same_as_outside] {
	foreach m [array names grid_mapping] {
	    set obj [lindex $m 0]
	    set part [lindex $m 1]
	    if {$part != "body"} continue
	    set bsh [$obj getval body_shape]
	    if {($bsh == "") || ([$bsh get -shtype] != "cyl")} continue
	    set vn1 "full_group($grid_mapping($m) 4)"
	    set vn2 "full_group($grid_mapping($m) 3)"
	    if [info exists $vn1] {
		set from [set $vn1]
		set to [set $vn2]
		$to add_other_contents $from 1
		[set $vn1] delete
		unset $vn1
	    }
	}
    }
    
    # Make sure that all the sides of an incline are in group 3 and not others
    foreach m [array names grid_mapping] {
	set obj [lindex $m 0]
	set part [lindex $m 1]
	if {$part == ""} continue
	set sh [$obj getval ${part}_shape]
	if {$sh == ""} continue
	if {[$sh get -shtype] != "incline"} continue
	
	set n [lindex $grid_mapping($sh) 0]
	
	if ![info exists "${full}_group($n 3)"] continue
	set to [set "${full}_group($n 3)"]
	foreach side {4 5 6} {
	    if [info exists "${full}_group($n $side)"] {
		set from [set "${full}_group($n $side)"]
		$to add_other_contents $from 1
		$from delete
		unset "${full}_group($n $side)"
	    }
	}
    }

    # For ellipsoidal cylinders and hexa meshes, we have to check to see if
    # the height was negative and then swap the ends, to make them consistent
    # with tetra.
    if {$grid_type == "auto"} {
	foreach m [array names grid_mapping] {
	    set obj [lindex $m 0]
	    set part [lindex $m 1]
	    if {$part == ""} continue
	    set sh [$obj getval ${part}_shape]
	    if {$sh == ""} continue
	    if {[$sh get -shtype] != "oval"} continue
	    if {[$sh get -simplified_height] >= 0} continue
	    set d1 ""; set d2 ""
	    set n $grid_mapping($m)
	    if [info exists "${full}_group($n 1)"] {
		set d1 [set "${full}_group($n 1)"]
	    }
	    if [info exists "${full}_group($n 2)"] {
		set d2 [set "${full}_group($n 2)"]
	    }
	    if {$d1 == ""} {
		catch {unset "${full}_group($n 2)" }
	    } else {
		set "${full}_group($n 2)" $d1
	    }
	    if {$d2 == ""} {
		catch {unset "${full}_group($n 1)" }
	    } else {
		set "${full}_group($n 1)" $d2
	    }
	}
    }

    # Just in case an object type has some special stuff it needs to do
    # to shuffle around its groups (for example waveguides need the top of
    # the polygon to be the logical not the physical top) call the app_ fn
    foreach m [array names grid_mapping] {
	set obj [lindex $m 0]
	set part [lindex $m 1]
	if {$part == ""} continue
	set sh [$obj getval ${part}_shape]
	set type [$obj getval obtype]
	if [info exists calledonce($sh)] continue
	set calledonce($sh) 1
	set func app_modify_mesh_pieces_$type
	if {[info command $func] != ""} {
	    $func $sh $full $grid_mapping($m)
	}
    }
    
    if {$full == "full"} {
	foreach {t n} {
	    hexa HEXA_8 penta PENTA_6 pyra PYRA_5 tetra TETRA_4 tri TRI_3 quad QUAD_4
	} {
	    if ![info exists full_element_set($t)] {
		set full_element_set($t) \
		    [element_set -mesh $full_unstruct -type $n]
	    }
	}
	
	# We need to do this here so we can mark the coupling faces as not
	# belonging to the objects.
	grid_make_coupling_data
    
	# This is no longer needed
	# if {[grid_conformalize modify] < 0} { return 0 }
	# but this can't hurt
	$full_unstruct fix_inverted_elements
	
	# First we need to go through the mesh and find all quads that are
	# sitting on a cutout object, and mark them as not being split.
	# Otherwise we would split faces on thin adiabatic plates and defeat
	# the purposes of doing the cutout.
	grid_mark_no_split_faces
	
	grid_split_faces
    }
    
    set use_objects [expr {$solve_solver != "none"}]
    set use_grid_mapping [info exists grid_mapping]
    
    # First make a list of all the keys for flat objects and for volumes.
    set flat_keys ""; set vol_keys ""; set vface_keys ""
    foreach i [lsort -command sort_by_object [array names ${full}_group]] {
	if !$use_grid_mapping {
	    # This is used for conformalize (standalone program for starlt)
	    if {[lindex $i 1] > 0} {
		lappend vface_keys $i
	    } else {
		lappend vol_keys $i
	    }
	    continue
	}
	if ![info exists grid_mapping([lindex $i 0])] continue
	if ![string compare $i "0 0"] continue
	if {[lindex $i 1] == "OPEN"} continue
	
	set key $grid_mapping([lindex $i 0])
	set obj [lindex $key 0]
	set part [lindex $key 1]
	if {$obj == ""} continue
	if [string match cutout* $part] continue
	set sh [$obj getval ${part}_shape]
	if {$sh == ""} { set sh [$obj getval body_shape] }
	if {$sh == ""} {
	    mess "Warning: [$obj getval obtype] \"[$obj getval name]\" $part\
               no longer exists - skipping\n" red
	    continue
	}
	
	if {![string match *comp [lindex $key 2]] && \
		([$sh get_volume] == 0) && ([$sh get -shtype] != "container")} {
	    lappend flat_keys $i
	} elseif {[lindex $i 1] > 0} {
	    lappend vface_keys $i
	} else {
	    lappend vol_keys $i
	    set obt [$obj getval obtype]
	    
	    # If this for form factors, we want to treat fluid blocks as 
	    # transparent but not resistances and sources,
	    if {(($obt == "block") && \
		     ([$obj si_value block_type] == "fluid")) || \
		    ($use_objects && (($obt == "resistance") ||
				      ($obt == "source")))} {
		set not_solid($i) 1
	    }
	}
    }
    
    # Now set all the pid's appropriately.
    set keys [concat [lsort -command sort_by_object $vface_keys] \
		  [lsort -command sort_by_object $flat_keys]]
    
    mesh_face_set_marks_and_pids $full $reduced $keys $vol_keys 
    
    puts [list sort_by_object $vface_keys]
    puts [list sort_by_object $flat_keys]
    
    if $verbose {
	puts "Low to high priority:"
	foreach k $keys {
	    puts "    [[lindex $grid_mapping([lindex $k 0]) 0] getval name] side [lindex $k 1] pid $pid_for_key($k)"
	}
    }
    
    #parray pid_for_key
    #parray used_count
    
    # In the special case where this is a tetra mesh, don't do anything
    if {($grid_type == "tetra") &&
	($grid_cutter_surface || $grid_tetra_surface)} { return 1 }

    set conn [grid_connectivity $full]
    
    # This has to be done first in case we have an opening or fan between two
    # blocks.
    set some_changed 0
    if ![info exists dont_remove_faces_between_solids] {
	# Now get rid of fan faces, etc that stick into solid volumes.
	foreach i $vol_keys {
	    if ![info exists not_solid($i)] {
		[set ${full}_group($i)] set_all_flags 1
	    }
	}
	foreach i $flat_keys {
	    set obj [lindex $grid_mapping([lindex $i 0]) 0]
	    set shname [lindex $grid_mapping([lindex $i 0]) 1]
	    
	    if [info exists object_cant_be_inside_solid([$obj getval obtype],$shname)] {
		set nr [$conn remove_by_neighbors [set ${reduced}_group($i)] 1 2]
		if $nr {
		    if $verbose {
			puts "Removed $nr faces from group $i (fan inside solid)"
		    }
		    set some_changed 1
		}
	    }
	}
    }
    
    mesh_face_set_marks_and_pids $full $reduced $keys $vol_keys
    
    # Now throw out all the quads and tris that don't belong.
    foreach i [lsort [array names used_count]] {
	set group [set ${reduced}_group($i)]
	set fgroup [set ${full}_group($i)]
	set c1 [$group count]
	set thispid $used_count($i)
	
	# What pids are in this group?
	set bad_pids ""
	foreach p [$group list_pids] {
	    if {$p == $thispid} continue
	    
	    if !$use_grid_mapping {
		lappend bad_pids $p
		continue
	    }
	    
	    set k2 $key_for_pid($p)
	    if {$k2 == "coupling"} {
		lappend bad_pids $p
		continue
	    }
	    
	    set obj [lindex $grid_mapping([lindex $i 0]) 0]
	    set obj2 [lindex $grid_mapping([lindex $k2 0]) 0]
	    set t1 [$obj getval obtype]
	    set t2 [$obj2 getval obtype]
	    
	    # Check to see if maybe these things can't touch each other.
	    if [info exists app_has_can_touch(${t1}_${t2})] {
		set m1 [app_can_touch_${t1}_${t2} $obj $obj2]
		if {$m1 != ""} { user_error $m1; return 0 }
	    } elseif [info exists app_has_can_touch(${t2}_${t1})] {
		set m1 [app_can_touch_${t2}_${t1} $obj2 $obj]
		if {$m1 != ""} { user_error $m1; return 0 }
	    }
	    
	    # There are some cases where individual pairs of objects need to
	    # violate the precedence rules.  For example a hollow block has
	    # lower priority than a wall but a solid block has higher, but
	    # blocks have to have the same priority among themselves.
	    if {[info exists app_has_special_priority(${t2}_${t1})] &&
		[app_special_priority_${t2}_${t1} $obj2 $obj]} {
		
		# Delete the stuff from the obj2's group instead.
		# The flow of control is a bit suboptimal here
		
		set group2 [set ${reduced}_group($k2)]
		$fgroup set_pids_if $p $thispid -1
		set n [$group2 delete_with_pids $thispid]
		if $n {
		    set obn [$obj2 getval name]
		    if $verbose {
			puts "special case: tossed out $n from $obn"
		    }
		}
		
		
	    } else {
		lappend bad_pids $p
		if $verbose { puts "$p is bad for $i because it's lower pri than $thispid" }
	    }
	}
	
	set n [$group delete_with_pids $bad_pids]
	set c2 [$group count]
	if $n {
	    if $verbose {
		puts "tossed out $n from [$obj getval name] [lindex $i 1]: group $group, before $c1 after $c2; bad pids were $bad_pids"
	    }
	}
    }
    
    # Now get rid of the vfaces that are between two solid objects.
    if {$solve_solver == "none"} {
	foreach i $vol_keys {
	    if ![info exists not_solid($i)] {
		[set ${full}_group($i)] set_all_flags 1
	    }
	}
    }
    if ![info exists dont_remove_faces_between_solids] {
	# Now, remove every element of the new group that is adjacent to
	# two marked elements.
	foreach i $vface_keys {
	    set nr [$conn remove_by_neighbors [set ${reduced}_group($i)] 1 2]
	    if $nr {
		if $verbose {
		    puts "Removed $nr faces from group $i (between two solids)"
		}
	    }
	}
	if 0 {
	    # Now get rid of fan faces, etc that stick into solid volumes.
	    foreach i $vol_keys {
		if ![info exists not_solid($i)] {
		    [set ${full}_group($i)] set_all_flags 1
		}
	    }
	    foreach i $flat_keys {
		set obj [lindex $grid_mapping([lindex $i 0]) 0]
		set shname [lindex $grid_mapping([lindex $i 0]) 1]
		
		if [info exists object_cant_be_inside_solid([$obj getval obtype],$shname)] {
		    set nr [$conn remove_by_neighbors [set ${reduced}_group($i)] 1 2]
		    if $nr {
			if $verbose {
			    puts "Removed $nr faces from group $i (fan inside solid)"
			}
		    }
		}
	    }
	}
    } else {
	# In this case just remove faces that are between two solids of 
	# the same type
	foreach i $vface_keys {
	    set nr [$conn remove_inside_solid [set ${reduced}_group($i)] -1]
	    if $nr {
		if $verbose {
		    puts "Removed $nr faces from group $i (inside one solid)"
		}
	    }
	}
	
    }
    
    if [info exists remove_vface_quads_between_fluid] {
	foreach i $vface_keys {
	    #puts "$conn remove_inside_solid [set ${reduced}_group($i)] 0"
	    set nr [$conn remove_inside_solid [set ${reduced}_group($i)] 0]
	    if $nr {
		if $verbose {
		    puts "Removed $nr faces from group $i (fluid on both sides)"
		}
	    }
	}
    }
    
    if !$tet_rad {
	# Also get rid of every quad that is not attached to at least one hexa.
	foreach i "$vface_keys $flat_keys" {
	    set nr [$conn remove_by_neighbors [set ${reduced}_group($i)] 0 1]
	    if $nr {
		if $verbose {
		    puts "Removed $nr faces from group $i\
			(adjacent to no continuum)"
		}
	    }
	}
	
	# Finally toss in the volumes.
	foreach i [lsort -command sort_by_object $vol_keys] {
	    set g [element_map -mesh [set ${full}_unstruct]]
	    $g add_other_contents [set ${full}_group($i)] 0
	    set ${reduced}_group($i) $g
	}
	delete_unused_quads $full
    }

    # Blow away all the empty groups now.
    foreach i [lsort [array names ${reduced}_group]] {
	set group [set ${reduced}_group($i)]
	if ![$group count] {
	    $group delete
	    unset ${reduced}_group($i)
	}
    }
    if {[info exists want_node_elements] && ($want_node_elements == 2) &&
	[info exists ${full}_element_set(node)]} {
	set n 0
	if [info exists domain_family_pid(DEF_POINT)] {
	    # Just delete all the ones that are in family DEF_POINT
	    incr n [[set ${full}_element_set(node)] delete_with_pid \
			$domain_family_pid(DEF_POINT) -1]
	}
	incr n [[set ${full}_element_set(node)] delete_with_pid 0 -1]
	if $verbose {
	    puts "Removed $n unused node elements"
	}
    }

    grid_invalidate_connectivity $full
    
    if {$full == "full"} {
	grid_make_wire_groups
    }
    
    return 1
}

proc mesh_face_set_marks_and_pids {full reduced keys vol_keys} {
    global ${full}_unstruct ${full}_group ${reduced}_group pid_for_key 
    global used_count key_for_pid full_element_map
    
    foreach s [[set ${full}_unstruct] list_sets] {
	$s set_pids 0 -1 0
	$s reset_flood_flags
    }
    
    catch {unset pid_for_key}
    catch {unset used_count}
    set count 1
    foreach i $keys {
	set g [element_map -mesh [set ${full}_unstruct]]
	$g add_other_contents [set ${full}_group($i)] 0
	set ${reduced}_group($i) $g
	$g set_pids $count -1
	set used_count($i) $count
	set pid_for_key($i) $count
	set key_for_pid($count) $i
	incr count
    }
    foreach i $vol_keys {
	set g [set ${full}_group($i)]
	$g set_pids $count -1
	set pid_for_key($i) $count
	set key_for_pid($count) $i
	incr count
    }

    # Finally mark the coupling faces.  These get the highest priority
    foreach name {coup0 coup1 coupi} {
	if [info exists full_element_map($name)] {
	    #puts "$full_element_map($name) set_pids $count -1"
	    $full_element_map($name) set_pids $count -1
	    set key_for_pid($count) coupling
	    incr count
	}
    }
}

proc sort_by_object {n1 n2} {
    set d [expr {[lindex $n1 0] - [lindex $n2 0]}]
    if {$d != 0} { return $d }
    if {([scan [lindex $n1 1] side1] == 1) &&
	([scan [lindex $n2 1] side2] == 1)} {
	return [expr {[lindex $side1 1] - [lindex $side2 1]}]
    } else {
	return 0
    }
}

proc grid_make_coupling_data {} {
    global grid_mesher_params full_couplings full_unstruct full_nodeset
    global full_element_set full_element_map grid_mesher_params
    global subtract_maps_from_couplings full_group grid_coupling_otherpart_group
    
    if {($full_couplings == "") && 
	([array names grid_coupling_otherpart_group] == "")} return
    
    #set only_nums1 {8} ;# 3 6 7 8
    #set only_nums0 {84} ;# 85 78 79
    set only_nums1 ""; set only_nums0 ""
    set reltol $grid_mesher_params(conformal_tol)
    
    set esq $full_element_set(quad)
    set est $full_element_set(tri)
    
    set conn [grid_connectivity]
    
    # Make these for display only
    set em0all [element_map -mesh $full_unstruct]
    set em1all [element_map -mesh $full_unstruct]
    set emiall [element_map -mesh $full_unstruct]
    
    set full_element_map(coup0) $em0all
    set full_element_map(coup1) $em1all
    set full_element_map(coupi) $emiall
	
    foreach coup $full_couplings {
	foreach n {part in_num out_num side side0 side1} v $coup { set $n $v }
	
	set em0 [element_map -mesh $full_unstruct]
	set em1 [element_map -mesh $full_unstruct]
	
	set full_element_map(coup0_${in_num}_${out_num}_${side}) $em0
	set full_element_map(coup1_${in_num}_${out_num}_${side}) $em1
	
	if {($only_nums0 != "") || ($only_nums1 != "")} {
	    foreach i {0 1} {
		set nc ""
		set coupn [set side$i]
		set only [set only_nums$i]
		set c 0
		foreach face $coupn {
		    if {($only == "") || ([lsearch $only $c] >= 0)} {
			lappend nc $face
		    }
		    incr c
		}
		set side$i $nc
	    }
	}
	
	foreach i {0 1} {
	    set coupn [set side$i]
	    catch {unset ns}
	    set c 0
	    foreach face $coupn {
		switch [llength $face] {
		    4 { set es $esq }
		    3 { set es $est }
		    default { error "wierd coupling" }
		}
		#foreach facen $face { [set nm$i] add_node $facen }
		set en [eval $conn lookup_element $es $face]
		if {$en == ""} {
		    set en [eval $es create_element 1 $face -1 $c]
		}
		lappend ns($es) $en
		incr c
	    }
	    foreach es [array names ns] {
		eval [set em$i] add_list_numbered $es $ns($es)
		eval [set em${i}all] add_list_numbered $es $ns($es)
	    }
	}
	
	foreach m $subtract_maps_from_couplings {
	    $em0 subtract_other_contents $m
	    $em0all subtract_other_contents $m
	}
    }
    foreach m $subtract_maps_from_couplings {
	# We have to do something with these mesh elements otherwise they will
	# cause uncovered faces.  Just put them in the outer domain map.
	# FIXME - put them where they really should be going
	set gn "full_group(0 1)"
	if ![info exists $gn] {
	    set $gn [element_map -mesh $full_unstruct]
	}
	[set $gn] add_other_contents $m 1
    }
    
    foreach opg [array names grid_coupling_otherpart_group] {
	$emiall add_other_contents $grid_coupling_otherpart_group($opg) 1
    }
    
    grid_invalidate_connectivity
}

proc coarsen_surface_mesh {pref prefg tol} {
    global ${prefg}_group ${pref}_element_set grid_type
    global grid_mesher_params grid_mesher_null null_unstruct solve_solver
    global ff_tolerance_coarsen writing_ff_grid grid_type
    global grid_tetra_smcoarse grid_tetra_smcasp grid_cutter_surface
    global grid_tetra_sm smallest_gap smooth_path null_element_set nullr_group
    global grid_tetra_surface grid_cad_objects_found no_basic_licenses
    
    if {$grid_type == "auto"} {
	busy 1
	
	foreach t {hexa penta pyra tetra} {
	    set x ${pref}_element_set($t)
	    if [info exists $x] {
		[set $x] delete
		unset $x
	    }
	}
	set set [set ${pref}_element_set(quad)]
	grid_invalidate_connectivity $pref
	set conn [grid_connectivity $pref]
	set before 0
	set deleted 0
	foreach key [lsort [array names ${prefg}_group]] {
	    if ![lindex $key 1] continue
	    
	    set map [set ${prefg}_group($key)]
	    incr before [$map count]
	    $set reset_flood_flags
	    $map set_all_flags 1
	    incr deleted [$map coarsen_surface $conn $tol]
	}
	
	busy 0
    } else {
	set before [[set ${pref}_element_set(tri)] get -num]
#	$null_element_set(tetra) delete
#	unset null_element_set(tetra)
	save_domain [directory]/grid_output_before_coarse null nullr
	save_domain [directory]/grid_output null nullr
	set fp [open [directory]/coarse_in w]
	puts $fp "smooth .5 5"
	set ctol [expr $smallest_gap() / 10.0]
	puts $fp "coarsen 0.001 tol $ctol n_iter 5 surface"
	puts $fp "save"
	puts $fp "quit"
	close $fp
	
	set cmd [list $smooth_path grid_output tetin_auto]
	if {!$grid_cad_objects_found && ![info exists no_basic_licenses]} {
	    #lappend cmd -basic
	}
	lappend cmd < coarse_in
	
	run_background $cmd [directory] 1
	grid_clear $pref 1
	set os $grid_tetra_surface
	set grid_tetra_surface 1
	grid_load_domain [directory]/grid_output $pref "" ""
	set grid_tetra_surface $os
	reduce_mesh_groups $pref $prefg
	set deleted [expr $before - [[set ${pref}_element_set(tri)] get -num]]
    }
    
    mess "Out of $before elements, $deleted deleted by coarsening.\n"
}

proc delete_unused_quads {full} {
    global ${full}_unstruct
    
    set uns [set ${full}_unstruct]
    set ns [$uns get -nodes]
    $ns start_renumbering
    
    foreach s [$uns list_sets] {
	$s start_renumbering
    }
    
    set sets ""
    foreach t {HEXA_8 PENTA_6 PYRA_5 TETRA_4 BAR_2} {
	set s [$uns list_type_sets $t]
	if {$s != ""} {
	    foreach s2 $s { lappend sets $s2 }
	}
    }
    foreach s $sets {
	set em [element_map -mesh $uns]
	$em add_range $s 0 -1
	$em renumber_nodes
	$em delete
    }
    
    set n [$ns purge_unused]
    #puts "$n nodes were unused"
    
    set n 0
    foreach s [$uns list_sets] {
	incr n [$s renumber_all]
    }
    #puts "$n elements were unused"
    
    foreach m [$uns list_maps] {
	set xx [$m fix_element_numbers]
    }
    
    foreach s [$uns list_sets] {
	$s finish_renumbering
    }
    $ns finish_renumbering
    
    grid_invalidate_connectivity
}

proc ensure_correct_surface_orientation {} {
    global full_group grid_mapping full_unstruct full_group app_has_is_vol_shape_solid
    
    # First make everything fluid
    foreach set [$full_unstruct list_sets] {
	$set set_flood_flags 1
    }
    
    foreach m [array names grid_mapping] {
	set obj [lindex $m 0]
	if {![string match model_object* $obj] || ([llength $m] != 2)} continue
	set part [lindex $m 1]
	set sh [$obj getval ${part}_shape ""]
	if {($sh == "") || ![$sh get_volume]} continue
	set type [$obj getval obtype]
	if {[info exists app_has_is_vol_shape_solid($type)] && ![app_is_vol_shape_solid_$type $sh]} continue
	# This defines a solid volume
	foreach n $grid_mapping($m) {
	    set gn "full_group($n 0)"
	    if [info exists $gn] {
		[set $gn] set_all_flags 0
	    }
	}
    }
    set conn [grid_connectivity]
    set n [$conn orient_surfaces_towards_marked_volumes]
    # if $n { puts "$n surface elements reoriented\n" }
}
