Esempio n. 1
0
int main (int    argc,
	  char** argv)
// ---------------------------------------------------------------------------
// Driver.
// ---------------------------------------------------------------------------
{
  Geometry::CoordSys         system;
  char                       *session, *dump, *func, fields[StrMax];
  int_t                      i, j, k, p, q;
  int_t                      np, nz, nel, allocSize, NCOM, NDIM;
  int_t                      iAdd = 0;
  bool                       add[FLAG_MAX], need[FLAG_MAX], gradient;
  ifstream                   file;
  FEML*                      F;
  Mesh*                      M;
  BCmgr*                     B;
  Domain*                    D;
  vector<Element*>           elmt;
  AuxField                   *Ens, *Hel, *Div, *Disc, *Strain;
  AuxField                   *Func, *Vtx, *DivL, *Nrg, *work;
  vector<AuxField*>          velocity, vorticity, lamb, addField(FLDS_MAX);
  vector<vector<AuxField*> > Vij;     // -- Usually computed, for internal use.
  vector<vector<real_t*> >   VijData; // -- For pointwise access in Vij.

  vector<real_t*> VorData; // -- Ditto in vorticity.
  vector<real_t*> LamData; // -- Ditto in Lamb vector.

  real_t          *DisData, *DivData, *StrData, *VtxData, *HelData, *EnsData;
  real_t          vel[3], vort[3], tensor[9];

  Femlib::initialize (&argc, &argv);
  for (i = 0; i < FLAG_MAX; i++) add [i] = need [i] = false;

  // -- Read command line.

  getargs (argc, argv, session, dump, func, add);

  file.open (dump);
  if (!file) message (prog, "can't open input field file", ERROR);

  // -- Set up domain.

  F      = new FEML (session);
  M      = new Mesh (F);
  nel    = M -> nEl ();  
  np     =  Femlib::ivalue ("N_P");
  nz     =  Femlib::ivalue ("N_Z");
  system = (Femlib::ivalue ("CYLINDRICAL") ) ?
                       Geometry::Cylindrical : Geometry::Cartesian;
  Geometry::set (np, nz, nel, system);

  allocSize = Geometry::nTotal();

  elmt.resize (nel);
  for (i = 0; i < nel; i++) elmt[i] = new Element (i, np, M);

  B = new BCmgr  (F, elmt);
  D = new Domain (F, elmt, B);

  if      (strstr (D -> field, "uvw")) NCOM = 3;
  else if (strstr (D -> field, "uv"))  NCOM = 2;
  else message (prog, "lacking velocity components: is session valid?", ERROR);
  NDIM = Geometry::nDim();

  velocity.resize   (NCOM);
  vorticity.resize ((NDIM == 2) ? 1 : 3);
  for (i = 0; i < NCOM; i++) velocity[i] = D -> u[i];

  // -- From the requested fields, flag dependencies.

  // -- First, only allow the "coherent structures" measures for flows
  //    that are 3D.

  if  (NDIM == 2)
    add[HELICITY]=add[DISCRIMINANT]=add[VORTEXCORE]=add[DIVLAMB] = false;

  for (p = 0, i = 0; i < FLAG_MAX; i++) p += (add[i]) ? 1 : 0;
  if  (p == 0) message (prog, "nothing to be done", ERROR);

  // -- Check if we just have the (first two) cases not requiring derivatives.

  for (p = 0, i = 0; i < FLAG_MAX; i++) p += (add[i]) ? (i + 1) : 0;
  if (p <= 2) gradient = false; else gradient = true;

  for (i = 0; i < FLAG_MAX; i++) need[i] = add[i];

  // -- If any gradients need to be computed, we make all of them.
  //    Vij = du_j/dx_i.  So j labels velocity component and i labels
  //    spatial dimension.

  // -- Despite the fact that it's overkill we will take the
  //    simple-minded approach and always make Vij as a 3x3 tensor.
  //    That's a wasteful for anything which is 2D but presumably for
  //    those cases the memory can be afforded.

  // -- The velocity gradient tensor is initially just a naive matrix
  //    of derivatives.  In cylindrical coordinates we need to make
  //    modifications.
  
  if (gradient) {
    Vij    .resize (3);
    VijData.resize (3);
    for (i = 0; i < 3; i++) {
      Vij    [i].resize (3);
      VijData[i].resize (3);
      for (j = 0; j < 3; j++) {
	VijData[i][j] = new real_t [allocSize];
	Vij    [i][j] = new AuxField (VijData[i][j], nz, elmt);
	*Vij   [i][j] = 0.0;
      }
    }
  }
  
  // -- Fields without dependants.

  if (need[ENERGY]) {
    Nrg = new AuxField (new real_t[allocSize], nz, elmt, 'q');
    addField[iAdd++] = Nrg;
  }

  if (need[FUNCTION]) {
    Func = new AuxField (new real_t[allocSize], nz, elmt, 'f');
    addField[iAdd++] = Func;
  }

  if (need[DIVERGENCE]) {
    DivData = new real_t [allocSize];
    *(Div  =  new AuxField (DivData, nz, elmt, 'd')) = 0.0;
    addField[iAdd++] = Div;
  }

  if (need[DISCRIMINANT]) {
    DisData = new real_t [allocSize];
    *(Disc = new AuxField (DisData, nz, elmt, 'D')) = 0.0;
    addField[iAdd++] = Disc;
  }

  if (need[STRAINRATE]) {
    StrData = new real_t [allocSize];
    Strain  = new AuxField (StrData, nz, elmt, 'g');
    addField[iAdd++] = Strain;
  }

  if (need[VORTEXCORE]) {
    VtxData = new real_t [allocSize];
    Vtx = new AuxField (VtxData, nz, elmt, 'J');
    addField[iAdd++] = Vtx;
  }

  if (need[VORTICITY])
    if (NDIM == 2) {
      vorticity.resize (1);
      VorData  .resize (1);
      VorData[0]       = new real_t [allocSize];
      vorticity[0]     = new AuxField (VorData[0], nz, elmt, 't');
      addField[iAdd++] = vorticity[0];
    } else {
      vorticity.resize (3);
      VorData  .resize (3);
      for (i = 0; i < 3; i++) {
	VorData[i]       = new real_t [allocSize];
	vorticity[i]     = new AuxField (VorData[i], nz, elmt, 'r' + i);
	addField[iAdd++] = vorticity[i];
      }
    }

  if (add[DIVLAMB]) { 		// -- Know also NDIM == 3.
    lamb   .resize (3);
    LamData.resize (3);
    for (i = 0; i < 3; i++) {
      LamData[i] = new real_t [allocSize];
      lamb[i]    = new AuxField (LamData[i], nz, elmt, 'L');
    }
    addField[iAdd++] = lamb[0];	// -- Where divergence will get stored.
  }

  if (need[ENSTROPHY]) {
    EnsData = new real_t[allocSize];
    Ens = new AuxField (EnsData, nz, elmt, 'e');
    if (add[ENSTROPHY]) addField[iAdd++] = Ens;
  }
  
  if (need[HELICITY]) {
    HelData = new real_t [allocSize];
    Hel = new AuxField (HelData, nz, elmt, 'H');
    if (add[HELICITY]) addField[iAdd++] = Hel;
  }

  // -- Cycle through field dump, first (if required) making the only
  //    two things which just need the velocity and no gradients, then
  //    if needed computing the full VG tensor and work forward from
  //    there.  The order of computation is determined by
  //    dependencies.  Then write requested output, listed in
  //    addField.
  
  while (getDump (D, file)) {
        
    if (need[FUNCTION]) *Func = func;

    if (need[ENERGY]) ((*Nrg) . innerProduct (velocity, velocity)) *= 0.5;

    if (gradient) {		// -- All other things.

      // -- First make all VG components.

      for (i = 0; i < NDIM ; i++)
	for (j = 0; j < NCOM ; j++) {
	  *Vij[i][j] = *velocity[j];
	  if (i == 2) Vij[i][j] -> transform (FORWARD);
	  Vij[i][j] -> gradient (i);
	  if (i == 2) Vij[i][j] -> transform (INVERSE);
	}

      if (Geometry::cylindrical()) {
	work = new AuxField (new real_t[allocSize],  nz, elmt);
	if (NDIM == 3) for (j = 0; j < NCOM; j++) Vij[2][j] -> divY();
	(*work = *velocity[1]) . divY(); *Vij[2][2] += *work;
#if 1
	if (NCOM == 3) { (*work = *velocity[2]) . divY(); *Vij[1][2] += *work; }
#else
	if (NCOM == 3) { (*work = *velocity[2]) . divY(); *Vij[1][2] -= *work; }
#endif
      }

#if 1
      // -- Loop over every point in the mesh and compute everything
      //    from Vij.  Quite likely this could be made more efficient
      //    but for now simplicity is the aim.

      for (i = 0; i < allocSize; i++) {

	for (k = 0, p = 0; p < 3; p++) {
	  for (q = 0; q < 3; q++, k++)
	    tensor [k] = VijData [p][q][i];
	}

	// -- These operations produce a simple scalar result from Vij.

	if (need[DIVERGENCE])   DivData[i] = tensor3::trace      (tensor);
	if (need[ENSTROPHY])    EnsData[i] = tensor3::enstrophy  (tensor);
	if (need[DISCRIMINANT]) DisData[i] = tensor3::discrimi   (tensor);
	if (need[STRAINRATE])   StrData[i] = tensor3::strainrate (tensor);
	if (need[VORTEXCORE])   VtxData[i] = tensor3::lambda2    (tensor);

	// -- Vorticity could be considered scalar in 2D.

	if (need[VORTICITY]) {
	  tensor3::vorticity (tensor, vort);
	  if (NDIM == 2) 
	    VorData[0][i] = vort[2];
	  else { 
	    VorData[0][i] = vort[0]; 
	    VorData[1][i] = vort[1]; 
	    VorData[2][i] = vort[2];
	  }
	}

	if (!(need[HELICITY] || need[DIVLAMB])) continue;

	// -- Last two measures need velocity too, only made for NDIM = 3.

	vel[0] = velocity[0] -> data()[i];
	vel[1] = velocity[1] -> data()[i];
	vel[2] = velocity[2] -> data()[i];

	if (need[HELICITY]) HelData[i] = tensor3::helicity (tensor, vel);


	if (need[DIVLAMB]) {
	  tensor3::lambvector (tensor, vel, vort); // -- vort is a dummy.
	  LamData[0][i] = vort[0]; 
	  LamData[1][i] = vort[1]; 
	  LamData[2][i] = vort[2];
	}
      }

      // -- For this case, we still need to compute a divergence:
      
      if (need[DIVLAMB]) {
	lamb[0] -> gradient (0);
	(*lamb[2]) . transform (FORWARD) . gradient (2) . transform(INVERSE);
	if (Geometry::cylindrical()) lamb[2] -> divY();
	*lamb[0] += *lamb[2];
	if (Geometry::cylindrical()) *lamb[0] += (*lamb[2] = *lamb[1]) . divY();
	*lamb[0] += (*lamb[1]) . gradient (1);
      }
    }
#else

      if (need[DISCRIMINANT]) {

	// -- 2nd invariant (Q from Chong et al.).
	
	InvQ -> times      (*Vij[0][0], *Vij[1][1]);
	InvQ -> timesMinus (*Vij[0][1], *Vij[1][0]);
	InvQ -> timesPlus  (*Vij[0][0], *Vij[2][2]);

	InvQ -> timesMinus (*Vij[0][2], *Vij[2][0]);
	InvQ -> timesPlus  (*Vij[1][1], *Vij[2][2]);
	InvQ -> timesMinus (*Vij[1][2], *Vij[2][1]);

	// -- 3rd invariant: determinant of Vij (R from Chong et al.).

	work -> times      (*Vij[1][1], *Vij[2][2]);
	work -> timesMinus (*Vij[2][1], *Vij[1][2]);
	InvR -> times      (*work,      *Vij[0][0]);

	work -> times      (*Vij[1][2], *Vij[2][0]);
	work -> timesMinus (*Vij[2][2], *Vij[1][0]);
	InvR -> timesPlus  (*work,      *Vij[0][1]);

	work -> times      (*Vij[2][1], *Vij[1][0]);
	work -> timesMinus (*Vij[1][1], *Vij[2][0]);
	InvR -> timesPlus  (*work,      *Vij[0][2]);

	// -- Discriminant L of Vij.
	//    NB: DIVERGENCE (P from Chong et al.) ASSUMED = 0.

	work -> times (*InvQ, *InvQ);
	Disc -> times (*work, *InvQ);
	work -> times (*InvR, *InvR);
	*work *= 6.75;
	*Disc += *work;
      }

      if (need[DIVERGENCE])
	for (i = 0; i < nComponent; i++) *Div += *Vij[i][i];

    
      if (need[VORTICITY]) {
	if (nComponent == 2) {
	  *vorticity[0]  = *Vij[1][0];
	  *vorticity[0] -= *Vij[0][1];
	} else {
	  *vorticity[0]  = *Vij[2][1];
	  *vorticity[0] -= *Vij[1][2];
	  *vorticity[1]  = *Vij[0][2];
	  *vorticity[1] -= *Vij[2][0];
	  *vorticity[2]  = *Vij[1][0];
	  *vorticity[2] -= *Vij[0][1];
	}
      }

      if (need[DIVLAMB]) {
	if (nComponent == 2) {
	  *lamb[0] = 0.0;
	  lamb[0] -> timesMinus (*D -> u[1], *vorticity[0]);
	  lamb[1] -> times      (*D -> u[0], *vorticity[0]);
	  *DivL  = (*work = *lamb[0]) . gradient (0);
	  *DivL += (*work = *lamb[1]) . gradient (1);
	  if (Geometry::cylindrical())
	    *DivL += (*work = *lamb[1]) . divY();
	} else {
	  lamb[0] -> times      (*D -> u[2], *vorticity[1]);
	  lamb[0] -> timesMinus (*D -> u[1], *vorticity[2]);
	  lamb[1] -> times      (*D -> u[0], *vorticity[2]);
	  lamb[1] -> timesMinus (*D -> u[2], *vorticity[0]);
	  lamb[2] -> times      (*D -> u[1], *vorticity[0]);
	  lamb[2] -> timesMinus (*D -> u[0], *vorticity[1]);
	  *DivL  = (*work = *lamb[0]) . gradient (0);
	  *DivL += (*work = *lamb[1]) . gradient (1);
	  (*work = *lamb[2]).transform(FORWARD).gradient(2).transform(INVERSE);
	  if (Geometry::cylindrical()) {
	    *DivL += work -> divY();
	    *DivL += (*work = *lamb[1]) . divY();
	  } else
	    *DivL += *work;
	}
      }
    
      if (need[ENSTROPHY])
	Ens -> innerProduct (vorticity, vorticity) *= 0.5;

      if (need[HELICITY])
	Hel -> innerProduct (vorticity, velocity)  *= 0.5;

      if (need[STRAINTENSOR]) {
	for (i = 0; i < nComponent; i++)
	  for (j = i; j < nComponent; j++) {
	    *Sij[i][j]  = *Vij[i][j];
	    *Sij[i][j] += *Vij[j][i];
	    *Sij[i][j] *= 0.5;
	  }
      }

      if (need[STRAINRATE]) {
	*Strain = 0.0;
	for (i = 0; i < nComponent; i++)
	  for (j = 0; j < nComponent; j++)
	    Strain -> timesPlus (*Sij[i][j], *Sij[j][i]);
	(*Strain *= 2.0) . sqroot();
      }

      if (need[VORTEXCORE])	// -- Only done for 3-component fields.
	for (i = 0; i < allocSize; i++) {
	  for (k = 0, p = 0; p < 3; p++)
	    for (q = 0; q < 3; q++, k++)
	      tensor [k] = VijData[p][q][i];
	  VtxData[i] = lambda2 (tensor);
	}
    }
#endif
    // -- Finally, add mass-projection smoothing on everything.

    for (i = 0; i < iAdd; i++) D -> u[0] -> smooth (addField[i]);

    putDump (D, addField, iAdd, cout);
  }
