#!/usr/bin/env perl
#!/usr/bin/perl
#
# Albert Danial Nov 3 2004
#
use warnings;
use strict;
use File::Basename;
use Getopt::Std;
use vars qw ( $opt_s );
use DBI;
use DBD::SQLite 1.00;
use IO::File;

my $COMMENT = '$'; # Nastran bulk data comment prefix

getopts('s:');
my $sol = 103;
   $sol = $opt_s if $opt_s;
my $script = basename $0;
die "$script [-s <sol #>]  <model .db file>
              Writes a Nastran bulk data file from the sofea 
              model database.  Can generate these entries:
                GRID
                CTRIA3
                SPC1
                SET
                FORCE
                PSHELL
                MAT1
              Limitations:
                - ignores input coordinate system for nodes, forces
                - FORCE set id hardcoded to 998
                - SPC1  set id hardcoded to 999
" unless scalar @ARGV >= 1;

my $model_db_file = shift @ARGV;
$model_db_file .= ".db" unless -r $model_db_file;
(my $outfile = basename $model_db_file) =~ s/db$/dat/;
die "Cannot read $model_db_file" unless -r $model_db_file;
die "$model_db_file -- expect database file to have .db extension" 
    unless $model_db_file =~ /\.db$/;

my %psid = (); # psid{ seq_no } = shell_prop_ref.seq_no if shell_prop_ref.id
               # is not a number otherwise
               # shell_prop_ref{ seq_no } = shell_prop_ref.id

my %gid  = (); # gid{ seq_no } = node.seq_no if node.id is not a number
               # otherwise gid{ seq_no } = node.id

my $dbh  = DBI->connect("dbi:SQLite:dbname=$model_db_file","","");

unlink $outfile if -e $outfile;
my $OUT  = new IO::File ">$outfile" or die "Cannot write to $outfile\n";

fms(    $OUT, $dbh ,  $sol);
grid(   $OUT, $dbh , \%gid);

my $shell_prop_ref = $dbh->selectall_hashref( 
                     "select seq_no,id,material_id,thick from shell_prop", 
                     "id");

ctria3( $OUT, $dbh , \%gid, $shell_prop_ref);

my $material_ref   = $dbh->selectall_hashref( 
                     "select seq_no,id,E,G,nu,rho from material", 
                     "id");

pshell( $OUT, $dbh ,  $shell_prop_ref, $material_ref);
mat1(   $OUT, $dbh ,  $material_ref);
spc1(   $OUT, $dbh , \%gid);
force(  $OUT, $dbh , \%gid);

print   $OUT "enddata\n";

$OUT->close;

my $rc = $dbh->disconnect;

print "Wrote $outfile\n" if -e $outfile;

