/***********************************************************************
*
*       ELMER, A Computational Fluid Dynamics Program.
*
*       Copyright 1st April 1995 - , Center for Scientific Computing,
*                                    Finland.
*
*       All rights reserved. No part of this program may be used,
*       reproduced or transmitted in any form or by any means
*       without the written permission of CSC.
*
*                Address: Center for Scientific Computing
*                         Tietotie 6, P.O. BOX 405
*                         02101 Espoo, Finland
*                         Tel.     +358 0 457 2001
*                         Telefax: +358 0 457 2302
*                         EMail:   Jari.Jarvinen@csc.fi
************************************************************************/

/***********************************************************************
Program:    ELMER Front 
Module:     ecif_inputAbaqus.cpp
Language:   C++
Date:       01.10.98
Version:    1.00
Author(s):  Martti Verho
Revisions:  

Abstract:   Implementation

************************************************************************/

#include "ecif_body.h"
#include "ecif_body2D.h"
#include "ecif_body3D.h"
#include "ecif_bodyElement.h"
#include "ecif_bodyElement2D.h"
#include "ecif_bodyElement3D.h"
#include "ecif_control.h"  
#include "ecif_geometry.h"
#include "ecif_inputAbaqus.h" 
#include "ecif_mesh.h"
#include "ecif_model.h"
#include "ecif_timer.h"
#include "ecif_userinterface.h"

extern char read_buffer[];

InputAbaqus::InputAbaqus(enum ecif_modelDimension m_dim,
                       ifstream& in_file, char* in_filename):
Input(m_dim, in_file, in_filename)
{
  for (short i = 0; i < 1 + MAX_NOF_ELEM_CODES; i++) {
    elementCodeCounters[i] = 0;
  }

  nofElementSets = 0;
  nofNodeSets = 0;
}


InputAbaqus::~InputAbaqus()
{
}


AbaqusElementSet*
InputAbaqus::addElementSet(char* elem_set_name)
{
  if (nofElementSets == MAX_NOF_ABAQUS_ELEMENT_SETS) {
    return NULL;
  }

  AbaqusElementSet* es = &(elementSets[nofElementSets]);

  update_dyna_string(es->name, elem_set_name);
  
  nofElementSets++;

  // Set ids (used for bodies etc.)
  es->extParentTag = nofElementSets;

  return es;
}


AbaqusElementSet*
InputAbaqus::findElementSet(char* elem_set_name)
{
  AbaqusElementSet* eset = NULL;

  // Check if this is a stored body name
  for (int i = 0; i < nofElementSets; i++) {
    eset = &(elementSets[i]);

    if ( LibFront::ncEqual(eset->name, elem_set_name) ) {
      return eset;
    }
  }

  return NULL;
}


enum ecif_modelDimension 
InputAbaqus::findMeshModelDimension()
{
  ecif_modelDimension dim = ECIF_ND;
  ecif_modelDimension box_dim = ECIF_ND;
  ecif_modelDimension elem_dim = ECIF_ND;
  RangeVector rv;
  bool x_is_const = false;
  bool y_is_const = false;
  bool z_is_const = false;
  
  meshBoundingBox.getRangeVector(rv);

  // Find model dimension by boundary box
  if ( isEqual(rv[0], rv[1]) ) {
    x_is_const = true;
  }

  if ( isEqual(rv[2], rv[3]) ) {
    y_is_const = true;
  }

  if ( isEqual(rv[4], rv[5]) ) {
    z_is_const = true;
  }

  if ( !(x_is_const || y_is_const || z_is_const) ) {
    box_dim = ECIF_3D;
  } else if ( !( x_is_const || y_is_const) && z_is_const ) {
    box_dim = ECIF_2D;
  } else if ( !(x_is_const && y_is_const && z_is_const) ) {
    box_dim = ECIF_1D;
  } else {
    box_dim = ECIF_ND;
  }

  // Find actual dimension
  //
  dim = findModelDimension(box_dim);

  return dim;
}


