Skip to content
Snippets Groups Projects
Forked from gmsh / gmsh
16541 commits behind the upstream repository.
PViewDataGModelIO.cpp 14.04 KiB
// $Id: PViewDataGModelIO.cpp,v 1.23 2008-03-30 11:53:04 geuzaine Exp $
//
// Copyright (C) 1997-2008 C. Geuzaine, J.-F. Remacle
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA.
// 
// Please report all bugs and problems to <gmsh@geuz.org>.
//
// Contributor(s):
// 

#include <string.h>
#include "Message.h"
#include "PViewDataGModel.h"
#include "MVertex.h"
#include "Numeric.h"
#include "StringUtils.h"

bool PViewDataGModel::readMSH(std::string fileName, int fileIndex, FILE *fp,
                              bool binary, bool swap, int step, double time, 
                              int partition, int numComp, int numNodes)
{
  Msg(INFO, "Reading step %d (time %g) partition %d: %d nodes", 
      step, time, partition, numNodes);

  while(step >= (int)_steps.size())
    _steps.push_back(new stepData<double>(GModel::current(), numComp));
  
  _steps[step]->setFileName(fileName);
  _steps[step]->setFileIndex(fileIndex);
  _steps[step]->setTime(time);

  // if we already have maxSteps for this view, return
  int numSteps = 0, maxSteps = 1000000000;
  for(unsigned int i = 0; i < _steps.size(); i++)
    numSteps += _steps[i]->getNumData() ? 1 : 0;
  if(numSteps > maxSteps) return true;

  _steps[step]->resizeData(numNodes);

  for(int i = 0; i < numNodes; i++){
    int num;
    if(binary){
      if(fread(&num, sizeof(int), 1, fp) != 1) return false;
      if(swap) swapBytes((char*)&num, sizeof(int), 1);
    }
    else{
      if(fscanf(fp, "%d", &num) != 1) return false;
    }
    double *d = _steps[step]->getData(num, true);
    if(binary){
      if((int)fread(d, sizeof(double), numComp, fp) != numComp) return false;
      if(swap) swapBytes((char*)d, sizeof(double), numComp);
    }
    else{
      for(int j = 0; j < numComp; j++)
        if(fscanf(fp, "%lf", &d[j]) != 1) return false;
    }
    double s = ComputeScalarRep(numComp, d);
    _steps[step]->setMin(std::min(_steps[step]->getMin(), s));
    _steps[step]->setMax(std::max(_steps[step]->getMax(), s));
  }

  _partitions.insert(partition);

  finalize();
  return true;
}

bool PViewDataGModel::writeMSH(std::string fileName, bool binary)
{
  if(_steps.empty()) return true;

  if(hasMultipleMeshes()){
    Msg(GERROR, "Export not done for multi-mesh views");
    return false;
  }

  GModel *model = _steps[0]->getModel();

  binary = true;

  if(!model->writeMSH(fileName, 2.0, binary, true)) return false;

  // append data
  FILE *fp = fopen(fileName.c_str(), binary ? "ab" : "a");
  if(!fp){
    Msg(GERROR, "Unable to open file '%s'", fileName.c_str());
    return false;
  }

  for(unsigned int step = 0; step < _steps.size(); step++){
    int numNodes = 0, numComp = _steps[step]->getNumComponents();
    for(int i = 0; i < _steps[step]->getNumData(); i++)
      if(_steps[step]->getData(i)) numNodes++;
    if(numNodes){
      fprintf(fp, "$NodeData\n");
      fprintf(fp, "\"%s\"\n", getName().c_str());
      fprintf(fp, "%d %.16g 0 0 %d %d\n", step, _steps[step]->getTime(), 
              numComp, numNodes);
      for(int i = 0; i < _steps[step]->getNumData(); i++){
        if(_steps[step]->getData(i)){
	  MVertex *v = _steps[step]->getModel()->getMeshVertexByTag(i);
	  if(!v){
	    Msg(GERROR, "Unknown vertex %d in data", i);
	    return false;
	  }
	  int num = v->getIndex();
          if(binary){
            fwrite(&num, sizeof(int), 1, fp);
            fwrite(_steps[step]->getData(i), sizeof(double), numComp, fp);
          }
          else{
            fprintf(fp, "%d", num);
            for(int k = 0; k < numComp; k++)
              fprintf(fp, " %.16g", _steps[step]->getData(i)[k]);
            fprintf(fp, "\n");
          }
        }
      }
      if(binary) fprintf(fp, "\n");
      fprintf(fp, "$EndNodeData\n");
    }
  }
    
  fclose(fp);
  return true;
}

