/* Fesslix - Stochastic Analysis
 * Copyright (C) 2010-2016 Wolfgang Betz
 *
 * Fesslix 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 3 of the License, or
 * (at your option) any later version.
 *
 * Fesslix 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 Fesslix.  If not, see <http://www.gnu.org/licenses/>. 
 */

#include "flxrandomFEM.h"
#include "flxdata.h"
#include "flxobjfem.h"
#include "flxrbrv_rvs.h"
#include <fstream>


flxDomain_base* FlxRandomField_base::Current_Element = NULL;
tdouble *FlxRandomField_base::LX = NULL;
tdouble *FlxRandomField_base::GX = NULL;
tdouble *FlxRandomField_base::GY = NULL;
tdouble *FlxRandomField_base::GZ = NULL;
tdouble *FlxRandomField_base::GX2 = NULL;
tdouble *FlxRandomField_base::GY2 = NULL;
tdouble *FlxRandomField_base::GZ2 = NULL;
tdouble *FlxRandomField_base::DELTAX = NULL;
tdouble *FlxRandomField_base::DELTAY = NULL;
tdouble *FlxRandomField_base::DELTAZ = NULL;
tdouble *FlxRandomField_base::DELTAP = NULL;
FlxData* FlxRandomField_base::data = NULL;

#include "flxMtx_Eigen.h"

#ifdef __WINDOWS__
  #pragma comment(lib, "flxlib")
#endif


FlxRFBox RFbox;
int current_RF_mode = -1;

FlxRFBox& RFbox_get()
{
  return RFbox;
}

const int current_RF_mode_get()
{
  return current_RF_mode;
}

void current_RF_mode_set(const int cRFm)
{
  current_RF_mode = cRFm;
}


RBRV_set_noise_RF::RBRV_set_noise_RF(const bool internal, const tuint sRV, const std::string& name, const bool noID)
: RBRV_set_noise(internal, sRV, name, noID, new RBRV_entry_RV_stdN("dummy",0),0,NULL ), RndBox_index(0)
{

}

void RBRV_set_noise_RF::set_x(const tdouble*const x_vec)
{
  RBRV_set_noise::set_x(x_vec);
  RBRV_set_noise::set_y(x_vec);
}

void RBRV_set_noise_RF::set_y(const tdouble*const y_vec)
{
  RBRV_set_noise::set_y(y_vec);
  RBRV_set_noise::set_x(y_vec);
}

void RBRV_set_noise_RF::transform_y2x()
{
  #if FLX_DEBUG
    if (valid) {
      throw FlxException_Crude("RBRV_set_noise::transform_y2x");
    }
  #endif
  x_of_set = y_of_set;
  #if FLX_DEBUG
    valid = true;
  #endif
}


FlxRandomField_base::~FlxRandomField_base()
{
  const size_t beS = boxEL_vec.size();
  for (size_t i=0;i<beS;++i) {
    delete boxEL_vec[i];
  }
  if (Eigenvalues) delete Eigenvalues;
}

void FlxRandomField_base::set_automatic()
{
  if (!is_assembled) {
    if (automatic_managed==false) {
      automatic_managed = true;
    } else {
      std::ostringstream ssV;
      ssV << "This random field has already been declared as automatic.";
      throw FlxException("FlxRandomField_base::set_automatic_1", ssV.str() );
    }
  } else {
    std::ostringstream ssV;
    ssV << "This random field has already been assembled.";
    throw FlxException("FlxRandomField_base::set_automatic_2", ssV.str(), "It is no longer possible to declare a random field as automatic after it has been assembled." );
  }
}

void FlxRandomField_base::set_element(flxDomain_base* pel, flxElement_KL_base* klb, const bool report, const tuint rfindex, const std::string& elID)
{
  std::pair<flxDomain_base*, flxElement_KL_base*>Element(pel, klb);
  if ( ! boxEl.insert(Element).second ) {
    delete Element.second;
    std::ostringstream ssV;
    ssV << "The specified 'element' (" << elID << ") is already part of the random field";
    GlobalVar.alert.alert("FlxRandomField_base::set_element",ssV.str());
  } else {
    boxEL_vec.push_back(klb);
    if (report) Element.second->log_report(rfindex,elID,get_Type());
  }
}

const tdouble FlxRandomField_base::get_domain() const
{
  double domain=ZERO;
  const size_t beS = boxEL_vec.size();
  for (size_t i=0;i<beS;++i) {
    domain+=boxEL_vec[i]->calc_domain(true);
  }
  return domain;
}

const tdouble FlxRandomField_base::get_domain_error(const int errMeasure)
{
  if (is_assembled) {
    if ((get_Type() == flxElement_KL_base::rfKLfemGauss || get_Type() == flxElement_KL_base::rfKLfemGaussProj || get_Type() == flxElement_KL_base::rfColloGauss ) && errMeasure == 1) {
      FlxRandomField_KL_FEM_Gauss* rft = dynamic_cast<FlxRandomField_KL_FEM_Gauss*>(this);
      if ( ! ConstantBox->dependOn_GlobalSpatialVar(rft->get_sD())) {
	tdouble t = ZERO;
	for (tuint i=0;i<DIM;++i) {
	  t+=pow2(((*Eigenvalues)[i]));
	}
	t/=pow2(rft->get_sD()->calc());
	const tdouble domain = get_domain();
	return (domain - t)/domain;
      }
    }
    flxElement_KL_base* cekl = Current_Element_KL;
    tdouble domain=ZERO;
    tdouble errI=ZERO;
    const size_t beS = boxEL_vec.size();
    for (tuint i=0;i<beS;++i) {
      Current_Element_KL = boxEL_vec[i];
      domain+=Current_Element_KL->calc_domain(true);
      errI+=get_domain_error_el(errMeasure);
    }
    Current_Element_KL = cekl;
    return errI/domain;
  } else {
    std::ostringstream ssV;
    ssV << "The requested random field has not yet been assembled.";
    throw FlxException("FlxRandomField_base::get_domain_error", ssV.str() );
  }
}

void FlxRandomField_base::create_RBRVs()
{
  std::ostringstream ssV;
  ssV << "rf_" << RF_index;
  ts = new RBRV_set_noise_RF(false,DIM,ssV.str(),false);
  try {
    data->rbrv_box.register_set(ts);
  } catch (FlxException &e) {
    delete ts;
    ts = NULL;
    throw;
  }
  is_assembled = true;
  y_smp = &(ts->get_y()[0]);
}

// define a type
  struct elh {
    tuint ID;
    flxElement_KL_base* ptr;
  };
  struct elhc {
    bool operator ()(elh const& a, elh const& b) const {
	if (a.ID < b.ID) return true;
	return false;
    }
  };  
void FlxRandomField_base::plot(std::ostream& sout, FlxMtxConstFun* ppdim, const int errMeasure, const int prec, const int fixW, const bool checkTOL)
{
  if (is_assembled) {
    std::vector<elh> v;
    for (std::map<flxDomain_base*, flxElement_KL_base*>::iterator pos = boxEl.begin(); pos != boxEl.end(); ++pos) {
      elh elht;
      elht.ID = pos->first->defID;
      elht.ptr = pos->second;
      v.push_back(elht);      
    }
    std::sort(v.begin(), v.end(), elhc());
    flxElement_KL_base* cekl = Current_Element_KL;
    for (std::vector<elh>::iterator pos = v.begin(); pos != v.end(); ++pos) {
      Current_Element_KL = (*pos).ptr;
      plot_el(sout,ppdim,errMeasure,prec,fixW,checkTOL);
    }
    Current_Element_KL = cekl;
  } else {
    std::ostringstream ssV;
    ssV << "The requested random field has not yet been assembled.";
    throw FlxException("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::plot", ssV.str() );
  }
}

void FlxRandomField_base::plotcov(std::ostream& sout, const flxPoint& pp, FlxMtxConstFun* ppdim, const int prec, const int fixW)
{
  if (is_assembled) {
    flxElement_KL_base* cekl = Current_Element_KL;
    // detect relevant element and compute constant terms
      bool b1 = false;
      std::map<flxDomain_base*, flxElement_KL_base*>::iterator pos;
      for (pos = boxEl.begin(); pos != boxEl.end(); ++pos) {
	if ((b1=(pos->second->map2local(pp)))) break;
      }
      if (!b1) {
	std::ostringstream ssV;
	ssV << "Specified point is not element of the random field domain.";
	throw FlxException("FlxRandomField_base::plotcov_1", ssV.str() );
      }
      flxVec tr = *Eigenvalues;	// assign square-root of eigenvalues
      Current_Element_KL = pos->second;
      pp.set_global();
      for (tuint i=0;i<DIM;++i) {
	tr[i] *= calc_Hi(i+1,false);
      }
    // do the actual plotting
      const size_t beS = boxEL_vec.size();
      for (size_t i=0;i<beS;++i) {
	Current_Element_KL = boxEL_vec[i];
	plotcov_el(sout,pp,ppdim,tr,prec,fixW);
      }
    Current_Element_KL = cekl;
  } else {
    std::ostringstream ssV;
    ssV << "The requested random field has not yet been assembled.";
    throw FlxException("FlxRandomField_base::plotcov_2", ssV.str() );
  }
}

FlxFunction* FlxRandomField_base::get_mean_fun()
{
  throw FlxException_NotImplemented("FlxRandomField_base::get_mean_fun");
}

const tdouble FlxRandomField_base::calc_Hi(int i, bool globalIndex)
{
  throw FlxException_NotImplemented("FlxRandomField_base::calc_Hi");
}

void FlxRandomField_base::set_Current_Element_KL()
{
  if (Current_Element==NULL) {
    Current_Element_KL = NULL;
    return;
  }
  std::map<flxDomain_base*, flxElement_KL_base*>::iterator pos = boxEl.find(Current_Element);
  if ( pos != boxEl.end() ) {
    Current_Element_KL = pos->second;
  } else {
    Current_Element_KL = NULL;
  }
}

void FlxRandomField_base::assignElementIDs(flxVec_ptr& vptr, const flxDomain_base::geoel_type typeEL, flxFEM* femptr)
{
  const size_t beS = boxEL_vec.size();
  vptr = new flxVec(beS);
  std::map<flxDomain_base*, flxElement_KL_base*>::iterator pos;
  tuint c = 0;
  for (pos = boxEl.begin(); pos != boxEl.end(); ++pos) {
    flxDomain_base* f1 = pos->first;
    switch(typeEL) {
      case flxDomain_base::geoel_edge:
      {
	flxEdge* ep = dynamic_cast<flxEdge*>(f1);
	if (ep==NULL) throw FlxException("FlxRandomField_base::assignElementIDs_1","Wrong element type.");
	(*vptr)[c] = femptr->EdgeList.get_index(ep);
	break;
      }
      case flxDomain_base::geoel_face:
      {
	flxFace* ep = dynamic_cast<flxFace*>(f1);
	if (ep==NULL) throw FlxException("FlxRandomField_base::assignElementIDs_2","Wrong element type.");
	(*vptr)[c] = femptr->FaceList.get_index(ep);
	break;
      }
      case flxDomain_base::geoel_box:
      {
	flxBox* ep = dynamic_cast<flxBox*>(f1);
	if (ep==NULL) throw FlxException("FlxRandomField_base::assignElementIDs_3","Wrong element type.");
	(*vptr)[c] = femptr->BoxList.get_index(ep);
	break;
      }
    }
    ++c;
  }
}

void FlxRandomField_base::set_Const()
{
  LX = ConstantBox->get("lx",true);
  GX = ConstantBox->get("gx",true);
  GY = ConstantBox->get("gy",true);
  GZ = ConstantBox->get("gz",true);
  GX2 = ConstantBox->get("gx2",true);
  GY2 = ConstantBox->get("gy2",true);
  GZ2 = ConstantBox->get("gz2",true);
  DELTAX = ConstantBox->get("deltax",true);
  DELTAY = ConstantBox->get("deltay",true);
  DELTAZ = ConstantBox->get("deltaz",true);
  DELTAP = ConstantBox->get("deltap",true);
}