Esempio n. 2
0
int main (int    argc,
	  char** argv)
// ---------------------------------------------------------------------------
// Driver.
// ---------------------------------------------------------------------------
{
  Geometry::CoordSys         system;
  char                       *session, *dump, *func, fields[StrMax];
  int_t                      i, j, k, p, q;
  int_t                      np, nz, nel, allocSize, nComponent;
  int_t                      iAdd = 0;
  bool                       add[FLAG_MAX], need[FLAG_MAX], gradient;
  ifstream                   file;
  FEML*                      F;
  Mesh*                      M;
  BCmgr*                     B;
  Domain*                    D;
  vector<Element*>           elmt;
  AuxField                   *Ens, *Hel, *Div, *InvQ, *InvR, *Disc, *Strain;
  AuxField                   *Func, *Ell, *Egr, *Vtx, *work, *DivL, *Nrg;
  vector<AuxField*>          lamb, velocity, vorticity, addField(FLDS_MAX);
  vector<vector<AuxField*> > Sij;
  vector<vector<AuxField*> > Vij;     // -- Usually computed, for internal use.
  vector<vector<real_t*> >   VijData; // -- For pointwise access.
  real_t                     *egrow, *VtxData; // -- Ditto.
  real_t                     tensor[9];

  Femlib::initialize (&argc, &argv);
  for (i = 0; i < FLAG_MAX; i++) add [i] = need [i] = false;

  // -- Read command line.

  getargs (argc, argv, session, dump, func, add);

  file.open (dump);
  if (!file) message (prog, "can't open input field file", ERROR);

  // -- Set up domain.

  F      = new FEML (session);
  M      = new Mesh (F);
  nel    = M -> nEl ();  
  np     =  Femlib::ivalue ("N_P");
  nz     =  Femlib::ivalue ("N_Z");
  system = (Femlib::ivalue ("CYLINDRICAL") ) ?
                       Geometry::Cylindrical : Geometry::Cartesian;
  Geometry::set (np, nz, nel, system);
  if   (nz > 1) strcpy (fields, "uvwp");
  else          strcpy (fields, "uvp");
  nComponent = Geometry::nDim();
  allocSize  = Geometry::nTotal();

  elmt.resize (nel);
  for (i = 0; i < nel; i++) elmt[i] = new Element (i, np, M);

  B = new BCmgr  (F, elmt);
  D = new Domain (F, elmt, B);

  velocity.resize   (nComponent);
  vorticity.resize ((nComponent == 2) ? 1 : 3);
  for (i = 0; i < nComponent; i++) velocity[i] = D -> u[i];

  // -- From the requested fields, flag dependencies.

  if  (nComponent < 3)
    add[HELICITY] = add[DISCRIMINANT] = add[VORTEXCORE] = false;

  for (p = 0, i = 0; i < FLAG_MAX; i++) p += (add[i]) ? 1 : 0;
  if  (p == 0) message (prog, "nothing to be done", ERROR);

  for (p = 0, i = 0; i < FLAG_MAX; i++)
    p += (add[i]) ? (i + 1) : 0;
  if (p <= 3) gradient = false; else gradient = true;

  for (i = 0; i < FLAG_MAX; i++) need[i] = add[i];

  if (add[ENSTROPHY]) {
    need[VORTICITY]    = true;
    need[ENSTROPHY]    = true;
  }
  if (add[HELICITY]) {
    need[VORTICITY]    = true;
  }
  if (add[ELLIPTICITY]) {
    need[VORTICITY]    = true;
    need[ENSTROPHY]    = true;
    need[STRAINRATE]   = true;
    need[STRAINTENSOR] = true;
  }
  if (add[STRAINRATE]) {
    need[STRAINTENSOR] = true;
  }
  if (add[EGROWTH]) {
    need[VORTICITY]    = true;
    need[ENSTROPHY]    = true;
    need[STRAINRATE]   = true;
    need[STRAINTENSOR] = true;
    need[ELLIPTICITY]  = true;
  }
  if (add[LAMBVECTOR]) {
    need[VORTICITY]    = true;
    need[LAMBVECTOR]   = true;
  }

  // -- If any gradients need to be computed, we make all of them.
  
  if (gradient) {
    Vij    .resize (nComponent);
    VijData.resize (nComponent);
    for (i = 0; i < nComponent; i++) {
      Vij    [i].resize (nComponent);
      VijData[i].resize (nComponent);
      for (j = 0; j < nComponent; j++) {
	VijData[i][j] = new real_t [allocSize];
	Vij   [i][j] = new AuxField (VijData[i][j], nz, elmt);
	*Vij  [i][j] = 0.0;
      }
    }
  }
  
  // -- Fields without dependants.

  work = new AuxField (new real_t[allocSize], nz, elmt);

  if (need[ENERGY]) {
    Nrg = new AuxField (new real_t[allocSize], nz, elmt, 'q');
    addField[iAdd++] = Nrg;
  }

  if (need[FUNCTION]) {
    Func = new AuxField (new real_t[allocSize], nz, elmt, 'f');
    addField[iAdd++] = Func;
  }

  if (need[DIVERGENCE]) {
    Div  =  new AuxField (new real_t[allocSize], nz, elmt, 'd');
    *Div = 0.0;
    addField[iAdd++] = Div;
  }

  if (need[DISCRIMINANT]) {
    *(InvQ = new AuxField (new real_t[allocSize], nz, elmt, 'Q')) = 0.0;
    *(InvR = new AuxField (new real_t[allocSize], nz, elmt, 'R')) = 0.0;
    *(Disc = new AuxField (new real_t[allocSize], nz, elmt, 'L')) = 0.0;
    addField[iAdd++] = Disc;
  }

  if (need[VORTEXCORE]) {
    VtxData = new real_t [allocSize];
    Vtx = new AuxField (VtxData, nz, elmt, 'J');
    addField[iAdd++] = Vtx;
  }

  // -- Vorticity and its dependants.

  if (need[VORTICITY])
    if (nComponent == 2) {
      vorticity[0] = new AuxField (new real_t[allocSize], nz, elmt, 't');
      if (add[VORTICITY])
	addField[iAdd++] = vorticity[0];
    } else {
      for (i = 0; i < nComponent; i++)
	vorticity[i] = new AuxField (new real_t[allocSize], nz, elmt, 'r' + i);
      if (add[VORTICITY])
	for (i = 0; i < 3; i++) addField[iAdd++] = vorticity[i];
    }

  if (add[LAMBVECTOR]) {
    if (nComponent == 2) {
      lamb.resize(2);
      for (i = 0; i < nComponent; i++)
	lamb[i] = new AuxField (new real_t[allocSize], nz, elmt, 'm' + i);
      for (i = 0; i < 2; i++) addField[iAdd++] = lamb[i];
    } else {
      lamb.resize(3);
      for (i = 0; i < nComponent; i++)
	lamb[i] = new AuxField (new real_t[allocSize], nz, elmt, 'm' + i);
      for (i = 0; i < 3; i++) addField[iAdd++] = lamb[i];
    }
    DivL = new AuxField (new real_t[allocSize], nz, elmt, 'Q');
    addField[iAdd++] = DivL;
  }

  if (need[ENSTROPHY]) {
    Ens = new AuxField (new real_t[allocSize], nz, elmt, 'e');
    if (add[ENSTROPHY]) addField[iAdd++] = Ens;
  }
  
  if (need[HELICITY]) {
    Hel = new AuxField (new real_t[allocSize], nz, elmt, 'h');
    if (add[HELICITY]) addField[iAdd++] = Hel;
  }

  if (need[ELLIPTICITY]) {
    Ell = new AuxField (new real_t[allocSize], nz, elmt, 'b');
    if (add[ELLIPTICITY]) addField[iAdd++] = Ell;
  }

  // -- Rate of strain tensor and its dependants.

  if (need[STRAINTENSOR]) {
    Sij.resize (nComponent);
    for (k = 0, i = 0; i < nComponent; i++) {
      Sij[i].resize (nComponent);
      for (j = 0; j < nComponent; j++) {
	if (j >= i) {
	  Sij[i][j] = new AuxField (new real_t[allocSize], nz, elmt, 'i'+k++);
	} else
	  Sij[i][j] = Sij[j][i];
      }
    }
  }

  if (need[STRAINRATE]) {
    *(Strain = new AuxField (new real_t[allocSize], nz, elmt, 'g')) = 0.0;
    if (add[STRAINRATE]) addField[iAdd++] = Strain;
  }

  if (need[EGROWTH]) {
    egrow = new real_t[allocSize]; // -- A handle for direct access to data.
    Egr = new AuxField (egrow, nz, elmt, 'a');
    if (add[EGROWTH]) addField[iAdd++] = Egr;
  }
  
  // -- Cycle through field dump, first computing the velocity
  //    gradient tensor and then the needed quantities -- vorticity
  //    etc.  The order of computation is determined by dependencies.
  //    Then write requested output, listed in addField.
  
  while (getDump (D, file)) {
        
    if (need[FUNCTION]) *Func = func;

    // -- Velocity gradient tensor; transform required for z (2) gradient.  

    if (nComponent > 2) D -> transform (FORWARD);

    if (gradient)
      for (i = 0; i < nComponent ; i++)
	for (j = 0; j < nComponent ; j++) {
	  (*Vij[i][j] = *velocity[i]) . gradient (j);
	  Vij[i][j] -> transform (INVERSE);
	}

    if (need[ENERGY]) {
      *Nrg = 0.0;
      for (i = 0; i < nComponent ; i++)
	Nrg -> timesPlus (*D -> u[i], *D -> u[i]);
      *Nrg *= 0.5;
    }

    if (nComponent > 2) D -> transform (INVERSE);

    if (Geometry::cylindrical() && nComponent == 3) {
      for (i = 0; i < nComponent; i++) Vij[i][2] -> divY();
      (*work = *D -> u[2]) . divY();  *Vij[1][2] -= *work;
      (*work = *D -> u[1]) . divY();  *Vij[2][2] += *work;
    }
    
    if (need[DISCRIMINANT]) {

      // -- 2nd invariant (Q from Chong et al.).

      InvQ -> times      (*Vij[0][0], *Vij[1][1]);
      InvQ -> timesMinus (*Vij[0][1], *Vij[1][0]);
      InvQ -> timesPlus  (*Vij[0][0], *Vij[2][2]);
      InvQ -> timesMinus (*Vij[0][2], *Vij[2][0]);
      InvQ -> timesPlus  (*Vij[1][1], *Vij[2][2]);
      InvQ -> timesMinus (*Vij[1][2], *Vij[2][1]);

      // -- 3rd invariant: determinant of Vij (R from Chong et al.).

      work -> times      (*Vij[1][1], *Vij[2][2]);
      work -> timesMinus (*Vij[2][1], *Vij[1][2]);
      InvR -> times      (*work,      *Vij[0][0]);

      work -> times      (*Vij[1][2], *Vij[2][0]);
      work -> timesMinus (*Vij[2][2], *Vij[1][0]);
      InvR -> timesPlus  (*work,      *Vij[0][1]);

      work -> times      (*Vij[2][1], *Vij[1][0]);
      work -> timesMinus (*Vij[1][1], *Vij[2][0]);
      InvR -> timesPlus  (*work,      *Vij[0][2]);

      // -- Discriminant L of Vij.
      //    NB: DIVERGENCE (P from Chong et al.) ASSUMED = 0.

      work -> times (*InvQ, *InvQ);
      Disc -> times (*work, *InvQ);
      work -> times (*InvR, *InvR);
      *work *= 6.75;
      *Disc += *work;
    }
	
    if (need[DIVERGENCE])
      for (i = 0; i < nComponent; i++) *Div += *Vij[i][i];
    
    if (need[VORTICITY]) {
      if (nComponent == 2) {
	*vorticity[0]  = *Vij[1][0];
	*vorticity[0] -= *Vij[0][1];
      } else {
	*vorticity[0]  = *Vij[2][1];
	*vorticity[0] -= *Vij[1][2];
	*vorticity[1]  = *Vij[0][2];
	*vorticity[1] -= *Vij[2][0];
	*vorticity[2]  = *Vij[1][0];
 	*vorticity[2] -= *Vij[0][1];
      }
    }

    if (need[LAMBVECTOR]) {
      if (nComponent == 2) {
	*lamb[0] = 0.0;
	lamb[0] -> timesMinus (*D -> u[1], *vorticity[0]);
	lamb[1] -> times      (*D -> u[0], *vorticity[0]);
	*DivL  = (*work = *lamb[0]) . gradient (0);
	*DivL += (*work = *lamb[1]) . gradient (1);
	if (Geometry::cylindrical())
	  *DivL += (*work = *lamb[1]) . divY();
      } else {
	lamb[0] -> times      (*D -> u[2], *vorticity[1]);
	lamb[0] -> timesMinus (*D -> u[1], *vorticity[2]);
	lamb[1] -> times      (*D -> u[0], *vorticity[2]);
	lamb[1] -> timesMinus (*D -> u[2], *vorticity[0]);
	lamb[2] -> times      (*D -> u[1], *vorticity[0]);
	lamb[2] -> timesMinus (*D -> u[0], *vorticity[1]);
	*DivL  = (*work = *lamb[0]) . gradient (0);
	*DivL += (*work = *lamb[1]) . gradient (1);
	(*work = *lamb[2]).transform(FORWARD).gradient(2).transform(INVERSE);
	if (Geometry::cylindrical()) {
	  *DivL += work -> divY();
	  *DivL += (*work = *lamb[1]) . divY();
	} else
	  *DivL += *work;
      }
    }
    
    if (need[ENSTROPHY])
      Ens -> innerProduct (vorticity, vorticity) *= 0.5;

    if (need[HELICITY])
      Hel -> innerProduct (vorticity, velocity)  *= 0.5;

    if (need[STRAINTENSOR]) {
      for (i = 0; i < nComponent; i++)
	for (j = i; j < nComponent; j++) {
	  *Sij[i][j]  = *Vij[i][j];
	  *Sij[i][j] += *Vij[j][i];
	  *Sij[i][j] *= 0.5;
	}
    }

    if (need[STRAINRATE]) {
      *Strain = 0.0;
      for (i = 0; i < nComponent; i++)
	for (j = 0; j < nComponent; j++)
	  Strain -> timesPlus (*Sij[i][j], *Sij[j][i]);
      (*Strain *= 2.0) . sqroot();
    }

    if (need[ELLIPTICITY]) {
      ((*work = *Ens) . sqroot() *= 2.0) += 1.0e-1;
      Ell -> divide (*Strain, *work);
    }

    if (need[EGROWTH]) {
      *Egr = *Ell;
      Veclib::clip (allocSize, 0.0, 1.0, egrow, 1, egrow, 1);
      Veclib::spow (allocSize, 2.811,    egrow, 1, egrow, 1);
      Veclib::vneg (allocSize,           egrow, 1, egrow, 1);
      Veclib::sadd (allocSize, 1.0,      egrow, 1, egrow, 1);
      Veclib::spow (allocSize, 0.3914,   egrow, 1, egrow, 1);
      Veclib::smul (allocSize, 9.0/16.0, egrow, 1, egrow, 1);
      (*work = *Strain) *= sqrt (1.0/8.0);
      Egr -> times (*Egr, *work);
    }

    if (need[VORTEXCORE])	// -- Only done for 3-component fields.
      for (i = 0; i < allocSize; i++)
	for (k = 0, p = 0; p < 3; p++)
	  for (q = 0; q < 3; q++, k++) {
	    tensor [k] = VijData[p][q][i];
	    VtxData[i] = lambda2 (tensor);
	  }

    // -- Finally, add mass-projection smoothing on everything.

    for (i = 0; i < iAdd; i++) D -> u[0] -> smooth (addField[i]);

    putDump (D, addField, iAdd, cout);
  }
  
  file.close();
  Femlib::finalize();
  return EXIT_SUCCESS;
}