#!/usr/bin/env perl
#!/usr/bin/perl -w
use strict;
use warnings;
#
# Albert Danial     April 19 2004
#
(my $script = $0) =~ s{.*/}{};  # extract script's basename
my $usage = "
Usage: $script  <gmsh .msh file>
                Creates a sofea .csv file containing the mesh data
                produced by gmsh (http://www.geuz.org/gmsh/).
                All elements get material property of aluminum,
                same thickness.
";
die $usage unless scalar @ARGV == 1;
my $gmsh_file = $ARGV[0];
$gmsh_file .= ".msh" unless -r $gmsh_file;
die "Expected a file that ends with .msh, not $gmsh_file\n"
    unless $gmsh_file =~ /\.msh$/;
die "Cannot read $gmsh_file\n" unless -r $gmsh_file;
my $csv_file     = $gmsh_file; $csv_file    =~ s/\.msh$/.csv/;
my $stadyn_file  = $gmsh_file; $stadyn_file =~ s/\.msh$/.stadyn/;

my %gmsh_model = load_gmsh_mesh($gmsh_file);
# use Data::Dumper; print Dumper(\%gmsh_model);

write_sofea_file( $csv_file,    \%gmsh_model);
write_stadyn_file($stadyn_file, \%gmsh_model);

sub load_gmsh_mesh {  # {{{1
    my ($file) = shift @_;
    my %model = (); # model{node}{id} = [x, y, z]
                    # model{elem}{type}{id} = [node list]
    my %elem_type = (
                        1 => 'bar2',
                        2 => 'tri3',
                        3 => 'quad4',
                        4 => 'tet4',
                        5 => 'hex8',
                        6 => 'prism6',
                        7 => 'pyramid5',
                       15 => 'conm',
                    );
    open  IN, $file or die "load_gmsh_mesh cannot read $file:  $!\n";
    my $mode = "";
    while (<IN>) {
        chomp;
        if (/^\$(\w+)/) {
            my $marker = $1;
            if      ($marker eq "NOD") {
                $mode = "node";
            } elsif ($marker eq "ELM") {
                $mode = "elem";
            } elsif ($marker =~ /^END/) {
                $mode = "";
            }
        } elsif (/^\s*\d+\s*$/) {  # a number by itself:  count of nodes/elem
            next;                  # don't need it
        } elsif (/^\s*\d+\s+-?\d+/) {
            my @entry = split(' ');
            my $ID    = $entry[0];
            if      ($mode eq "node") {
                die "Unusual column count for nodal data file $file line $.\n"
                    unless scalar @entry == 4;
                @{$model{$mode}{$ID}} = @entry[1,2,3];
            } elsif ($mode eq "elem") {
                die "Unusual column count for element data file $file line $.\n"
                    unless scalar @entry >= 7;
                die "Unknown element type '$entry[1]' file $file line $.\n"
                    unless defined $elem_type{$entry[1]};
                my $type   = $elem_type{$entry[1]};
                @{$model{$mode}{$type}{$ID}} = @entry[5..$#entry];
            } else {
                die "load_gmsh_mesh file $file line $. unknown mode for " .
                    "\n$_\n";
            }
        } else {
            die "load_gmsh_mesh file $file line $. parse error:\n$_\n";
        }
    }
    close IN;

    return %model;
} # 1}}}
sub write_stadyn_file { # {{{1
my ($file, $ref_model) = @_;
use POSIX;
my $timestamp = POSIX::strftime("%Y-%m-%d %H:%M:%S", localtime(time));

open  OUT, ">$file" or die "write_stadyn_file:  cannot write $file:  $!\n";

# header data
#
printf OUT "%s [%s %s]\n", $file, $script, $timestamp;
print  OUT "32\n";                 # 32 = folded plate
print  OUT "1 1 1 1\n"; # echo:  connectivity material numbering loads
print  OUT "end\n";

# remap node and triangular element ID's into canonical form, from 1..n
# 
my %can_node_ID = ();  # can_node_ID{ input ID } = ID from 1 to n (# nodes)
my %can_elem_ID = ();  # can_elem_ID{ input ID } = ID from 1 to m (# elements)
my $n = 0;
my $m = 0;
foreach my $ID (sort {$a <=> $b} keys %{$ref_model->{elem}{tri3}} ) {
    ++$m;
    $can_elem_ID{ $ID } = $m;
    foreach (0..2) {
        if (!defined $can_node_ID{ $ref_model->{elem}{tri3}{$ID}[$_] } ) {
            ++$n;
            $can_node_ID{ $ref_model->{elem}{tri3}{$ID}[$_] } = $n;
        }
    }
}

# triangular element data
#
my $nElem = scalar keys %{$ref_model->{elem}{tri3}};
print OUT "$nElem\n";
foreach my $ID (sort 
                {$can_elem_ID{$a} <=> $can_elem_ID{$b}} 
                keys %{$ref_model->{elem}{tri3}} ) {
    printf OUT "%d 4 %d %d %d\n",  # 3=frame   4=plate
               $can_elem_ID{ $ID },
               $can_node_ID{ $ref_model->{elem}{tri3}{$ID}[0] },
               $can_node_ID{ $ref_model->{elem}{tri3}{$ID}[1] },
               $can_node_ID{ $ref_model->{elem}{tri3}{$ID}[2] };
}
print OUT "end\n";

# number of material groups
#
print OUT "1\n";             # one material group
print OUT "1 $nElem 1\n";    # all elements belong to material group 1
print OUT "end\n";

# node data
#
my $nNode = scalar keys %{$ref_model->{node}};
print OUT "$nNode\n";
foreach my $ID (sort 
                {$can_node_ID{$a} <=> $can_node_ID{$b}} 
                keys %{$ref_model->{node}} ) {
    printf OUT "%d %12.6f %12.6f %12.6f\n", 
               $can_node_ID{ $ID }, @{$ref_model->{node}{$ID}};
}
print OUT "end\n";

# boundary conditions
#
# (0 entries = free standing)
print OUT 
"0
end
";
#print OUT 
#"3
#3 1 0 0 0 0 0
#5 1 0 0 0 0 0
#6 0 1 0 0 0 0
#end
#";

# applied load and concentrated mass
#
print OUT 
"1
 4 1000.0 0.0 0.0 0.0 0.0   0.0 0.0
end
";

# material data
#
# id E    G    t    rho    1=pln  I alpha beta
print OUT 
"1
1 10.0e6 4.0e6 0.30 2.50e-4 1.0 0.0024 1.5 0.5
end
";

# damping data
#
print OUT 
"0
end
";
close(OUT);
print "Wrote $file ($nElem elements, $nNode nodes)\n";
} # 1}}}
sub write_sofea_file { # {{{1
my ($file, $ref_model) = @_;
use POSIX;
my $timestamp = POSIX::strftime("%Y-%m-%d %H:%M:%S", localtime(time));

open  OUT, ">$file" or die "write_stadyn_file:  cannot write $file:  $!\n";

printf OUT "# %s [%s %s]\n", $file, $script, $timestamp;

# element data
#
my $nElem = scalar keys %{$ref_model->{elem}{tri3}};
foreach my $ID (sort {$a <=> $b} keys %{$ref_model->{elem}{tri3}} ) {
    print OUT "tri3,$ID,45_mil_alum,",
              join(",", @{$ref_model->{elem}{tri3}{$ID}}), 
              "\n";
}

# node data
#
my $nNode = scalar keys %{$ref_model->{node}};
foreach my $ID (sort {$a <=> $b} keys %{$ref_model->{node}} ) {
    printf OUT "node,%d, 1, %12.6f,%12.6f,%12.6f,\n", 
               $ID, @{$ref_model->{node}{$ID}};
}

print OUT
"shell_prop, 45_mil_alum, al_6061, 4.5e-2
material,al_6061, 9.9E+06, 3.72180450E+06, 3.3E-01, 2.54e-4, 20000
coord, 1, , 1, , 0.0,0.0,0.0, , 0.0,0.0,1.0, ,1.0,0.0,1.0
";

close(OUT);
print "Wrote $file ($nElem elements, $nNode nodes)\n";
} # 1}}}
