//------------------------------------------------------------
// $Id: volume.cc,v 1.2 2000/06/14 13:29:37 yamauchi Exp $
//------------------------------------------------------------
//	(M\pi)^2 implementation of volume
//	Copyright (C) 1997--1998 YAMAUCHI Hitoshi 山内 斉
//------------------------------------------------------------
#include <stdio.h>
#include <math.h>
#include <iostream.h>
#include <assert.h>
#include "volume.h"
#include "mpi2_funcs.h"
#include "mpi2exit.h"

//------------------------------------------------------------
// volume.h implementation
//------------------------------------------------------------
// protected functions 
//------------------------------------------------------------
/**
 * この volume に加えることのできる pnm buf かどうかを判断する
 * @param    pPNMBuf  PNMReader : ファイルから読み込んだスライス
 * @return   加えることができる場合には YES 
 */
YorN VolumeClass::
checkCanAddPNM(PNMReader *pPNMBuf) const
{
    const char *pMes = "Error! : can't add slice : checkCanAddPNM : ";
    // 初期化されているか?
    if((this->mGlobalGrid.isInited() == NO)||(this->mSubGrid.isInited() == NO))
    {
	cout << pMes << "this grid is not initialized."<< endl; 
	cout << "global grid info... " << 
	    ((this->mGlobalGrid.isInited() == NO) ? "NO" : "YES") << endl;
	cout << "sub grid info...... " << 
	    ((this->mSubGrid.isInited() == NO) ?    "NO" : "YES") << endl;
	return(NO);
    }
   
    // grid の大きさと pnm buf の大きさは一致するか?
    // Now, created volume direction is fixed (変換は後) FIXHERE

    // まず global と sub の一貫性を確かめる
    if(this->isConsistentGlobalAndSubGridInfo() == NO)
    {
	cout << pMes << "inconsistent between global and sub info." << endl;
	return(NO);
    }

    // check pPNMBuf with this gginfo and sginfo
    //     大きすぎる場合には用意した領域に入らない
    if((this->mSubGrid.getResolSize()).get(0) < pPNMBuf->getResolSizeX())
    {
	cout << pMes << "pnmBuf : size X is too large." << endl;
	return(NO);
    }
    if((this->mSubGrid.getResolSize()).get(1) < pPNMBuf->getResolSizeY())
    {
	cout << pMes << "pnmBuf : size Y is too large." << endl;
	return(NO);
    }

    return(YES);
}

/** ------------------------------
 * volume に設定したものと，読み込んだ PNM (PNMReader) が丁度一致しているか 
 * PNM が小さめの 場合には DEFAULTVAL で埋めているが，これは外した方良
 * いかもしれない．以前 genvol が小さめの slice を出力するバグがあった
 * ためにそうしていたが，これは修正されている 1998年11月22日(日)
 * 
 * @param pPNMBuf PNMReader* : これからこの volume に加えようとしているスライス
 */
void VolumeClass::
keepConsistentWithPNM(PNMReader *pPNMBuf) 
{
    if((pPNMBuf->getResolSizeX() != this->getGridResSize(0)) ||
       (pPNMBuf->getResolSizeY() != this->getGridResSize(1)))
    {
	const double DEFAULTVAL = 0.0;
	cout << "Warning! : PNMbuf size != Grid size, [" <<
	    pPNMBuf->getResolSizeX() << ", " <<
		pPNMBuf->getResolSizeY() << "] != [";
	cout << this->getGridResSize(0) << ", " <<
	    this->getGridResSize(1) << "] : there are "
		"uninitialized region, set to " << DEFAULTVAL << endl;
	const int base_x = (this->mSubGrid.getMinIdx()).get(0);
	const int base_y = (this->mSubGrid.getMinIdx()).get(1);
	int idx[3];
	idx[0] = base_x;
	idx[1] = base_y;
	idx[2] = (this->mSubGrid.getMinIdx()).get(2); // Z dir
	// GridColorClass gridcol;
	for(int y = 0; y <= this->mSubGrid.getMaxIdx().get(1); y++)
	{	
	    idx[1] = base_y + y;
	    cout << "# ";
	    for(int x = 0; x <= this->mSubGrid.getMaxIdx().get(1); x++)
	    {	
		idx[0] = base_x + x;
		if(!(this->mGrid.get(idx).isInitialized()))
		{
		    // 未初期化のものは zero color で初期化する．
		    this->setGlobalGridIdxColor(idx, 
						GridColorClass::
						getZeroColor());
		    cout << "[" << x << ", " << y << "]";
		}
	    }
	}
	cout << endl;	
    }
}