#if defined(HAVE_MED)

extern "C" {
#include <med.h>
}

bool PViewDataGModel::readMED(std::string fileName, int fileIndex)
{
  med_idt fid = MEDouvrir((char*)fileName.c_str(), MED_LECTURE);
  if(fid < 0) {
    Msg(GERROR, "Unable to open file '%s'", fileName.c_str());
    return false;
  }
  
  med_int numComp = MEDnChamp(fid, fileIndex + 1);
  if(numComp <= 0){
    Msg(GERROR, "Could not get number of components for MED field");
    return false;
  }

  char name[MED_TAILLE_NOM + 1];
  std::vector<char> compName(numComp * MED_TAILLE_PNOM + 1);
  std::vector<char> compUnit(numComp * MED_TAILLE_PNOM + 1);
  med_type_champ type;
  if(MEDchampInfo(fid, fileIndex + 1, name, &type, &compName[0], &compUnit[0], 
		  numComp) < 0){
    Msg(GERROR, "Could not get MED field info");
    return false;
  }

  Msg(INFO, "Reading %d-component field <<%s>>", numComp, name);
  setName(name);

  // the ordering of the elements in the following lists is important:
  // it should match the ordering of the MSH element types (when
  // elements are saved without tags, this governs the order with
  // which we implicitly index them in GModel::readMED)
  const med_entite_maillage entType[] = 
    {MED_NOEUD, MED_MAILLE, MED_NOEUD_ELEMENT};
  // don't import points for now (points are not numbered in the same
  // sequence as MElements)
  const med_geometrie_element eleType[] = 
    {MED_NONE, MED_SEG2, MED_TRIA3, MED_QUAD4, MED_TETRA4, MED_HEXA8, 
     MED_PENTA6, MED_PYRA5, MED_SEG3, MED_TRIA6, MED_TETRA10, 
     /* MED_POINT1, */ MED_QUAD8, MED_HEXA20, MED_PENTA15, MED_PYRA13};
  const int nodesPerEle[] = 
    {0, 2, 3, 4, 4, 8, 6, 5, 3, 6, 10, /* 1, */ 8, 20, 15, 13};

  med_int numSteps = 0;
  std::vector<std::pair<int, int> > pairs;
  for(int i = 0; i < sizeof(entType) / sizeof(entType[0]); i++){
    for(int j = 0; j < sizeof(eleType) / sizeof(eleType[0]); j++){
      med_int n = MEDnPasdetemps(fid, name, entType[i], eleType[j]);
      if(n > 0){
	pairs.push_back(std::pair<int, int>(i, j));
	numSteps = std::max(numSteps, n);
      }
      if(!i && !j) break; // MED_NOEUD does not care about eleType
    }
  }    

  if(numSteps < 1 || pairs.empty()){
    Msg(GERROR, "Nothing to import from MED file");
    return false;
  }
  else{
    med_entite_maillage ent = entType[pairs[0].first];
    setType((ent == MED_NOEUD) ? NodeData : 
	    (ent == MED_MAILLE) ? ElementData :
	    ElementNodeData);
  }

  _steps.clear();
  for(int step = 0; step < numSteps; step++){
    for(unsigned int pair = 0; pair < pairs.size(); pair++){
      // get step info
      med_entite_maillage ent = entType[pairs[pair].first];
      med_geometrie_element ele = eleType[pairs[pair].second];
      med_int numdt, numo, ngauss, numMeshes;
      char dtunit[MED_TAILLE_PNOM + 1], meshName[MED_TAILLE_NOM + 1];
      med_float dt;
      med_booleen local;
      if(MEDpasdetempsInfo(fid, name, ent, ele, step + 1, &ngauss, &numdt, &numo,
			   dtunit, &dt, meshName, &local, &numMeshes) < 0){
	Msg(GERROR, "Could not read step info");
	return false;
      }

      // create step data
      // FIXME: search for model (i.e., mesh) by name and use the right one!
      if(!pair){
	int numCompMsh = (numComp == 1) ? 1 : (numComp < 3) ? 3 : 9;
	while(step >= (int)_steps.size())
	  _steps.push_back(new stepData<double>(GModel::current(), numCompMsh));
	_steps[step]->setFileName(fileName);
	_steps[step]->setFileIndex(fileIndex);
	_steps[step]->setTime(dt);
      }

      // get number of values in the field (numVal takes the number of
      // Gauss points or the number of nodes per element into account,
      // but not the number of components)
      med_int numVal = MEDnVal(fid, name, ent, ele, numdt, numo, meshName,
			       MED_COMPACT);
      if(numVal <= 0) continue;
      int mult = 1;
      if(getType() == ElementNodeData) mult = nodesPerEle[pairs[pair].second];
      if(ngauss) mult *= ngauss;
      // only a guess, since several element types may be combined
      _steps[step]->resizeData(numVal / mult);

      // read field data
      std::vector<double> val(numVal * numComp);
      char locname[MED_TAILLE_NOM + 1], profileName[MED_TAILLE_NOM + 1];
      if(MEDchampLire(fid, meshName, name, (unsigned char*)&val[0], MED_FULL_INTERLACE,
		      MED_ALL, locname, profileName, MED_COMPACT, ent, ele, 
		      numdt, numo) < 0){
	Msg(GERROR, "Could not read field values");
	return false;
      }

      // compute profile (indices in full array of entities of given type)
      std::vector<med_int> profile;
      if(std::string(profileName) != MED_NOPFL){
	med_int n = MEDnValProfil(fid, profileName);
	if(n > 0){
	  profile.resize(n);
	  if(MEDprofilLire(fid, &profile[0], profileName) < 0){
	    Msg(GERROR, "Could not read profile");
	    return false;
	  }
	}
      }
      if(profile.empty()){
	profile.resize(numVal / mult);
	for(unsigned int i = 0; i < profile.size(); i++)
	  profile[i] = i + 1;
      }

      // get size of full array and tags (if any) of entities
      bool nodal = (ent == MED_NOEUD);
      med_int numEnt = MEDnEntMaa(fid, meshName, nodal ? MED_COOR : MED_CONN, 
				  nodal ? MED_NOEUD : MED_MAILLE, 
				  nodal ? MED_NONE : ele, 
				  nodal ? (med_connectivite)0 : MED_NOD);
      std::vector<med_int> tags(numEnt);
      if(MEDnumLire(fid, meshName, &tags[0], numEnt, nodal ? MED_NOEUD : MED_MAILLE,
		    nodal ? MED_NONE : ele) < 0)
	tags.clear();

      // if we don't have tags, compute the starting index (i.e., how
      // many elements of different type are in the mesh before these
      // ones)
      int startIndex = 0;
      if(!nodal && tags.empty()){
	for(int i = 1; i < pairs[pair].second; i++){
	  med_int n = MEDnEntMaa(fid, meshName, MED_CONN, MED_MAILLE,
				 eleType[i], MED_NOD);
	  if(n > 0) startIndex += n;
	}
      }
      
      // compute entity numbers using profile, then fill step data
      for(unsigned int i = 0; i < profile.size(); i++){
	int num;
	if(tags.empty()){
	  num = startIndex + profile[i];
	}
	else{
	  if(profile[i] == 0 || profile[i] > tags.size()){
	    Msg(GERROR, "Wrong index in profile");
	    Msg(DEBUG, "nodal=%d prof[%d]=%d #prof=%d #tags=%d numVal=%d mult=%d", 
		nodal, i, profile[i], profile.size(), tags.size(), numVal, mult);
	    return false;
	  }
	  num = tags[profile[i] - 1];
	}
	double *d = _steps[step]->getData(num, true, mult);
	for(int j = 0; j < numComp * mult; j++)
	  d[j] = val[numComp * i + j];
	for(int j = numComp; j < _steps[step]->getNumComponents(); j++)
	  d[j] = 0.;
	double s = ComputeScalarRep(_steps[step]->getNumComponents(), d);
	_steps[step]->setMin(std::min(_steps[step]->getMin(), s));
	_steps[step]->setMax(std::max(_steps[step]->getMax(), s));
      }
    }
  }

  finalize();

  if(MEDfermer(fid) < 0){
    Msg(GERROR, "Unable to close file '%s'", (char*)fileName.c_str());
    return false;
  }
  return true;
}