FlxRandomField_KL_FEM_Gauss::FlxRandomField_KL_FEM_Gauss(tuint RF_index, FlxFunction* mu, FlxFunction* sD, FlxFunction* rho, const int ev_solver)
: FlxRandomField_base(RF_index), mu(mu), sD(sD), rho(rho), ev_solver(ev_solver)
{

}

FlxRandomField_KL_FEM_Gauss::~FlxRandomField_KL_FEM_Gauss()
{
  delete mu;
  delete sD;
  delete rho;
}

const tdouble FlxRandomField_KL_FEM_Gauss::get_realization() const
{
  if (is_assembled) {
    if (Current_Element_KL) {
      if ( Current_Element_KL->calc_geo_fun()+GlobalVar.TOL() >= ZERO ) {
	tdouble t = ZERO;
	for (tuint i=0;i<DIM;++i) {
	  t+=((*Eigenvalues)[i]*Current_Element_KL->calc_djNj( &((Eigenvectors[i])[0]),*LX,*(LX-1),*(LX-2))) * (y_smp[i]);
	}
	t+=mu->calc();
	return t;
      } else {
	return mu->calc();
      }
    } else {
      std::ostringstream ssV;
      ssV << "Out of domain of definiton of the random field";
      throw FlxException("FlxRandomField_KL_FEM_Gauss::get_realization_1", ssV.str() );
    }
  } else {
    std::ostringstream ssV;
    ssV << "The requested random field has not yet been assembled.";
    throw FlxException("FlxRandomField_KL_FEM_Gauss::get_realization_2", ssV.str() );
  }
}

const tdouble FlxRandomField_KL_FEM_Gauss::get_domain_error_el(const int errMeasure) const
{
  #if FLX_DEBUG
  if (Current_Element_KL!=NULL) {
  #endif
    if (errMeasure == 1) {
      GlobalVar.alert.alert("FlxRandomField_KL_FEM_Gauss::get_domain_error_el_0","This feature has not yet been implemented.");
      return ZERO;
    } else if (errMeasure == 101) {	
      return Current_Element_KL->err_var_domain();
    } else if (errMeasure == 2) {
      return Current_Element_KL->err_cov_domain();
    } else if (errMeasure == 3) {
      return Current_Element_KL->err_var_domain_sep();
    } else {
      std::ostringstream ssV;
      ssV << "Unknown error measure (" << errMeasure << ")";
      throw FlxException("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::get_domain_error_el_4", ssV.str() );
    }
  #if FLX_DEBUG
  } else {
    std::ostringstream ssV;
    ssV << "Out of domain of definiton of the random field";
    throw FlxException("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::get_domain_error_el_1", ssV.str() );
  }
  #endif
}

void FlxRandomField_KL_FEM_Gauss::plot_el(std::ostream& sout, FlxMtxConstFun* ppdim, const int errMeasure, const int prec, const int fixW, const bool checkTOL) const
{
  #if FLX_DEBUG
  if (Current_Element_KL) {
  #endif
    Current_Element_KL->plot_el(sout,ppdim,errMeasure,prec,fixW, checkTOL);
  #if FLX_DEBUG
  } else {
    std::ostringstream ssV;
    ssV << "Out of domain of definiton of the random field";
    throw FlxException("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::plot_el_1", ssV.str() );
  }
  #endif
}

void FlxRandomField_KL_FEM_Gauss::plotcov_el(std::ostream& sout, const flxPoint& pp, FlxMtxConstFun* ppdim, const flxVec& tr, const int prec, const int fixW) const
{
  #if FLX_DEBUG
  if (Current_Element_KL) {
  #endif 
    Current_Element_KL->plotcov_el(sout,pp,ppdim,tr,prec,fixW);
  #if FLX_DEBUG
  } else {
    std::ostringstream ssV;
    ssV << "Out of domain of definiton of the random field";
    throw FlxException("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::plotcov_el_1", ssV.str() );
  }
  #endif
}

void FlxRandomField_KL_FEM_Gauss::assembleElementDOFs()
{
  tnlong DOFindex = 0;
  nodalM.clear();
  edgeM.clear();
  faceM.clear();
    std::vector<const flxVertex*> nV;
    std::vector<const flxEdge*> eV;
    std::vector<const flxFace*> fV;
  const size_t beS = boxEL_vec.size();
  // collect the DOFs
    for (size_t i=0;i<beS;++i) {
      boxEL_vec[i]->countDOFs(nodalM,edgeM,faceM,nV,eV,fV);
    }
  // assign the indices
    for (size_t i=0;i<nV.size();++i) {
      std::map<const flxVertex*,tnlong>::iterator pos = nodalM.find(nV[i]);
      pos->second = DOFindex++;
    }
    for (size_t i=0;i<eV.size();++i) {
      std::map<const flxEdge*,eiMap_sec>::iterator pos = edgeM.find(eV[i]);
      eiMap_sec &ems = pos->second;
      ems.dofindex = DOFindex;
      DOFindex += ems.count;
    }
    for (size_t i=0;i<fV.size();++i) {
      std::map<const flxFace*,fiMap_sec>::iterator pos = faceM.find(fV[i]);
      fiMap_sec &ems = pos->second;
      ems.dofindex = DOFindex;
      DOFindex += ems.count;
    }
  // assemble the Dofs
    for (size_t i=0;i<beS;++i) {
      boxEL_vec[i]->assembleDOFs(nodalM,edgeM,faceM);
    }
  Ndof = DOFindex;
}

const tnlong FlxRandomField_KL_FEM_Gauss::solve(FlxRFsolveProp* RFsolveProp,std::ostream& sout)
{
  std::ostream& slog = GlobalVar.slogcout(4);
  const size_t beS = boxEL_vec.size();
  if (is_assembled) {
    std::ostringstream ssV;
    ssV << "The random field has already been assembled.";
    throw FlxException("FlxRandomField_KL_FEM_Gauss::solve_1", ssV.str() );
  }
  if ( beS == 0 ) {
    std::ostringstream ssV;
    ssV << "At least one element required for this type of random field.";
    throw FlxException("FlxRandomField_KL_FEM_Gauss::solve_2", ssV.str() );
  }
  assembleElementDOFs();
  if (Ndof == 0) {
    std::ostringstream ssV;
    ssV << "Problem has no DOFs.";
    throw FlxException("FlxRandomField_KL_FEM_Gauss::solve_3", ssV.str() );
  }
  DIM = RFsolveProp->get_dim();
  
  // prepare elements
    for (size_t i=0;i<beS;++i) {
      boxEL_vec[i]->set_ptrFunSigma(&(sD->get_ptrFun()));
      boxEL_vec[i]->set_ptrFunRho(&(rho->get_ptrFun()));
      boxEL_vec[i]->set_ptrBoxElvec(&boxEL_vec);
    }
  
  FlxMtxSym* Mmtx = NULL;
  FlxMtxSym* Cmtx = NULL;
  
  try {
    if (RFsolveProp->is_load()==false) {
      // Create M
	slog << "  ... assemble 'mass' matrix" << std::endl;
	Mmtx = new FlxMtxSym(Ndof);
	for (size_t i=0;i<beS;++i) {
	  boxEL_vec[i]->calc_local_MassMatrix_KLE(*Mmtx);
	}
      
      // Create D-Matrix
	slog << "  ... assemble 'correlation' matrix (" << Ndof << " DOFs)" << std::endl;
	const tuint Nel = beS;
	std::vector<flxElement_KL_base*> ElVec(Nel);
	ElVec = boxEL_vec;
	Cmtx = new FlxMtxSym(Ndof);
	
	// register progress-bar
	  std::ostream &op = *(GlobalVar.get_cout());
	  FlxProgress prg(op,true);
	  op.flush();
	  const tuint prg_Nt = (Nel*Nel+Nel)/2;
	  prg.start(prg_Nt);
	for (tuint i=1;i<=Nel;++i) {
	  ElVec[Nel-i]->calc_local_corrMtx_KLE(*Cmtx,ElVec);
	  ElVec.pop_back();
	  prg.tick(prg_Nt - (pow2(Nel-i)+Nel-i)/2);
	}
	// deactivate the progress-bar
	  prg.stop();
    
      if (RFsolveProp->get_octaveOutput()) {
	sout << "%Octave-Input" << std::endl;
	sout << "%============" << std::endl;
	sout << "A=";
	Cmtx->output_OctaveMtx(sout,true,false);
	sout << ";" << std::endl << "B=";
	Mmtx->output_OctaveMtx(sout,true,false);
	sout << ";" << std::endl << "qz(A,B)" << std::endl;
      }
    }
    
    // Create Storage for Eigenvalues and Eigenvectors
      Eigenvalues = new flxVec(DIM);
      Eigenvectors.clear();
      for (tuint i = 0; i < DIM; i++) {
	flxVec Vh(Ndof);
	Eigenvectors.push_back(Vh);
      }
    
    if (RFsolveProp->is_load()==false) {
      // Solve the generalized eigenvalue problem
	slog << "  ... solve the eigenvalue problem" << std::endl;
	MtxEigenValue(*Cmtx,*Mmtx,DIM,*Eigenvalues,Eigenvectors,ev_solver);
	
      delete Cmtx; Cmtx = NULL;
      delete Mmtx; Mmtx = NULL;
    }
    
  } catch (...) {
    FLXMSG("FlxRandomField_KL_FEM_Gauss::solve_98",1);
    if (Cmtx) delete Cmtx; Cmtx = NULL;
    if (Mmtx) delete Mmtx; Mmtx = NULL;
    throw;
  }
  
  // load or store solution
    if (RFsolveProp->is_load()) {
      const std::string fn = RFsolveProp->get_file_name();
      ReadStream* ifp = new ReadStream(fn.c_str());
      std::vector<tuint> Cvec;
      Cvec.push_back(1);
      FlxIstream_file ip("dummy",ifp,true,1000,1,Cvec);
      tuint dummy;
      // Eigenvalues
	ip.get_vec(*Eigenvalues,dummy);
      // Eigenvectors
	for (tuint i=0;i<DIM;++i) {
	  flxVec& EV = Eigenvectors[i];
	  ip.get_vec(EV,dummy);
	}
      GlobalVar.slog(1) << "Random field: loaded solution" << std::endl;
    } else if (RFsolveProp->is_store()) {
      const std::string fn = RFsolveProp->get_file_name();
      std::ofstream fp (fn.c_str());
      if (fp.is_open()==false) {
	std::ostringstream ssV;
	ssV << "The file '" << fn << "' could not be opened.";
	throw FlxException("FlxRandomField_KL_FEM_Gauss::solve_97", ssV.str() );
      }
      // Eigenvalues
	for (tuint i=0;i<DIM;++i) {
	  fp << GlobalVar.Double2String((*Eigenvalues)[i],false,12) << ", ";
	}
	fp << std::endl;
      // Eigenvectors
	for (tuint i=0;i<DIM;++i) {
	  flxVec& EV = Eigenvectors[i];
	  for (tuint j=0;j<Ndof;++j) {
	    fp << GlobalVar.Double2String(EV[j],false,12) << ", ";
	  }
	  fp << std::endl;
	}
      fp.close();
      GlobalVar.slog(1) << "Random field: stored solution" << std::endl;
    }
  
  // Normalize the Eigenfunctions
    slog << "  ... normalize the eigenfunctions" << std::endl;
    for (tuint i=0;i<DIM;i++) {
      flxVec& EV = Eigenvectors[i];
      EV.check_TOL();
      tdouble D=ZERO;
      for (size_t j=0;j<beS;++j) {
	D+=boxEL_vec[j]->calc_djNj2(&EV[0]);
      }
      D=ONE/sqrt(D);
      EV*=D;
      // Compute square-roots of Eigenvalues
	if ((*Eigenvalues)[i]<=ZERO) {
	  std::ostringstream ssV;
	  ssV << "The " << i+1 << "th eigenvalue is negative.";
	  throw FlxException("FlxRandomField_KL_FEM_Gauss::solve_99", ssV.str() );
	}
	(*Eigenvalues)[i] = sqrt((*Eigenvalues)[i]);
    }
  // update elements
    for (size_t i=0;i<beS;++i) {
      boxEL_vec[i]->set_ptrEigenvectors(&Eigenvectors);
      boxEL_vec[i]->set_ptrEigenvalues(Eigenvalues);
    }
  
  create_RBRVs();
  return Ndof;
}