void 
InputAbaqus::countNofBoundaryAndBulkElements()
{
  for (short i = 0; i < MAX_NOF_ELEM_CODES; i++) {

    int nof_elements = elementCodeCounters[i];

    if (nof_elements == 0) continue;

    //--These are 2D bulk elements of 3D boundary elements
    if (i = MEC_000 && i < MEC_504) {

      //-2D bulk elements or 3D boundary model 'bulk' elements
      if ( modelDimension == ECIF_2D ) {
        nofBulkElements += nof_elements;

      //-3D boundary elements
      } else {
        nofBoundaryElements += nof_elements;
      }

    //--These are 3D bulk elements
    } else if ( i >= MEC_504 ) {
      nofBulkElements += nof_elements;
    }

  }
}



// Find ELMER element code for an Abaqus element name
//
// NOTE: Currently only solid Abaqus elements and not even all
// of them are supported, they really have elements!!!
//
int
InputAbaqus::findElementCode(char* abq_name)
{
  // NOTE: Keep the falses at the end of each if statements
  //       This way it is safer to insert new types

  //---BEAMS
  // Linear (2 nodes)
  if ( LibFront::ncEqualPartial(abq_name, "DC1D2")   ||
       false
     ) {
    return 202;
  }

  // Parabolic (3 nodes)
  if ( LibFront::ncEqualPartial(abq_name, "DC1D3")   ||
       false
     ) {
    return 203;
  }

  //---TRIANGLES
  // Linear (3 nodes)
  if ( LibFront::ncEqualPartial(abq_name, "CPE3")    ||
       LibFront::ncEqualPartial(abq_name, "CPS3")    ||
       LibFront::ncEqualPartial(abq_name, "DC2D3")   ||
       LibFront::ncEqualPartial(abq_name, "CAX3")    ||
       LibFront::ncEqualPartial(abq_name, "CGAX3")   ||
       LibFront::ncEqualPartial(abq_name, "DCAX3")   ||
       LibFront::ncEqualPartial(abq_name, "S3")      ||
       false
     ) {
    return 303;
  }

  // Parabolic (6 nodes)
  if ( LibFront::ncEqualPartial(abq_name, "CPE6")    ||
       LibFront::ncEqualPartial(abq_name, "CPS6")    ||
       LibFront::ncEqualPartial(abq_name, "DC2D6")   ||
       LibFront::ncEqualPartial(abq_name, "CAX6")    ||
       LibFront::ncEqualPartial(abq_name, "CGAX6")   ||
       LibFront::ncEqualPartial(abq_name, "DCAX6")   ||
       LibFront::ncEqualPartial(abq_name, "S6")      ||
       false
     ) {
    return 306;
  }

  //---QUADRILATERALS
  // Linear (4 nodes)
  if ( LibFront::ncEqualPartial(abq_name, "CPE4")    ||
       LibFront::ncEqualPartial(abq_name, "CPS4")    ||
       LibFront::ncEqualPartial(abq_name, "DC2D4")   ||
       LibFront::ncEqualPartial(abq_name, "AC2D4")   ||
       LibFront::ncEqualPartial(abq_name, "CAX4")    ||
       LibFront::ncEqualPartial(abq_name, "CGAX4")   ||
       LibFront::ncEqualPartial(abq_name, "DCAX4")   ||
       LibFront::ncEqualPartial(abq_name, "S4")      ||
       false
     ) {
    return 404;
  }

  // Parabolic (8 nodes)
  if ( LibFront::ncEqualPartial(abq_name, "CPE8")    ||
       LibFront::ncEqualPartial(abq_name, "CPS8")    ||
       LibFront::ncEqualPartial(abq_name, "C2D8")    ||
       LibFront::ncEqualPartial(abq_name, "DC2D8")   ||
       LibFront::ncEqualPartial(abq_name, "AC2D8")   ||
       LibFront::ncEqualPartial(abq_name, "CAX8")    ||
       LibFront::ncEqualPartial(abq_name, "CGAX8")   ||
       LibFront::ncEqualPartial(abq_name, "DCAX8")   ||
       LibFront::ncEqualPartial(abq_name, "S8")      ||
       false
    ) {
    return 408;
  }

  //---TETRAS
  // Linear (4 nodes)
  if ( LibFront::ncEqualPartial(abq_name, "C3D4")    ||
       LibFront::ncEqualPartial(abq_name, "DC3D4")   ||
       false
     ) {
    return 504;
  }

  // Parabolic (10 nodes)
  if ( LibFront::ncEqualPartial(abq_name, "C3D10")    ||
       LibFront::ncEqualPartial(abq_name, "DC3D10")   ||
       false
     ) {
    return 510;
  }

  //---WEDGE
  // Linear (6 nodes)
  if ( LibFront::ncEqualPartial(abq_name, "C3D6")    ||
       LibFront::ncEqualPartial(abq_name, "DC3D6")   ||
       false
     ) {
    return 706;
  }

  //---BRICKS
  // Linear (8 nodes)
  if ( LibFront::ncEqualPartial(abq_name, "C3D8")    ||
       LibFront::ncEqualPartial(abq_name, "DC3D8")   ||
       false
     ) {
    return 808;
  }

  // Parabolic (20 nodes)
  if ( LibFront::ncEqualPartial(abq_name, "C3D20")   ||
       LibFront::ncEqualPartial(abq_name, "DC3D20")  ||
       false
     ) {
    return 820;
  }

  // Parabolic (27 nodes)
  if ( LibFront::ncEqualPartial(abq_name, "C3D27")   ||
       LibFront::ncEqualPartial(abq_name, "DC3D27")  ||
       false
     ) {
    return 827;
  }

  //---Unknown or unsupported!!!
  return (int)NO_INDEX;
}