bool PViewDataGModel::writeMED(std::string fileName)
{
  if(_steps.empty()) return true;

  if(hasMultipleMeshes()){
    Msg(GERROR, "Export not done for multi-mesh views");
    return false;
  }

  GModel *model = _steps[0]->getModel();

  // save the mesh
  if(!model->writeMED(fileName, true)) return false;

  char *meshName = (char*)model->getName().c_str();
  char *fieldName = (char*)getName().c_str();

  med_idt fid = MEDouvrir((char*)fileName.c_str(), MED_LECTURE_AJOUT);
  if(fid < 0) {
    Msg(GERROR, "Unable to open file '%s'", fileName.c_str());
    return false;
  }

  // compute profile
  std::vector<med_int> profile, nums;
  for(int i = 0; i < _steps[0]->getNumData(); i++){
    if(_steps[0]->getData(i)){
      MVertex *v = _steps[0]->getModel()->getMeshVertexByTag(i);
      if(!v){
	Msg(GERROR, "Unknown vertex %d in data", i);
	return false;
      }
      profile.push_back(v->getIndex());
      nums.push_back(i);
    }
  }
  if(profile.empty()){
    Msg(GERROR, "Nothing to save");
    return false;
  }
  char *profileName = (char*)"nodeProfile";
  if(MEDprofilEcr(fid, &profile[0], (med_int)profile.size(), profileName) < 0){
    Msg(GERROR, "Could not create MED profile");
    return false;
  }

  int numComp = _steps[0]->getNumComponents();
  if(MEDchampCr(fid, fieldName, MED_FLOAT64, (char*)"unknown", (char*)"unknown",
		(med_int)numComp) < 0){
    Msg(GERROR, "Could not create MED field");
    return false;
  }

  med_int numNodes = MEDnEntMaa(fid, meshName, MED_COOR, MED_NOEUD, 
				MED_NONE, (med_connectivite)0);
  if(numNodes <= 0){
    Msg(GERROR, "Could not get valid number of nodes in mesh");
    return false;
  }

  for(unsigned int step = 0; step < _steps.size(); step++){
    unsigned int n = 0;
    for(int i = 0; i < _steps[step]->getNumData(); i++)
      if(_steps[step]->getData(i)) n++;
    if(n != profile.size() || numComp != _steps[step]->getNumComponents()){
      Msg(GERROR, "Skipping incompatible step");
      continue;
    }
    double time = _steps[step]->getTime();
    std::vector<double> val(profile.size() * numComp);
    for(unsigned int i = 0; i < profile.size(); i++)
      for(int k = 0; k < numComp; k++)
	val[i * numComp + k] = _steps[step]->getData(nums[i])[k];
    if(MEDchampEcr(fid, meshName, fieldName, (unsigned char*)&val[0], 
		   MED_FULL_INTERLACE, numNodes, MED_NOGAUSS, MED_ALL,
		   profileName, MED_COMPACT, MED_NOEUD, MED_NONE, (med_int)step,
		   (char*)"unknown", time, MED_NONOR) < 0) {
      Msg(GERROR, "Could not write MED field");
      return false;
    }
  }
  
  if(MEDfermer(fid) < 0){
    Msg(GERROR, "Unable to close file '%s'", (char*)fileName.c_str());
    return false;
  }
  return true;
}

#else

bool PViewDataGModel::readMED(std::string fileName, int fileIndex)
{
  Msg(GERROR, "Gmsh must be compiled with MED support to read '%s'", 
      fileName.c_str());
  return false;
}

bool PViewDataGModel::writeMED(std::string fileName)
{
  Msg(GERROR, "Gmsh must be compiled with MED support to write '%s'",
      fileName.c_str());
  return false;
}

#endif