/**
 * 加えることのできる volume か
 *     volume は境界部分が重なっても良い
 * @param VolumeClass *pAddVol : これから加える volume
 * @return 加えられる場合には YES
 */
YorN VolumeClass::
checkCanAddVolume(VolumeClass *pAddVol) const
{
    const char *pMes = "Error! : can't add subvolume : checkCanAddVolume : ";
    // ID は同じか?
    if(this->getID() != pAddVol->getID())
    {
	cerr << pMes << "This ID = " << this->getID() << ", added ID = "
	    << pAddVol->getID() << endl;  
	return(NO);
    }
    
    // subvolume の大きさは足りているか?
    int i;
    for(i = 0; i < 3; i++)
    {
	if((pAddVol->getGridMinIdx(i) 	< this->mGrid.getMinIdx(i)) ||
	   (this->mGrid.getMaxIdx(i)    < pAddVol->getGridMaxIdx(i)))
	{
	    cerr << pMes << "too large subvolume." << endl;
	    return(NO);	    
	}
    }

    // global position は一致するか
    if((this->mGlobalGrid.getMinPos() != pAddVol->mGlobalGrid.getMinPos()) ||
       (this->mGlobalGrid.getMaxPos() != pAddVol->mGlobalGrid.getMaxPos()))
    {
	cerr << pMes << "different global volume position." << endl;
	cerr << "***** This volume  *****" << endl;
	this->print();
	cerr << "***** added volume *****" << endl;	    
	pAddVol->print();
	return(NO);
    }
    
    // subvolume の範囲は正常かについては subvolume を add してからの
    // 調べないと正しいものであるかどうかは不明であるのでここでは行わ
    // ない．

    // voxel の大きさは一致するか
    if(this->getVoxelEdgeLen() != pAddVol->getVoxelEdgeLen())
    {
	cerr << pMes << "different voxel size." << endl;
	return(NO);	
    }
    
    // spcpos は一致するか? 
    if(this->mSubGrid.getSpcPos() != pAddVol->mSubGrid.getSpcPos())
    {
	cerr << pMes << "different spcpos." << endl;
	return(NO);	
    }

    return(YES);
}

//------------------------------------------------------------
// public functions 
//------------------------------------------------------------
/**
 * constructor
 */
// static int debugnewnum = 0;

VolumeClass::
VolumeClass(void) 
{
    //     cout << "debug : new volume 1 " << debugnewnum << endl;
    //     debugnewnum++;
}

VolumeClass::
VolumeClass(const VolumeClass& rhs)
{
    this->mGrid		= rhs.mGrid;
    this->mGlobalGrid	= rhs.mGlobalGrid;
    this->mSubGrid	= rhs.mSubGrid;
    this->mEffiGrid 	= rhs.mEffiGrid;

    //     cout << "debug : new volume 2 " << debugnewnum << endl;
    //     debugnewnum++;
}

const VolumeClass& VolumeClass::
operator=(const VolumeClass& rhs)
{
    if(this != &rhs)
    {
	this->mGrid		= rhs.mGrid;
	this->mGlobalGrid	= rhs.mGlobalGrid;
	this->mSubGrid		= rhs.mSubGrid;
	this->mEffiGrid		= rhs.mEffiGrid;
    }
    //     cout << "debug : new volume 3 " << debugnewnum << endl;
    //     debugnewnum++;

    return(*this);
}

// destructor
VolumeClass::
~VolumeClass(void)
{
    //     static int debugdelnum = 0;
    //     cout << "debug : delete volume " << debugdelnum << endl;
    //     debugdelnum++;
}

int operator == (const VolumeClass& a, const VolumeClass& b)
{
    return((a.mGrid 		== b.mGrid) 	  &&
	   (a.mGlobalGrid	== b.mGlobalGrid) && 
	   (a.mSubGrid		== b.mSubGrid)	  &&
	   (a.mEffiGrid		== b.mEffiGrid));
}

//------------------------------------------------------------
// null volume 操作
//------------------------------------------------------------
/**------------------------------
 *  この volume を null volume として初期化 する
 */