// Check if element (set) name is for an axisymmetric
// solid Abaqus element type
bool
InputAbaqus::isAxisymmetricElement(char* abq_name)
{
  if ( LibFront::ncEqualPartial(abq_name, "CAX")     ||
       LibFront::ncEqualPartial(abq_name, "CGAX")    ||
       LibFront::ncEqualPartial(abq_name, "DCAX")    
     )
    return true;

  return false;
}


bool
InputAbaqus::next_is_keyword_line()
{
  eat_white(infile);

  bool is_keyword_line = false;

  char c = infile.get();

  // If first is '*', check if next is not
  // '*' and we really have a keyword line
  if ( c == '*' && infile.peek() != '*' ) {
    is_keyword_line = true;
  }

  infile.putback(c);

  return is_keyword_line;

}



Rc
InputAbaqus::readElements(AbaqusElementSet* es, int elem_type,
                          bool count_only, int& element_counter)
{
  Rc rc;
  int ext_elem_id, ext_parent_tag;

  static int ext_node_ids[MAX_NOF_NODES];
  char number_buffer[81];

  char* strm_buffer = new char[BUFFER_LEN];
  strstream strm(strm_buffer, BUFFER_LEN, ios::app);

  // Initially eof flag set true
  strm.clear(ios::eofbit);

  meshElementCode elem_code = model->convertElementType(elem_type);

  if ( elem_code == MEC_000 ) {
    return ECIF_ERROR;
  }

  short nof_nodes = MeshElementDesc[elem_code][DESC_NOF_NODES];
  short nof_missing_nodes = 0;

  if ( es != NULL ) {
    ext_parent_tag = es->extParentTag;
  } else {
    ext_parent_tag = NO_INDEX;
  }

	while ( !infile.eof() ) {
 
    theControlCenter->update(element_counter, GUI_UPDATE_INTERVAL);

    // Read new line if needed
    if ( !infile.eof() && strm.eof() ) {

      skip_comment_lines();

      // If data is at end!!!
      if ( next_is_keyword_line() ) {
        break;
      }

      readFileLine(infile, read_buffer);

      // Change comma-separator to blanko
      for (int i = 0; i < strlen(read_buffer); i++) {

        if (read_buffer[i] == ',') {
          read_buffer[i] = ' ';
        }
      }

      reset(strm);
      strm << read_buffer << ends;
    }

    // Read possible missing node ids for the element
    while ( !strm.eof() && nof_missing_nodes > 0 ) {

      strm >> number_buffer;

      if (strlen(number_buffer) == 0) {
        strm.clear(ios::eofbit);
        break;
      }

      ext_node_ids[nof_nodes - nof_missing_nodes] = atol(number_buffer);

      nof_missing_nodes--;
    }

    // Read new element id if no node ids are missing for the
    // previous
    if ( !strm.eof() && nof_missing_nodes == 0 ) {

      strm >> number_buffer;

      // No more numbers in the stream
      if (strlen(number_buffer) == 0) {
        strm.clear(ios::eofbit);

      // Start new element
      } else {

        ext_elem_id = atol(number_buffer);
        element_counter++;
        elementCodeCounters[elem_code]++;

        if (es != NULL) {
          es->nofElements++;
        }

        nof_missing_nodes = nof_nodes;
      }
    }

    // Update max-external-id
    if ( maxExternalElementId < ext_elem_id ) {
      maxExternalElementId = ext_elem_id;
    }

    // Check element type
    bool is_bulk;
    bool is_bndr;
    bool is_edge;

    model->checkMeshElementType(elem_type, is_bulk, is_bndr, is_edge);
    
    // Add element to the model's input-element table if not in count-only mode
    //
    if ( !count_only && nof_missing_nodes == 0) {

      // NOTE: Parabolic beam is the only Abaqus element where
      // we have to reorder nodes!!!
      // (Why on the earth they did this exception!?!)
      if ( elem_type == 203 ) {
        int tmp = ext_node_ids[1]; // Pick middle node
        ext_node_ids[1] = ext_node_ids[2];
        ext_node_ids[2] = tmp; // Put middle node at the end
      }
     
      rc = model->addMeshInputElement(elem_type,
                                      ext_elem_id, NO_INDEX, ext_parent_tag,
                                      ext_node_ids);

      if (rc != ECIF_OK) return rc;

      if ( is_bulk ) {
        nofInputBulkElements++;
      } else if (is_bndr ) {
        nofInputBoundaryElements++;
      } else if (is_edge ) {
        nofInputEdgeElements++;
      } else {
        nofInputVertexElements++;
      }
    }
 
  } // while not eof()

  delete[] strm_buffer;

	return ECIF_OK;
}