FlxFunction* FlxRandomField_KL_FEM_Gauss::get_mean_fun()
{
  return new FlxFunction(*mu);
}

const tdouble FlxRandomField_KL_FEM_Gauss::calc_Hi(int i, bool globalIndex)
{
  // correct the index -> set to local index
  if (globalIndex && i > 0) {
    if (i-1>=(int)(ts->get_RndBox_index()) && i<=(int)(ts->get_RndBox_index()+DIM)) {
      i -= ts->get_RndBox_index();
    } else {
      return ZERO;
    }
  }
  // check for special case -2;
    if (i==-2) i = (int)DIM;
    
  if (i<0) {
    throw FlxException_Crude("FlxRandomField_KL_FEM_Gauss::calc_Hi_0");
  } else if (i==0) {
    return mu->calc();
  } else if (i>(int)DIM) {
    throw FlxException_Crude("FlxRandomField_KL_FEM_Gauss::calc_Hi_1");
  } else  {
    if (Current_Element_KL->calc_geo_fun() >= ZERO) {
      return (*Eigenvalues)[i-1]*Current_Element_KL->calc_djNj( &((Eigenvectors[i-1])[0]),*LX,*(LX-1),*(LX-2));
    } else {
      return ZERO;
    }
  }
}

const tdouble FlxRandomField_KL_FEM_Gauss::get_Eigenvalue(const int i, bool errSerious)
{
  if (i>=(int)DIM) {
    std::ostringstream ssV;
    ssV << "Index is too large. Not enough eigenvalues.";
    FlxError(errSerious,"FlxRandomField_KL_FEM_Gauss::get_Eigenvalue", ssV.str() );
  }
  return pow2((*Eigenvalues)[i]);
}

void FlxRandomField_KL_FEM_Gauss::print_para(std::ostream& sout) const
{
  sout << "Gaussian random field (FEM-KL-expansion)";
}

FlxRandomField_KL_FEM_Gauss_proj::FlxRandomField_KL_FEM_Gauss_proj(tuint RF_index, FlxFunction* mu, FlxFunction* sD, FlxFunction* rho, const int ev_solver, const bool proj_h12, const bool solve_full)
: FlxRandomField_KL_FEM_Gauss(RF_index, mu, sD, rho, ev_solver), proj_h12(proj_h12), solve_full(solve_full)
{
  
}

FlxRandomField_KL_FEM_Gauss_proj::~FlxRandomField_KL_FEM_Gauss_proj()
{
  
}

void FlxRandomField_KL_FEM_Gauss_proj::print_para(std::ostream& sout) const
{
  sout << "Gaussian random field (FEM-KL-expansion - projection)";
}

