示例#1
0
/** Allocate a character buffer based on number of coords and whether 
  * velocities/box info is present.
  */
int Traj_AmberRestart::setupTrajout(FileName const& fname, Topology* trajParm,
                                    CoordinateInfo const& cInfoIn, 
                                    int NframesToWrite, bool append)
{
  if (append) {
    mprinterr("Error: Append not supported for Amber Restart.\n");
    return 1;
  }
  CoordinateInfo cInfo = cInfoIn;
  if (!cInfo.HasTemp() && outputTemp_) cInfo.SetTemperature(true);
  // If temperature requested write time as well or format will break.
  if (cInfo.HasTemp()) {
    outputTime_ = true;
    if (!cInfo.HasTime() && time0_ < 0.0) time0_ = 1.0;
  }
  if (cInfo.HasVel() && !outputVel_) cInfo.SetVelocity(false);
  if (outputTime_) {
    if (!cInfo.HasTime() && time0_ >= 0) cInfo.SetTime(true);
  } else
    cInfo.SetTime(false);
  SetCoordInfo( cInfo );
  if (file_.SetupWrite( fname, debug_ )) return 1;
  readAccess_ = false;
  // Set trajectory info
  natom3_ = trajParm->Natom() * 3;
  // Calculate the length of coordinate frame in bytes
  file_.SetupFrameBuffer( natom3_, 12, 6 ); 
  // Dont know ahead of time if velocities will be used, allocate space
  // just in case. Velocity will not be written if V input is null.
  file_.ResizeBuffer( natom3_ );
  // If box coords are present, allocate extra space for them
  if (CoordInfo().HasBox()) {
    numBoxCoords_ = 6;
    file_.ResizeBuffer( numBoxCoords_ );
  }
  // If number of frames to write == 1 set singleWrite so we dont append
  // frame # to filename.
  if (NframesToWrite==1) singleWrite_ = true;
  // Set up title
  std::string outTitle = Title();
  if (outTitle.empty()) {
    outTitle.assign("Cpptraj Generated Restart");
    outTitle.resize(80, ' ');
  } else {
    if ( outTitle.size() > 80) {
      mprintf("Warning: Amber restart title for %s too long: truncating.\n[%s]\n",
              file_.Filename().base(), outTitle.c_str());
      outTitle.resize(80);
    }
  }
  SetTitle( outTitle );

  return 0;
}
示例#2
0
/** Create Netcdf file specified by filename and set up dimension and
  * variable IDs. 
  */
int Traj_AmberNetcdf::setupTrajout(FileName const& fname, Topology* trajParm,
                                   CoordinateInfo const& cInfoIn, 
                                   int NframesToWrite, bool append)
{
  readAccess_ = false;
  if (!append) {
    CoordinateInfo cInfo = cInfoIn;
    // Deal with output options
    // For backwards compatibility always write temperature if remdtraj is true.
    if (outputTemp_ && !cInfo.HasTemp()) cInfo.SetTemperature(true);
    // Explicitly write velocity - initial frames may not have velocity info.
    if (outputVel_ && !cInfo.HasVel()) cInfo.SetVelocity(true);
    SetCoordInfo( cInfo );
    filename_ = fname;
    // Set up title
    if (Title().empty())
      SetTitle("Cpptraj Generated trajectory");
    // Create NetCDF file.
    if (NC_create( filename_.Full(), NC_AMBERTRAJ, trajParm->Natom(), CoordInfo(), Title() ))
      return 1;
    if (debug_>1) NetcdfDebug();
    // Close Netcdf file. It will be reopened write.
    NC_close();
    // Allocate memory
    if (Coord_!=0) delete[] Coord_;
    Coord_ = new float[ Ncatom3() ];
  } else { // NOTE: File existence is checked for in Trajout
    // Call setupTrajin to set input parameters. This will also allocate
    // memory for coords.
    if (setupTrajin(fname, trajParm) == TRAJIN_ERR) return 1;
    // Check output options.
    if (outputTemp_ && !CoordInfo().HasTemp())
      mprintf("Warning: Cannot append temperature data to NetCDF file '%s'; no temperature dimension.\n",
              filename_.base());
    if (outputVel_ && !CoordInfo().HasVel())
      mprintf("Warning: Cannot append velocity data to NetCDF file '%s'; no velocity dimension.\n",
              filename_.base());
    if (debug_ > 0)
      mprintf("\tNetCDF: Appending %s starting at frame %i\n", filename_.base(), Ncframe()); 
  }
  // Open file
  if ( NC_openWrite( filename_.Full() ) != 0 ) {
    mprinterr("Error: Opening Netcdf file %s for Write.\n", filename_.base());
    return 1;
  }
  return 0;
}
示例#3
0
int DataSet_Coords_TRJ::CoordsSetup(Topology const& topIn, CoordinateInfo const& cInfoIn ) {
  if (trajinList_.empty()) {
    top_ = topIn;
    cInfo_ = cInfoIn;
  } else {
    if ( topIn.Natom() != top_.Natom() ) {
      mprinterr("Error: For TRAJ data set currently all trajectories must have same number\n"
                "Error:  of atoms: %i != %i. Recommended course of action is to\n"
                "Error:  create a trajectory where all frames have been stripped to the same\n"
                "Error:  number of atoms first.\n", topIn.Natom(), top_.Natom());
      return 1;
    }
    // Since velocity/force info is not always allocated in Frame, if one traj
    // has velocity/force info ensure that all do.
    if (cInfoIn.HasVel()) cInfo_.SetVelocity( true );
    if (cInfoIn.HasForce()) cInfo_.SetForce( true );
  }
  return 0;
}
// Action_CreateReservoir::Setup()
Action::RetType Action_CreateReservoir::Setup(ActionSetup& setup) {
  // Check that input parm matches current parm
  if (original_trajparm_->Pindex() != setup.Top().Pindex()) {
    mprintf("Info: createreservoir was set up for topology %s\n", original_trajparm_->c_str());
    mprintf("Info: skipping topology %s\n", setup.Top().c_str());
    return Action::SKIP;
  }
  if (!trajIsOpen_) {
    mprintf("\tCreating reservoir file %s\n", filename_.full());
    CoordinateInfo cinfo = setup.CoordInfo();
    if (!useVelocity_) cinfo.SetVelocity(false);
    if (!useForce_)    cinfo.SetForce(false);
    if (reservoir_.InitReservoir(filename_, title_, cinfo, setup.Top().Natom(),
                                 (bin_ != 0), reservoirT_, iseed_))
    {
      mprinterr("Error: Could not set up NetCDF reservoir.\n");
      return Action::ERR;
    }
    trajIsOpen_ = true;
    nframes_ = 0;
  }
  return Action::OK;
}
/** Like the strip action, closest will modify the current parm keeping info
  * for atoms in mask plus the closestWaters solvent molecules. Set up the
  * vector of MolDist objects, one for every solvent molecule in the original
  * parm file. Atom masks for each solvent molecule will be set up.
  */