bool
InputAbaqus::readElementSet(AbaqusElementSet* es, int& body_counter, int& bndr_counter, bool count_only)
{
  if (es == NULL ) return false;

  char* strm_buffer = new char[BUFFER_LEN];
  strstream strm(strm_buffer, BUFFER_LEN, ios::app);

  // Initially eof flag set true
  strm.clear(ios::eofbit);

  char number_buffer[80];
  int parent_ids[2] = {NO_INDEX, NO_INDEX};
  
  // Only counting elements
  // ----------------------
  if ( count_only ) {
    es->nofElements = 0;

  // Create body/boundary
  // --------------------
  } else {
    
    int ext_elem_id = es->firstElementId;
    int int_elem_id = model->getMeshInputElementIdExt2Int(ext_elem_id);
    int elem_type = model->getMeshInputElementType(int_elem_id);

    bool is_bulk;
    bool is_bndr;
    bool is_edge;

    model->checkMeshElementType(elem_type, is_bulk, is_bndr, is_edge);
    
    // Create a new body
    // -----------------
    if ( is_bulk ) {

      Body* body = NULL;
 
      int int_id = ++body_counter; // Body internal id!
      int ext_id = es->extParentTag;   // Body external id!

      if ( modelDimension == ECIF_2D ) {
        body = new Body2D(MESH_BODY, int_id, ext_id, 0, NULL);

      } else if ( modelDimension == ECIF_3D ) {
        body = new Body3D(MESH_BODY, int_id, ext_id, 0, NULL);
      }

      if (body != NULL) {
        model->addBody(body);
        body->setName(es->name);
      }
    
      // Ok, home found/created for the bulk elements!
      if ( body != NULL ) {
        es->parentTag = body->Tag();
      }
      
    // Create a new boundary
    // ---------------------
    } else if ( is_bndr ) {

      BodyElement* be = NULL;
      
      int int_id = ++bndr_counter; // Internal id!

      // Create a proper boundary (body element)
      if ( modelDimension == ECIF_2D ) {
        be = new BodyElement2D(int_id, NO_INDEX, NO_INDEX, es->nofElements);

      } else if ( modelDimension == ECIF_3D ) {
        be = new BodyElement3D(int_id, NO_INDEX, NO_INDEX, es->nofElements);
      }

      if (be != NULL) {
        model->addBodyElement(be);
        be->setName(es->name);
      }

      // Ok, home found/created for the boundary elements!
      if ( be != NULL ) {
        es->parentTag = be->Tag();
      }
    }

  } // Create body/boundary


  // Read element ids
  // ----------------
  while ( !infile.eof() ) {

    skip_comment_lines();

    // If set data is at end, stop!
    //
    if ( next_is_keyword_line() ) break;

    theControlCenter->update(es->nofElements, GUI_UPDATE_INTERVAL);

    // Read new line if needed
    if ( !infile.eof() && strm.eof() ) {
      readFileLine(infile, read_buffer);

      // Change comma-separators to blankos
      for (int i = 0; i < strlen(read_buffer); i++) {
        if (read_buffer[i] == ',')
          read_buffer[i] = ' ';
      }

      reset(strm);
      strm << read_buffer << ends;
    }

    // Read element ids in the set
    //
    while ( !strm.eof() ) {
      strm >> number_buffer;

      if (strlen(number_buffer) == 0) {
        strm.clear(ios::eofbit);
        break;
      }

      int ext_elem_id = atol(number_buffer);

      if ( count_only ) {
        es->nofElements++;
        
        // Store first element id, so that we can find the
        // type of the boundary!
        //
        if ( es->nofElements == 1 ) {
          es->firstElementId = ext_elem_id;
        }

        continue;
      }

      // Ok, input element tables have been created, set parent tags
      // to the element
      //
      int int_elem_id = model->getMeshInputElementIdExt2Int(ext_elem_id);
      model->setMeshInputElementParentTag(int_elem_id, es->parentTag);
      model->setMeshInputElementExtParentTag(int_elem_id, es->extParentTag);

    }

  } // while not eof()

  delete[] strm_buffer;


  return true;
}