const tnlong FlxRandomField_KL_FEM_Gauss_proj::solve(FlxRFsolveProp* RFsolveProp,std::ostream& sout)
{
  const size_t beS = boxEL_vec.size();
  if (is_assembled) {
    std::ostringstream ssV;
    ssV << "The random field has already been assembled.";
    throw FlxException("FlxRandomField_KL_FEM_Gauss_proj::solve_1", ssV.str() );
  }
  if ( beS == 0 ) {
    std::ostringstream ssV;
    ssV << "At least one element required for this type of random field.";
    throw FlxException("FlxRandomField_KL_FEM_Gauss_proj::solve_2", ssV.str() );
  }
  assembleElementDOFs();
  if (Ndof == 0) {
    std::ostringstream ssV;
    ssV << "Problem has no DOFs.";
    throw FlxException("FlxRandomField_KL_FEM_Gauss_proj::solve_3", ssV.str() );
  }
  DIM = RFsolveProp->get_dim();
  
  // prepare elements
    for (size_t i=0;i<beS;++i) {
      boxEL_vec[i]->set_ptrFunSigma(&(sD->get_ptrFun()));
      boxEL_vec[i]->set_ptrFunRho(&(rho->get_ptrFun()));
      boxEL_vec[i]->set_ptrBoxElvec(&boxEL_vec);
    }
    
  FlxMtxSym* Mmtx = NULL;
  FlxMtxLTri* L = NULL;
  FlxMtxSym* Cmtx = NULL;
    
  try {
    
  // Create M-Matrix
    GlobalVar.slogcout(4) << "  ... assemble 'mass' matrix" << std::endl;
    Mmtx = new FlxMtxSym(Ndof);
    for (size_t i=0;i<beS;++i) {
      boxEL_vec[i]->calc_local_MassMatrix_KLE(*Mmtx);
    }
    if (!solve_full) {
      L = new FlxMtxLTri(Ndof);
      L->CholeskyDec(*Mmtx);
    }
    
  // Create C-Matrix
    GlobalVar.slogcout(4) << "  ... assemble 'correlation' matrix (" << Ndof << " DOFs)" << std::endl;
    // arrange the nodes in a certain order
      const tuint Nnb = nodalM.size();
      std::vector<const flxVertex*> NVec(Nnb);
      std::vector<tnlong> NiVec(Nnb);
      tuint i=0,j;
      for (std::map<const flxVertex*, tnlong>::const_iterator pos = nodalM.begin(); pos != nodalM.end(); ++pos) {
	NiVec[i] = pos->second;
	NVec[i++] = pos->first;
      }
    Cmtx = new FlxMtxSym(Ndof);
    // use H_1/2-projection
    if (proj_h12) {
      GlobalVar.slogcout(4) << "    ... perform the H-1/2 projection " << std::endl;
      tdouble sD1;
      // perform the nodal projection
	for (i=0;i<Nnb;++i) {
	  NVec[i]->get_point().set_global();
	  NVec[i]->get_point().set_global2();
	  sD1 = sD->calc();
	  for (j=0;j<=i;++j) {
	    NVec[j]->get_point().set_global_dist();
	    Cmtx->operator()(NiVec[i],NiVec[j]) = sD1*sD->calc()*rho->calc();
	  }
	}
      // arrange edges in a certain order
	const tuint Enb = edgeM.size();
	std::vector<const flxEdge*> EVec(Enb);
	std::vector<eiMap_sec> EiVec(Enb);
	i=0; j=0;
	for (std::map<const flxEdge*, eiMap_sec>::const_iterator pos = edgeM.begin(); pos != edgeM.end(); ++pos) {
	  EiVec[i] = pos->second;
	  if (EiVec[i].count>j) j=EiVec[i].count;
	  EVec[i++] = pos->first;
	}
	const tuint maxedof = j;
	if (maxedof>0) {
	  flxVec tvec_e(maxedof*maxedof);	// for linear-edge & edge-edge
	// combination: linear-edge (single integral)
	  for (i=0;i<Nnb;++i) {	// loop over nodes
	    NVec[i]->get_point().set_global();
	    NVec[i]->get_point().set_global2();
	    sD1 = sD->calc();
	    for (j=0;j<Enb;++j) {	// loop over edges
	      if (EiVec[j].count==0) continue;
	      const tdouble cp1 = Cmtx->operator()(NiVec[i],nodalM[&(EVec[j]->get_vertex_1())]);
	      const tdouble cp2 = Cmtx->operator()(NiVec[i],nodalM[&(EVec[j]->get_vertex_2())]);
	      EVec[j]->KL_integ_1(tvec_e,cp1,cp2,EiVec[j].count,sD,rho,sD1);
	      for (tuint k=0;k<EiVec[j].count;++k) {	// assemble matrix
		Cmtx->operator()(NiVec[i],EiVec[j].dofindex+k) = tvec_e[k];
	      }
	    }
	  }
	// combination: edge-edge (two-element-integral)
	  for (i=0;i<Enb;++i) {
	    if (EiVec[i].count==0) continue;
	    const tuint ia = nodalM[&(EVec[i]->get_vertex_1())];
	    const tuint ib = nodalM[&(EVec[i]->get_vertex_2())];
	    flxVec e1(EiVec[i].count); 
	    flxVec e3(EiVec[i].count);
	    for (j=0;j<=i;++j) {
	      if (EiVec[j].count==0) continue;
	      // nodal information
		const tuint ic = nodalM[&(EVec[j]->get_vertex_1())];
		const tuint id = nodalM[&(EVec[j]->get_vertex_2())];
		tdouble ni[4];
		ni[0] = Cmtx->operator()(ia,ic);	// links to nodal values
		ni[1] = Cmtx->operator()(ib,ic);
		ni[2] = Cmtx->operator()(ib,id);
		ni[3] = Cmtx->operator()(ia,id);
	      // node-edge information
		flxVec e2(EiVec[j].count);
		flxVec e4(EiVec[j].count);
		for (tuint k=0;k<EiVec[i].count;++k) {
		  e1[k] = Cmtx->operator()(ic,EiVec[i].dofindex+k);
		}
		for (tuint k=0;k<EiVec[j].count;++k) {
		  e2[k] = Cmtx->operator()(ib,EiVec[j].dofindex+k);
		}
		for (tuint k=0;k<EiVec[i].count;++k) {
		  e3[k] = Cmtx->operator()(id,EiVec[i].dofindex+k);
		}
		for (tuint k=0;k<EiVec[j].count;++k) {
		  e4[k] = Cmtx->operator()(ia,EiVec[j].dofindex+k);
		}
	      // solve the sub-problem
		tdouble* tp = &(tvec_e[0]);
		EVec[i]->KL_integ_2(tp,ni,e1,e2,e3,e4,sD,rho,*(EVec[j]));
	      // update Cmtx
		for (tuint k=0,c=0;k<EiVec[i].count;++k) {
		  for (tuint l=0;l<EiVec[j].count;++l) {
		    Cmtx->operator()(EiVec[i].dofindex+k,EiVec[j].dofindex+l) = tp[c++];
		  }
		}
	    }
	  }
	  if (faceM.size()>0) {
	    throw FlxException_NotImplemented("FlxRandomField_KL_FEM_Gauss_proj::solve_4");
	  }
	}
    } else {
    // use discrete projection
      GlobalVar.slogcout(4) << "    ... perform the discrete projection ";
      FlxTimer tim;
      // guess the number of required points
	tuint NpointsP = 0;
	for (size_t i=0;i<beS;++i) {
	  NpointsP += boxEL_vec[i]->guess_N_EOLEpoints();
	}
      // gather all points
	std::vector<flxPoint> pointVec;
	  pointVec.reserve(NpointsP);
	flxVec* Niap = new flxVec(NpointsP*Ndof);
	flxVec& Nia = *Niap;
	for (size_t i=0;i<beS;++i) {
	  boxEL_vec[i]->assembleEOLEpointVec(pointVec,NpointsP,&(Nia[0]));
	}
	const tuint Npt = pointVec.size();
	GlobalVar.slogcout(4) << "(" << Npt << " points) "; GlobalVar.slogcout(4).flush();
      // compute the correlation
	const tnlong Npt2 = pow2(Npt);
	flxVec* Kabpp = new flxVec((pow2(Npt)+Npt)/2);
	flxVec& Kab = *Kabpp;
	tdouble* Kabp = Kab.get_tmp_vptr();
	tnlong c = 0;
	for (tnlong i=0;i<Npt;++i) {
	  pointVec[i].set_global2();
	  const tdouble sD1 = sD->calc();
	  for (tnlong j=0;j<=i;++j) {
	    pointVec[j].set_global_dist();
	    Kabp[c++] = sD1*sD->calc()*rho->calc();
	  }
	}
      // compute right-hand-side
	const tnlong ij_comp = (pow2(Ndof)+Ndof)/2;
	flxVec Kij(ij_comp);
	tdouble* Kijp = Kij.get_tmp_vptr();
	c = 0;
	for (tnlong i=0;i<Ndof;++i) {
	  const tdouble *Ni = &Nia[i*NpointsP];
	  for (tnlong j=0;j<=i;++j) {
	    const tdouble *Nj = &Nia[j*NpointsP];
	    pdouble res = ZERO;
	    tnlong c2 = 0;
	    for (tnlong k=0;k<Npt;++k) {
	      const tdouble Ni_a = Ni[k];
	      const tdouble Nj_a = Nj[k];
	      for (tnlong l=0;l<k;++l) {
		res += Ni_a*Nj[l]*Kabp[c2];
		res += Nj_a*Ni[l]*Kabp[c2++];
	      }
	      res += Ni_a*Nj_a*Kabp[c2++];
	    }
	    if (i!=j) res*=2;
	    Kijp[c++] = res.cast2double() / Npt2;
	  }
	}
	delete Kabpp;
      // compute left-hand-side
	tim.start();
	// set-up N_ij
	  flxVec* Nijpp = new flxVec((pow2(Ndof)+Ndof)*Npt);
	  flxVec& Nij = *Nijpp;
	  tdouble* Nijp = Nij.get_tmp_vptr();
	  c = 0;
	  for (tnlong i=0;i<Ndof;++i) {
	    const tdouble *Ni = &Nia[i*NpointsP];
	    for (tnlong j=0;j<=i;++j) {
	      const tdouble *Nj = &Nia[j*NpointsP];
	      pdouble s = ZERO;
	      for (tuint a=0;a<Npt;++a) {
		register const tdouble t  = Ni[a]*Nj[a];
		Nijp[c++] = t;
		Nijp[c++] = s.cast2double();
		s += t;
	      }
	    }
	  }
	  delete Niap;
	GlobalVar.slogcout(4) << std::endl << "         ... assemble the LHS "; GlobalVar.slogcout(4).flush();
	FlxProgress prg(*GlobalVar.get_cout(),true);
	prg.start((pow2(ij_comp)+ij_comp)/2);
	FlxMtxSym Nijmn(ij_comp);
	tdouble* Nijmnp = &(Nijmn.get_VecPointer()[0]);
	c = 0;
	for (tnlong i=0;i<Ndof;++i) {
	  for (tnlong j=0;j<=i;++j) {
	    for (tnlong m=0;m<=i;++m) {
	      const tdouble *Nim_a = &(Nijp[((i*i+i)/2+m)*Npt*2]);
	      const tnlong j2 = (j>=m?j:m);
	      const tnlong m2 = (j>=m?m:j);
	      const tdouble *Njm_a = &(Nijp[((j2*j2+j2)/2+m2)*Npt*2]);
	      const tnlong nT = (m==i?j:m);
	      for (tnlong n=0;n<=nT;++n) {
		const tdouble *Nin_a = &(Nijp[((i*i+i)/2+n)*Npt*2]);
		const tnlong j2 = (j>=n?j:n);
		const tnlong n2 = (j>=n?n:j);
		const tdouble *Njn_a = &(Nijp[((j2*j2+j2)/2+n2)*Npt*2]);
		pdouble res = ZERO;
		pdouble res2 = ZERO;
		for (tuint a=0;a<Npt*2;a+=2) {
		  res += Nim_a[a]*Njn_a[a];
		  res += Nim_a[a]*Njn_a[a+1]+Njn_a[a]*Nim_a[a+1];
		  res2 += Nin_a[a]*Njm_a[a];
		  res2 += Nin_a[a]*Njm_a[a+1]+Njm_a[a]*Nin_a[a+1];
		}
		if (i==j && m==n) {
		  Nijmnp[c++] = res.cast2double() / Npt2;
		} else if (i!=j && m!=n) {
		  res += res2;
		  Nijmnp[c++] = res.cast2double()*2 / Npt2;
		} else {
		  res += res2;
		  Nijmnp[c++] = res.cast2double()  / Npt2;
		}
	      }
	    }
	    prg.tick(c);
	  }
	}
	tim.stop();
	prg.stop();
	delete Nijpp;
	GlobalVar.slogcout(4) << "- done";
	GlobalVar.slogcout(5) << " in " << GlobalVar.Double2String(tim.get_time()) << " seconds"; 
      // solve the problem
	flxVec Cij(ij_comp);
	tdouble* Cijp = Cij.get_tmp_vptr();
	tdouble eps = 1e-8;
	tnlong iter = tnlong(ij_comp*1.5);
	tuint pcn = 4;
	GlobalVar.slogcout(4) << std::endl << "         ... transfrom to sparse matrix "; GlobalVar.slogcout(4).flush();
	tim.reset(); tim.start();
	FlxMtxSparsSym Nsparse(Nijmn);
	GlobalVar.slogcout(4) << "(" << Nsparse.Percentage_of_NonZero()*100 <<  "%) ";
	GlobalVar.slogcout(4) << std::endl << "         ... solve the linear system (unknowns: " << Nsparse.nrows() << ") ";
	GlobalVar.slogcout(4).flush();
	Kij.check_TOL();
	try {
	  Nsparse.solve_CG(Cij,Kij,eps,iter,pcn,true);
	} catch (FlxException& e) {
	  FLXMSG("FlxRandomField_KL_FEM_Gauss_proj_5",1);
	  GlobalVar.slogcout(3) << e.what();
	  GlobalVar.slogcout(4) << std::endl << "            !!! problems with the Cholesky-decomposition"; 
	  GlobalVar.slogcout(4) << std::endl << "            ... a diagonal preconditioner is used instead"; GlobalVar.slogcout(4).flush();
	  pcn = 1;
	  Nsparse.solve_CG(Cij,Kij,eps,iter,pcn,true);
	}
	tim.stop();
	GlobalVar.slogcout(4) << "- done (iter=" << iter;
	GlobalVar.slogcout(5) << "; eps=" << GlobalVar.Double2String(eps);
	GlobalVar.slogcout(4) << ")";
	GlobalVar.slogcout(5) << " in " << GlobalVar.Double2String(tim.get_time()) << " seconds"; GlobalVar.slogcout(4).flush();
	tdouble* Cmtxp = Cmtx->get_VecPointer();
	c = 0;
	for (tnlong i=0;i<Ndof;++i) {
	  for (tnlong j=0;j<=i;++j) {
	    Cmtxp[c] = Cijp[c];
	    ++c;
	  }
	}
	GlobalVar.slogcout(4) << std::endl;
    }
    
    // B = ???
      if (solve_full) { // MKM
	GlobalVar.slogcout(4) << "    ... perform B = MKM " << std::endl;
	FlxMtx tm(Mmtx);
	MtxProd_BTKB_mtx(tm,*Cmtx,*Mmtx);
	FlxMtxSym* tp = Cmtx;
	Cmtx = Mmtx;
	Mmtx = tp;
	tdouble* pv = Mmtx->get_VecPointer();
	const tnlong nr = Mmtx->nrows();
	const tdouble* tmvp = tm.get_internalPtr(true);
	for (tnlong i=0;i<nr;++i) {
	  for (tnlong j=0;j<=i;++j) {
	    *(pv++) = tmvp[i*nr+j];
	  }
	}
      } else {  	// M = L^T C L
	GlobalVar.slogcout(4) << "    ... perform B = L^T C L " << std::endl;
	MtxProd_BTKB_ltri(*L,*Cmtx,*Mmtx);
	delete Cmtx;
	Cmtx = Mmtx; Mmtx = NULL;
      }
    
  // Create Storage for Eigenvalues and Eigenvectors
    Eigenvalues = new flxVec(DIM);
    Eigenvectors.clear();
    for (tuint i = 0; i < DIM; i++) {
      flxVec Vh(Ndof);
      Eigenvectors.push_back(Vh);
    }
    
    if (RFsolveProp->get_octaveOutput()) {
      sout << "%Octave-Input" << std::endl;
      sout << "%============" << std::endl;
      sout << "A=";
      Cmtx->output_OctaveMtx(sout,true,false);
      if (!solve_full) {
	sout << ";" << std::endl;
      } else {
	sout << ";" << std::endl << "B=";
	Mmtx->output_OctaveMtx(sout,true,false);
	sout << ";" << std::endl << "qz(A,B)" << std::endl;
      }
    }
    if (RFsolveProp->get_octaveOutput()) {
      sout << "%Octave-Input" << std::endl;
      sout << "%============" << std::endl;
      sout << "A=";
      Cmtx->output_OctaveMtx(sout,true,false);
      sout << ";" << std::endl << "B=";
      Mmtx->output_OctaveMtx(sout,true,false);
      sout << ";" << std::endl << "qz(A,B)" << std::endl;
    }
    
  // Solve the standard eigenvalue problem
    GlobalVar.slogcout(4) << "  ... solve the matrix eigenvalue problem " << std::endl;
    if (solve_full) {
      MtxEigenValue(*Cmtx,*Mmtx,DIM,*Eigenvalues,Eigenvectors,ev_solver);
      delete Cmtx; Cmtx = NULL;
      delete Mmtx; Mmtx = NULL;
    } else {
      MtxEigenValue(*Cmtx,DIM,*Eigenvalues,Eigenvectors,ev_solver);
      delete Cmtx; Cmtx = NULL;
      L->Invert();
    }

  // Normalize the Eigenfunctions
    GlobalVar.slogcout(4) << "  ... normalize the eigenfunctions " << std::endl;
    for (tuint i=0;i<DIM;i++) {
      flxVec& EV = Eigenvectors[i];
      if (!solve_full) L->TransMultVec(EV);
      EV.check_TOL();;
      tdouble D=ZERO;
      for (size_t j=0;j<beS;++j) {
	D+=boxEL_vec[j]->calc_djNj2(&EV[0]);
      }
      D=1/sqrt(D);
      EV*=D;
      // Compute square-roots of Eigenvalues
	if ((*Eigenvalues)[i]<=ZERO) {
	  std::ostringstream ssV;
	  ssV << "The " << i+1 << ". eigenvalue is negative.";
	  throw FlxException("FlxRandomField_KL_FEM_Gauss_proj::solve_90", ssV.str() );
	}
	(*Eigenvalues)[i] = sqrt((*Eigenvalues)[i]);
    }
    if (!solve_full) { delete L; L = NULL; }
  // update elements
    for (size_t i=0;i<beS;++i) {
      boxEL_vec[i]->set_ptrEigenvectors(&Eigenvectors);
      boxEL_vec[i]->set_ptrEigenvalues(Eigenvalues);
    }
  
  create_RBRVs();
    
  } catch (...) {
    FLXMSG("FlxRandomField_KL_FEM_Gauss_proj_91",1);
    if (Cmtx) delete Cmtx;
    if (Mmtx) delete Mmtx;
    if (L) delete L;
    throw;
  }
    
    return Ndof;
    
}

const tdouble FlxRandomField_KL_FEM_Gauss_proj::get_domain_error_el(const int errMeasure) const
{
  int errM = errMeasure;
  //if (errM==1) errM = 101;
  return FlxRandomField_KL_FEM_Gauss::get_domain_error_el(errM);
}

void FlxRandomField_KL_FEM_Gauss_proj::plot_el(std::ostream& sout, FlxMtxConstFun* ppdim, const int errMeasure, const int prec, const int fixW, const bool checkTOL) const
{
  int errM = errMeasure;
  //if (errM==1) errM = 101;
  FlxRandomField_KL_FEM_Gauss::plot_el(sout, ppdim, errM,prec,fixW,checkTOL);
}


FlxRandomField_KL_Collo_Gauss::FlxRandomField_KL_Collo_Gauss(tuint RF_index, FlxFunction* mu, FlxFunction* sD, FlxFunction* rho, const int ev_solver)
: FlxRandomField_KL_FEM_Gauss(RF_index, mu, sD, rho, ev_solver)
{
  
}

FlxRandomField_KL_Collo_Gauss::~FlxRandomField_KL_Collo_Gauss()
{
  
}

void FlxRandomField_KL_Collo_Gauss::print_para(std::ostream& sout) const
{
  sout << "Gaussian random field (Collocation KL)";
}