void VolumeClass::
initAsNullVolume(void)
{
    GlobalGridInfoClass gginfo;
    gginfo.initAsNullVolume();

    SubGridInfoClass    sginfo;
    sginfo.initAsNullVolume();

    EffectiveGridInfoClass eginfo;
    eginfo.initAsNullVolume();

    this->initGlobalGridInfo(gginfo);
    this->initSubGridInfo(sginfo);
    this->initEffiGridInfo(eginfo);    
}

/**------------------------------
 *  grid の領域のみを確保する．確保するだけ，情報のコピーなどはない
 *	[pGridResMinIdx, pGridResMaxIdx] が確保される．したがってサイ
 *	ズは pGridResMaxIdx - pGridResMinIdx + 1 を基本とする
 *
 *  @param const int *pGridResMinIdx : grid の最小 index
 *  @param const int *pGridResMaxIdx : grid の最大 index
 */
void VolumeClass::
initSize(const int *pGridResMinIdx, const int *pGridResMaxIdx)
{
    if(this->mGrid.isInited())
    {
	cerr << "Error! : this volume is already initialized. "
	    "VolumeClass::initSize" << endl;
	abort();
    }
    this->mGrid.init(pGridResMinIdx, pGridResMaxIdx);
}


/**------------------------------
 * この subspace に実際に存在する grid の範囲を求める
 * 範囲は minCellPos <= gridMinPos <= gridMaxPos <= maxCellPos
 * を満たす grid index [effectivrmin,  effectivemax]
 * 
 * @param const Vector3& cellMinPos 
 * @param const Vector3& cellMaxPos 
 * @return 異常があれば NO を返す
 */
YorN VolumeClass::
calcEffectiveGridRange(const Vector3& cellMinPos, const Vector3& cellMaxPos)
{
    // SubGridInfo を変更する場合には mInitedEffectiveRange フラグを降
    // ろすこと  
    if(this->mEffiGrid.isInited() != NO)
    {
	cout << "Warning! : effective range is already inited." << endl;
    }
    
    // calc effective grid range
    double effiminpos[3], effimaxpos[3];
    for(int i = 0; i < 3; i++)
    {
	// effi minpos
	if((this->mSubGrid.getMinPos()).get(i) < cellMinPos.get(i))
	{
	    effiminpos[i] = cellMinPos.get(i);
	}else
	{
	    effiminpos[i] = (this->mSubGrid.getMinPos()).get(i);	    
	}
	
	// effi maxpos
	if(cellMaxPos.get(i) < (this->mSubGrid.getMaxPos()).get(i))
	{
	    effimaxpos[i] = cellMaxPos.get(i);
	}else
	{
	    effimaxpos[i] = (this->mSubGrid.getMaxPos()).get(i);	    
	}
    }
    this->mEffiGrid.initAll(Vector3(effiminpos), Vector3(effimaxpos));
    return(YES);
}

/**------------------------------
 * 初期化されているかどうか
 *
 * @param 初期化されていれば YES
 */
YorN VolumeClass::
isInited(void) const 
{
    if((this->getID()		 >= -1)	&& // -2 notinit, -1 null volume
       (this->mGrid.isInited() 	 == !0) && 
       (this->mGlobalGrid.isInited() == YES) &&
       (this->mSubGrid.isInited()    == YES) &&
       (this->mEffiGrid.isInited()   == YES))
    {
	return(YES);
    }else
    {
	return(NO);	    
    }
}

/**------------------------------------------------------------
 * get color (GridColorClass) from global volume index 
 * 	volume index は [effectiveMin, effectiveMax] の範囲
 *
 * @param int volumeGlobalIdx[] : global な grid の index
 */
GridColorClass VolumeClass::
getGlobalEffiGridIdxColor(int volumeGlobalIdx[]) const
{
    return(this->mGrid.get(volumeGlobalIdx));
}

//------------------------------------------------------------
// global grid manipulation
//------------------------------------------------------------
/**
 * Global grid の初期化
 *
 * @param const GlobalGridInfoClass &gginfo
 */
void VolumeClass::
initGlobalGridInfo(const GlobalGridInfoClass &gginfo)
{
    this->mGlobalGrid = gginfo;
}

//------------------------------------------------------------
// effective grid info manipulation
//------------------------------------------------------------
void VolumeClass::
initEffiGridInfo(const EffectiveGridInfoClass& eginfo)
{
    this->mEffiGrid = eginfo;
}