// Returns true if a new element set was encountered
// buffer: element set name
bool
InputAbaqus::readElementSetName(istrstream* strm, char* buffer, int buffer_len)
{
  if ( !readKeywordValue(strm, "ELSET=", buffer, buffer_len) ) {
    return false;
  }

  // Check if element set (name) is already stored
  AbaqusElementSet* es = findElementSet(buffer);

  // Add new set
  if ( es == NULL ) {
    es = addElementSet(buffer);
  }

  if (es == NULL)
    return false;
  else
    return true;
}
 


bool
InputAbaqus::readElementType(istrstream* strm, char* buffer, int buffer_len)
{
  return readKeywordValue(strm, "TYPE=", buffer, buffer_len);;
}


bool
InputAbaqus::readKeyword(istrstream* strm, char* buffer, int buffer_len)
{
  if ( strm->eof() )
    return false;

  strm->getline(buffer, buffer_len, ',');

  // Trim trailing blanks
  int len = strlen(buffer);
  int pos = len - 1;

  while ( pos >= 0 && buffer[pos] == ' ' )
    pos--;

  buffer[1 + pos] = '\0';

  return true;
}


bool
InputAbaqus::readKeywordValue(istrstream* istrm, char* keyword, char* buffer, int buffer_len)
{
  // Work stream
  char* tmp = ((strstream*)istrm)->str(); // A stupid cast for Sgi!
  int tmp_buf_len = strlen(tmp);
  char* tmp_buf = new char[1 + tmp_buf_len];
  strstream strm(tmp_buf, 1 + tmp_buf_len, ios::app);
  strm << tmp << ends;

  // Work buffer
  char* tmp_buf2 = new char[1 + tmp_buf_len];

  bool found = false;

  // Try to read keyword value
  while ( !strm.eof() ) {

    // Read next keyword
    strm.getline(tmp_buf2, tmp_buf_len, ',');

    // Skip leading blanks
    char* tmp = tmp_buf2;
    int len2 = strlen(tmp_buf2);
    int pos = 0;
    while ( pos < len2 && tmp[pos] == ' ')
      pos++;
    tmp += pos;

    // If this was searched eyword
    if ( LibFront::ncEqualPartial(tmp, keyword) ) {
      found = true;
      // Jump over the keyword
      tmp += strlen(keyword);

      // Make a new stream from what is left
      reset(strm);
      strm << tmp << ends;
      break;
    }
  }

  if (!found)
    return false;

  // Pick keyword value to tmp_buf
  strm.getline(tmp_buf, tmp_buf_len, ',');

  // Trim trailing blanks
  int len = strlen(tmp_buf);
  while (len > 0 && tmp_buf[len - 1] == ' ' )
    len--;
  tmp_buf[len] = '\0';

  // Check that value fits to the final buffer
  if ( strlen(tmp_buf) <= buffer_len ) {
    strcpy(buffer, tmp_buf);
    found = true;
  }
  else {
    found = false;
  }

  delete[] tmp_buf;
  delete[] tmp_buf2;

  return found;
}