const tnlong FlxRandomField_KL_Collo_Gauss::solve(FlxRFsolveProp* RFsolveProp,std::ostream& sout)
{
  const size_t beS = boxEL_vec.size();
  if (is_assembled) {
    std::ostringstream ssV;
    ssV << "The random field has already been assembled.";
    throw FlxException("FlxRandomField_KL_Collo_Gauss::solve_1", ssV.str() );
  }
  if ( beS == 0 ) {
    std::ostringstream ssV;
    ssV << "At least one element required for this type of random field.";
    throw FlxException("FlxRandomField_KL_Collo_Gauss::solve_2", ssV.str() );
  }
  assembleElementDOFs();
  if (Ndof == 0) {
    std::ostringstream ssV;
    ssV << "Problem has no DOFs.";
    throw FlxException("FlxRandomField_KL_Collo_Gauss::solve_3", ssV.str() );
  }
  DIM = RFsolveProp->get_dim();
  
  // prepare elements
    for (size_t i=0;i<beS;++i) {
      boxEL_vec[i]->set_ptrFunSigma(&(sD->get_ptrFun()));
      boxEL_vec[i]->set_ptrFunRho(&(rho->get_ptrFun()));
      boxEL_vec[i]->set_ptrBoxElvec(&boxEL_vec);
    }

  // Create A-Matrix
  GlobalVar.slog(4) << "  ... assemble 'correlation' matrix (" << Ndof << " DOFs)" << std::endl;
  // make sure that there are no higher-order elements
    if (Ndof!=nodalM.size()) {
      std::ostringstream ssV;
      ssV << "The KL collocation method is not implemented for higher-order basis functions.";
      throw FlxException("FlxRandomField_KL_Collo_Gauss::solve_4", ssV.str() );
    }
  FlxMtx Amtx(Ndof,Ndof);
  // loop over all nodes (this are the rows)
    for (std::map<const flxVertex*, tnlong>::const_iterator pos = nodalM.begin(); pos != nodalM.end(); ++pos) {
      const flxVertex* nk = pos->first;
      const tnlong nid = pos->second;	// the index of the node
      nk->get_point().set_global2();
      const tdouble sigma_eval = sD->calc();
      // loop over all the elements ('columns')
      for (size_t i=0;i<beS;++i) {
	boxEL_vec[i]->calc_local_collocMtx(nid,Amtx,sigma_eval);
      }
    }
    
  // Create Storage for Eigenvalues and Eigenvectors
    Eigenvalues = new flxVec(DIM);
    Eigenvectors.clear();
    for (tuint i = 0; i < DIM; i++) {
      flxVec Vh(Ndof);
      Eigenvectors.push_back(Vh);
    }
    
    if (RFsolveProp->get_octaveOutput()) {
      sout << "%Octave-Input" << std::endl;
      sout << "%============" << std::endl;
      sout << "A=";
      Amtx.output_OctaveMtx(sout,true,false);
      sout << ";" << std::endl;
    }

    // Solve the standard eigenvalue problem
    MtxEigenValue(Amtx,DIM,*Eigenvalues,Eigenvectors,ev_solver);
    
  // Normalize the Eigenfunctions
    for (tuint i=0;i<DIM;i++) {
      flxVec& EV = Eigenvectors[i];
      EV.check_TOL();
      tdouble D=ZERO;
      for (size_t j=0;j<beS;++j) {
	D+=boxEL_vec[j]->calc_djNj2(&EV[0]);
      }
      D=ONE/sqrt(D);
      EV*=D;
      // Compute square-roots of Eigenvalues
	if ((*Eigenvalues)[i]<=ZERO) {
	  std::ostringstream ssV;
	  ssV << "The " << i+1 << ". eigenvalue is negative.";
	  throw FlxException("FlxRandomField_KL_FEM_Gauss_proj::solve_99", ssV.str() );
	}
	(*Eigenvalues)[i] = sqrt((*Eigenvalues)[i]);
    }
  // update elements
    for (size_t i=0;i<beS;++i) {
      boxEL_vec[i]->set_ptrEigenvectors(&Eigenvectors);
      boxEL_vec[i]->set_ptrEigenvalues(Eigenvalues);
    }
  
  create_RBRVs();
  return Ndof;
}

FlxRandomField_EOLE_Gauss::FlxRandomField_EOLE_Gauss(tuint RF_index, FlxFunction* mu, FlxFunction* sD, FlxFunction* rho, const int ev_solver)
: FlxRandomField_KL_FEM_Gauss(RF_index, mu, sD, rho, ev_solver),sdVec(NULL),Hx(NULL)
{
  
}

FlxRandomField_EOLE_Gauss::~FlxRandomField_EOLE_Gauss()
{
  if (sdVec) delete sdVec;
  if (Hx) delete Hx;
}

const tdouble FlxRandomField_EOLE_Gauss::get_domain_error_el(const int errMeasure) const
{
  #if FLX_DEBUG
  if (Current_Element_KL) {
  #endif
    if (errMeasure == 1 || errMeasure==3) {	
      return Current_Element_KL->err_var_domain_EOLE(*Hx,pointVec,*sdVec,errMeasure==3);
    } else if (errMeasure == 2) {	
      return Current_Element_KL->err_cov_domain_EOLE(pointVec,*sdVec);
    } else {
      std::ostringstream ssV;
      ssV << "Unknown error measure (" << errMeasure << ")";
      throw FlxException("FlxRandomField_EOLE_Gauss::get_domain_error_el_4", ssV.str() );
    }
  #if FLX_DEBUG
  } else {
    std::ostringstream ssV;
    ssV << "Out of domain of definiton of the random field";
    throw FlxException("FlxRandomField_EOLE_Gauss::get_domain_error_el_1", ssV.str() );
  }
  #endif
}

const tdouble FlxRandomField_EOLE_Gauss::get_error(int errMeasure) const
{
  if (errMeasure==1 || errMeasure==3) {
    const tdouble sD_curr_x_2 = pow2(sD->calc());
    tdouble t = sD_curr_x_2;
    for (tuint i=0;i<DIM;++i) {	
      t -= pow2( ( Eigenvectors[i]* (*Hx) ) * (*Eigenvalues)[i] );
    }
    t /= sD_curr_x_2;
    if (errMeasure==1) {
      return t;
    } else {
      return fabs(t);
    }
  } else if (errMeasure==2) {
    *GX2 = *GX; 
    *GY2 = *GY;
    *GZ2 = *GZ;
    flxVec tr(DIM);
    flxVec Hx2(Ndof);
    return Current_Element_KL->get_error_2i_EOLE(Hx2,tr,pointVec,*sdVec);
  } else {
    std::ostringstream ssV;
    ssV << "Unknown error measure (" << errMeasure << ")";
    throw FlxException("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::get_error_2", ssV.str() );
  }
}

void FlxRandomField_EOLE_Gauss::plot_el(std::ostream& sout, FlxMtxConstFun* ppdim, const int errMeasure, const int prec, const int fixW, const bool checkTOL) const
{
  #if FLX_DEBUG
  if (Current_Element_KL) {
  #endif
    std::vector<flxPoint> pointVec;
    std::vector< tuint > geoVec;
    Current_Element_KL->get_PlotPoints(pointVec,geoVec,ppdim);
    tuint k = 0;
    if (geoVec.size() > 0 && geoVec[0]==0) ++k;
    for (tuint i=0;i<pointVec.size();++i) {
      if (k<geoVec.size() && geoVec[k]==i) {
	sout << std::endl;
	++k;
	while (k < geoVec.size() && geoVec[k]==i) ++k;
      }
      pointVec[i].set_global();
      assemble_Hx();
      sout << GlobalVar.Double2String(*GX,false,prec,fixW) << ' ' << GlobalVar.Double2String(*GY,false,prec,fixW) << ' ' << GlobalVar.Double2String(*GZ,false,prec,fixW) << ' ';
      sout << GlobalVar.Double2String(get_error(errMeasure),checkTOL,prec,fixW);
      for (tuint j=0;j<DIM;++j) {
	sout << ' ' << GlobalVar.Double2String( ( Eigenvectors[j] * (*Hx) ) * (*Eigenvalues)[j], true,prec,fixW);
      }
      sout << std::endl;
    }
    sout << std::endl << std::endl;
  #if FLX_DEBUG
  } else {
    std::ostringstream ssV;
    ssV << "Out of domain of definiton of the random field";
    throw FlxException("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::plot_el_1", ssV.str() );
  }
  #endif
}

void FlxRandomField_EOLE_Gauss::plotcov_el(std::ostream& sout, const flxPoint& pp, FlxMtxConstFun* ppdim, const flxVec& tr, const int prec, const int fixW) const
{
  #if FLX_DEBUG
  if (Current_Element_KL) {
  #endif
    std::vector<flxPoint> pointVec;
    std::vector< tuint > geoVec;
    // determine sd(pp)
      pp.set_global();
      tdouble sd_pp_X = sD->cast2positive();
    tdouble sd_cur_X, cov_ana, cov_EOLE;
    Current_Element_KL->get_PlotPoints(pointVec,geoVec,ppdim);
    tuint i,j,k=0;
    if (geoVec.size() > 0 && geoVec[0]==0) ++k;
    for (i=0;i<pointVec.size();++i) {
      if (k<geoVec.size() && geoVec[k]==i) {
	sout << std::endl;
	++k;
	while (k < geoVec.size() && geoVec[k]==i) ++k;
      }
      pointVec[i].set_global();
      sd_cur_X = assemble_Hx();
      pp.set_global2_dist();
      sout << GlobalVar.Double2String(*GX,false,prec,fixW) << ' ' << GlobalVar.Double2String(*GY,false,prec,fixW) << ' ' << GlobalVar.Double2String(*GZ,false,prec,fixW) << ' ';
      cov_EOLE = ZERO;
      for (j=0;j<DIM;++j) {
	cov_EOLE += (Eigenvectors[j]*(*Hx)) * tr[j] ;
      }
      cov_ana = sd_cur_X * sd_pp_X * rho->calc();
      sout << GlobalVar.Double2String(cov_EOLE,false,prec,fixW) << ' ' << GlobalVar.Double2String( cov_ana,false,prec,fixW ) << ' ' << GlobalVar.Double2String( cov_ana-cov_EOLE,false,prec,fixW );
      sout << std::endl;
    }
    sout << std::endl;
  #if FLX_DEBUG
  } else {
    std::ostringstream ssV;
    ssV << "Out of domain of definiton of the random field";
    throw FlxException("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::plot_el_1", ssV.str() );
  }
  #endif
}

FlxFunction* FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::get_mean_fun()
{
  return new FlxFunction(*mu);
}

const tdouble FlxRandomField_EOLE_Gauss::get_realization() const
{
  if (is_assembled) {
    if (Current_Element_KL) {
      if ( Current_Element_KL->calc_geo_fun()+GlobalVar.TOL() >= ZERO ) {
	assemble_Hx();
	tdouble t = ZERO;
	for (tuint i=0;i<DIM;++i) {
	  t+= ( Eigenvectors[i] * (*Hx) ) * (*Eigenvalues)[i] * (y_smp[i]) ;
	}
	t+=mu->calc();
	return t;
      } else {
	return mu->calc();
      }
    } else {
      std::ostringstream ssV;
      ssV << "Out of domain of definiton of the random field";
      throw FlxException("FlxRandomField_EOLE_Gauss::get_realization_1", ssV.str() );
    }
  } else {
    std::ostringstream ssV;
    ssV << "The requested random field has not yet been assembled.";
    throw FlxException("FlxRandomField_EOLE_Gauss::get_realization_2", ssV.str() );
  }
}

void FlxRandomField_EOLE_Gauss::print_para(std::ostream& sout) const
{
  sout << "Gaussian random field (EOLE-expansion)";
}

void FlxRandomField_EOLE_Gauss::assembleElementDOFs()
{
  pointVec.clear();
  size_t beS = boxEL_vec.size();
  // guess the number of required points
    tuint Npoints = 0;
    for (size_t i=0;i<beS;++i) {
      Npoints += boxEL_vec[i]->guess_N_EOLEpoints();
    }
    pointVec.reserve(Npoints);
  // get the points
    for (size_t i=0;i<beS;++i) {
      boxEL_vec[i]->assembleEOLEpointVec(pointVec);
    }
  Ndof = pointVec.size();
}