void VolumeClass::
invalidateEffiGridInfo(void)
{
    this->mEffiGrid.invalidate();
}

//------------------------------------------------------------
// sub grid manipulation
//------------------------------------------------------------
/**
* Sub grid の初期化 : grid そのものの領域も確保する．
*
* @param const SubGridInfoClass &sginfo
*/
void VolumeClass::
initSubGridInfo(const SubGridInfoClass &sginfo)
{
    this->mSubGrid = sginfo;

    // sub grid 分の grid array を確保する
    int min[3], max[3];
    (sginfo.getMinIdx()).getAsArray(min);
    (sginfo.getMaxIdx()).getAsArray(max);
    this->initSize(min, max);
    this->invalidateEffiGridInfo();
} 

//------------------------------------------------------------
// subvolume manipulation
//------------------------------------------------------------
/**
 * PNMReader で読んだスライスファイルを volume としてここに加える．
 *	slice は重なって登録されることはない
 * 
 * @param PNMReader *pPNMBuf 
 */
void VolumeClass::
addPNM(PNMReader *pPNMBuf)
{
    if(this->checkCanAddPNM(pPNMBuf) == NO)
    {
	cout << "----- This grid -----" << endl;
	this->print();
	cout << "----- added PNM -----" << endl;	    
	pPNMBuf->print();
	commMpi2_exit(1);
    }	

    // ここでは grid は z 方向に厚みがないものとし初期化されていると仮定
    if((this->mSubGrid.getMinIdx()).get(2) != 
       (this->mSubGrid.getMaxIdx()).get(2))
    {
	cout << "Error! : this grid can not initialize with pnmbuf." << endl;
	commMpi2_exit(1);
    }

    // base_x, base_y grid をずらす．ただし，pnm は常に 0 からアクセス
    const int base_x = this->mSubGrid.getMinIdx().get(0);
    const int base_y = this->mSubGrid.getMinIdx().get(1);
    int idx[3];
    idx[0] = base_x;
    idx[1] = base_y;
    idx[2] = this->mSubGrid.getMinIdx().get(2); // Z dir
    int cols[PNMReader::NUM_OF_PNMCOLOR];

    GridColorClass gridcol;
    for(int y = 0; y < pPNMBuf->getResolSizeY(); y++)
    {	
	idx[1] = base_y + y;
	for(int x = 0; x < pPNMBuf->getResolSizeX(); x++)
	{	
	    pPNMBuf->getRGBColors(x, y, cols);
	    gridcol.setRGBColors(cols);
	    idx[0] = base_x + x;
	    if((this->mGrid.get(idx)).isInitialized())
	    {
		cerr << "Warning! : subvolume is overwritten with a slice."
		    << endl;
	    }
	    this->setGlobalGridIdxColor(idx, gridcol);
	}
    }

    // check and keep consistency
    this->keepConsistentWithPNM(pPNMBuf);
}    

/**------------------------------
 * get pnmfile : volume から zpos 位置のスライスを一枚 PNM として取り出す
 * 
 * @param zpos : global grid index 
 * @return PNMReader * : スライス
 */
PNMReader *VolumeClass::
getNewPNM(int zpos)
{
    const int depth = 255;
    const int type  = 6;	// P6 format (= color RAW)
    int gridResMin[3], gridResMax[3], size[3];
    (this->mSubGrid.getMinIdx()).getAsArray(gridResMin);
    (this->mSubGrid.getMaxIdx()).getAsArray(gridResMax);
    (this->mSubGrid.getResolSize()).getAsArray(size);
    if((zpos < gridResMin[2]) || (gridResMax[2] < zpos))
    {
	cout << "Error! : no such z position[" << zpos << "]" << endl;
	commMpi2_exit(1);
	return(NULL);
    }
    PNMReader *pRetPNM = new PNMReader();
    pRetPNM->init(size[0], size[1], type, depth);

    int idx[3];
    idx[0] = gridResMin[0];
    idx[1] = gridResMin[1];
    idx[2] = zpos;
    int col[3];
    col[1] = col[2] = 0; 
    for(idx[1] = gridResMin[1]; idx[1] <= gridResMax[1]; idx[1]++)
    {	
	for(idx[0] = gridResMin[0]; idx[0] <= gridResMax[0]; idx[0]++)
	{	
	    (this->mGrid.get(idx)).getRGBColorAsInt3Array(col);
	    assert(col[0] >= 0);
	    int x = idx[0] - gridResMin[0];
	    int y = idx[1] - gridResMin[1];
	    pRetPNM->setRGBColors(x, y, col);
	}
    }
    return(pRetPNM);
}