// Reads nodes, elements etc.
Rc
InputAbaqus::readMeshData(int& element_counter, int& node_counter, bool count_only)
{
  Rc rc;

  char* strm_buffer = new char[BUFFER_LEN];
  strstream strm(strm_buffer, BUFFER_LEN, ios::app);

  int keyword_buffer_len = 80;
  char* keyword_buffer = new char[keyword_buffer_len];

  int name_buffer_len = 80;
  char* name_buffer = new char[name_buffer_len];

  UserInterface* gui = theControlCenter->getGui();

  int body_counter = 0;
  int bndr_counter = 0;

  // Read file
  //
  while ( !infile.eof() ) {

    skip_comment_lines();

    bool keyword_found = false;

    if ( next_is_keyword_line() ) 
      keyword_found = true;

    // Read new line
    readFileLine(infile, read_buffer);

    if (!keyword_found)
      continue;

    char* tmp = read_buffer;
    tmp++;

    reset(strm);
    strm << tmp << ends;


    istrstream* istrm = (istrstream*) &strm;

    if ( !readKeyword(istrm, keyword_buffer, 80) )
      keyword_found = false;

    // Uups, something went wrong!!!
    if (!keyword_found)
      continue;

    // Elements
    // ========
    if ( LibFront::ncEqual(keyword_buffer, (char*)ABQ_ELEMENT) ) {

      // Read element type
      if ( !readElementType(istrm, name_buffer, name_buffer_len) ) {
        return ECIF_ERROR;
      }

      int elem_code = findElementCode(name_buffer);

      if (elem_code == (int)NO_INDEX) {

        strstream strm;
        strm << "***ERROR: Unsupported Abaqus element type: ";
        strm << name_buffer << ends;
        gui->showMsg(strm.str());

        return ECIF_ERROR;
      }

      AbaqusElementSet* es = NULL;

      // Read possible element set (=body) name

      // NOTE: Element sets are also given in separete sections where only
      // element ids are given
      //
      if ( readElementSetName(istrm, name_buffer, name_buffer_len) ) {
        es = findElementSet(name_buffer);
      }

      rc = readElements(es, elem_code, count_only, element_counter);

      if ( rc != ECIF_OK ) return rc;
    }

    // Nodes
    // =====
    else if ( LibFront::ncEqual(keyword_buffer, (char*)ABQ_NODE) ) {

      readNodeSetName(istrm, name_buffer, name_buffer_len);
      readNodes(count_only, node_counter);

      if (!count_only) {
        model->setMeshNodes();

      // We can now set the model dimension!
      } else {
        modelDimension = findMeshModelDimension();
      }
    }

    // Element set
    // ===========
    else if ( LibFront::ncEqual(keyword_buffer, (char*)ABQ_ELSET) ) {
      AbaqusElementSet* es = NULL;

      if ( readElementSetName(istrm, name_buffer, name_buffer_len) ) {
        es = findElementSet(name_buffer);
      }

      readElementSet(es, body_counter, bndr_counter, count_only);
    }

    // Node set (node set section is not actually used!)
    // ========
    else if ( LibFront::ncEqual(keyword_buffer, (char*)ABQ_NSET) ) {
      readNodeSetName(istrm, name_buffer, name_buffer_len);
      readNodeSet(name_buffer, count_only);
    }

	}

  delete[] keyword_buffer;
  delete[] name_buffer;
  delete[] strm_buffer;

	return ECIF_OK;
}