const tdouble FlxRandomField_EOLE_Gauss::assemble_Hx() const
{
  tdouble sD_curr_x = sD->calc();
  for (tuint i=0;i<Ndof;++i) {
    pointVec[i].set_global2_dist();
    (*Hx)[i] = sD_curr_x * (*sdVec)[i] * rho->calc();
  }
  return sD_curr_x;
}

const tnlong FlxRandomField_EOLE_Gauss::solve(FlxRFsolveProp* RFsolveProp,std::ostream& sout)
{
  const size_t beS = boxEL_vec.size();
  if (is_assembled) {
    std::ostringstream ssV;
    ssV << "The random field has already been assembled.";
    throw FlxException("FlxRandomField_EOLE_Gauss::solve_1", ssV.str() );
  }
  if ( beS == 0 ) {
    std::ostringstream ssV;
    ssV << "At least one element required for this type of random field.";
    throw FlxException("FlxRandomField_EOLE_Gauss::solve_2", ssV.str() );
  }
  assembleElementDOFs();	// update 
  if (Ndof == 0) {
    std::ostringstream ssV;
    ssV << "Problem has no DOFs.";
    throw FlxException("FlxRandomField_EOLE_Gauss::solve_3", ssV.str() );
  }
  DIM = RFsolveProp->get_dim();
  
  // prepare elements
    for (size_t i=0;i<beS;++i) {
      boxEL_vec[i]->set_ptrFunSigma(&(sD->get_ptrFun()));
      boxEL_vec[i]->set_ptrFunRho(&(rho->get_ptrFun()));
      boxEL_vec[i]->set_ptrBoxElvec(&boxEL_vec);
    }
    
  // Create Storage for Eigenvalues and Eigenvectors
    Eigenvalues = new flxVec(DIM);
    Eigenvectors.clear();
    for (tuint i = 0; i < DIM; i++) {
      flxVec Vh(Ndof);
      Eigenvectors.push_back(Vh);
    }
    
  
      sdVec = new flxVec(Ndof);
      Hx = new flxVec(Ndof);
  if (RFsolveProp->is_load()==false) {
    // Create C-Matrix
      GlobalVar.slog(4) << "  ... assemble 'correlation' matrix (" << Ndof << " points)" << std::endl;
      FlxMtxSym* Cmtx = new FlxMtxSym(Ndof);
      for (tuint i=0;i<Ndof;++i) {
	(pointVec[i]).set_global();
	sdVec->operator[](i) = sD->calc();		// update the standard deviation
	for (tuint j=0;j<=i;++j) {
	  (pointVec[j]).set_global2_dist();
	  Cmtx->operator()(i,j) = sdVec->operator[](i)*sdVec->operator[](j)*rho->calc();
	}
      }
      
      if (RFsolveProp->get_octaveOutput()) {
	sout << "%Octave-Input" << std::endl;
	sout << "%============" << std::endl;
	sout << "A=";
	Cmtx->output_OctaveMtx(sout,true,false);
	sout << ";" << std::endl;
      }
    // Solve the standard eigenvalue problem
      try {
	MtxEigenValue(*Cmtx,DIM,*Eigenvalues,Eigenvectors,ev_solver);
      } catch (FlxException &e) {
	FLXMSG("FlxRandomField_EOLE_Gauss::solve_4",1);
	delete Cmtx;
	throw;
      }
      delete Cmtx;
  } else {
    for (tuint i=0;i<Ndof;++i) {
      (pointVec[i]).set_global();
      sdVec->operator[](i) = sD->calc();		// update the standard deviation
    }
  }
    
  // load or store solution
    if (RFsolveProp->is_load()) {
      const std::string fn = RFsolveProp->get_file_name();
      ReadStream* ifp = new ReadStream(fn.c_str());
      std::vector<tuint> Cvec;
      Cvec.push_back(1);
      FlxIstream_file ip("dummy",ifp,true,1000,1,Cvec);
      tuint dummy;
      // Eigenvalues
	ip.get_vec(*Eigenvalues,dummy);
      // Eigenvectors
	for (tuint i=0;i<DIM;++i) {
	  flxVec& EV = Eigenvectors[i];
	  ip.get_vec(EV,dummy);
	}
      GlobalVar.slog(1) << "Random field: loaded solution" << std::endl;
    } else if (RFsolveProp->is_store()) {
      const std::string fn = RFsolveProp->get_file_name();
      std::ofstream fp (fn.c_str());
      if (fp.is_open()==false) {
	std::ostringstream ssV;
	ssV << "The file '" << fn << "' could not be opened.";
	throw FlxException("FlxRandomField_KL_FEM_Gauss::solve_97", ssV.str() );
      }
      // Eigenvalues
	for (tuint i=0;i<DIM;++i) {
	  fp << GlobalVar.Double2String((*Eigenvalues)[i],false,12) << ", ";
	}
	fp << std::endl;
      // Eigenvectors
	for (tuint i=0;i<DIM;++i) {
	  flxVec& EV = Eigenvectors[i];
	  for (tuint j=0;j<Ndof;++j) {
	    fp << GlobalVar.Double2String(EV[j],false,12) << ", ";
	  }
	  fp << std::endl;
	}
      fp.close();
      GlobalVar.slog(1) << "Random field: stored solution" << std::endl;
    }
    
  // Normalize the Eigenvectors
    for (tuint i=0;i<DIM;i++) {
      flxVec& EV = Eigenvectors[i];
      EV/=EV.get_Norm2();
      EV.check_TOL();
      (*Eigenvalues)[i] = ONE / sqrt((*Eigenvalues)[i]);
    }
  // update elements
    for (size_t i=0;i<beS;++i) {
      boxEL_vec[i]->set_ptrEigenvectors(&Eigenvectors);
      boxEL_vec[i]->set_ptrEigenvalues(Eigenvalues);
    }
  
  create_RBRVs();
  return Ndof;
}

const tdouble FlxRandomField_EOLE_Gauss::calc_Hi(int i, bool globalIndex)
{
  // correct the index -> set to local index
  if (globalIndex && i > 0) {
    if (i-1>=(int)(ts->get_RndBox_index()) && i<=(int)(ts->get_RndBox_index()+DIM)) {
      i -= ts->get_RndBox_index();
    } else {
      return ZERO;
    }
  }
  // check for special case -2;
    if (i==-2) i = DIM;
    
  if (i<0) {
    throw FlxException_Crude("FlxRandomField_KL_FEM_Gauss::calc_Hi_0");
  } else if (i==0) {
    return mu->calc();
  } else if (i>(int)DIM) {
    throw FlxException_Crude("FlxRandomField_KL_FEM_Gauss::calc_Hi_1");
  } else  {
    if (Current_Element_KL->calc_geo_fun() >= ZERO) {
      assemble_Hx();
      return ( Eigenvectors[i-1] * (*Hx) ) * (*Eigenvalues)[i-1];
    } else {
      return ZERO;
    }
  }
}

const tdouble FlxRandomField_EOLE_Gauss::get_Eigenvalue(const int i, bool errSerious)
{
  if (i>=(int)DIM) {
    std::ostringstream ssV;
    ssV << "Index is too large. Not enough eigenvalues.";
    FlxError(errSerious,"FlxRandomField_KL_FEM_Gauss::get_Eigenvalue", ssV.str() );
  }
  const tdouble A = get_domain();
  return ONE / pow2((*Eigenvalues)[i]) / Ndof * A;
}

const tnlong FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::solve(FlxRFsolveProp* RFsolveProp,std::ostream& sout)
{
  if (is_assembled) {
    std::ostringstream ssV;
    ssV << "The random field has already been assembled.";
    throw FlxException("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::solve_1", ssV.str() );
  }
  const tdouble x_startO = 1e-5;
  const tdouble err = 1e-14;
  DIM = RFsolveProp->get_dim();
  if ( boxEL_vec.size() != 1 ) {
    std::ostringstream ssV;
    ssV << "Not more than one element allowed for this type of random field.";
    throw FlxException("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::solve_2", ssV.str() );
  }
  etkl = dynamic_cast<flxElement_KL_1D*>(boxEL_vec[0]);
  if ( etkl == NULL ) {
    std::ostringstream ssV;
    ssV << "This type of random field just works with 1D-elements.";
    throw FlxException("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::solve_3", ssV.str() );
  }
  if (etkl->has_geometry()) {
    std::ostringstream ssV;
    ssV << "This type of random field cannot deal with geometries.";
    throw FlxException("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::solve_4", ssV.str() );
  }
  Eigenvalues = new flxVec(DIM);
  alpha_i = new flxVec(DIM);
  omega_i = new flxVec(DIM);
  domain_l = etkl->calc_length() / 2;
  const tdouble lt =  corrL;
  const tdouble lti = ONE/lt;
  // solve the actual problem
  tdouble x_startR = x_startO;
  tdouble x, x_old, f, fp;
  tnlong lc;
  for (tuint i=0;i<DIM;++i) {
    if (i%2==0) {	// i+1 is odd
      // compute omega_i
	x_old =(tdouble(i/2)+ONE/2)*PI/domain_l;
	x = x_old - x_startR;
	f = lti - x*tan(domain_l*x);
	lc = 0;
	while (fabs(f) > err && x_old!=x && ++lc < 1e4) {
	  fp = -tan(domain_l*x)-domain_l*x/pow2(cos(domain_l*x));
	  x_old = x;
	  x -= f/fp;
	  f = lti - x*tan(domain_l*x);
	}
	(*omega_i)[i] = x;
	x_startR = (tdouble(i/2)+ONE/2)*PI/domain_l-x;
      // compute alpha_i
	(*alpha_i)[i] = ONE/sqrt(domain_l+sin(2*domain_l*(*omega_i)[i])/(2.0*(*omega_i)[i]));
    } else {		// i+1 is even
      // compute omega_i
	x_old =(tdouble((i-1)/2)+ONE/2)*PI/domain_l;
	x = x_old + x_startO;
	f = tan(domain_l*x)*lti + x;
	lc = 0;
	while (fabs(f) > err && x_old!=x &&  ++lc < 1e4 ) {
	  fp = domain_l/(lt*pow2(cos(domain_l*x)))+1;
	  x_old = x;
	  x -= f/fp;
	  f = tan(domain_l*x)*lti + x;
	}
	(*omega_i)[i] = x;
      // compute alpha_i
	(*alpha_i)[i] = ONE/sqrt((domain_l-(sin(2.0*domain_l*(*omega_i)[i]))/(2.0*(*omega_i)[i])));
    }
    // compute the square root of the eigenvalues
      (*Eigenvalues)[i] = sqrt((2*lt)/(ONE+pow2((*omega_i)[i]*lt)));
  }
  create_RBRVs();
  return 0;
}

const tdouble FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::calc_Hi(int i, bool globalIndex)
{
  // correct the index -> set to local index
    if (globalIndex && i>0) i -= ts->get_RndBox_index();
  // check for special case -2;
    if (i==-2) i = (int)DIM;
    
  if (i<0) {
    throw FlxException_Crude("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::calc_Hi_0");
  } else if (i==0) {
    return mu->calc();
  } else if (i>(int)DIM) {
    throw FlxException_Crude("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::calc_Hi_1");
  } else  {
    if (i%2==0) {
      return sD*(((*Eigenvalues)[i-1])*(((*alpha_i)[i-1] * sin((*omega_i)[i-1]*(*LX)*domain_l))));
    } else {
      return sD*(((*Eigenvalues)[i-1])*(((*alpha_i)[i-1] * cos((*omega_i)[i-1]*(*LX)*domain_l))));
    }
  }
}

const tdouble FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::get_Eigenvalue(const int i, bool errSerious)
{
  if (i>=(int)DIM) {
    std::ostringstream ssV;
    ssV << "Index is too large. Not enough eigenvalues.";
    FlxError(errSerious,"FlxRandomField_KL_FEM_Gauss::get_Eigenvalue", ssV.str() );
  }
  return pow2(sD*(*Eigenvalues)[i]);
}