/**------------------------------
 * get pnmfile : 最小の Z 位置のものを返す
 * 	これは一枚のスライスファイルを volume とした時のものを取り出す
 *	のに便利である．Z 位置は global grid なので覚えておかねばなら
 *	ない
 *
 * @return PNMReader * : スライス
 */
PNMReader *VolumeClass::
getNewPNM(void)
{
    // 最小の Z 位置のものを返す
    int gridResMin[3];
    (this->mSubGrid.getMinIdx()).getAsArray(gridResMin);
    return(this->getNewPNM(gridResMin[2]));
}

    
/**--------------------------------------------------
 * add volume to this volume 
 * @param VolumeClass *pAddVol : ここに加える sub volume
 */
void VolumeClass::
addSubVolume(VolumeClass *pAddVol)
{
    if(this->checkCanAddVolume(pAddVol) == NO)
    {
	cerr << "----- This volume -----" << endl;
	this->print();
	cerr << "----- added volume -----" << endl;	    
	pAddVol->print();
	commMpi2_exit(1);
    }	

    int idx[3];
    for(idx[2]  = pAddVol->getGridMinIdx(2); 
	idx[2] <= pAddVol->getGridMaxIdx(2); (idx[2])++)
    {	
	for(idx[1]  = pAddVol->getGridMinIdx(1); 
	    idx[1] <= pAddVol->getGridMaxIdx(1); (idx[1])++)
	{	
	    for(idx[0]  = pAddVol->getGridMinIdx(0); 
		idx[0] <= pAddVol->getGridMaxIdx(0); (idx[0])++)
	    {	
		GridColorClass gcol = pAddVol->getGlobalGridIdxColor(idx);
		if((this->mGrid.get(idx)).isInitialized()) // implement depend
		{
		    if(this->mGrid.get(idx) != gcol)
		    {
			cout << "<< Warning! : subvolume is overwritten "
			  "as different color." << endl;
		    }
		}
		this->setGlobalGridIdxColor(idx, gcol);
	    }
	}
    }
}

//------------------------------------------------------------
// get global volume position from volume index
//------------------------------------------------------------
/**------------------------------
 * global index のみから位置を求める subvolume の範囲を越えてていも良い
 * @param int idx[] : global grid index
 */ 
YorN VolumeClass::
checkGridIdxRangeForDEBUG(int idx[]) const
{
    // check range (in array3d.h) range 外なら getInternalIdx の中で 
    // abort が発生する 
    this->mGrid.abortIfOutOfRange(idx);
    return(YES);
}

/**------------------------------
 * global index のみから位置を求める subvolume の範囲を越えてていても良い
 * @param const IntVec3 &idx : global grid index
 */ 
YorN    VolumeClass::
checkGridIdxRangeForDEBUG(const IntVec3& idx) const
{
    int idxarray[3]; idx.getAsArray(idxarray);
    return(this->checkGridIdxRangeForDEBUG(idxarray));
}

/**------------------------------
 * global index -> 位置 : assert で range を check する
 * @param int idx[] : global grid index
 */ 
Vector3 VolumeClass::
getGlobalGridPosFromIdx(int idx[]) const
{
    assert(this->checkGridIdxRangeForDEBUG(idx) == YES); 
    return(getPosition(this->mGlobalGrid.getMinPos(), 
		       this->mGlobalGrid.getVoxelEdgeLen(),
		       Vector3(double(idx[0]), double(idx[1]), 
			       double(idx[2]))));
}

/**------------------------------
 * global index -> 位置 : range check 無し
 * @param int idx[] : global grid index
 */ 
Vector3 VolumeClass::
getGlobalGridPosFromIdxNoRangeCheck(int idx[]) const
{
    return(getPosition(this->mGlobalGrid.getMinPos(), 
		       this->mGlobalGrid.getVoxelEdgeLen(),
		       Vector3(double(idx[0]), double(idx[1]),
			       double(idx[2]))));
}

/**------------------------------
 * global grid info と sub grid info 間の一貫性を check する
 * 
 * @return 一貫していれば YES 
 */