sub fms { # {{{1
    # file management section, case control
    my ($fh, $dbh, $sol, ) = @_;

    print $OUT "sol $sol\n";
    print $OUT "cend\n";
    print $OUT "method = 1\n";
    print $OUT "load = 99\n";
    print $OUT "spc  = 999\n";
    print $OUT "disp = all\n";
    print $OUT "begin bulk\n";
    print $OUT "param, grdpnt, 0\n";   # grid point weight generator w.r.t. BCS
    print $OUT "param, wtmass, 1.0\n"; #
    print $OUT "eigrl, 1, ,  ,  12\n"; # method #, Freq_low, Freq_hi, nModes
} # 1}}}
sub mat1 { # {{{1
    my ($fh, $dbh ) = @_;

    # materials
    foreach my $mid (sort {$a <=> $b} keys %{$material_ref}) {
        printf $fh "\$ %s\n", $material_ref->{$mid}{id};
        printf $fh "mat1, %d, %e, %e, %e, %e\n", # mid, E, G, nu, rho
                $material_ref->{$mid}{seq_no},
                $material_ref->{$mid}{E}     ,
                $material_ref->{$mid}{G}     ,
                $material_ref->{$mid}{nu}    ,
                $material_ref->{$mid}{rho}   ;
    } 
} # 1}}}
sub grid { # {{{1
    my ($fh, $dbh, $rh_gid, ) = @_;

    # nodes
    my $node_ref = $dbh->selectall_hashref( 
                   "select seq_no,id,coord_in,x1_in,x2_in,x3_in from node",
                   "seq_no");
    foreach my $seq_no (sort {$a <=> $b} keys %{$node_ref}) {
        $node_ref->{$seq_no}{coord_in} = "" 
            unless $node_ref->{$seq_no}{coord_in};

        # deliberately omit coordinate system for now
        $node_ref->{$seq_no}{coord_in} = "";

        if ($node_ref->{$seq_no}{seq_no} =~ /\D/) { # contains non digit
            $rh_gid->{ $seq_no } = $seq_no;
        } else { # ID is an integer, use it
            $rh_gid->{ $seq_no } = $node_ref->{$seq_no}{id};
        }

        printf $OUT "grid, %d, %s,% 12.6e,% 12.6e,% 12.6e\n", 
                    $rh_gid->{$seq_no},
                    $node_ref->{$seq_no}{coord_in},
                    $node_ref->{$seq_no}{x1_in},
                    $node_ref->{$seq_no}{x2_in},
                    $node_ref->{$seq_no}{x3_in};
    }
} # 1}}}
sub ctria3 { # {{{1
    my ($fh, $dbh, $rh_gid, $shell_prop_ref, ) = @_;

    my $tri3_ref = $dbh->selectall_hashref( 
                            "select T.id,T.shell_prop,E.id
                             from tri3 T,element E
                             where T.id = E.seq_no;", "id");

    my $tri3_node_ref = $dbh->selectall_arrayref(
                            "select E.id,EN.nid from tri3 E, element_node EN 
                             where EN.eid=E.id order by EN.seq_no;");

    for (my $e = 0; $e < scalar @{$tri3_node_ref}; $e += 3) {
        my $seq_no = $tri3_node_ref->[$e][0];
        my $ID;
        if ($seq_no =~ /\D/) { # element ID has nondigit, must use seq_no
            $ID = $seq_no;
        } else {
            $ID = $tri3_ref->{ $seq_no }{id}; # can use element.id value 
        }
        printf $OUT "ctria3, %d, %d, %d, %d, %d\n",
               $ID,
               $shell_prop_ref->{ $tri3_ref->{$seq_no}{shell_prop}}{seq_no},
               $rh_gid->{ $tri3_node_ref->[$e  ][1] }  ,
               $rh_gid->{ $tri3_node_ref->[$e+1][1] }  ,
               $rh_gid->{ $tri3_node_ref->[$e+2][1] }  ;
    }
} # 1}}}
sub pshell { # {{{1
    my ($fh, $dbh, $shell_prop_ref, ) = @_;
    foreach my $pid (sort {$a <=> $b} keys %{$shell_prop_ref}) {
        printf $OUT "\$ %s\n", $shell_prop_ref->{$pid}{id};
        printf $OUT "pshell, %d, %d, %e, %d\n", # pid, mid1, t, mid2
                $shell_prop_ref->{$pid}{seq_no} ,
                $material_ref->{ $shell_prop_ref->{$pid}{material_id} }{seq_no},
                $shell_prop_ref->{$pid}{thick}  ,
                $material_ref->{ $shell_prop_ref->{$pid}{material_id} }{seq_no};
    }
} # 1}}}
sub spc1 { # {{{1
    my ($fh, $dbh, $rh_gid, ) = @_;

    my $spc_ref = $dbh->selectall_hashref( 
                   "select T.entity_id, S.dof, S.id from sets T, spc S 
                    where T.sid = S.sid",
                   "entity_id");

    # Group the node ID's together based on constrained dof to minimize
    # the number of SPC1 entries written.  
    my %dof_type = ();  # dof_type{ constraint type} = [list of node seq no's]
    my $first_node = 0; # pick any node seq_no to use as a key for set name
    foreach my $node_seq_no (sort {$a <=> $b} keys %{$spc_ref}) {
        push @{$dof_type{ $spc_ref->{$node_seq_no}{dof} }}, $node_seq_no;
        $first_node = $node_seq_no unless $first_node;
    }

    printf $fh "%s %s\n", $COMMENT, $spc_ref->{$first_node}{id};

    foreach my $DOF (sort {$a <=> $b} keys %dof_type) {
        my $entry_start = sprintf "spc1, 999, %d", $DOF;
        printf $OUT $entry_start;
        # write a maximum of six grid ID's per SPC line
        for (my $i = 0; $i < scalar @{$dof_type{$DOF}}; $i++) {
            my $node_seq_no = $dof_type{$DOF}[$i];
            printf $OUT ",%d", $rh_gid->{$node_seq_no};
            if (!(($i + 1) % 6)) {
                printf $OUT "\n";
                printf $OUT $entry_start if ($i+1) < scalar @{$dof_type{$DOF}};
            }
        }
        printf $OUT "\n" if (scalar @{$dof_type{$DOF}}) % 6;
    }

} # 1}}}
sub force { # {{{1
    my ($fh, $dbh, $rh_gid, ) = @_;

    my $force_ref = $dbh->selectall_hashref( 
                   "select nid,Fmag,F1,F2,F3 from nodal_load", "nid");

    my $CID = "";
    foreach my $node_seq_no (sort {$a <=> $b} keys %{$force_ref}) {
        printf $OUT "force,99,%d,%s,% 11.4e,% 11.4e,% 11.4e,% 11.4e\n", 
               $rh_gid->{$node_seq_no}         , 
               $CID                            ,
               $force_ref->{$node_seq_no}{Fmag},
               $force_ref->{$node_seq_no}{F1}  ,
               $force_ref->{$node_seq_no}{F2}  ,
               $force_ref->{$node_seq_no}{F3}  ;
    }

} # 1}}}