const tdouble FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::get_realization() const
{
  if (is_assembled) {
    if (Current_Element_KL) {
      tdouble t = ZERO;
      for (tuint i=0;i<DIM;i=i+2) {	// i+1 is odd
	t+=(((*Eigenvalues)[i])*(((*alpha_i)[i] * cos((*omega_i)[i]*(*LX)*domain_l)))) * (y_smp[i]);
      }
      for (tuint i=1;i<DIM;i=i+2) {	// i+1 is even
	t+=(((*Eigenvalues)[i])*(((*alpha_i)[i] * sin((*omega_i)[i]*(*LX)*domain_l)))) * (y_smp[i]);
      }
      t*=sD;
      t+=mu->calc();
      return t;
    } else {
      std::ostringstream ssV;
      ssV << "Out of domain of definiton of the random field";
      throw FlxException("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::get_realization_1", ssV.str() );
    }
  } else {
    std::ostringstream ssV;
    ssV << "The requested random field has not yet been assembled.";
    throw FlxException("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::get_realization_2", ssV.str() );
  }
}

const tdouble FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::get_error(const int errMeasure) const
{
  if (errMeasure==1) {
    tdouble t = ZERO;
    for (tuint i=0;i<DIM;i=i+2) {	// i+1 is odd
      t+=pow2(((*Eigenvalues)[i])*(((*alpha_i)[i] * cos((*omega_i)[i]*domain_l*(*LX)))));
    }
    for (tuint i=1;i<DIM;i=i+2) {	// i+1 is even
      t+=pow2(((*Eigenvalues)[i])*(((*alpha_i)[i] * sin((*omega_i)[i]*domain_l*(*LX)))));
    }
    return ONE-t;
  } else if (errMeasure==2) {
    // refine iVec
      flxElement_KL_1D* curElKL = dynamic_cast<flxElement_KL_1D*>(Current_Element_KL);
      #if FLX_DEBUG
	if (curElKL==NULL) {
	  throw FlxException_Crude("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::get_error_1");
	}
      #endif
      const std::vector<tdouble> &iVecR = curElKL->get_IntervalVec_err2(0);
    // get Gauss-points
      const tuint GN = GI->pDegree2GPs(curElKL->get_pOcovOrder());		// of course this won't be an exact integration
      const tdouble* GP = GI->get_GP(GN);
      const tdouble* GW = GI->get_GW(GN);
    return get_error_cov(iVecR,GN,GP,GW);
  } else {
    std::ostringstream ssV;
    ssV << "Unknown error measure (" << errMeasure << ")";
    throw FlxException("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::get_error_2", ssV.str() );
  }
}

const tdouble FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::get_error_cov(const std::vector< tdouble >& iVec, const tuint GN, const tdouble* GP, const tdouble* GW) const
{
  // precompute some coefficients
    flxVec tr = *Eigenvalues;
    for (tuint i=0;i<DIM;++i) { tr[i] *= tr[i] * pow2((*alpha_i)[i]); }
    for (tuint i=0;i<DIM;i=i+2) {	// i+1 is odd
      tr[i] *= cos((*omega_i)[i]*(*LX)*domain_l);
    }
    for (tuint i=1;i<DIM;i=i+2) {	// i+1 is odd
      tr[i] *= sin((*omega_i)[i]*(*LX)*domain_l);
    }
  // integral rho(x,x') over \Omega
    tdouble tt = corrL*(2.0 - exp(-ONE*(*LX+ONE)*domain_l/corrL) - exp((*LX-ONE)*domain_l/corrL) );
  // integration (staggered Gaussian)
    tdouble dx,lx,r,d=ZERO;
    const tdouble lxT = *LX;
    register tuint j,k;
    register tdouble dd;
    for (tuint i=1;i<iVec.size();++i) {
      dx = (iVec[i]-iVec[i-1])/2.0;
      r = ZERO;
      for (j=0;j<GN;++j) {
	lx = (GP[j]+ONE)*dx+iVec[i-1];
	etkl->update_globalConst(lx);
	dd = exp(-1*(fabs(lxT-(*LX))*domain_l)/corrL);
	for (k=0;k<DIM;k=k+2) {
	  dd -= tr[k] * cos((*omega_i)[k]*(*LX)*domain_l);
	}
	for (k=1;k<DIM;k=k+2) {
	  dd -= tr[k] * sin((*omega_i)[k]*(*LX)*domain_l);
	}
	r+= fabs(dd) * GW[j];
      }
      r*=dx;
      d+=r;
    }
    d*=domain_l;
    *LX = lxT;
    return d/tt;
}

const tdouble FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::get_domain_error_el(const int errMeasure) const
{
  #if FLX_DEBUG
  if (Current_Element_KL) {
  #endif
    if (errMeasure==1 || errMeasure==3) {
      tdouble t = ZERO;
      for (tuint i=0;i<DIM;++i) {
	t+=pow2(((*Eigenvalues)[i]));
      }
      return (domain_l*2.0-t);
    } else if (errMeasure == 2) {
      flxElement_KL_1D* curElKL = dynamic_cast<flxElement_KL_1D*>(Current_Element_KL);
      #if FLX_DEBUG
	if (curElKL==NULL) {
	  throw FlxException_Crude("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::get_error_1");
	}
      #endif
      // refine inner interval vector
	const std::vector<tdouble> &iVecI = curElKL->get_IntervalVec_err2(0);
      // refine outer interval vector
	const std::vector<tdouble> &iVecO = curElKL->get_IntervalVec_err2(2);
	const size_t iVecOs = iVecO.size();
      // get Gauss-points (for inner AND outer integral)
	const tuint GN = GI->pDegree2GPs(curElKL->get_pOcovOrder());		// of course this won't be an exact integration
	const tdouble* GP = GI->get_GP(GN);
	const tdouble* GW = GI->get_GW(GN);
      // perform the integration
	tdouble res=ZERO, dx, r, lx;
	register tuint j;
	for (tuint i=1;i<iVecOs;++i) {
	  dx = (iVecO[i]-iVecO[i-1])/2.0;
	  r = ZERO;
	  for (j=0;j<GN;++j) {
	    lx = (GP[j]+ONE)*dx+iVecO[i-1];
	    etkl->update_globalConst(lx);
	    r += get_error_cov(iVecI,GN,GP,GW)*GW[j];
	  }
	  r*=dx;
	  res+=r;
	}
	res*=domain_l;
      return res;
    } else {
      std::ostringstream ssV;
      ssV << "Unknown error measure (" << errMeasure << ").";
      throw FlxException("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::get_domain_error_el_1", ssV.str() );
    }
  #if FLX_DEBUG
  } else {
    std::ostringstream ssV;
    ssV << "Out of domain of definiton of the random field";
    throw FlxException("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::get_domain_error_el_2", ssV.str() );
  }
  #endif
}

void FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::plot_el(std::ostream& sout, FlxMtxConstFun* ppdim, const int errMeasure, const int prec, const int fixW, const bool checkTOL) const
{
  #if FLX_DEBUG
  if (Current_Element_KL!=NULL) {
  #endif
    iVec ppdim_help = ConstMtxBox->get_Vec_cast2tuint(ppdim->eval(),true);
    if (ppdim_help.size()<1) {
      std::ostringstream ssV;
      ssV << "The dimension of the vector has to be at least '1'.";
      throw FlxException("flxElement_KL_1D::get_PlotPoints", ssV.str() );
    }
    const tdouble d = 2.0/ppdim_help[0];
    const tdouble DU = d/2+ONE;
    for (tdouble i=-ONE;i<=DU;i+=d) {
      etkl->update_globalConst(i);
      sout << GlobalVar.Double2String(*GX,false,prec,fixW) << ' ' << GlobalVar.Double2String(*GY,false,prec,fixW) << ' ' << GlobalVar.Double2String(*GZ,false,prec,fixW) << ' ';
      sout << GlobalVar.Double2String(get_error(errMeasure),checkTOL,prec,fixW);
      for (tuint j=0;j<DIM;++j) {
	if (j%2==0) {
	  sout << ' ' << GlobalVar.Double2String((((*alpha_i)[j] * cos((*omega_i)[j]*domain_l*(*LX)))),checkTOL,prec,fixW);
	} else {
	  sout << ' ' << GlobalVar.Double2String((((*alpha_i)[j] * sin((*omega_i)[j]*domain_l*(*LX)))),checkTOL,prec,fixW);
	}
      }
      sout << std::endl;
    }
    sout << std::endl;
  #if FLX_DEBUG
  } else {
    std::ostringstream ssV;
    ssV << "Out of domain of definiton of the random field";
    throw FlxException("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::plot_el_1", ssV.str() );
  }
  #endif
}

void FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::plotcov_el(std::ostream& sout, const flxPoint& pp, FlxMtxConstFun* ppdim, const flxVec& tr, const int prec, const int fixW) const
{
  #if FLX_DEBUG
  if (Current_Element_KL) {
  #endif  
    iVec ppdim_help = ConstMtxBox->get_Vec_cast2tuint(ppdim->eval(),true);
    if (ppdim_help.size()<1) {
      std::ostringstream ssV;
      ssV << "The dimension of the vector has to be at least '1'.";
      throw FlxException("flxElement_KL_1D::get_PlotPoints", ssV.str() );
    }
    const tdouble d = 2.0/ppdim_help[0];
    *GX2 = pp.get_x();
    *GY2 = pp.get_y();
    *GZ2 = pp.get_z();
    const tdouble DU = d/2+ONE;
    for (tdouble i=-ONE;i<=DU;i+=d) {
      etkl->update_globalConst(i);
      *DELTAX = fabs(*GX2-*GX);
      *DELTAY = fabs(*GY2-*GY);
      *DELTAZ = fabs(*GZ2-*GZ);
      *DELTAP = sqrt(pow2(*DELTAX)+pow2(*DELTAY)+pow2(*DELTAZ));
      sout << GlobalVar.Double2String(*GX,false,prec,fixW) << ' ' << GlobalVar.Double2String(*GY,false,prec,fixW) << ' ' << GlobalVar.Double2String(*GZ,false,prec,fixW) << ' ';
      tdouble t = ZERO;
      for (tuint i=0;i<DIM;i=i+2) {	// i+1 is odd
	t+=tr[i]*sD*((*alpha_i)[i] * cos((*omega_i)[i]*domain_l*(*LX)));
      }
      for (tuint i=1;i<DIM;i=i+2) {	// i+1 is even
	t+=tr[i]*sD*((*alpha_i)[i] * sin((*omega_i)[i]*domain_l*(*LX)));
      }
      const tdouble tt = pow2(sD) * exp(-(*DELTAP)/corrL);
      sout << GlobalVar.Double2String(t,false,prec,fixW) << ' ' << GlobalVar.Double2String(tt,false,prec,fixW) << ' ' << GlobalVar.Double2String(tt-t,false,prec,fixW);
      sout << std::endl;
    }
    sout << std::endl;
  #if FLX_DEBUG
  } else {
    std::ostringstream ssV;
    ssV << "Out of domain of definiton of the random field";
    throw FlxException("FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::plotcov_el_1", ssV.str() );
  }
  #endif
}

void FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::print_para(std::ostream& sout) const
{
  sout << "Gaussian random field (Analytical-KL-expansion; exponential autocorrelation function)";
}

FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D(tuint RF_index, FlxFunction* mu, tdouble sD, tdouble corrL)
: FlxRandomField_base(RF_index), mu(mu), sD(sD), corrL(corrL),domain_l(ONE),alpha_i(NULL),omega_i(NULL),etkl(NULL)
{

}

FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D::~FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D()
{
  delete mu;
  delete alpha_i;
  delete omega_i;
}

FlxRFBox::FlxRFBox()
{
  flxElement_base::set_RFbox(this);
  flxEdgeList::set_RFbox(this);
  flxFaceList::set_RFbox(this);
  flxBoxList::set_RFbox(this);
}