YorN VolumeClass::
isConsistentGlobalAndSubGridInfo(void) const
{
    const char *pM = "Error!: VolumeClass::isConsistentGlobalAndSubGridInfo :";
    for(int i = 0; i < 3; i++)
    {
	// 位置
	if((this->mGlobalGrid.getMinPos()).get(i) >
	   (this->mSubGrid.getMinPos()).get(i))
	{
	    cout << pM << "min pos\n" << "global min (" << 
		this->mGlobalGrid.getMinPos() << "), sub min (" 
		    << this->mSubGrid.getMinPos() << ")" << endl;
	    return(NO);
	}
	if((this->mGlobalGrid.getMaxPos()).get(i) <
	   (this->mSubGrid.getMaxPos()).get(i))
	{
	    cout << pM << "max pos\n" "global max (" << 
		this->mGlobalGrid.getMaxPos() << "), sub max (" 
		    << this->mSubGrid.getMaxPos() << ")" << endl;
	    return(NO);
	}

	// サイズ
	if(this->mSubGrid.getMinIdx().get(i) < 0)
	{
	    // 小さい方は >= 0
	    cout << pM << "grid range min\n" << "sub range min [" << i 
		<< "] = " << this->mSubGrid.getMinIdx().get(i) << endl;
	    return(NO);
	}
	if(this->mSubGrid.getMaxIdx().get(i) > 
	   (this->mGlobalGrid.getResolSize()).get(i))
	{
	    cout << pM << "grid range max" << endl;
	    cout << "sub range max [" << i << "] = " << 
		this->mSubGrid.getMaxIdx().get(i) << endl;
	    cout << "global range max " << 
		this->mGlobalGrid.getResolSize() << endl;
	}
    }	   
    return(YES);
}

/**------------------------------------------------------------
 * print out this object 
 */
void VolumeClass::
print(void) const
{
    if(this->isInited() == NO)
    {
	cout << "----- Volume:: not initialized -----" << endl;	
	// 初期化されていなくても内容を見ることができるようにここで 
	// return はしない
    }
    this->mGrid.print();
    
    this->mGlobalGrid.print();
    this->mSubGrid.print();
    this->mEffiGrid.print();
}

//------------------------------------------------------------
// check all voxel is initialized 
//------------------------------------------------------------
YorN VolumeClass::
isAllFill(void)
{
    YorN allfill = YES;
    int idx[3];
    for(idx[2]  = this->mGrid.getMinIdx(2); 
	idx[2] <= this->mGrid.getMaxIdx(2); (idx[2])++)
    {
	for(idx[1]  = this->mGrid.getMinIdx(1); 
	    idx[1] <= this->mGrid.getMaxIdx(1); (idx[1])++)
	{
	    for(idx[0]  = this->mGrid.getMinIdx(0); 
		idx[0] <= this->mGrid.getMaxIdx(0); (idx[0])++)
	    {
		if(!((this->mGrid.get(idx)).isInitialized()))
		{
		    cerr << "Error! : mpCol[" << idx[0] << "][" << idx[1] 
			<< "][" << idx[2] << "] " << 
			    this->mGrid.get(idx) << endl;
		    allfill = NO;
		    // this->outVolumeAsPPM();
		    cerr << "Error! : fail VolumeClass:isAllFill()" << endl;
		    abort();
		}
	    }
	}
    }
    return(allfill);
}

//------------------------------------------------------------
// newSubVolume
//------------------------------------------------------------
/**------------------------------
 *  [minGridIdx, maxGridIdx] の範囲かつ sub volume を取り出す
 *	この範囲の subvolume は spcpos に属するとして定義される
 * 	つまりここで渡す spcpos は無条件で信じられるので注意．なぜなら， 
 *	spcpos の情報は定義空間などを知らないといけないが，単なる 
 *	volume はそんなことを知らないからである．
 *
 * @param int minGridIdx[]	
 * @param int maxGridIdx[] 	
 * @param const IntVec3& spcpos	
 */