// Create mesh geometry from Abaqus input file
//
// --Data should be in the following order in the input file:
// Nodes, Elements, Element sets
//
// --File is read in two phases:
// 1. phase: calculate nof nodes, nof elements and nof elements in sets
// 2. phase: install nodes, elements and create bodies/boundaries by element set
//
// --Those bulk and boundary elements which are not listed in sets, are processed later
// and the bodies/boundaries based on them are then created
//
// --Model dimension is known when nodes are read
//
bool
InputAbaqus::readMeshGeometry()
{
  // For the very first, check that file makes sense!
  if (!readMeshHeader()) {
    modelDimension = ECIF_ND;
    infile.close();
    return false;
  }

  Rc rc;

  int element_counter;
  int node_counter;
  bool count_only;

  //--- 1. pass. Calc nof nodes and elements, create element sets,
  //
  element_counter = 0;
  node_counter = 0;
  count_only = true;

  rc = readMeshData(element_counter, node_counter, count_only);

  nofElements = element_counter;
  nofNodes = node_counter;

  if ( rc != ECIF_OK || nofElements == 0 || nofNodes == 0 ) {
    modelDimension = ECIF_ND;
    return false;
  }

  // Model dimension is known when nodes have been read!
  model->setModelDimension(modelDimension);

  //---Allocate temporary mesh elements table (for bulk+bndr explicitely in file)
  model->allocateMeshInputElements(nofElements, maxExternalElementId);

  model->allocateMeshNodes(nofNodes, maxExternalNodeId);

  //--- 2. pass. Install nodes and elements, create bodies/boundaries by sets
  //
  infile.clear();
  infile.seekg(0);
  element_counter = 0;
  node_counter = 0;
  count_only = false;

  rc = readMeshData(element_counter, node_counter, count_only);

  if (rc != ECIF_OK) {
    modelDimension = ECIF_ND;
    return false;
  }

  return true;
}


// Check Abaqus-type header
bool
InputAbaqus::readMeshHeader()
{
  return true;
}


Rc
InputAbaqus::readNodes(bool count_only, int& node_counter)
{
  Rc rc;

  int external_nd_id;
  static Point3 point;
  char number_buffer[81];

  char* strm_buffer = new char[BUFFER_LEN];
  strstream strm(strm_buffer, BUFFER_LEN, ios::app);
  // Initially eof flag set true
  strm.clear(ios::eofbit);

  short nof_coord = 3;
  short nof_missing_coord = 0;
  
  // Init point buffer
  point[0] = point[1] = point[2] = 0.0;

  // Read nodes until a non-data line is encountered
	while ( !infile.eof() ) {

    theControlCenter->update(node_counter, GUI_UPDATE_INTERVAL);

    // Read new line if needed
    if ( !infile.eof() && strm.eof() ) {

      skip_comment_lines();
      // Check that data is not at end
      if ( next_is_keyword_line() )
        break;

      readFileLine(infile, read_buffer);

      // Change comma-separator to blankos
      for (int i = 0; i < strlen(read_buffer); i++) {
        if (read_buffer[i] == ',')
          read_buffer[i] = ' ';
      }

      reset(strm);
      strm << read_buffer << ends;
    }

    // Read possible missing coordinates for the node
    while ( !strm.eof() && nof_missing_coord > 0 ) {
      strm >> number_buffer;

      if (strlen(number_buffer) == 0) {
        strm.clear(ios::eofbit);
        break;
      }

      point[nof_coord - nof_missing_coord] = atof(number_buffer);
      nof_missing_coord--;
    }

    // Read possible node id if no coordinates are missing
    if ( !strm.eof() && nof_missing_coord == 0 ) {
      strm >> number_buffer;

      // No more numbers in the stream
      if (strlen(number_buffer) == 0) {
        strm.clear(ios::eofbit);

      // Start new node
      } else {
        external_nd_id = atol(number_buffer);
        node_counter++;

        if (external_nd_id > maxExternalNodeId)
          maxExternalNodeId = external_nd_id;

        nof_missing_coord = nof_coord;
      }

    }

    // Add node to the model if everything is ready
    if ( !count_only && nof_missing_coord == 0 )
      rc = model->addMeshNode(node_counter - 1, external_nd_id, point);
    
    if ( count_only ) {
      meshBoundingBox.extendByPoint(point);
    }

  } // while not eof()

  delete[] strm_buffer;

	return ECIF_OK;
}


bool
InputAbaqus::readNodeSet(char* node_set_name, bool count_only)
{
  return true;
}


bool
InputAbaqus::readNodeSetName(istrstream* strm, char* buffer, int buffer_len)
{
  return readKeywordValue(strm, "NSET=", buffer, buffer_len);;
}


void
InputAbaqus::skip_comment_lines()
{
  while ( !infile.eof() ) {

    eat_white(infile);

    char c = infile.get();

    // If first is '*', check if next is also
    // a '*' and we really have a comment line
    // and we skip it
    if ( c == '*' && infile.peek() == '*' ) {
      readFileLine(infile, read_buffer);
    }
    // Otherwise we put character back and stop
    else {
      infile.putback(c);
      break;
    }
  }

}