FlxRFBox::~FlxRFBox()
{
  for (std::map<tuint, FlxRandomField_base*>::iterator pos = boxM.begin(); pos != boxM.end(); ++pos) {
    delete pos->second;
  }
  if (FEMbox_get()) {
    delete FEMbox_get();
    FEMbox_get() = NULL;
  }
}


void FlxRFBox::set_Current_Element_KL(flxDomain_base* El)
{
  FlxRandomField_base::set_Current_Element(El);
  for (std::map<tuint, FlxRandomField_base*>::iterator pos = boxM.begin(); pos != boxM.end(); ++pos) {
    pos->second->set_Current_Element_KL();
  }
}

void FlxRFBox::insert(const tuint index, FlxRandomField_base* value)
{
  std::pair<tuint, FlxRandomField_base*>Element(index, value);
  if ( ! boxM.insert(Element).second ) {;
    delete value;
    std::ostringstream ssV;
    ssV << "Random field '" << index << "' has already been defined.";
    throw FlxException("FlxRFBox::insert_1", ssV.str() );
  }
  GlobalVar.slog(3) << "Random field: defined new random field with index '" << index << "'." << std::endl;
  GlobalVar.slog(4) << "  Type of RF: ";
  value->print_para(GlobalVar.slog(4));
  GlobalVar.slog(4) << std::endl;
}

FlxRandomField_base* FlxRFBox::get(const tuint index, const bool errSerious)
{
  std::map<tuint, FlxRandomField_base*>::iterator pos = boxM.find(index);
  if ( pos != boxM.end() ) {
    return pos->second;
  } else {
    std::ostringstream ssV;
    ssV << "Random field with index '" << index << "' has not been defined.";
    FlxError(errSerious, "FlxRFBox::get", ssV.str() ); return NULL; // dummy-return
  }
}

std::string FlxRF_Generator_base::get_rft(flxElement_KL_base::rftype rft)
{
  switch(rft) {
    case flxElement_KL_base::rfKLfemGauss:
      return "kl_fem_gauss";
      break;
    case flxElement_KL_base::rfKLfemGaussProj:
      return "kl_fem_gauss_proj";
      break;
    case flxElement_KL_base::rfColloGauss:
      return "kl_collo_gauss";
      break;
    case flxElement_KL_base::rfKLAnaExpCorrGauss1D:
      return "kl_analytical_expcorr_gauss_1d";
      break;
    case flxElement_KL_base::rfEOLEgauss:
      return "kl_eole_gauss";
      break;
    default:
      return "";
  }
}

flxElement_KL_base::rftype FlxRF_Generator_base::get_rft(std::string strrft, bool errSerious)
{
  std::transform(strrft.begin(), strrft.end(), strrft.begin(), (int(*)(int)) std::tolower);
  if (strrft == "kl_fem_gauss" ) {
    return flxElement_KL_base::rfKLfemGauss;
  } else if (strrft == "kl_fem_gauss_proj") {
    return flxElement_KL_base::rfKLfemGaussProj;
  } else if (strrft == "kl_collo_gauss") {
    return flxElement_KL_base::rfColloGauss;
  } else if (strrft == "kl_analytical_expcorr_gauss_1d" ) {
    return flxElement_KL_base::rfKLAnaExpCorrGauss1D;
  } else if (strrft == "kl_eole_gauss") {
    return flxElement_KL_base::rfEOLEgauss;
  } else {
    std::ostringstream ssV;
    ssV << "Unkown type of random field '" << strrft << "'.";
    FlxError(errSerious,"FlxRF_Generator_base::get_rft", ssV.str() ); return flxElement_KL_base::rfKLfemGauss; // dummy-return
  }
}

FlxRF_Generator_base* FlxRF_Generator_base::createRF(flxElement_KL_base::rftype rft)
{
  switch(rft) {
    case flxElement_KL_base::rfKLfemGauss:
      return new FlxRF_Generator_KL_FEM_Gauss();
      break;
    case flxElement_KL_base::rfKLfemGaussProj:
      return new FlxRF_Generator_KL_FEM_Gauss_proj();
      break;
    case flxElement_KL_base::rfColloGauss:
      return new FlxRF_Generator_KL_FEM_Gauss_Collo();
      break;
    case flxElement_KL_base::rfKLAnaExpCorrGauss1D:
      return new FlxRF_Generator_KL_Analytical_ExpCorr_Gauss_1D();
      break;
    case flxElement_KL_base::rfEOLEgauss:
      return new FlxRF_Generator_EOLE_Gauss();
      break;
    default:
      std::ostringstream ssV;
      ssV << "ERROR.";
      throw FlxException("FlxRF_Generator_base::createRF", ssV.str() );
  }
}

FlxRF_Generator_KL_FEM_Gauss::FlxRF_Generator_KL_FEM_Gauss()
:mu(NULL),sD(NULL),rho(NULL),evtype(NULL)
{
  try {
    reader->getWord("mu");
    mu = new FlxFunction(funReader);
    reader->getWord("sd");
    sD = new FlxFunction(funReader);
    reader->getWord("rhocor");
    rho = new FlxFunction(funReader);
    if (reader->whatIsNextString(6,true) == "evtype") {
      reader->getWord("evtype");
      evtype = new FlxFunction(funReader);
    } else {
      evtype = new FlxFunction(new FunNumber(ONE));
    }
  } catch (FlxException &e) {
    FLXMSG("FlxRF_Generator_KL_FEM_Gauss::FlxRF_Generator_KL_FEM_Gauss",1);
    if (mu) delete mu;
    if (sD) delete sD;
    if (rho) delete rho;
    if (evtype) delete evtype;
    throw;
  }
}

FlxRF_Generator_KL_FEM_Gauss::~FlxRF_Generator_KL_FEM_Gauss()
{
  delete mu; 
  delete sD; 
  delete rho;
  delete evtype;
}

FlxRandomField_base* FlxRF_Generator_KL_FEM_Gauss::generate_RF(tuint RF_index, bool errSerious)
{
  return new FlxRandomField_KL_FEM_Gauss(RF_index, new FlxFunction(*mu),new FlxFunction(*sD),new FlxFunction(*rho),evtype->cast2int());
}

FlxRF_Generator_KL_FEM_Gauss_proj::FlxRF_Generator_KL_FEM_Gauss_proj()
{
  if (reader->whatIsNextString(7,true) == "projh12") {
    reader->getWord("projh12");
    proj_h12 = reader->getBool(true);
  } else {
    proj_h12 = true;
  }
  if (reader->whatIsNextString(10,true) == "solve_full") {
    reader->getWord("solve_full");
    solve_full = reader->getBool(true);
  } else {
    solve_full = false;
  }
}

FlxRandomField_base* FlxRF_Generator_KL_FEM_Gauss_proj::generate_RF(tuint RF_index, bool errSerious)
{
  return new FlxRandomField_KL_FEM_Gauss_proj(RF_index, new FlxFunction(*mu),new FlxFunction(*sD),new FlxFunction(*rho),evtype->cast2int(),proj_h12,solve_full);
}

FlxRandomField_base* FlxRF_Generator_KL_FEM_Gauss_Collo::generate_RF(tuint RF_index, bool errSerious)
{
  return new FlxRandomField_KL_Collo_Gauss(RF_index, new FlxFunction(*mu),new FlxFunction(*sD),new FlxFunction(*rho),evtype->cast2int());
}

FlxRandomField_base* FlxRF_Generator_EOLE_Gauss::generate_RF(tuint RF_index, bool errSerious)
{
  return new FlxRandomField_EOLE_Gauss(RF_index, new FlxFunction(*mu),new FlxFunction(*sD),new FlxFunction(*rho),evtype->cast2int());
}

FlxRF_Generator_KL_Analytical_ExpCorr_Gauss_1D::FlxRF_Generator_KL_Analytical_ExpCorr_Gauss_1D()
{
  reader->getWord("mu");
  mu = new FlxFunction(funReader);
  reader->getWord("sd");
  sD = new FlxFunction(funReader);
  reader->getWord("corrlength");
  corrL = new FlxFunction(funReader);
}

FlxRandomField_base* FlxRF_Generator_KL_Analytical_ExpCorr_Gauss_1D::generate_RF(tuint RF_index, bool errSerious)
{
  if (ConstantBox->dependOn_GlobalSpatialVar(sD) || ConstantBox->dependOn_GlobalSpatialVar(corrL) ) {
    std::ostringstream ssV;
    ssV << "The standard deviation or the correlation length must not depend on spatial global variables.";
    FlxError(errSerious, "FlxRF_Generator_KL_Analytical_ExpCorr_Gauss_1D::generate_RF_1", ssV.str() );
  }
  tdouble sdt = sD->cast2positive(errSerious);
  tdouble clt = corrL->cast2positive(errSerious);
  return new FlxRandomField_KL_Analytical_ExpCorr_Gauss_1D(RF_index, new FlxFunction(*mu),sdt,clt);
}

FlxRFsolveProp::FlxRFsolveProp(bool errSerious)
:dim(NULL),octaveOutput(false),calcerr(NULL),file(NULL), load(true)
{
  try {
    bool b=true;
    do {
      if (reader->whatIsNextString(3,true) == "dim") {
	reader->getWord("dim",false);
	reader->getChar('=',false);
	if (dim) {
	  std::ostringstream ssV;
	  ssV << "'dim' has already been defined.";
	  throw FlxException("FlxRFsolveProp::FlxRFsolveProp_1", ssV.str() );
	}
	dim = new FlxFunction(funReader,false);
      } else if (reader->whatIsNextString(12,true) == "octaveoutput") {
	reader->getWord("octaveoutput",false);
	reader->getChar('=',false);
	octaveOutput = reader->getBool(false);
      } else if (reader->whatIsNextString(7,true) == "calcerr") {
	reader->getWord("calcerr",false);
	reader->getChar('=',false);
	if (calcerr) {
	  std::ostringstream ssV;
	  ssV << "'calcerr' has already been defined.";
	  throw FlxException("FlxRFsolveProp::FlxRFsolveProp_2", ssV.str() );
	}
	calcerr = new FlxFunction(funReader,false);
      } else if (reader->whatIsNextString(4,true) == "load") {
	reader->getWord("load",false);
	reader->getChar('=',false);
	if (file) {
	  std::ostringstream ssV;
	  ssV << "'load' or 'store' have already been defined.";
	  throw FlxException("FlxRFsolveProp::FlxRFsolveProp_3", ssV.str() );
	}
	file = new FlxString(false,true);
	load = true;
      } else if (reader->whatIsNextString(5,true) == "store") {
	reader->getWord("store",false);
	reader->getChar('=',false);
	if (file) {
	  std::ostringstream ssV;
	  ssV << "'load' or 'store' have already been defined.";
	  throw FlxException("FlxRFsolveProp::FlxRFsolveProp_3", ssV.str() );
	}
	file = new FlxString(false,true);
	load = false;
      } else {
	b=false;
      }
    } while (b);
  } catch (FlxException &e) {
    FLXMSG("FlxRFsolveProp::FlxRFsolveProp_5",1);
    if (dim) {
      delete dim;
      dim=NULL;
    }
    if (calcerr) {
      delete calcerr;
      calcerr=NULL;
    }
    throw;
  }
}

FlxRFsolveProp::~FlxRFsolveProp()
{
  if (dim) delete dim;
  if (calcerr) delete calcerr;
  if (file) delete file;
}

const tuint FlxRFsolveProp::get_dim()
{
  if (dim) {
    return dim->cast2tuint();
  } else {
    std::ostringstream ssV;
    ssV << "'dim' has not been defined";
    throw FlxException("FlxRFsolveProp::get_dim", ssV.str() );
  }
}

const bool FlxRFsolveProp::get_calcerr()
{
  if (calcerr) {
    return calcerr->cast2int();
  } else {
    return true;
  }
}

const std::string FlxRFsolveProp::get_file_name()
{
  if (file) {
    return file->eval(false);
  } else {
    throw FlxException_Crude("FlxRFsolveProp::get_file_name");
  }
}