VolumeClass *VolumeClass::
newSubVolume(int minGridIdx[], int maxGridIdx[], const IntVec3& spcpos) const
{
    // check index range
    const char *mes = "Error! : VolumeClass::newSubVolume ";
    if((this->checkGridIdxRangeForDEBUG(minGridIdx) == NO) ||
       (this->checkGridIdxRangeForDEBUG(maxGridIdx) == NO))
    {
	cout << mes << "Illegal grid range." << endl;
	commMpi2_exit(1);
    }

    // check myself : 分割は effective grid が初期化されていなくとも可能
    if(((this->getGlobalGrid()).isInited() == NO) ||
       ((this->getSubGrid()).isInited()    == NO))
    {
	cout << mes << "This volume is not initialized."  << endl;
	this->print();
	commMpi2_exit(1);
    }

    // new volume
    VolumeClass *pRet = new VolumeClass();

    // init GlobalGrid Info
    pRet->initGlobalGridInfo(this->mGlobalGrid);

    // init SubGrid Info    
    SubGridInfoClass newSgi;
    
    newSgi.initAll(this->getGlobalGridPosFromIdx(minGridIdx), 
		   this->getGlobalGridPosFromIdx(maxGridIdx),
		   spcpos, IntVec3(minGridIdx), IntVec3(maxGridIdx));
    pRet->initSubGridInfo(newSgi);
    
    // copy GridColor
    int idx[3];
    for(idx[2] = minGridIdx[2]; idx[2] <= maxGridIdx[2]; idx[2]++)
    {
	for(idx[1] = minGridIdx[1]; idx[1] <= maxGridIdx[1]; idx[1]++)
	{
	    for(idx[0] = minGridIdx[0]; idx[0] <= maxGridIdx[0]; idx[0]++)
	    {
		GridColorClass col = this->getGlobalGridIdxColor(idx);
		pRet->setGlobalGridIdxColor(idx, col);
	    }
	}
    }
    return(pRet);
}

//------------------------------------------------------------
// test for volume class 
//------------------------------------------------------------
#ifdef VOLUME_TEST
int main(int argc, char *argv[])
{
    // global grid
    int volumeID = 1;
    IntVec3 globalGridResSize(100, 100, 100);
    Vector3 globalGridMinPos(  0.0,   0.0,   0.0);
    Vector3 globalGridMaxPos(100.0, 100.0, 100.0);
    double  voxelEdgeLen = 2.0;
    GlobalGridInfoClass gginfo;
    gginfo.initAll(volumeID, globalGridResSize, 
                   globalGridMinPos, globalGridMaxPos, voxelEdgeLen);
    
    // subgrid
    const Vector3 minpos(10.0, 10.0, 10.0);
    const Vector3 maxpos(90.0, 90.0, 90.0);
    const IntVec3 spcpos(1,2,3);
    const int resolMin[] = {  5,  5,  5, };
    const int resolMax[] = { 25, 25, 25, };

    SubGridInfoClass sginfo;
    sginfo.initAll(minpos, maxpos, spcpos, resolMin, resolMax);

    // efficient grid
    EffectiveGridInfoClass eginfo;
    const Vector3 eminpos(11.0, 11.0, 11.0);
    const Vector3 emaxpos(89.0, 89.0, 89.0);
    const IntVec3 emingrid(6, 6, 6);    
    const IntVec3 emaxgrid(24, 24, 24);        
    eginfo.initAll(eminpos, emaxpos);

    // init volume
    VolumeClass v;
    v.initGlobalGridInfo(gginfo);
    v.initSubGridInfo(sginfo);    
    v.initEffiGridInfo(eginfo);

    // serialize and deserialize
    VolumeClass recvVol;

    while(1)
    {
	cout << "DEBUG : serialize" << endl;
	int bufsize = v.getSerializedSize();
	double *pSendbuf = v.newSerializedArray();
	
 	if(v == recvVol)
 	{
 	    cout << "??? two volumes must be different" << endl;
 	}else
 	{
 	    cout << "OK! now difference" << endl;
 	}
 	
 	recvVol.deserialize(pSendbuf);
 	for(int i = 0; i < bufsize; i++)
 	{
 	    pSendbuf[i] = -1.0;
 	}

	delete [] pSendbuf;	// double *pSendbuf = v.newSerializedArray();

	cout << "DEBUG : deserialize" << endl;
	
	if(v == recvVol)
	{
	    cout << "Serialize succeeded!" << endl;
	}else
	{
	    cout << "Serialize fail!" << endl;
	}
	// recvVol.print();
    }
}

#endif				// #ifdef VOLUME_TEST
//
// g++ -c -I../mpi2dataReader  IntVec3.cc Vector3.cc
// g++ -g -I../mpi2dataReader -DVOLUME_TEST volume.cc volumeSerializable.cc IntVec3.o Vector3.o
//