Action_Closest::RetType Action_Closest::Setup(Topology const& topIn, CoordinateInfo const& cInfoIn)
{
  // If there are no solvent molecules this action is not valid.
  if (topIn.Nsolvent()==0) {
    mprintf("Warning: Parm %s does not contain solvent.\n",topIn.c_str());
    return Action_Closest::SKIP;
  }
  // If # solvent to keep >= solvent in this parm the action is not valid.
  if (closestWaters_ >= topIn.Nsolvent()) {
    mprintf("Warning: # solvent to keep (%i) >= # solvent molecules in '%s' (%i)\n",
            closestWaters_, topIn.c_str(), topIn.Nsolvent());
    return Action_Closest::SKIP;
  }
  image_.SetupImaging( cInfoIn.TrajBox().Type() );
  if (image_.ImagingEnabled())
    mprintf("\tDistances will be imaged.\n");
  else
    mprintf("\tImaging off.\n"); 
  // LOOP OVER MOLECULES
  // 1: Check that all solvent molecules contain same # atoms. Solvent 
  //    molecules must be identical for the command to work properly; 
  //    the prmtop strip occurs only once so the solvent params become fixed.
  // 2: Set up a mask for all solvent molecules.
  SolventMols_.clear();
  // NOTE: May not be necessary to init 'solvent'
  MolDist solvent;
  solvent.D = 0.0;
  solvent.mol = 0;
  SolventMols_.resize(topIn.Nsolvent(), solvent);
  std::vector<MolDist>::iterator mdist = SolventMols_.begin();
  // 3: Set up the soluteMask for all non-solvent molecules.
  int molnum = 1;
  int nclosest = 0;
  int NsolventAtoms = -1;
  for (Topology::mol_iterator Mol = topIn.MolStart();
                              Mol != topIn.MolEnd(); ++Mol)
  {
    if ( Mol->IsSolvent() ) {
      // Solvent, check for same # of atoms.
      if (NsolventAtoms == -1)
        NsolventAtoms = Mol->NumAtoms();
      else if ( NsolventAtoms != Mol->NumAtoms() ) {
        mprinterr("Error: Solvent molecules in '%s' are not of uniform size.\n"
                  "Error:   First solvent mol = %i atoms, solvent mol %i = %i atoms.\n",
                  topIn.c_str(), NsolventAtoms, molnum, (*Mol).NumAtoms());
        return Action_Closest::ERR;
      }
      // mol here is the output molecule number which is why it starts from 1.
      mdist->mol = molnum;
      // Solvent molecule mask
      mdist->mask.AddAtomRange( Mol->BeginAtom(), Mol->EndAtom() );
      // Atoms in the solvent molecule to actually calculate distances to.
      if (firstAtom_) {
        mdist->solventAtoms.assign(1, Mol->BeginAtom() );
      } else {
        mdist->solventAtoms.clear();
        mdist->solventAtoms.reserve( Mol->NumAtoms() );
        for (int svatom = Mol->BeginAtom(); svatom < Mol->EndAtom(); svatom++)
          mdist->solventAtoms.push_back( svatom );
      }
      if (debug_ > 0) {
        mprintf("DEBUG:\tSet up mol %i:", mdist->mol); // DEBUG
        mdist->mask.PrintMaskAtoms("solvent"); // DEBUG
        mprintf("\n"); // DEBUG
      }
      ++mdist;
    }
    ++molnum;
  }

  // Setup distance atom mask
  // NOTE: Should ensure that no solvent atoms are selected!
  if ( topIn.SetupIntegerMask(distanceMask_) ) return Action_Closest::ERR;
  if (distanceMask_.None()) {
    mprintf("Warning: Distance mask '%s' contains no atoms.\n",
            distanceMask_.MaskString());
    return Action_Closest::SKIP;
  }
  distanceMask_.MaskInfo();

  // Check the total number of solvent atoms to be kept.
  NsolventAtoms *= closestWaters_;
  mprintf("\tKeeping %i solvent atoms.\n",NsolventAtoms);
  if (NsolventAtoms < 1) {
    mprintf("Warning: # of solvent atoms to be kept is < 1.\n");
    return Action_Closest::SKIP;
  }
  NsolventMolecules_ = (int)SolventMols_.size();

  return Action_Closest::OK;
}
示例#6
0
// Traj_NcEnsemble::setupTrajout()
int Traj_NcEnsemble::setupTrajout(FileName const& fname, Topology* trajParm,
                                  CoordinateInfo const& cInfoIn,
                                  int NframesToWrite, bool append)
{
  int err = 0;
# ifdef MPI
  if (NoPnetcdf()) return 1;
# endif
  readAccess_ = false;
  if (!append) {
    CoordinateInfo cInfo = cInfoIn;
    // TODO: File output modifications
    SetCoordInfo( cInfo );
#   ifdef MPI
    ensembleStart_ = Parallel::World().Rank();
    ensembleEnd_ = Parallel::World().Rank() + 1;
#   else
    ensembleStart_ = 0;
    ensembleEnd_ = cInfo.EnsembleSize();;
#   endif
    filename_ = fname;
    // Set up title
    if (Title().empty())
      SetTitle("Cpptraj Generated trajectory");
#   ifdef MPI
    if (Parallel::World().Master()) { // Only master creates file.
#   endif
      // Create NetCDF file.
      err = NC_create(filename_.Full(), NC_AMBERENSEMBLE, trajParm->Natom(), CoordInfo(),
                      Title(), debug_);
      // Close Netcdf file. It will be reopened write. FIXME should NC_create leave it closed?
      NC_close();
#   ifdef MPI
    }
    Parallel::World().MasterBcast(&err, 1, MPI_INT);
#   endif
    if (err != 0) return 1;
#   ifdef MPI
    // Synchronize netcdf info on non-master threads
    Sync(Parallel::World());
    // DEBUG: Print info for all ranks
    DebugVIDs();
#   endif
    // Allocate memory
    if (Coord_!=0) delete[] Coord_;
    Coord_ = new float[ Ncatom3() ];
  } else { // NOTE: File existence is checked for in Trajout
    // Call setupTrajin to set input parameters. This will also allocate
    // memory for coords.
    if (setupTrajin(fname, trajParm) == TRAJIN_ERR) return 1;
    if (debug_ > 0)
      mprintf("\tNetCDF: Appending %s starting at frame %i\n", filename_.base(), Ncframe());
  }
  // Open file
# ifdef HAS_PNETCDF
  err = ncmpi_open(MPI_COMM_WORLD, filename_.full(), NC_WRITE, MPI_INFO_NULL, &ncid_);
  // TODO: Graceful error handling
# else
  err = NC_openWrite( filename_.Full() );
# endif  
  if ( err != 0 ) {
    mprinterr("Error: Opening Netcdf file %s for Write.\n", filename_.base());
    return 1;
  }

  return 0;
}
示例#7
0
/** Loop over all filenames in replica_filenames, set up TrajectoryIO. */
int TrajIOarray::SetupIOarray(ArgList& argIn, TrajFrameCounter& counter,
                              CoordinateInfo& cInfo, Topology* trajParm)
{
  // Sanity check
  if (!IOarray_.empty()) {
    mprinterr("Internal Error: SetupIOarray() has been called twice.\n");
    return 1;
  }
  // Save arguments that have not been processed so they can be passed
  // to each replica in turn. Only the lowest replica will use argIn.
  ArgList argCopy( argIn );
  bool lowestRep = true;
  int rep0Frames = TrajectoryIO::TRAJIN_UNK;  // Total frames in replica 0
  int totalFrames = TrajectoryIO::TRAJIN_UNK; // Total # frames to be read from ensemble
  TrajectoryFile::TrajFormatType lastRepFmt = TrajectoryFile::UNKNOWN_TRAJ;
  // Right now enforce that all replicas have the same metadata as lowest
  // replica, e.g. if replica 0 has temperature, replica 1 does too etc.
  for (File::NameArray::const_iterator repfile = replica_filenames_.begin();
                                       repfile != replica_filenames_.end(); ++repfile)
  {
    // Detect format
    TrajectoryFile::TrajFormatType repformat = TrajectoryFile::UNKNOWN_TRAJ;
    TrajectoryIO* replica0 = TrajectoryFile::DetectFormat( *repfile, repformat );
    if ( replica0 == 0 ) {
      mprinterr("Error: Could not set up replica file %s\n", repfile->full());
      return 1;
    }
    if (repformat != lastRepFmt)
      mprintf("\tReading '%s' as %s\n", repfile->full(), TrajectoryFile::FormatString(repformat));
    lastRepFmt = repformat;
    replica0->SetDebug( debug_ );
    // Pushing replica0 here allows the destructor to handle it on errors
    IOarray_.push_back( replica0 );
    // Process format-specific read args. Do not exit on error in case
    // replicas have different formats supporting different args.
    if (lowestRep) {
      replica0->processReadArgs( argIn );
    } else {
      ArgList argtmp( argCopy );
      replica0->processReadArgs( argtmp );
    }
    // Set up replica for reading and get the number of frames.
    int nframes = replica0->setupTrajin( *repfile, trajParm );
    if (nframes == TrajectoryIO::TRAJIN_ERR) {
      mprinterr("Error: Could not set up %s for reading.\n", repfile->full());
      return 1;
    }
    // TODO: Do not allow unknown number of frames?
    if (lowestRep) {
      cInfo = replica0->CoordInfo();
      rep0Frames = nframes;
      totalFrames = nframes;
      if (cInfo.ReplicaDimensions().Ndims() > 0) {
        mprintf("\tReplica dimensions:\n");
        for (int rd = 0; rd < cInfo.ReplicaDimensions().Ndims(); rd++)
          mprintf("\t\t%i: %s\n", rd+1, cInfo.ReplicaDimensions().Description(rd));
      }
    } else {
      // Check total frames in this replica against lowest rep.
      if (nframes != rep0Frames)
        mprintf("Warning: Replica %s frames (%i) does not match # frames in first replica (%i).\n",
                repfile->base(), nframes, rep0Frames);
      //if (repframes < 0) {
      //  mprinterr("Error: RemdTraj: Unknown # of frames in replica.\n");
      //  return 1;
      //}
      if (nframes < totalFrames) {
        totalFrames = nframes; 
        mprintf("Warning: Setting total # of frames to read from replica ensemble to %i\n",
                totalFrames);
      }
      // Check box info against lowest rep.
      if ( replica0->CoordInfo().HasBox() != cInfo.HasBox() ) {
        mprinterr("Error: Replica %s box info does not match first replica.\n",
                  repfile->full());
        return 1;
      }
      // TODO: Check specific box type
      // Check velocity info against lowest rep.
      if ( replica0->CoordInfo().HasVel() != cInfo.HasVel() ) {
        mprinterr("Error: Replica %s velocity info does not match first replica.\n",
                  repfile->full());
        return 1;
      }
      // Check # dimensions and types against lowest rep
      if ( replica0->CoordInfo().ReplicaDimensions() != cInfo.ReplicaDimensions() ) {
        mprinterr("Error: Replica %s dimension info does not match first replica.\n",
                  repfile->full());
        ReplicaDimArray const& thisRepDims = replica0->CoordInfo().ReplicaDimensions();
        for (int rd = 0; rd < thisRepDims.Ndims(); rd++)
          mprinterr("\t\t%i: %s\n", rd+1, thisRepDims.Description(rd)); 
        return 1;
      }
      // If temperature/time info does not match set to false.
      if (cInfo.HasTemp() != replica0->CoordInfo().HasTemp())
        cInfo.SetTemperature( false );
      if (cInfo.HasTime() != replica0->CoordInfo().HasTime())
        cInfo.SetTime( false );
    }
    lowestRep = false;
  }
  // Check how many frames will actually be read
  if (counter.CheckFrameArgs( totalFrames, argIn )) return 1;
  // Check for errors.
  if (IOarray_.empty()) {
    mprinterr("Error: No replica trajectories set up.\n");
    return 1;
  }
  if (IOarray_.size() != replica_filenames_.size()) { // SANITY CHECK
    mprinterr("Error: Not all replica files were set up.\n");
    return 1;
  }
  // Update ensemble size
  cInfo.SetEnsembleSize( (int)IOarray_.size() );
  if (debug_ > 0)
    cInfo.PrintCoordInfo( replica_filenames_[0].full(), trajParm->c_str() );

  return 0;
}
示例#8
0
/** Each rank only sets up file that it will process. */
int TrajIOarray::SetupIOarray(ArgList& argIn, TrajFrameCounter& counter,
                              CoordinateInfo& cInfo, Topology* trajParm,
                              Parallel::Comm const& ensComm, Parallel::Comm const& trajComm)
{
  // Sanity check
  if (!IOarray_.empty()) {
    mprinterr("Internal Error: SetupIOarray() has been called twice.\n");
    return 1;
  }
  // Detect format
  FileName const& repFname = replica_filenames_[ensComm.Rank()];
  TrajectoryFile::TrajFormatType repformat = TrajectoryFile::UNKNOWN_TRAJ;
  TrajectoryIO* replica0 = TrajectoryFile::DetectFormat( repFname, repformat );
  if ( replica0 == 0 ) {
    mprinterr("Error: Could not set up replica file %s\n", repFname.full());
    return 1;
  }
  mprintf("\tReading '%s' as %s\n", repFname.full(), TrajectoryFile::FormatString(repformat));
  replica0->SetDebug( debug_ );
  // Construct the IOarray_ with blanks for all except this rank.
  for (int member = 0; member != ensComm.Size(); member++)
    if (member == ensComm.Rank())
      IOarray_.push_back( replica0 );
    else
      IOarray_.push_back( 0 );
  // Process format-specific read args.
  replica0->processReadArgs( argIn );
  // Set up replica for reading and get # frames
  int nframes = replica0->setupTrajin( repFname, trajParm );
  if (nframes == TrajectoryIO::TRAJIN_ERR) {
    mprinterr("Error: Could not set up %s for reading.\n", repFname.full());
    return 1;
  }
  // Set coordinate info
  cInfo = replica0->CoordInfo();
  int totalFrames = nframes;
  if (cInfo.ReplicaDimensions().Ndims() > 0) { // TODO put in common routine
    mprintf("\tReplica dimensions:\n");
    for (int rd = 0; rd < cInfo.ReplicaDimensions().Ndims(); rd++)
      mprintf("\t\t%i: %s\n", rd+1, cInfo.ReplicaDimensions().Description(rd));
  }
  // Check # frames in all files, use lowest.
  Parallel::World().AllReduce( &totalFrames, &nframes, 1, MPI_INT, MPI_MIN );
  if (totalFrames != nframes) {
    rprintf("Warning: Replica '%s' frames (%i) is > # frames in shortest replica.\n",
            repFname.full(), nframes);
    mprintf("Warning: Setting total # of frames to read from replica ensemble to %i\n",
            totalFrames);
  }
  if (trajComm.Master()) {
    static const int iSize = 6;
    static const char* iTitle[iSize] = {"box", "velocity", "temperature", "time", "force",
                                        "replica dimensions"};
    // Check coordinate info of all files               0    1    2     3     4      5
    std::vector<int> Info( iSize * ensComm.Size() ); // box, vel, temp, time, force, nRepDims
    int rank_info[iSize];
    rank_info[0] = (int)cInfo.TrajBox().Type();
    rank_info[1] = (int)cInfo.HasVel();
    rank_info[2] = (int)cInfo.HasTemp();
    rank_info[3] = (int)cInfo.HasTime();
    rank_info[4] = (int)cInfo.HasForce();
    rank_info[5] = cInfo.ReplicaDimensions().Ndims();
    ensComm.AllGather( rank_info, iSize, MPI_INT, &Info[0] );
    // TODO Should mismatches be errors instead?
    for (int midx = 0; midx != iSize; midx++) {
      for (int ridx = midx + iSize; ridx < (int)Info.size(); ridx += iSize) {
        if (Info[midx] != Info[ridx]) {
          rprintf("Warning: Replica %i %s info does not match first replica.\n",
                  ridx/iSize, iTitle[midx]);
        }
      }
    }
  }
  // TODO: Put code below into a common routine with serial version
  // Check how many frames will actually be read
  if (counter.CheckFrameArgs( totalFrames, argIn )) return 1;
  // SANITY CHECK
  if (IOarray_.size() != replica_filenames_.size()) {
    mprinterr("Error: Not all replica files were set up.\n");
    return 1;
  }
  // Update ensemble size
  cInfo.SetEnsembleSize( (int)IOarray_.size() );
  if (debug_ > 0)
    cInfo.PrintCoordInfo( repFname.full(), trajParm->c_str() );

  return 0;
}
示例#9
0
// NetcdfFile::NC_create()
int NetcdfFile::NC_create(std::string const& Name, NCTYPE type, int natomIn,
                          CoordinateInfo const& coordInfo, std::string const& title)
{
    if (Name.empty()) return 1;
    int dimensionID[NC_MAX_VAR_DIMS];
    int NDIM;
    nc_type dataType;

    if (ncdebug_>1)
        mprintf("DEBUG: NC_create: %s  natom=%i V=%i F=%i box=%i  temp=%i  time=%i\n",
                Name.c_str(),natomIn,(int)coordInfo.HasVel(),
                (int)coordInfo.HasForce(),(int)coordInfo.HasBox(),
                (int)coordInfo.HasTemp(),(int)coordInfo.HasTime());

    if ( checkNCerr( nc_create( Name.c_str(), NC_64BIT_OFFSET, &ncid_) ) )
        return 1;

    ncatom_ = natomIn;
    ncatom3_ = ncatom_ * 3;

    // Set number of dimensions based on file type
    switch (type) {
    case NC_AMBERENSEMBLE:
        NDIM = 4;
        dataType = NC_FLOAT;
        break;
    case NC_AMBERTRAJ:
        NDIM = 3;
        dataType = NC_FLOAT;
        break;
    case NC_AMBERRESTART:
        NDIM = 2;
        dataType = NC_DOUBLE;
        break;
    default:
        mprinterr("Error: NC_create (%s): Unrecognized type (%i)\n",Name.c_str(),(int)type);
        return 1;
    }

    if (type == NC_AMBERENSEMBLE) {
        // Ensemble dimension for ensemble
        if (coordInfo.EnsembleSize() < 1) {
            mprinterr("Internal Error: NetcdfFile: ensembleSize < 1\n");
            return 1;
        }
        if ( checkNCerr(nc_def_dim(ncid_, NCENSEMBLE, coordInfo.EnsembleSize(), &ensembleDID_)) ) {
            mprinterr("Error: Defining ensemble dimension.\n");
            return 1;
        }
        dimensionID[1] = ensembleDID_;
    }
    ncframe_ = 0;
    if (type == NC_AMBERTRAJ || type == NC_AMBERENSEMBLE) {
        // Frame dimension for traj
        if ( checkNCerr( nc_def_dim( ncid_, NCFRAME, NC_UNLIMITED, &frameDID_)) ) {
            mprinterr("Error: Defining frame dimension.\n");
            return 1;
        }
        // Since frame is UNLIMITED, it must be lowest dim.
        dimensionID[0] = frameDID_;
    }
    // Time variable and units
    if (coordInfo.HasTime()) {
        if ( checkNCerr( nc_def_var(ncid_, NCTIME, dataType, NDIM-2, dimensionID, &timeVID_)) ) {
            mprinterr("Error: Defining time variable.\n");
            return 1;
        }
        if ( checkNCerr( nc_put_att_text(ncid_, timeVID_, "units", 10, "picosecond")) ) {
            mprinterr("Error: Writing time VID units.\n");
            return 1;
        }
    }
    // Spatial dimension and variable
    if ( checkNCerr( nc_def_dim( ncid_, NCSPATIAL, 3, &spatialDID_)) ) {
        mprinterr("Error: Defining spatial dimension.\n");
        return 1;
    }
    dimensionID[0] = spatialDID_;
    if ( checkNCerr( nc_def_var( ncid_, NCSPATIAL, NC_CHAR, 1, dimensionID, &spatialVID_)) ) {
        mprinterr("Error: Defining spatial variable.\n");
        return 1;
    }
    // Atom dimension
    if ( checkNCerr( nc_def_dim( ncid_, NCATOM, ncatom_, &atomDID_)) ) {
        mprinterr("Error: Defining atom dimension.\n");
        return 1;
    }
    // Setup dimensions for Coords/Velocity
    // NOTE: THIS MUST BE MODIFIED IF NEW TYPES ADDED
    if (type == NC_AMBERENSEMBLE) {
        dimensionID[0] = frameDID_;
        dimensionID[1] = ensembleDID_;
        dimensionID[2] = atomDID_;
        dimensionID[3] = spatialDID_;
    } else if (type == NC_AMBERTRAJ) {
        dimensionID[0] = frameDID_;
        dimensionID[1] = atomDID_;
        dimensionID[2] = spatialDID_;
    } else {
        dimensionID[0] = atomDID_;
        dimensionID[1] = spatialDID_;
    }
    // Coord variable
    if ( checkNCerr( nc_def_var( ncid_, NCCOORDS, dataType, NDIM, dimensionID, &coordVID_)) ) {
        mprinterr("Error: Defining coordinates variable.\n");
        return 1;
    }
    if ( checkNCerr( nc_put_att_text( ncid_, coordVID_, "units", 8, "angstrom")) ) {
        mprinterr("Error: Writing coordinates variable units.\n");
        return 1;
    }
    // Velocity variable
    if (coordInfo.HasVel()) {
        if ( checkNCerr( nc_def_var( ncid_, NCVELO, dataType, NDIM, dimensionID, &velocityVID_)) ) {
            mprinterr("Error: Defining velocities variable.\n");
            return 1;
        }
        if ( checkNCerr( nc_put_att_text( ncid_, velocityVID_, "units", 19, "angstrom/picosecond")) )
        {
            mprinterr("Error: Writing velocities variable units.\n");
            return 1;
        }
        if ( checkNCerr( nc_put_att_double( ncid_, velocityVID_, "scale_factor", NC_DOUBLE, 1,
                                            &Constants::AMBERTIME_TO_PS)) )
        {
            mprinterr("Error: Writing velocities scale factor.\n");
            return 1;
        }
    }
    // Force variable
    if (coordInfo.HasForce()) {
        if ( checkNCerr( nc_def_var( ncid_, NCFRC, dataType, NDIM, dimensionID, &frcVID_)) ) {
            mprinterr("Error: Defining forces variable\n");
            return 1;
        }
        if ( checkNCerr( nc_put_att_text( ncid_, frcVID_, "units", 25, "kilocalorie/mole/angstrom")) )
        {
            mprinterr("Error: Writing forces variable units.\n");
            return 1;
        }
    }
    // Replica Temperature
    if (coordInfo.HasTemp()) {
        // NOTE: Setting dimensionID should be OK for Restart, will not be used.
        dimensionID[0] = frameDID_;
        if ( NC_defineTemperature( dimensionID, NDIM-2 ) ) return 1;
    }
    // Replica indices
    int remDimTypeVID = -1;
    if (coordInfo.HasReplicaDims()) {
        // Define number of replica dimensions
        remd_dimension_ = coordInfo.ReplicaDimensions().Ndims();
        int remDimDID = -1;
        if ( checkNCerr(nc_def_dim(ncid_, NCREMD_DIMENSION, remd_dimension_, &remDimDID)) ) {
            mprinterr("Error: Defining replica indices dimension.\n");
            return 1;
        }
        dimensionID[0] = remDimDID;
        // For each dimension, store the type
        if ( checkNCerr(nc_def_var(ncid_, NCREMD_DIMTYPE, NC_INT, 1, dimensionID, &remDimTypeVID)) )
        {
            mprinterr("Error: Defining replica dimension type variable.\n");
            return 1;
        }
        // Need to store the indices of replica in each dimension each frame
        // NOTE: THIS MUST BE MODIFIED IF NEW TYPES ADDED
        if (type == NC_AMBERENSEMBLE) {
            dimensionID[0] = frameDID_;
            dimensionID[1] = ensembleDID_;
            dimensionID[2] = remDimDID;
        } else if (type == NC_AMBERTRAJ) {
            dimensionID[0] = frameDID_;
            dimensionID[1] = remDimDID;
        } else {
            dimensionID[0] = remDimDID;
        }
        if (checkNCerr(nc_def_var(ncid_, NCREMD_INDICES, NC_INT, NDIM-1, dimensionID, &indicesVID_)))
        {
            mprinterr("Error: Defining replica indices variable ID.\n");
            return 1;
        }
        // TODO: Determine if groups are really necessary for restarts. If not,
        // remove from AmberNetcdf.F90.
    }
    // Box Info
    if (coordInfo.HasBox()) {
        // Cell Spatial
        if ( checkNCerr( nc_def_dim( ncid_, NCCELL_SPATIAL, 3, &cell_spatialDID_)) ) {
            mprinterr("Error: Defining cell spatial dimension.\n");
            return 1;
        }
        dimensionID[0] = cell_spatialDID_;
        if ( checkNCerr( nc_def_var(ncid_, NCCELL_SPATIAL, NC_CHAR, 1, dimensionID, &cell_spatialVID_)))
        {
            mprinterr("Error: Defining cell spatial variable.\n");
            return 1;
        }
        // Cell angular
        if ( checkNCerr( nc_def_dim( ncid_, NCLABEL, NCLABELLEN, &labelDID_)) ) {
            mprinterr("Error: Defining label dimension.\n");
            return 1;
        }
        if ( checkNCerr( nc_def_dim( ncid_, NCCELL_ANGULAR, 3, &cell_angularDID_)) ) {
            mprinterr("Error: Defining cell angular dimension.\n");
            return 1;
        }
        dimensionID[0] = cell_angularDID_;
        dimensionID[1] = labelDID_;
        if ( checkNCerr( nc_def_var( ncid_, NCCELL_ANGULAR, NC_CHAR, 2, dimensionID,
                                     &cell_angularVID_)) )
        {
            mprinterr("Error: Defining cell angular variable.\n");
            return 1;
        }
        // Setup dimensions for Box
        // NOTE: This must be modified if more types added
        int boxdim;
        if (type == NC_AMBERENSEMBLE) {
            dimensionID[0] = frameDID_;
            dimensionID[1] = ensembleDID_;
            boxdim = 2;
        } else if (type == NC_AMBERTRAJ) {
            dimensionID[0] = frameDID_;
            boxdim = 1;
        } else {
            boxdim = 0;
        }
        dimensionID[boxdim] = cell_spatialDID_;
        if ( checkNCerr( nc_def_var( ncid_, NCCELL_LENGTHS, NC_DOUBLE, NDIM-1, dimensionID,
                                     &cellLengthVID_)) )
        {
            mprinterr("Error: Defining cell length variable.\n");
            return 1;
        }
        if ( checkNCerr( nc_put_att_text( ncid_, cellLengthVID_, "units", 8, "angstrom")) ) {
            mprinterr("Error: Writing cell length variable units.\n");
            return 1;
        }
        dimensionID[boxdim] = cell_angularDID_;
        if ( checkNCerr( nc_def_var( ncid_, NCCELL_ANGLES, NC_DOUBLE, NDIM-1, dimensionID,
                                     &cellAngleVID_)) )
        {
            mprinterr("Error: Defining cell angle variable.\n");
            return 1;
        }
        if ( checkNCerr( nc_put_att_text( ncid_, cellAngleVID_, "units", 6, "degree")) ) {
            mprinterr("Error: Writing cell angle variable units.\n");
            return 1;
        }
    }

    // Attributes
    if (checkNCerr(nc_put_att_text(ncid_,NC_GLOBAL,"title",title.size(),title.c_str())) ) {
        mprinterr("Error: Writing title.\n");
        return 1;
    }
    if (checkNCerr(nc_put_att_text(ncid_,NC_GLOBAL,"application",5,"AMBER")) ) {
        mprinterr("Error: Writing application.\n");
        return 1;
    }
    if (checkNCerr(nc_put_att_text(ncid_,NC_GLOBAL,"program",7,"cpptraj")) ) {
        mprinterr("Error: Writing program.\n");
        return 1;
    }
    if (checkNCerr(nc_put_att_text(ncid_,NC_GLOBAL,"programVersion",
                                   NETCDF_VERSION_STRLEN, NETCDF_VERSION_STRING)) )
    {
        mprinterr("Error: Writing program version.\n");
        return 1;
    }
    // TODO: Make conventions a static string
    bool errOccurred = false;
    if ( type == NC_AMBERENSEMBLE )
        errOccurred = checkNCerr(nc_put_att_text(ncid_,NC_GLOBAL,"Conventions",13,"AMBERENSEMBLE"));
    else if ( type == NC_AMBERTRAJ )
        errOccurred = checkNCerr(nc_put_att_text(ncid_,NC_GLOBAL,"Conventions",5,"AMBER"));
    else
        errOccurred = checkNCerr(nc_put_att_text(ncid_,NC_GLOBAL,"Conventions",12,"AMBERRESTART"));
    if (errOccurred) {
        mprinterr("Error: Writing conventions.\n");
        return 1;
    }
    if (checkNCerr(nc_put_att_text(ncid_,NC_GLOBAL,"ConventionVersion",3,"1.0")) ) {
        mprinterr("Error: Writing conventions version.\n");
        return 1;
    }

    // Set fill mode
    if (checkNCerr(nc_set_fill(ncid_, NC_NOFILL, dimensionID))) {
        mprinterr("Error: NetCDF setting fill value.\n");
        return 1;
    }

    // End netcdf definitions
    if (checkNCerr(nc_enddef(ncid_))) {
        mprinterr("NetCDF error on ending definitions.");
        return 1;
    }

    // Specify spatial dimension labels
    start_[0] = 0;
    count_[0] = 3;
    char xyz[3];
    xyz[0] = 'x';
    xyz[1] = 'y';
    xyz[2] = 'z';
    if (checkNCerr(nc_put_vara_text(ncid_, spatialVID_, start_, count_, xyz))) {
        mprinterr("Error on NetCDF output of spatial VID 'x', 'y' and 'z'");
        return 1;
    }
    if ( coordInfo.HasBox() ) {
        xyz[0] = 'a';
        xyz[1] = 'b';
        xyz[2] = 'c';
        if (checkNCerr(nc_put_vara_text(ncid_, cell_spatialVID_, start_, count_, xyz))) {
            mprinterr("Error on NetCDF output of cell spatial VID 'a', 'b' and 'c'");
            return 1;
        }
        char abc[15] = { 'a', 'l', 'p', 'h', 'a',
                         'b', 'e', 't', 'a', ' ',
                         'g', 'a', 'm', 'm', 'a'
                       };
        start_[0] = 0;
        start_[1] = 0;
        count_[0] = 3;
        count_[1] = NCLABELLEN;
        if (checkNCerr(nc_put_vara_text(ncid_, cell_angularVID_, start_, count_, abc))) {
            mprinterr("Error on NetCDF output of cell angular VID 'alpha', 'beta ' and 'gamma'");
            return 1;
        }
    }

    // Store the type of each replica dimension.
    if (coordInfo.HasReplicaDims()) {
        ReplicaDimArray const& remdDim = coordInfo.ReplicaDimensions();
        start_[0] = 0;
        count_[0] = remd_dimension_;
        int* tempDims = new int[ remd_dimension_ ];
        for (int i = 0; i < remd_dimension_; ++i)
            tempDims[i] = remdDim[i];
        if (checkNCerr(nc_put_vara_int(ncid_, remDimTypeVID, start_, count_, tempDims))) {
            mprinterr("Error: writing replica dimension types.\n");
            delete[] tempDims;
            return 1;
        }
        delete[] tempDims;
    }

    return 0;
}