/* 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 "flxfem_geometry.h"
#include "flxrandomFEM.h"


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


tdouble *flxDomain_base::LX = NULL;
tuint flxDomain_base::countID = 0;
flxVertexList* flxVertex::vl = NULL;
flxVertexList* flxEdgeList::vl = NULL;
tdouble *flxDOFe::LX = NULL;
flxEdgeList* flxEdge::egl = NULL;
#if FLX_DEBUG
  int flxVertexList::instance = 0;
  int flxEdgeList::instance = 0;
  int flxFaceList::instance = 0;
  int flxBoxList::instance = 0;
#endif
FlxRFBox *flxEdgeList::RFbox = NULL;
tdouble *flxDOFf::LX = NULL;
flxFaceList* flxFace::fcl = NULL;
flxFEM* flxFace::FEM = NULL;
flxEdgeList* flxFaceList::el = NULL;
FlxRFBox *flxFaceList::RFbox = NULL;
flxFaceList* flxBoxList::fl = NULL;
FlxRFBox *flxBoxList::RFbox = NULL;

flxDOFn::flxDOFn(const FlxDof::dof dofDir, const FlxDof::dof_fixed_state state, FlxFunction* state_valueV, flxDOFnPtr& start, flxVertexList& vl)
: next(NULL),dofDir(dofDir),state(state),DOFindex(0),state_value(state_valueV)
{
  // check if everything makes sense
    #if FLX_DEBUG
      if (state_value==NULL && !(state==FlxDof::dof_free||state==FlxDof::dof_fixed) ) {
	throw FlxException_Crude("flxDOFn::flxDOFn_1");
      }
    #endif
  // find correct position in list
    if (start) {
      start = start->link2list(this);
    } else {
      start = this;
    }
  // link DOF if it is a linked DOF
    if (state==FlxDof::dof_link) {
      const tnlong l = state_valueV->cast2tnlongW0();	// vertex index
      delete state_valueV;
      flxVertex& vtx = *(vl.get(l,true));
      flxDOFn* d2l = vtx.find_dof(dofDir,true);
      if (d2l->state == FlxDof::dof_link) {
	throw FlxException("flxDOFn::flxDOFn_2","A DOF must never be linked to another DOF which is itself linked.");
      }
      state_value = d2l;
    }
}

flxDOFn::~flxDOFn()
{
  if (next) delete next;
  if (state_value) {
    switch (state) {
      case FlxDof::dof_free:
      case FlxDof::dof_fixed:
	#if FLX_DEBUG
	  throw FlxException_Crude("flxDOFn::~flxDOFn");
	#endif
	break;
      case FlxDof::dof_spring:
      case FlxDof::dof_predispl:
      {
	FlxFunction* f = (FlxFunction*)state_value;
	delete f;
	break;
      }
      case FlxDof::dof_link:
	break;
    }
  }
}

flxDOFn* flxDOFn::link2list(flxDOFn* el2link)
{
  if (el2link->dofDir < this->dofDir) {
    el2link->next = this;
    return el2link;
  } 
  #if FLX_DEBUG
    else if (el2link->dofDir == this->dofDir) {
      throw FlxException_Crude("flxDOFn::link2list");
    } 
  #endif
  if (this->next == NULL) {
    this->next = el2link;
  } else {
    this->next = next->link2list(el2link);
  }
  return this;
}

flxDOFn* flxDOFn::find_dof(const FlxDof::dof dofDirV)
{
  if (dofDir==dofDirV) {
    return this;
  } else if (dofDir<dofDirV) {
    if (next) {
      return next->find_dof(dofDirV);
    }
  }
  return NULL;
}

void flxDOFn::print_dof(std::ostream& sout, const flxFEM& FEM) const
{
  const tdouble u = FEM.StiffnessMtx->get_U(get_DOFindex());
  tdouble f = ZERO;
  switch (state) {
    case FlxDof::dof_spring:
    case FlxDof::dof_fixed:
    case FlxDof::dof_predispl:
      f = FEM.StiffnessMtx->get_F(this);
      break;
    default:
      break;
  };
  if ( (fabs(u)+fabs(f)) > GlobalVar.TOL()) {
    sout << "  " << FlxDof::get_dof(dofDir) << "=" << GlobalVar.Double2String(u); 
    if (fabs(f) > GlobalVar.TOL()) {
      sout << "\tf=" << GlobalVar.Double2String(f);
    }
    switch (state) {
      case FlxDof::dof_free:
	break;
      case FlxDof::dof_spring:
	sout << "\t(spring)";
	break;
      case FlxDof::dof_fixed:
	sout << "\t(fixed)";
	break;
      case FlxDof::dof_predispl:
	sout << "\t(predispl)";
	break;
      case FlxDof::dof_link:
	sout << "\t(link)";
	break;
    };
    sout << std::endl;
  }
  if (next) next->print_dof(sout,FEM);
}

void flxDOFn::get_mappingVector(tnlong* mapVec, bool* hasMap)
{
  mapVec[dofDir]=DOFindex;
  if (hasMap) hasMap[dofDir]=true;
  if (next) next->get_mappingVector(mapVec,hasMap);
}

void flxDOFn::countDOF(tnlong& DofA, tnlong& FixA, tnlong& preDispA, tnlong& DofSum) const
{
  switch (state) {
    case FlxDof::dof_free:
    case FlxDof::dof_spring:
      ++DofA;
      ++DofSum;
      break;
    case FlxDof::dof_fixed:
      ++FixA;
      ++DofSum;
      break;
    case FlxDof::dof_predispl:
      ++FixA;
      ++preDispA;
      ++DofSum;
      break;
    case FlxDof::dof_link:
      break;
  }
  if (next) next->countDOF(DofA,FixA,preDispA,DofSum);
}

void flxDOFn::assembleDOF(tnlong& DofIndexFree, tnlong& FixPreIndex, tnlong& FixZeroIndex)
{
  switch (state) {
    case FlxDof::dof_free:
    case FlxDof::dof_spring:
      DOFindex = DofIndexFree;
      ++DofIndexFree;
      break;
    case FlxDof::dof_fixed:
      DOFindex = FixZeroIndex;
      ++FixZeroIndex;
      break;
    case FlxDof::dof_predispl:
      DOFindex = FixPreIndex;
      ++FixPreIndex;
      break;
    case FlxDof::dof_link:
      break;
  }
  if (next) next->assembleDOF(DofIndexFree,FixPreIndex,FixZeroIndex);
}

void flxDOFn::assembleStf(flxStiffnessMtxBase& stfmtx)
{
  switch (state) {
    case FlxDof::dof_free:
      break;
    case FlxDof::dof_spring:
      stfmtx.Stf_set_value(DOFindex,DOFindex,((FlxFunction*)state_value)->calc());
      break;
    case FlxDof::dof_fixed:
      break;
    case FlxDof::dof_predispl:
      stfmtx.Stf_set_predispl_value(DOFindex,((FlxFunction*)state_value)->calc());
      break;
    case FlxDof::dof_link:
    {
      const flxDOFn* ld = (const flxDOFn*)state_value;
      DOFindex = ld->DOFindex;
      break;
    }
  }
  if (next!=NULL) next->assembleStf(stfmtx);
}

const tdouble flxDOFn::get_state_value() const
{
  switch (state) {
    case FlxDof::dof_free:
    case FlxDof::dof_fixed:
      return ZERO;
    case FlxDof::dof_spring:
    case FlxDof::dof_predispl:
    {
      FlxFunction* f = (FlxFunction*)state_value;
      return f->calc();
    }
    case FlxDof::dof_link:
      return ZERO;
    default:
      #if FLX_DEBUG
	throw FlxException_Crude("flxDOFn::get_state_value");
      #endif
      return ZERO;
  }
}

flxDOFe::flxDOFe(const FlxDof::dof dofDir, const FlxDof::dof_fixed_state state, const tuint DOFpOrderV, FlxFunDeg* state_valueDV, flxDOFePtr& start, const flxEdgeList& egl)
: next(NULL),dofDir(dofDir),state(state),internalDOF(true),DOFpOrder(DOFpOrderV),DOFindex_internal(0),DOFindex(0),state_valueD(state_valueDV)
{
  // check if everything makes sense
    #if FLX_DEBUG
      if (state_valueD==NULL && !(state==FlxDof::dof_free||state==FlxDof::dof_fixed) ) {
	throw FlxException_Crude("flxDOFe::flxDOFe_1");
      }
    #endif
    if (DOFpOrder<1) {
      throw FlxException("flxDOFe::flxDOFe_2","The order of an edge-DOF has to be at least '1'.");
    }
    // make sure that enough shape functions are available
      hLP_check_order(DOFpOrder);
  // find correct position in list
    if (start) {
      start = start->link2list(this);
    } else {
      start = this;
    }
  // link DOF if it is a linked DOF
    if (state==FlxDof::dof_link) {
      const tuint l = state_valueDV->get_fun()->cast2tuintW0();	// edge index
      delete state_valueDV;
      flxEdge& edg_Link = *(egl.get(l,true));
      flxDOFe* d2l_Link = edg_Link.find_dof(dofDir,true);
      if (d2l_Link->state == FlxDof::dof_link) {
	throw FlxException("flxDOFe::flxDOFe_3","A DOF must never be linked to another DOF which is itself linked.");
      }
      state_valueD = d2l_Link;
      internalDOF = false;
      d2l_Link->internalDOF = false;
      DOFpOrder = d2l_Link->DOFpOrder;
    }
}

flxDOFe::~flxDOFe()
{
  if (next) delete next;
  if (state_valueD) {
    switch (state) {
      case FlxDof::dof_free:
      case FlxDof::dof_fixed:
	#if FLX_DEBUG
	  throw FlxException_Crude("flxDOFn::~flxDOFn");
	#endif
	break;
      case FlxDof::dof_spring:
      case FlxDof::dof_predispl:
      {
	FlxFunDeg* f = (FlxFunDeg*)state_valueD;
	delete f;
	break;
      }
      case FlxDof::dof_link:
	break;
    }
  }
}

flxDOFe* flxDOFe::link2list(flxDOFe* el2link)
{
  if (el2link->dofDir < this->dofDir) {
    el2link->next = this;
    return el2link;
  } 
  #if FLX_DEBUG
    else if (el2link->dofDir == this->dofDir) {
      throw FlxException_Crude("flxDOFe::link2list");
    } 
  #endif
  if (this->next == NULL) {
    this->next = el2link;
  } else {
    this->next = next->link2list(el2link);
  }
  return this;
}

flxDOFe* flxDOFe::find_dof(const FlxDof::dof dofDirV)
{
  if (dofDir==dofDirV) {
    return this;
  } else if (dofDir<dofDirV) {
    if (next) {
      return next->find_dof(dofDirV);
    }
  }
  return NULL;
}

const bool flxDOFe::has_only_displDOFs() const
{
  if (next) return next->has_only_displDOFs();
  else return FlxDof::is_force(dofDir);
}

void flxDOFe::requires_nodal_DOF(const bool* DOFnMap, bool* reqMap) const
{
  const tuint startR = (FlxDof::is_force(dofDir))?0:3;
  const tuint startM = FlxDof::get_dir(dofDir);
  for (tuint i=0; i<3; ++i) {
    if (DOFnMap[startM*3+i]) {
      reqMap[startR+i] = true;
    }
  }
  if (next) next->requires_nodal_DOF(DOFnMap, reqMap);
}

void flxDOFe::countDOF(tnlong& DofA, tnlong& FixA, tnlong& preDispA, tnlong& DofSum, tuint& i_DofA, tuint& LinkDofA, const bool condenseDOF, const tuint Nintervals, const flxEdgeList& egl, const flxEdge& currentEdge)
{
  // if is linked dof -> update information (not allowed to change afterwards) AND check if link is valid
    if (state==FlxDof::dof_link) {
      const flxDOFe* d2l_link = (flxDOFe*)state_valueD;
      internalDOF = d2l_link->internalDOF;
      #if FLX_DEBUG
	if (internalDOF) throw FlxException_Crude("flxDOFe::countDOF_1");
      #endif
      DOFpOrder = d2l_link->DOFpOrder;
      // check if direction of the linked edges is the same
	const flxEdge &edg_link = *(egl.find(d2l_link,true));
	const flxEdge &edg_this = currentEdge;
	tdouble l1,lx1,ly1,lz1,l2,lx2,ly2,lz2;
	edg_link.get_vertex_1().get_point().get_d(edg_link.get_vertex_2().get_point(),lx1,ly1,lz1,l1);
	edg_this.get_vertex_1().get_point().get_d(edg_this.get_vertex_2().get_point(),lx2,ly2,lz2,l2);
	// check direction
	  if (lx1!=lx2||ly1!=ly2||lz1!=lz2||l1!=l2) {
	    std::ostringstream ssV;
	    ssV << "Edges to link have not the same direction.";
	    throw FlxException("flxDOFe::countDOF_2", ssV.str() );
	  }
	// TODO make sure that : same orientation
	// check geometry
	  const tdouble* v1 = edg_link.get_geometry();
	  const tdouble* v2 = edg_this.get_geometry();
	  if (edg_link.get_geo_Ninter()!=edg_this.get_geo_Ninter()) {
	    std::ostringstream ssV;
	    ssV << "Edges to link have not the same geometry.";
	    throw FlxException("flxDOFe::countDOF_3", ssV.str() );
	  }
	  const size_t N = edg_link.get_geo_Ninter();
	  for (tuint i=0;i<N;++i) {
	    if (v1[i]!=v2[i]) {
	      std::ostringstream ssV;
	      ssV << "Edges to link have not the same geometry.";
	      throw FlxException("flxDOFe::countDOF_4", ssV.str() );
	    }
	  }
	// link the involved nodal DOFs
	  const bool* DOFnMap = currentEdge.get_DOFnMap();
	  edg_this.get_vertex_1().link_DOFs(DOFnMap,dofDir,edg_link.get_vertex_1());
	  edg_this.get_vertex_2().link_DOFs(DOFnMap,dofDir,edg_link.get_vertex_2());
    }
  // make sure nodal degrees of freedom are fixed if edge is fixed AND predisplacements are continuous
    if (state==FlxDof::dof_fixed || state==FlxDof::dof_predispl) {
      internalDOF=false;
      const bool* DOFnMap = currentEdge.get_DOFnMap();
      *LX = -ONE;
      currentEdge.get_vertex_1().fix_DOFs(DOFnMap,dofDir,(state==FlxDof::dof_predispl)?(((FlxFunDeg*)state_valueD)->get_fun()):NULL);
      *LX = ONE;
      currentEdge.get_vertex_2().fix_DOFs(DOFnMap,dofDir,(state==FlxDof::dof_predispl)?(((FlxFunDeg*)state_valueD)->get_fun()):NULL);
    }
  // count the DOFs    
    const tuint ti = DOFpOrder*Nintervals-1;
    if (condenseDOF&&internalDOF) {
      i_DofA+=ti;
    } else {		// no internal DOFs
      switch (state) {
	case FlxDof::dof_free:
	case FlxDof::dof_spring:
	  DofA+=ti;
	  DofSum+=ti;
	  break;
	case FlxDof::dof_fixed:
	  FixA+=ti;
	  DofSum+=ti;
	  break;
	case FlxDof::dof_predispl:
	  FixA+=ti;
	  preDispA+=ti;
	  DofSum+=ti;
	  break;
	case FlxDof::dof_link:
	  LinkDofA+=ti;
	  break;
      }
    }
  // deal with the next edge DOF in the list
    if (next) next->countDOF(DofA,FixA,preDispA,DofSum,i_DofA,LinkDofA,condenseDOF,Nintervals,egl,currentEdge);
}

void flxDOFe::assembleDOF(tnlong& DofIndexFree, tnlong& FixPreIndex, tnlong& FixZeroIndex, const bool condenseDOF, tuint& iDOF_external, tuint& iDOF_internal, const tuint Nintervals)
{
  const tuint ti = DOFpOrder*Nintervals-1;
  if (condenseDOF&&internalDOF) {	// internal DOFs
    DOFindex_internal = iDOF_internal;
    iDOF_internal+=ti;
  } else {				// external DOFs
    DOFindex_internal = iDOF_external;
    iDOF_external+=ti;
    switch (state) {
      case FlxDof::dof_free:
      case FlxDof::dof_spring:
	DOFindex = DofIndexFree;
	DofIndexFree+=ti;
	break;
      case FlxDof::dof_fixed:
	DOFindex = FixZeroIndex;
	FixZeroIndex+=ti;
	break;
      case FlxDof::dof_predispl:
	DOFindex = FixPreIndex;
	FixPreIndex+=ti;
	break;
      case FlxDof::dof_link:
	break;
    }
  }
  
  if (next) next->assembleDOF(DofIndexFree,FixPreIndex,FixZeroIndex,condenseDOF,iDOF_external,iDOF_internal,Nintervals);
}

void flxDOFe::assembleStf(flxEdge& edge, const tdouble* meshInterval, const tuint meshInterval_size)
{
  switch (state) {
    case FlxDof::dof_free:
      break;
    case FlxDof::dof_spring:
    {
      FlxFunDeg* fd = (FlxFunDeg*)state_valueD;
      // get Gauss information
	const tuint N = GI->pDegree2GPs(DOFpOrder*2+fd->get_deg());
	GI->check_GA(N);
	const tdouble* GP = GI->get_GP(N);
	const tdouble* GW = GI->get_GW(N);
      FlxFunction &k = *(fd->get_fun());
      flxVec t((DOFpOrder*DOFpOrder+3*DOFpOrder+2)/2);
      flxVec th(DOFpOrder+1);
      tdouble *tp = t.get_tmp_vptr();
      tdouble *thp = th.get_tmp_vptr();
      tdouble dx2,lxgm,fac;
      const tdouble Lh = edge.calc_length()/2.0;
      tuint c;
      for (tuint i=0;i<meshInterval_size;++i) {	// iterate over all sub-elements
	t.set_zero();
	dx2 = (meshInterval[i+1]-meshInterval[i])/2.0;
	lxgm = (meshInterval[i]+meshInterval[i+1])/2.0;
	// Gauss-integration
	  for (tuint p=0;p<N;++p) {
	    edge.update_globalConst(lxgm + dx2*GP[p]);
	    hLP_vec(th,GP[p]);
	    fac = GW[p]*k.calc();
	    c=0;
	    for (tuint j=0;j<DOFpOrder+1;++j) {
	      for (tuint k=0;k<=j;++k) {
		tp[c++] += fac*thp[j]*thp[k];
	      }
	    }
	  }
	  t*=Lh*dx2;
	// map to local stiffness matrix
	  edge.update_local_StfMtx(i,this,0,this,0,tp[0]);
	  edge.update_local_StfMtx(i,this,1,this,0,tp[1]);
	  edge.update_local_StfMtx(i,this,1,this,1,tp[2]);
	  for (tuint j=0;j<DOFpOrder-1;++j) {
	    edge.update_local_StfMtx(i,this,2+j,this,0,tp[(j*(j+5)+6)/2]);
	    edge.update_local_StfMtx(i,this,2+j,this,1,tp[(j*(j+5)+6)/2+1]);
	    edge.update_local_StfMtx(i,this,2+j,this,2,&tp[(j*(j+5)+6)/2+2],DOFpOrder-1);
	  }
      }
      break;
    }
    case FlxDof::dof_fixed:
      break;
    case FlxDof::dof_predispl:
      throw FlxException_NotImplemented("flxDOFe::assembleStf");
      break;
    case FlxDof::dof_link:
      flxDOFe* ld = (flxDOFe*)state_valueD;
      DOFindex = ld->DOFindex;
      DOFindex_internal = ld->DOFindex_internal;
      break;
  }
  if (next) next->assembleStf(edge,meshInterval,meshInterval_size);
}

const tuint flxDOFe::get_max_pOrder() const
{
  tuint pOm = 0;
  if (next)pOm = next->get_max_pOrder();
  if (DOFpOrder>pOm) pOm = DOFpOrder;
  return pOm;
}

void flxDOFe::set_Const(FlxConstantBox& ConstantBox)
{
  LX = ConstantBox.get("lx",true);
}

flxVertex::~flxVertex()
{
  if (dofList) delete dofList;
  if (loadList) delete loadList;
}

flxDOFn* flxVertex::find_dof(const FlxDof::dof dofDir, const bool do_create, const bool throwError)
{
  flxDOFn* dofF = NULL;
  if (dofList) {
    dofF = dofList->find_dof(dofDir);
  }
  if (dofF==NULL) {
    if (do_create) {
      if (vl->is_locked()) {
	throw FlxException_Crude("flxVertex::find_dof_1");
      }
      return new flxDOFn(dofDir,FlxDof::dof_free,NULL,dofList,*vl);
    } else {
      if (throwError) {
	std::ostringstream ssV;
	ssV << "node=" << vl->get_index(this) << "; DOF=" << FlxDof::get_dof(dofDir);
	throw FlxException("flxVertex::find_dof_2","Requested DOF does not exist.",ssV.str());
      } else {
	return NULL;
      }
    }
  }
  return dofF;
}

void flxVertex::insert_Nload(const FlxDof::dof ndof, FlxFunction* value, const FlxLoadCase& lc, const bool replace)
{
  // make sure the corresponding DOF exists
    find_dof(ndof,true);
  // find an already existing load ...
    flxNLoad* nl = NULL;
    if (loadList) {
      nl = loadList->find(ndof,lc);
    }
  if (nl) {	// load has already been defined
    if (replace) {
      if (nl->value != value) {
	delete nl->value;
	nl->value = value;
      }
      GlobalVar.slog(4) << "FEM: redefined (replaced) "; 
    } else {
      nl->value = new FlxFunction_Combine_Add(nl->value, value);
      value = nl->value;
      GlobalVar.slog(4) << "FEM: combined "; 
    }
  } else {	// load has not yet been defined
    new flxNLoad(ndof,value,lc,loadList);
    GlobalVar.slog(4) << "FEM: defined new "; 
  }
  GlobalVar.slog(4) << "nodal load (node=" << vl->get_index(this) << "; dof=" << FlxDof::get_dof(ndof) ;
  GlobalVar.slog(4) << "; lc=" << lc.get_index() << "; load=" << value->write() << ")" << std::endl;
}

void flxVertex::fix_DOFs(const bool* DOFnMap, const FlxDof::dof dofV, FlxFunction* preDispl)
{
  const bool isForce = FlxDof::is_force(dofV);
  const tuint dir = FlxDof::get_dir(dofV);
  tuint c=0, m=0;
  for (tuint i=0;i<3;++i) {
    if (DOFnMap[i+dir*3]) { 
      ++c;
      m = i;
    }
  }
  #if FLX_DEBUG
    if (c==0) {
      throw FlxException_Crude("flxDOFn::fix_DOFs_1");
    }
  #endif
  if (c==1) {
    flxDOFn *dn = find_dof(FlxDof::get_dof(FlxDof::get_dir(m),isForce),true);
    if (dn->state == FlxDof::dof_link) {	// find DOF which is linked to
      dn = (flxDOFn*)(dn->state_value);
    }
    if (preDispl) {
      switch (dn->state) {
	case FlxDof::dof_free:
	case FlxDof::dof_spring:
	  dn->state = FlxDof::dof_predispl;
	  if (dn->state_value) delete (FlxFunction*)dn->state_value;
	  dn->state_value = new FlxFunction(preDispl);
	  break;
	case FlxDof::dof_fixed:
	{
	  std::ostringstream ssV;
	  ssV << "Predisplacement cannot be set to zero.";
	  throw FlxException("flxDOFn::fix_DOFs_2", ssV.str() );
	  break;
	}
	case FlxDof::dof_predispl:
	  p.set_global();
	  if ( fabs(preDispl->calc()-((FlxFunction*)dn->state_value)->calc())>GlobalVar.TOL() ) {
	    std::ostringstream ssV;
	    ssV << "Mismatch in value of predisplacements.";
	    throw FlxException("flxDOFn::fix_DOFs_3", ssV.str() );
	  }
	  break;
	case FlxDof::dof_link:
	  throw FlxException_NotImplemented("flxDOFn::fix_DOFs_4");
      }
    } else {		// just fixed
      switch (dn->state) {
	case FlxDof::dof_free:
	case FlxDof::dof_spring:
	  dn->state = FlxDof::dof_fixed;
	  if (dn->state_value) delete (FlxFunction*)dn->state_value;
	  dn->state_value = NULL;
	  break;
	case FlxDof::dof_fixed:
	  break;
	case FlxDof::dof_predispl:
	  throw FlxException("flxDOFn::fix_DOFs_5", "Predisplacement cannot be set to zero." );
	  break;
	case FlxDof::dof_link:
	  throw FlxException_NotImplemented("flxDOFn::fix_DOFs_6");
      }
    }
  } else {
    if (preDispl) {
      throw FlxException_NotImplemented("flxDOFn::fix_DOFs_7");
    } else {
      for (tuint i=0;i<3;++i) {
	if (DOFnMap[i+dir*3]) { 
	  const flxDOFn *dn = find_dof(FlxDof::get_dof(FlxDof::get_dir(m),isForce),false,true);
	  if (dn->state == FlxDof::dof_link) {	// find DOF which is linked to
	    dn = (const flxDOFn*)(dn->state_value);
	  }
	  if ( dn->state!=FlxDof::dof_fixed ) {
	    throw FlxException_NotImplemented("flxDOFn::fix_DOFs_8");
	  }
	}
      }
    }
  }
}

void flxVertex::link_DOFs(const bool* DOFnMap, const FlxDof::dof dofV, flxVertex& vtx)
{
  if (&vtx == this) return;
  const bool isForce = FlxDof::is_force(dofV);
  const tuint dir = FlxDof::get_dir(dofV);
  tuint c=0, m=0;
  for (tuint i=0;i<3;++i) {
    if (DOFnMap[i+dir*3]) { 
      ++c;
      m = i;
    }
  }
  #if FLX_DEBUG
    if (c==0) {
      throw FlxException_Crude("flxVertex::link_DOFs_1");
    }
  #endif
  if (c==1) {
    // find the DOF which should be linked
      flxDOFn *dn2link = find_dof(FlxDof::get_dof(FlxDof::get_dir(m),isForce),true);
    // find the DOF which should be the reference of the link
      flxDOFn *dn2ref = vtx.find_dof(FlxDof::get_dof(FlxDof::get_dir(m),isForce),true);
    switch (dn2link->state) {
      case FlxDof::dof_free:
	if (dn2ref->state == FlxDof::dof_link) {
	  throw FlxException("flxVertex::link_DOFs_2","Cannot link a DOF to a link.");
	} else {
	  dn2link->state = FlxDof::dof_link;
	  dn2link->state_value = dn2ref;
	}
	break;
      case FlxDof::dof_spring:
	throw FlxException_NotImplemented("flxVertex::link_DOFs_3");
	break;
      case FlxDof::dof_fixed:
	if ( dn2ref->state == FlxDof::dof_fixed ) {
	  return;
	} else {
	  throw FlxException_NotImplemented("flxVertex::link_DOFs_4");
	}
	break;
      case FlxDof::dof_predispl:
	throw FlxException_NotImplemented("flxVertex::link_DOFs_5");
	break;
      case FlxDof::dof_link:
	if (dn2ref == (flxDOFn*)(dn2link->state_value)) {
	  return;
	} else {
	  throw FlxException("flxVertex::link_DOFs_6","The requested nodal DOF is already linked to another nodal DOF.");
	}
	break;
    }
  } else {	// c > 1
    throw FlxException_NotImplemented("flxVertex::link_DOFs_7");
  }
}

void flxVertex::get_mappingVector(tnlong* mapVec, bool* hasMap)
{
  if (hasMap) for (register tuint i=0;i<6;++i) { hasMap[i] = false; }
  if (dofList) dofList->get_mappingVector(mapVec,hasMap);
}

void flxVertex::print_dof(std::ostream& sout, const flxFEM& FEM) const
{
  if (dofList) dofList->print_dof(sout,FEM);
}

void flxVertex::assembleStf(flxStiffnessMtxBase& stfmtx)
{
  if (dofList) {
    p.set_global();
    dofList->assembleStf(stfmtx);
  }
}

void flxVertex::assembleForces(flxStiffnessMtxBase& stfmtx) const
{
  if (loadList) {
    p.set_global();
    loadList->assembleForces(stfmtx,dofList);
  }
}

flxVertexList::flxVertexList()
: locked(false) 
{
  flxVertex::vl = this;
  flxEdgeList::set_VertexList(this);
  #if FLX_DEBUG
    ++instance;
    if (instance>1) throw FlxException_Crude("flxVertexList::flxVertexList");
  #endif
}

flxVertexList::~flxVertexList()
{
  for (std::map<tnlong, flxVertex*>::iterator pos = box.begin(); pos != box.end(); ++pos) {
    delete pos->second;
  }
  #if FLX_DEBUG
    --instance;
  #endif
}

void flxVertexList::insert(const tnlong index, flxVertex* vertex, const bool errSerious)
{
  if (is_locked()) {
    delete vertex;
    FlxError(errSerious, "flxVertexList::insert_1", "The FEM-System is locked, no more nodes can be created." );
  }
  std::pair<tuint, flxVertex*>Element(index, vertex);
  if ( ! box.insert(Element).second ) {
    delete vertex;
    std::ostringstream ssV;
    ssV << "Node '" << index << "' has already been defined.";
    FlxError(errSerious, "flxVertexList::insert_2", ssV.str() );
  }
}

flxVertex* flxVertexList::get(const tuint index, const bool throwErr)
{
  std::map<tuint, flxVertex*>::iterator pos = box.find(index);
  if ( pos != box.end() ) {
    return pos->second;
  } else {
    if (throwErr) {
      std::ostringstream ssV;
      ssV << "Node '" << index << "' does not exist.";
      throw FlxException("flxVertexList::get", ssV.str() );
    } else {
      return NULL;
    }
  }
}

const tuint flxVertexList::get_index(const flxVertex*const node) const
{
  for (std::map<tuint, flxVertex*>::const_iterator pos = box.begin(); pos != box.end(); ++pos) {
    if (pos->second == node) {
      return pos->first;
    }
  }
  std::ostringstream ssV;
  ssV << "The given node is not in the list.";
  throw FlxException("flxVertexList::get_index", ssV.str() );
}

flxVertex* flxVertexList::find(const flxDOFn* DOF)
{
  const FlxDof::dof dofDir = DOF->get_dofDir();
  for (std::map<tuint, flxVertex*>::iterator pos = box.begin(); pos != box.end(); ++pos) {
    if ( pos->second->find_dof(dofDir,false,false) == DOF) return pos->second;
  }
  return NULL;
}

void flxVertexList::print(std::ostream& sout, const bool printSolution, const flxFEM* FEM) const
{
  for (std::map<tuint, flxVertex*>::const_iterator pos = box.begin(); pos != box.end(); ++pos) {
    sout << "Node " << pos->first << " " << pos->second->get_point() << std::endl;
    if (printSolution) pos->second->print_dof(sout,*FEM);
  }
}

void flxVertexList::countDOF(tnlong& DofA, tnlong& FixA, tnlong& preDispA, tnlong& DofSum)
{
  for (std::map<tnlong, flxVertex*>::const_iterator pos = box.begin(); pos != box.end(); ++pos) {
    pos->second->countDOF(DofA,FixA,preDispA,DofSum);
  }
  if (locked) {
    throw FlxException_Crude("flxVertexList::countDOF");
  }
  locked = true;
}

void flxVertexList::assembleDOF(tnlong& DofIndexFree, tnlong& FixPreIndex, tnlong& FixZeroIndex)
{
  for (std::map<tnlong, flxVertex*>::iterator pos = box.begin(); pos != box.end(); ++pos) {
    pos->second->assembleDOF(DofIndexFree,FixPreIndex,FixZeroIndex);
  }
}

void flxVertexList::assembleStf(flxStiffnessMtxBase& stfmtx)
{
  for (std::map<tnlong, flxVertex*>::iterator pos = box.begin(); pos != box.end(); ++pos) {
    pos->second->assembleStf(stfmtx);
  }
}

void flxVertexList::assembleForces(flxStiffnessMtxBase& stfmtx) const
{
  for (std::map<tnlong, flxVertex*>::const_iterator pos = box.begin(); pos != box.end(); ++pos) {
    pos->second->assembleForces(stfmtx);
  }
}

flxFEMrefine_1D::flxFEMrefine_1D(const flxFEMrefine_1D& ref)
:do_refine(ref.do_refine),do_loadrefine(ref.do_loadrefine),
minF(new FlxFunction(*(ref.minF))),maxF(new FlxFunction(*(ref.maxF))),mesh(ref.mesh)
{
  size_t N = ref.sections.size();
  for (size_t i=0;i<N;++i) {
    FlxFunction** fa = new FlxFunction*[3];
    for (int j=0;j<3;++j) {
     fa[j]=new FlxFunction(*(ref.sections[i][j]));
    }
    sections.push_back(fa);
  }
}

flxFEMrefine_1D& flxFEMrefine_1D::operator=(const flxFEMrefine_1D& ref)
{
  if (this!=&ref) {
    do_refine = ref.do_refine;
    do_loadrefine = ref.do_loadrefine;
    // minF
      if (minF!=NULL) delete minF;
      if (ref.minF==NULL) minF = NULL;
      else minF = new FlxFunction(*(ref.minF));
    // maxF
      if (maxF!=NULL) delete maxF;
      if (ref.maxF==NULL) maxF = NULL;
      else maxF = new FlxFunction(*(ref.maxF));
    mesh = ref.mesh;
    delete_sections();
    size_t N = ref.sections.size();
    for (size_t i=0;i<N;++i) {
      FlxFunction** fa = new FlxFunction*[3];
      for (int j=0;j<3;++j) {
      fa[j]=new FlxFunction(*(ref.sections[i][j]));
      }
      sections.push_back(fa);
    }
  }
  return *this;
}

flxFEMrefine_1D::flxFEMrefine_1D(bool do_refine, bool do_loadrefine, FlxFunction* minF, FlxFunction* maxF)
:do_refine(do_refine), do_loadrefine(do_loadrefine), minF(minF), maxF(maxF)
{
  // the two boundaries
    mesh.push_back(-ONE);
    mesh.push_back(ONE);
}

flxFEMrefine_1D::flxFEMrefine_1D(bool errSerious)
:do_refine(false),do_loadrefine(true),minF(new FlxFunction(new FunNumber(1e-2))), maxF(new FlxFunction(new FunNumber(ONE)))
{
  try {
  // the two boundaries
    mesh.push_back(-ONE);
    mesh.push_back(ONE);
  // reading from input
    reader->getChar('(',errSerious);
    do_refine = reader->getBool(errSerious);
    while (reader->whatIsNextChar()==',') {
      reader->getChar(',');
      const std::string strV = reader->getWord(true,errSerious);
      reader->getChar('=');
      if (strV=="min") {
	delete minF; minF = NULL;
	minF = new FlxFunction(funReader,errSerious);
      } else if (strV=="max") {
	delete maxF; maxF = NULL;
	maxF = new FlxFunction(funReader,errSerious);
      } else if (strV=="loadrefine") {
	do_loadrefine = reader->getBool(errSerious);
      } else if (strV=="section") {
	FlxFunction** fa = new FlxFunction*[3]; for(int i=0;i<3;++i) fa[i]=NULL;
	try {
	reader->getChar('[');
	  fa[0] = new FlxFunction(funReader,errSerious);
	reader->getChar(',');
	  fa[1] = new FlxFunction(funReader,errSerious);
	reader->getChar(']');
	reader->getChar('(');
	  fa[2] = new FlxFunction(funReader,errSerious);
	reader->getChar(')');
	} catch (FlxException &e) {
	  FLXMSG("flxFEMrefine_1D::flxFEMrefine_1D_1",1);
	  if (fa[0]!=NULL) delete fa[0];
	  if (fa[1]!=NULL) delete fa[1];
	  if (fa[2]!=NULL) delete fa[2];
	  delete [] fa;
	  throw;
	}
	sections.push_back(fa);
      } else {
	std::ostringstream ssV;
	ssV << "Unknown keyword ('" << strV << "')";
	FlxError(errSerious,"flxFEMrefine_1D::flxFEMrefine_1D_2", ssV.str(),reader->getCurrentPos() );
      }
    }
    reader->getChar(')',errSerious);
  } catch (FlxException& e) {
    FLXMSG("flxFEMrefine_1D::flxFEMrefine_1D_3",1);
    if (minF) delete minF;
    if (maxF) delete maxF;
    delete_sections();
    throw;
  }
}

void flxFEMrefine_1D::delete_sections()
{
  const size_t N = sections.size();
  for (size_t i=0;i<N;++i) {
    for (int j=0;j<3;++j) {
     delete sections[i][j]; 
    }
    delete [] sections[i];
  }
  sections.clear();
}

flxFEMrefine_1D::~flxFEMrefine_1D()
{
  if (minF) delete minF;
  if (maxF) delete maxF;
  delete_sections();
}

void flxFEMrefine_1D::clear()
{
  mesh.clear();
  // the two boundaries
    mesh.push_back(-ONE);
    mesh.push_back(ONE);
}

const bool flxFEMrefine_1D::search_interval(tuint& index, const tdouble& lx, const tdouble* mp, const tuint N)
{
  register tuint start = 0;
  register tuint length = N;
  register tuint t;
  while (length!=1) {
    t = (length+1)/2;
    if (lx<mp[start+t]) {
      length = t;
    } else {
      start += t;
      length-= t;
    }
  }
  index = start;
  if (mp[index]==lx) return true;
  else return false;
}

const tuint flxFEMrefine_1D::insert_point(const tdouble& lx)
{
  // check range
    if (lx<-ONE || lx>ONE) {
      std::ostringstream ssV;
      ssV << "Local coordinate lx (lx=" << lx << ") is not within the interval [-1;1].";
      throw FlxException_NeglectInInteractive("lxFEMrefine_1D::insert_point", ssV.str() );
    }
  tuint si;
  if (search_interval(si,lx,&mesh[0],mesh.size())) return si; 	// in this case the requested point already exists
  mesh.insert(mesh.begin()+si+1,lx);
  return si+1;
}

void flxFEMrefine_1D::refine()
{
  if (maxF==NULL || minF==NULL) {
    throw FlxException_Crude("flxFEMrefine_1D::refine_0");
  }
  // check values
    tdouble max = maxF->calc();
    if (max<=ZERO || max > ONE) {
      std::ostringstream ssV;
      ssV << "max='" << max << "' is not within the valid range ]0;1]";
      throw FlxException("flxFEMrefine_1D::refine_1", ssV.str() );
    }
    tdouble min = minF->calc();
    if (min<=ZERO || min > ONE) {
      std::ostringstream ssV;
      ssV << "min='" << min << "' is not within the valid range [0;1]";
      throw FlxException("flxFEMrefine_1D::refine_2", ssV.str() );
    }
  // deal with too large elements
    tdouble xs,xe,le;
    bool redo=false;
    for (size_t i=0;mesh[i]<ONE;++i) {
      if (redo) {
	--i;
	redo = false;
      }
      xs = mesh[i];
      xe = mesh[i+1];
      le = (xe-xs)/2;
      if (le>max) {
	insert_point(xs+le);
	redo = true;
      }
    }
  // sectional refinement
    FlxFunction** fs;
    tuint is;
    tdouble dx;
    for (size_t i=0; i<sections.size();++i) {
      fs = sections[i];
      xs = fs[0]->calc();
      xe = fs[1]->calc();
      dx = fs[2]->cast2positive();
      if (xe<=xs) {
	std::ostringstream ssV;
	ssV << "The start point of a section (" << xs << ") has to be larger than its end point (" << xe << ").";
	throw FlxException("flxFEMrefine_1D::refine_3", ssV.str() );
      }
      is = insert_point(xs);	// start point
      insert_point(xe);	// end point
      for (size_t i=is;mesh[i]<xe;++i) {
	if (redo) {
	  --i;
	  redo = false;
	}
	xs = mesh[i];
	le = (mesh[i+1]-xs)/2;
	if (le>dx) {
	  insert_point(xs+le);
	  redo = true;
	}
      }
    }
  // free some memory
    delete maxF; maxF = NULL;
    delete minF; minF = NULL;
    delete_sections();
}

void flxFEMrefine_1D::copy_geometry(tdouble* v) const
{
  size_t N = mesh.size();
  const tdouble* wp = &mesh[0];
  for (size_t i=0;i<N;++i) v[i] = wp[i];
}

const std::string flxFEMrefine_1D::write()
{
  std::string strV = "(";
  if (do_refine) {
    strV+="true";
  } else {
    strV+="false";
  }
  if (maxF!=NULL) {
    strV+=",max=";
    strV+=maxF->write();
  }
  if (minF!=NULL) {
    strV+=",min=";
    strV+=minF->write();
  }
  strV+=",loadrefine=";
  if (do_refine) {
    strV+="true";
  } else {
    strV+="false";
  }
  for (size_t i=0;i<sections.size();++i) {
    strV+=",section=[";
    strV+=sections[i][0]->write();
    strV+=",";
    strV+=sections[i][1]->write();
    strV+="](";
    strV+=sections[i][2]->write();
    strV+=")";
  }
  strV+=")";
  return strV;
}

const flxDomain_base::geoel_type flxDomain_base::get_geoel_type(const std::string& str, const bool errSerious)
{
  if (str == "edge" ) {
    return geoel_edge;
  } else if (str == "face" ) {
    return geoel_face;
  } else if (str == "box" ) {
    return geoel_box;
  } else {
    std::ostringstream ssV;
    ssV << "Unkown geometrical type '" << str << "'.";
    FlxError(errSerious,"get_dof", ssV.str() );	return geoel_edge; // dummy-return
  }
}

const std::string flxDomain_base::write(const flxDomain_base::geoel_type gt)
{
  switch(gt) {
    case geoel_edge:
      return "edge";
    case geoel_face:
      return "face";
    case geoel_box:
      return "box";
    default:
      throw FlxException_Crude("flxDomain_base::write");
      return "";
  }
}



void flxDomain_base::set_Const(FlxConstantBox& ConstantBox)
{
  LX = ConstantBox.get("lx",true);
}

flxEdge::flxEdge(flxVertex& vertex1, flxVertex& vertex2, flxDOFe* dofList, flxFEMrefine_1D* meshV, const tuint default_order, const bool condenseDOF)
: vertex1(vertex1),vertex2(vertex2),dofList(dofList),elList(NULL),loadList(NULL),meshInterval(NULL),meshInterval_size(0),condenseDOF(condenseDOF),default_order(default_order),
DOFe_start(0),DOFe_external(0),DOFe_internal(0),eStfMtx(NULL),eForceVec(NULL),eDisplVec(NULL),mesh(meshV),
DegStd(0),DegCorr(0),KLmesh_i(1),KLmesh_o(1)
{
  // check if points are different
    if (vertex1.get_point()==vertex2.get_point()) {
      std::ostringstream ssV;
      ssV << "Start and end point of the edge are the same.";
      throw FlxException("flxEdge::flxEdge", ssV.str(), "The length of an edge must not be zero." );
    }
  // check if mesh-refinement is switched on
    if (mesh) {
      if (mesh->get_do_refine() == false) {
	delete mesh;
	mesh = NULL;
      }
    }
}

flxEdge::~flxEdge()
{
  if (dofList) delete dofList;
  if (elList) delete elList;
  if (loadList) delete loadList;
  if (meshInterval) delete [] meshInterval;
  if (eStfMtx && DOFe_internal>0) delete eStfMtx;
  if (eForceVec && DOFe_internal>0) delete eForceVec;
  if (eDisplVec) delete eDisplVec;
  if (mesh) delete mesh;
}

const tuint flxEdge::get_parallel_axis() const
{
  flxPoint p = vertex2.get_point() - vertex1.get_point();
  tuint counted = 0;
  tuint nonzero = 0;
  for (tuint i=0;i<3;++i) {
    if (fabs(p.id_fast(i))<=GlobalVar.TOL()) {
      ++counted;
    } else {
      nonzero = i;
    }
  }
  if (counted!=2) {
    return 4;
  } else {
    return nonzero;
  }
}

flxDOFe* flxEdge::find_dof(const FlxDof::dof dofDir, const bool do_create, const bool throwError)
{
  flxDOFe* dofF = NULL;
  if (dofList) {
    dofF = dofList->find_dof(dofDir);
  }
  if (dofF==NULL) {
    if (do_create) {
      if (egl->is_locked()) {
	throw FlxException_Crude("flxEdge::find_dof_1");
      }
      return new flxDOFe(dofDir,FlxDof::dof_free,default_order,NULL,dofList,*egl);
    } else {
      if (throwError) {
	std::ostringstream ssV;
	ssV << "node=" << egl->get_index(this) << "; DOF=" << FlxDof::get_dof(dofDir);
	throw FlxException("flxEdge::find_dof_2","Requested DOF does not exist.");
      } else {
	return NULL;
      }
    }
  }
  return dofF;
}

const tuint flxEdge::get_max_pOrder() const
{
  tuint pOm = default_order;
  if (dofList) {
    tuint it = dofList->get_max_pOrder();
    if (it>pOm) pOm = it;
    return it;
  } else {
    return pOm;
  }
}

flxElement_1D_Ptr& flxEdge::get_ElPointer()
{
  return elList;
}

const tuint flxEdge::get_localIndex(const tuint elNumb, const flxDOFe*const dofe, const tuint dofID) const
{
  if (elNumb==0 && dofID==0) {				// nodal dof
    const tuint i = (FlxDof::is_force(dofe->dofDir))?0:6;
    return i+FlxDof::get_dir(dofe->dofDir);
  } else if (elNumb==meshInterval_size-1 && dofID==1) {	// nodal dof
    const tuint i = (FlxDof::is_force(dofe->dofDir))?3:9;
    return i+FlxDof::get_dir(dofe->dofDir);
  } else {
    const tuint PORD = dofe->DOFpOrder;
    switch (dofID) {
      case 0:
	return dofe->get_DOFindex_internal() + PORD*elNumb-1;
      case 1:
	return dofe->get_DOFindex_internal() + PORD*(elNumb+1)-1;
      default:		// internal DOF
	return dofe->get_DOFindex_internal() + PORD*elNumb+dofID-2;
    }
  }
}

void flxEdge::refineMesh()
{
  if (!meshInterval) {
    // set up the DOFnMap
      tdouble trmtx[9];
      get_transformationMatrix(trmtx);
      for (tuint i=0;i<9;++i) {
	DOFnMap[i] = (fabs(trmtx[i])>GlobalVar.TOL());
      }
    // refine the mesh
      if (loadList) loadList->refine_mesh(mesh,*this,DOFnMap);	// refine mesh AND generate necessary DOFs
      if (mesh) { 
	mesh->refine();
	if (mesh->get_element_number()==1) {	// what if element is not refined ??? -> clear the memory !!!
	  delete mesh;
	  mesh = NULL;
	}
      }
    // 'load' the geometry
      if (mesh) {
	meshInterval_size = mesh->get_element_number();
	meshInterval = new tdouble[meshInterval_size+1];
	mesh->copy_geometry(meshInterval);
	delete mesh;
	mesh = NULL;
      } else {
	meshInterval_size = 1;
	meshInterval = new tdouble[2];
	meshInterval[0] = -ONE;
	meshInterval[1] = ONE;
      }
  }
}

void flxEdge::countDOF(tnlong& DofA, tnlong& FixA, tnlong& preDispA, tnlong& DofSum, flxVertexList& vl, tuint& tmpVec_size)
{
  // make sure we have to do this
    if (!dofList) return;
  tuint i_DofA=0;	// internal DOFs -> cannot be FIXED !!!
  tuint LinkDofA=0;	// linked DOFs
  const tnlong DofSum_t = DofSum;
  dofList->countDOF(DofA,FixA,preDispA,DofSum,i_DofA,LinkDofA,condenseDOF,meshInterval_size,*egl,*this);
  if (dofList->has_only_displDOFs()) {
    DOFe_start = 6;
  } else {
    DOFe_start = 12;
  }
  DOFe_external = DofSum - DofSum_t + LinkDofA;
  DOFe_internal = i_DofA;
  // make sure required nodal DOFs exist
    for (tuint i=0;i<6;++i) reqDOFn[i] = false;
    if (dofList) dofList->requires_nodal_DOF(DOFnMap,reqDOFn);
    for (tuint i=0;i<6;++i) {
      if (reqDOFn[i]) {
	vertex1.find_dof(FlxDof::get_dof(i),true);
	vertex2.find_dof(FlxDof::get_dof(i),true);
      }
    }
  if (DOFe_internal == 0) {
    const tuint N = DOFe_start+DOFe_external;
    if (N>tmpVec_size) tmpVec_size = N;
  }
}

void flxEdge::assembleDOF(tnlong& DofIndexFree, tnlong& FixPreIndex, tnlong& FixZeroIndex)
{
  // make sure we have to do this
    if (!dofList) return;
  // allocate some memory
    #if FLX_DEBUG
      if (eStfMtx || eForceVec || eDisplVec) {
	throw FlxException_Crude("flxEdge::countDOF");
      }
    #endif
    const tuint N = DOFe_start+DOFe_external+DOFe_internal;
    if (DOFe_internal>0) {
      eStfMtx = new flxVec((N*N+N)/2);
      eForceVec = new flxVec(N);
    }
    if (elList) eDisplVec = new flxVec(N);
  // do the acutal assembly
    tuint iDOF_external = DOFe_start;
    tuint iDOF_internal = DOFe_start+DOFe_external;
    if (dofList) dofList->assembleDOF(DofIndexFree,FixPreIndex,FixZeroIndex,condenseDOF,iDOF_external,iDOF_internal,meshInterval_size);
}

void flxEdge::assembleStf(flxStiffnessMtxBase& stfmtx, flxVec* tmpVec)
{
  // make sure we have to do this
    if (!dofList) return;
  // check condensation
    if (DOFe_internal>0 && !stfmtx.condensation_is_allowed()) {
      std::ostringstream ssV;
      ssV << "Condensation not allowed in SSFEM";
      throw FlxException("flxEdge::assembleStf_1", ssV.str() );
    }
  // assign temporary StfMtx
    if (DOFe_internal==0) {
      #if FLX_DEBUG
	if (eStfMtx) throw FlxException_Crude("flxEdge::assembleStf_2");
      #endif
      eStfMtx = tmpVec;
    }
    
    eStfMtx->set_zero();	// set all entries to zero
    tdouble* p_eStfMtx = eStfMtx->get_tmp_vptr();
  // assemble the local stiffness matrix
    if (dofList) dofList->assembleStf(*this,meshInterval,meshInterval_size);
    if (elList) elList->assembleStf(meshInterval,meshInterval_size);
  // condense the local stiffness matrix
    if (DOFe_internal>0&&condenseDOF) {
      const tuint N_ext = DOFe_start+DOFe_external;
      tdouble *kp1, *kp2;
      tdouble alpha;
      for (tuint i=DOFe_start+DOFe_external+DOFe_internal-1;i>=N_ext;--i) {
	kp1 = &p_eStfMtx[(i*i+i)/2];
	for (tuint j=0; j<i; ++j) {
	  alpha = kp1[j]/kp1[i];
	  kp2 = &p_eStfMtx[(j*j+j)/2];
	  for (tuint k=0;k<=j;++k) {
	    kp2[k] -= alpha*kp1[k];
	  }
	}
      }
    }
  // assemble edge-DOFs to global StfMtx  (part edge x edge)
    flxDOFe* dptr; flxDOFe *dptr2;
    dptr = dofList;
    tuint c=DOFe_start;				// local index (row)
    while (dptr) {
      if (!(condenseDOF&&dptr->internalDOF)) {
	const tuint N = dptr->get_DOFpOrder()*meshInterval_size-1;
	tnlong d = dptr->get_DOFindex();	// global index (row)
	for (tuint j=0;j<N;++j) {
	  dptr2 = dofList;
	  tuint c2=DOFe_start;			// local index (column)
	  while (dptr2 && c2 <= c) {
	    if (!(condenseDOF&&dptr2->internalDOF)) {
	      const tuint N2 = dptr2->get_DOFpOrder()*meshInterval_size-1;
	      const tuint N2c = (c2+N2-1>c)?(c+1-c2):N2;
	      tnlong d2 = dptr2->get_DOFindex();	// global index (column)
	      stfmtx.Stf_set_value(d,d2,&p_eStfMtx[(c*c+c)/2+c2],N2c);
	      c2+=N2;
	    }
	    dptr2 = dptr2->next;
	  }
	  ++c; ++d;
	}
      }
      dptr = dptr->next;
    }
    #if FLX_DEBUG
      if (c!=DOFe_external+DOFe_start) {
	throw FlxException_Crude("flxEdge::assembleStf_3");
      }
    #endif
  // transform nodal DOFs (and assemble globla StfMtx)
    tdouble T[18];	// transpose of the displ. Transformation matrix
    tdouble K[9];
    tdouble Kt[3];
    tnlong mapVec_1[6];
    tnlong mapVec_2[6];
    tdouble* Tp;
    bool *p_req1; bool *p_req2;
    tnlong *p_map1; tnlong *p_map2;
    get_transformationMatrix(T,true,true);
    if (DOFe_start==12) get_transformationMatrix(&T[9],false,true);
    vertex1.get_mappingVector(mapVec_1);
    vertex2.get_mappingVector(mapVec_2);
    for (tuint i=0;i<DOFe_start;i+=3) {	// part nodal x nodal
      for (tuint j=0;j<=i;j+=3) {	
	if (i==j) { 				// copy part of matrix
	  for (tuint k=0;k<3;++k) {		
	    for (tuint l=0;l<3;++l) {
	      tuint m = i+k;
	      tuint n = j+l;
	      if (n>m) std::swap(m,n);
	      K[k*3+l] = p_eStfMtx[(m*(m+1))/2+n];
	    }
	  }
	} else {
	  for (tuint k=0;k<3;++k) {		
	    for (tuint l=0;l<3;++l) {
	      K[k*3+l] = p_eStfMtx[(pow2(i+k)+i+k)/2+j+l];
	    }
	  }
	}
	Tp = (j<6)?T:(&T[9]);			// transformation: step 1
	for (tuint k=0;k<3;++k) {
	  for (tuint l=0;l<3;++l) {
	    Kt[l] = ZERO;
	    for (tuint m=0;m<3;++m) {
	      Kt[l] += K[k*3+m]*Tp[l*3+m];
	    }
	  }
	  for (tuint l=0;l<3;++l) {
	    K[k*3+l] = Kt[l];
	  }
	}
	Tp = (i<6)?T:(&T[9]);			// transformation: step 2
	for (tuint k=0;k<3;++k) {
	  for (tuint l=0;l<3;++l) {
	    Kt[l] = ZERO;
	    for (tuint m=0;m<3;++m) {
	      Kt[l] += K[m*3+k]*Tp[l*3+m];
	    }
	  }
	  for (tuint l=0;l<3;++l) {
	    K[l*3+k] = Kt[l];
	  }
	}
	switch (i/3) {				// map  to global StfMtx
	  case 0:
	    p_map1 = mapVec_1;
	    break;
	  case 1:
	    p_map1 = mapVec_2;
	    break;
	  case 2:
	    p_map1 = &mapVec_1[3];
	    break;
	  case 3:
	    p_map1 = &mapVec_2[3];
	    break;
	  default:
	    throw FlxException_Crude("flxEdge::assembleStf_4"); p_map1 = mapVec_1;
	}
	switch (j/3) {
	  case 0:
	    p_map2 = mapVec_1;
	    break;
	  case 1:
	    p_map2 = mapVec_2;
	    break;
	  case 2:
	    p_map2 = &mapVec_1[3];
	    break;
	  case 3:
	    p_map2 = &mapVec_2[3];
	    break;
	  default:
	    throw FlxException_Crude("flxEdge::assembleStf_5"); p_map2 = mapVec_1;
	}
	p_req1 = (i<6)?reqDOFn:(&reqDOFn[3]);
	p_req2 = (j<6)?reqDOFn:(&reqDOFn[3]);
	if (i==j) {
	  for (tuint k=0;k<3;++k) {		
	    if (p_req1[k]) {
	      for (tuint l=0;l<=k;++l) {
		if (p_req2[l]) {
		  stfmtx.Stf_set_value(p_map1[k],p_map2[l],K[k*3+l]);
		}
	      }
	    }
	  }
	} else {
	  for (tuint k=0;k<3;++k) {		
	    if (p_req1[k]) {
	      for (tuint l=0;l<3;++l) {
		if (p_req2[l]) {
		  stfmtx.Stf_set_value(p_map1[k],p_map2[l],K[k*3+l]);
		}
	      }
	    }
	  }
	}
      }
    }
    for (tuint i=0;i<DOFe_start;i+=3) {	// part edge x nodal
      Tp = (i<6)?T:(&T[9]);
      p_req1 = (i<6)?reqDOFn:(&reqDOFn[3]);
      switch (i/3) {
	case 0:
	  p_map1 = mapVec_1;
	  break;
	case 1:
	  p_map1 = mapVec_2;
	  break;
	case 2:
	  p_map1 = &mapVec_1[3];
	  break;
	case 3:
	  p_map1 = &mapVec_2[3];
	  break;
      }
      dptr = dofList;
      tuint c=DOFe_start;
      while (dptr) {
	if (!(condenseDOF&&dptr->internalDOF)) {
	  const tuint N = dptr->get_DOFpOrder()*meshInterval_size-1;
	  tnlong d = dptr->get_DOFindex();
	  for (tuint j=0;j<N;++j) {
	    for (tuint k=0;k<3;++k) {
	      if (p_req1[k]) {
		Kt[0] = ZERO;
		for (tuint l=0;l<3;++l) {
		  Kt[0] += p_eStfMtx[(c*c+c)/2+l+i]*Tp[k*3+l];
		}
		stfmtx.Stf_set_value(d,p_map1[k],Kt[0]);
	      }
	    }
	    ++c; ++d;
	  }
	}
	dptr = dptr->next;
      }
      #if FLX_DEBUG
	if (c!=DOFe_external+DOFe_start) {
	  throw FlxException_Crude("flxEdge::assembleStf_6");
	}
      #endif
    }
  // redo assign of temporary stiffness matrix
    if (DOFe_internal==0) {
      eStfMtx = NULL;
    }
}

void flxEdge::assembleForces(flxStiffnessMtxBase& stfmtx, flxVec* tmpVec)
{
  // prepare the force vector
    if (DOFe_internal==0) {
      #if FLX_DEBUG
	if (eForceVec) throw FlxException_Crude("flxEdge::assembleForces_0");
      #endif
      eForceVec = tmpVec;
    }
    *eForceVec = ZERO;
    tdouble* fp = &((*eForceVec)[0]);
  // perform the actual assembly
    if (loadList) loadList->assembleForces(*this,meshInterval,meshInterval_size);
  // condensation of the force vector
    const tuint N_ext = DOFe_start+DOFe_external;
    if (DOFe_internal) {	// make sure a local stiffness matrix exists ... and condense force vector
      tdouble* p_eStfMtx = &((*eStfMtx)[0]);
      tdouble *kp1;
      tdouble alpha;
      for (tuint i=DOFe_start+DOFe_external+DOFe_internal-1;i>=N_ext;--i) {
	kp1 = &p_eStfMtx[(i*i+i)/2];
	if (kp1[i]==ZERO) {
	  throw FlxException("flxEdge::assembleForces_1","Please assemble the stiffness matrix before the condensation of the force vector.");
	}
	for (tuint j=0;j<i;++j) {
	  alpha = kp1[j]/kp1[i];
	  fp[j] -= alpha*fp[i];
	}
      }
    }
  // transform and map nodal part of the force vector
    tdouble tf[3];
    tdouble T[18];	// transpose of the displ. Transformation matrix
    tnlong mapVec_1[6];
    tnlong mapVec_2[6];
    tdouble* t;
    tnlong* p_map;
    bool *p_req;
    get_transformationMatrix(T,true,true);
    if (DOFe_start==12) get_transformationMatrix(&T[9],false,true);
    vertex1.get_mappingVector(mapVec_1);
    vertex2.get_mappingVector(mapVec_2);
    for (tuint i=0;i<DOFe_start;i+=3) {
      t = (i<6)?T:(&T[9]);			// transform
      for (tuint j=0;j<3;++j) {
	tf[j] = ZERO;
	for (tuint k=0;k<3;++k) {
	  tf[j]+= t[j*3+k]*fp[i+k];
	}
      }
      switch (i/3) {				// map  to global StfMtx
	case 0:
	  p_map = mapVec_1;
	  break;
	case 1:
	  p_map = mapVec_2;
	  break;
	case 2:
	  p_map = &mapVec_1[3];
	  break;
	case 3:
	  p_map = &mapVec_2[3];
	  break;
	default:
	  throw FlxException_Crude("flxEdge::assembleForces_2"); p_map = mapVec_1;
      }
      p_req = (i<6)?reqDOFn:(&reqDOFn[3]);
      for (tuint j=0;j<3;++j) {
	if (p_req[j]) {
	  stfmtx.ForceVec_set_value(p_map[j],tf[j]);
	}
      }
    }
  // map force vector to the global one - edge section (external)
    flxDOFe* dptr = dofList;
    tuint c=DOFe_start;				// local index (row)
    while (dptr) {
      if (!(condenseDOF&&dptr->internalDOF)) {
	const tuint N = dptr->get_DOFpOrder()*meshInterval_size-1;
	const tnlong d = dptr->get_DOFindex();	// global index (row)
	stfmtx.ForceVec_set_value(d,&fp[c],N);
	c+=N;
      }
      dptr = dptr->next;
    }
    #if FLX_DEBUG
      if (c!=DOFe_start+DOFe_external) {
	throw FlxException_Crude("flxEdge::assembleForces_3");
      }
    #endif
  // deal with a temporary force vector
    if (DOFe_internal==0) {
      eForceVec = NULL;
    }
}

void flxEdge::updateDisplVec(const flxStiffnessMtxBase& stfmtx)
{
  // make sure we have to do this
    if (!elList) return;
    #if FLX_DEBUG
      if (eDisplVec==NULL) throw FlxException_Crude("flxEdge::updateDisplVec_0");
    #endif
  // get the nodal displacements .. and transform them
    tdouble* dv = &((*eDisplVec)[0]);
    tdouble T[18];	// transpose of the displ. Transformation matrix
    tdouble td[3];
    tnlong mapVec_1[6];
    tnlong mapVec_2[6];
    bool hasDOFn_1[6];
    bool hasDOFn_2[6];
    tdouble* t;
    tnlong* p_map;
    bool *p_req;
    get_transformationMatrix(T,true,false);
    if (DOFe_start==12) get_transformationMatrix(&T[9],false,false);
    vertex1.get_mappingVector(mapVec_1,hasDOFn_1);
    vertex2.get_mappingVector(mapVec_2,hasDOFn_2);
    for (tuint i=0;i<DOFe_start;i+=3) {
      t = (i<6)?T:(&T[9]);
      switch (i/3) {
	case 0:
	  p_map = mapVec_1;
	  p_req = hasDOFn_1;
	  break;
	case 1:
	  p_map = mapVec_2;
	  p_req = hasDOFn_2;
	  break;
	case 2:
	  p_map = &mapVec_1[3];
	  p_req = &hasDOFn_1[3];
	  break;
	case 3:
	  p_map = &mapVec_2[3];
	  p_req = &hasDOFn_2[3];
	  break;
	default:
	  throw FlxException_Crude("flxEdge::updateDisplVec_1");
      }
      for (tuint j=0;j<3;++j) {
	if (p_req[j]) {
	  td[j] = stfmtx.get_U(p_map[j]);
	} else {
	  td[j] = ZERO;
	}
      }
      for (tuint j=0;j<3;++j) {
	dv[i+j] = ZERO;
	for (tuint k=0;k<3;++k) {
	  dv[i+j] += t[j*3+k]*td[k];
	}
      }
    }
  // get the external edge displacements
    flxDOFe* dptr = dofList;
    tuint c=DOFe_start;				// local index (row)
    while (dptr) {
      if (!(condenseDOF&&dptr->internalDOF)) {
	const tuint N = dptr->get_DOFpOrder()*meshInterval_size-1;
	const tnlong d = dptr->get_DOFindex();	// global index (row)
	stfmtx.get_U(d,&dv[c],N);
	c+=N;
      }
      dptr = dptr->next;
    }
    #if FLX_DEBUG
      if (c!=DOFe_start+DOFe_external) {
	throw FlxException_Crude("flxEdge::updateDisplVec_2");
      }
    #endif
  // undo the condensation for the internal DOFs
    if (DOFe_internal) {
      #if FLX_DEBUG
	if (eStfMtx==NULL) throw FlxException_Crude("flxEdge::updateDisplVec_3");
      #endif
      tdouble *k = &((*eStfMtx)[0]);
      tdouble *f = &((*eForceVec)[0]);
      tdouble s;
      const tuint N = DOFe_start+DOFe_external+DOFe_internal;
      for (tuint i=DOFe_start+DOFe_external;i<N;++i) {
	s = ZERO;
	for (tuint j=0;j<i;++j) {
	  s += k[(i*(i+1))/2+j] * dv[j];
	}
	dv[i] = (f[i]-s)/(k[(i*(i+3))/2]);
      }
    }
}

void flxEdge::insert_Eload(const FlxDof::dof dofDir, const bool global, FlxFunDeg* value, FlxFunction* pos1, FlxFunction* pos2, const FlxLoadCase& lc)
{
  flxELoad* eloa = new flxELoad(dofDir,global,value,pos1,pos2,lc,loadList);
  GlobalVar.slog(4) << "FEM: defined new edge load for edge " << egl->get_index(this) << " (";
  eloa->log_info(GlobalVar.slog(4));
  GlobalVar.slog(4) << ")" << std::endl;
}

void flxEdge::get_transformationMatrix(tdouble* mtxptr, const bool force, const bool transpose) const
{
  tdouble lxV,lyV,lzV,l;
  vertex1.get_point().get_d(vertex2.get_point(),lxV,lyV,lzV,l);
  const tdouble lxz=sqrt(lxV*lxV+lzV*lzV);
  tdouble LxOLxz=ONE;
  tdouble LzOLxz=ZERO;
  if (lxz!=0) {
    LxOLxz = lxV/lxz;
    LzOLxz = lzV/lxz;
  }
  if (force) {
    if (transpose) {
      mtxptr[0] = lxV/l;
      mtxptr[1] = -1*lyV/l*LxOLxz;
      mtxptr[2] = -ONE*LzOLxz;
      mtxptr[3] = lyV/l;
      mtxptr[4] = lxz/l;
      mtxptr[5] = ZERO;
      mtxptr[6] = lzV/l;
      mtxptr[7] = -ONE*lyV/l*LzOLxz;
      mtxptr[8] = LxOLxz;
    } else {
      mtxptr[0] = lxV/l;
      mtxptr[1] = lyV/l;
      mtxptr[2] = lzV/l;
      mtxptr[3] = -1*lyV/l*LxOLxz;
      mtxptr[4] = lxz/l;
      mtxptr[5] = -ONE*lyV/l*LzOLxz;
      mtxptr[6] = -ONE*LzOLxz;
      mtxptr[7] = ZERO;
      mtxptr[8] = LxOLxz;
    }
  } else {
    if (transpose) {
      mtxptr[0] = lxV/l;
      mtxptr[1] = -ONE*lyV/l*LxOLxz;
      mtxptr[2] = LzOLxz;
      mtxptr[3] = lyV/l;
      mtxptr[4] = lxz/l;
      mtxptr[5] = ZERO;
      mtxptr[6] = -lzV/l;
      mtxptr[7] = lyV/l*LzOLxz;
      mtxptr[8] = LxOLxz;
    } else {
      mtxptr[0] = lxV/l;
      mtxptr[1] = lyV/l;
      mtxptr[2] = -lzV/l;
      mtxptr[3] = -ONE*lyV/l*LxOLxz;
      mtxptr[4] = lxz/l;
      mtxptr[5] = lyV/l*LzOLxz;
      mtxptr[6] = LzOLxz;
      mtxptr[7] = ZERO;
      mtxptr[8] = LxOLxz;
    }
  }
}

void flxEdge::update_local_StfMtx(const tuint elNumb, const flxDOFe*const dofe1, const tuint dofID1, const flxDOFe*const dofe2, const tuint dofID2, const tdouble& value)
{
  tuint idx1 = get_localIndex(elNumb,dofe1,dofID1);
  tuint idx2 = get_localIndex(elNumb,dofe2,dofID2);
  if (idx2>idx1) std::swap(idx1,idx2);
  eStfMtx->operator[]((idx1*(idx1+1))/2+idx2) += value;  
}

void flxEdge::update_local_StfMtx(const tuint elNumb, const flxDOFe*const dofe1, const tuint dofID1, const flxDOFe*const dofe2, const tuint dofID2, const tdouble* value, const tuint N)
{
  tuint idx1 = get_localIndex(elNumb,dofe1,dofID1);
  tuint idx2 = get_localIndex(elNumb,dofe2,dofID2);
  if (idx2>idx1) {
    std::swap(idx1,idx2);
    tdouble* sm = &(eStfMtx->operator[](0));
    for (tuint i=0;i<N;++i) {
      sm[((idx1+i)*(idx1+i+1))/2+idx2] += value[i];  
    }
  } else {
    tdouble* sm = &(eStfMtx->operator[]((idx1*(idx1+1))/2+idx2));
    for (tuint i=0;i<N;++i) {
      sm[i] += value[i];  
    }
  }
}

void flxEdge::update_local_ForceVec(const tuint elNumb, const flxDOFe*const dofe, const tuint dofID, const tdouble& value)
{
  tuint idx = get_localIndex(elNumb,dofe,dofID);
  eForceVec->operator[](idx) += value;
}

void flxEdge::update_local_ForceVec(const tuint elNumb, const flxDOFe*const dofe, const tuint dofID, const tdouble* value, const tuint N)
{
  const tuint idx = get_localIndex(elNumb,dofe,dofID);
  tdouble *fv = &(eForceVec->operator[](idx));
  for (tuint i=0;i<N;++i) {
    fv[i] += value[i];
  }
}

const tdouble flxEdge::get_displacement(const FlxDof::dof dofDir, const tdouble& lx, bool derivative)
{
  #if FLX_DEBUG
    if (eDisplVec==NULL) throw FlxException_Crude("flxEdge::get_displacement");
  #endif
  flxDOFe* de = find_dof(dofDir,false,false);
  const tuint ni=FlxDof::is_force(dofDir)?0:6;
  if (de==NULL) {		// ruled only by global displacement
    if ( ni==6 && DOFe_start<12 ) return ZERO;
    const tdouble c0 = (*eDisplVec)[ni+FlxDof::get_dir(dofDir)];
    const tdouble c1 = (*eDisplVec)[ni+FlxDof::get_dir(dofDir)+3];
    if (derivative) {
      return (c0*dhLP0(lx) + c1*dhLP1(lx))*2.0/calc_length();
    } else {
      return c0*hLP0(lx) + c1*hLP1(lx);
    }
  }
  const tuint PORDER = de->get_DOFpOrder();
  tuint sen;
  if (flxFEMrefine_1D::search_interval(sen,lx,meshInterval,meshInterval_size) && !derivative) {
    if (sen==0) {
      return (*eDisplVec)[ni+FlxDof::get_dir(dofDir)];
    } else {
      return (*eDisplVec)[de->DOFindex_internal+PORDER*sen-1];
    }
  }
  tdouble lxt = flxFEMrefine_1D::get_lx_sub(sen,lx,meshInterval);
  tdouble c0,c1;
  // start point
    if (sen==0) {
      c0 = (*eDisplVec)[ni+FlxDof::get_dir(dofDir)];
    } else {
      c0 = (*eDisplVec)[de->DOFindex_internal+PORDER*sen-1];
    }
  // end point
    if (sen==meshInterval_size-1) {
      c1 = (*eDisplVec)[ni+FlxDof::get_dir(dofDir)+3];
    } else {
      c1 = (*eDisplVec)[de->DOFindex_internal+PORDER*(sen+1)-1];
    }
  // higher-order-vector
    tdouble* hop = ((PORDER>1)?(&((*eDisplVec)[de->DOFindex_internal+PORDER*sen])):NULL);
  // calculate the displacement value
    if (derivative) {
      tdouble t = c0*dhLP0(lxt) + c1*dhLP1(lxt);
      for (tuint i=2;i<=PORDER;++i) {
	t+=hop[i-2]*dhLP[i](lxt);
      }
      return t*4.0/(calc_length()*(meshInterval[sen+1]-meshInterval[sen]));
    } else {
      return hLP_rlz(PORDER,lxt,c0,c1,hop);
    }
}

flxElement_KL_base* flxEdge::create_KL_element(FlxKLelementProp_1D* KLelProp)
{
  std::vector<tdouble> iVec_i1, iVec_i2, iVec_o, iVec_e2_i1, iVec_e2_i2, iVec_e2_o,iVecGeo;
  std::vector<bool> iVec_i1b, iVec_i2b, iVec_ob, iVec_e2_i1b, iVec_e2_i2b, iVec_e2_ob,iVecGeob;
  if (KLelProp->do_geo_refine_1D(*this) ) {
    KLelProp->get_refine_1D(0,iVec_i1,iVec_i1b);
    KLelProp->get_refine_1D(1,iVec_i2,iVec_i2b);
    KLelProp->get_refine_1D(2,iVec_o,iVec_ob);
    KLelProp->refine_vec(0,iVec_e2_i1,iVec_e2_i1b,iVec_i1,iVec_i1b);
    KLelProp->refine_vec(1,iVec_e2_i2,iVec_e2_i2b,iVec_i2,iVec_i2b);
    KLelProp->refine_vec(2,iVec_e2_o,iVec_e2_ob,iVec_o,iVec_ob);
  }
  KLelProp->get_geo_info(iVecGeo,iVecGeob);
  return new flxElement_KL_1D(this,KLelProp->get_pOrder(),KLelProp->get_degCorr(),KLelProp->get_degStd(),iVec_i1,iVec_i1b,iVec_i2,iVec_i2b,iVec_o,iVec_ob,iVec_e2_i1,iVec_e2_i1b,iVec_e2_i2,iVec_e2_i2b,iVec_e2_o,iVec_e2_ob,iVecGeo,iVecGeob,KLelProp->get_geo_fun());
}

const bool flxEdge::map2local(const flxPoint& p) const
{
  const flxPoint &p1 = vertex1.get_point();
  const flxPoint &p2 = vertex2.get_point();
  const flxPoint a = p2-p1;
  const flxPoint b = p-p1;
  if ( (a%b).length() < GlobalVar.TOL() ) {	// a || b
    if (a*b < 0) return false;
    const tdouble lx = b.length()/a.length()*2.0-ONE;
    if (lx < -ONE || lx > ONE) return false;
    *(flxDOFe::LX) = lx;
    *(flxDOFe::LX+1) = ZERO;
    *(flxDOFe::LX+2) = ZERO;
    return true;
  } else {
    return false;
  }
}

void flxEdge::KL_integ_1(flxVec& tvec_e, const tdouble cp1, const tdouble cp2, const tuint N, FlxFunction* sD, FlxFunction* rho, const tdouble sD1) const
{
  flxVec s(N+2);
  tdouble* sp = s.get_tmp_vptr();
  // assemble local mass matrix
  #if FLX_USE_GEGENBAUERPOLY_FOR_KL==0
    FlxMtxSym M(N);
    {
      GI->check_GA_polynomialDegree((N+1)*2);
      const tuint NGP = GI->pDegree2GPs((N+1)*2);
      const tdouble* GP = GI->get_GP(NGP);
      const tdouble* GW = GI->get_GW(NGP);
      tdouble* p = M.get_VecPointer();
      tnlong c;
      for (tuint i=0;i<NGP;++i) {
	XXX_vec(s,GP[i]);				// calculate shape functions
	c=0;
	for (tuint k=0;k<N;++k) {
	  register const tdouble f = GW[i]*sp[k+2];
	  for (register tuint j=0;j<=k;++j) {
	    p[c++]+=f*sp[j+2];
	  }
	}
      }
    }
  #endif
  // assemble vector
    flxVec f(N);
    flxVec h(N);
    {
      const tuint pd = DegStd+DegCorr+N+1;
      GI->check_GA_polynomialDegree(pd);
      const tuint NGP = GI->pDegree2GPs(pd);
      const tdouble* GP = GI->get_GP(NGP);
      const tdouble* GW = GI->get_GW(NGP);
      tdouble* hp = &(h[0]);
      const tdouble dx = 1./KLmesh_i;
      for (tuint m=0;m<KLmesh_i;++m) {
	h=ZERO;
	const tdouble x = (2*m+1)*dx-ONE;
	for (tuint i=0;i<NGP;++i) {
	  const tdouble lx = x+GP[i]*dx;
	  XXX_vec(s,lx);				// calculate shape functions
	  vertex1.get_point().set_global_dist(vertex2.get_point(),lx/2.0+0.5);
	  register const tdouble a = (sD1*sD->calc()*rho->calc()-cp1*sp[0]-cp2*sp[1])*GW[i];
	  s *= a;
	  for (register tuint j=0;j<N;++j) {
	    hp[j] += sp[j+2];
	  }
	}
	f += h;
      }
      f*=dx;
    }
  #if FLX_USE_GEGENBAUERPOLY_FOR_KL
    for (tuint i=0;i<N;++i) tvec_e[i] = f[i];
  #else
  // solve the system
    flxVec& x = h; x = 0.;
    tdouble eps = 1e-8;
    tnlong iter = N*N;
    M.solve_CG(x,f,eps,iter,tuint(3),false);
    for (tuint i=0;i<N;++i) tvec_e[i] = x[i];
  #endif
}

void flxEdge::KL_integ_2(tdouble* tvec_e, const tdouble* ni, const flxVec& e1, const flxVec& e2, const flxVec& e3, const flxVec& e4, FlxFunction* sD, FlxFunction* rho, const flxEdge& edg2) const
{
  const tuint npx = e1.get_N();
  const tuint npy = e2.get_N();
  flxVec s(npx+2);
  flxVec t(npy+2);
  flxVec st(npx*npy);
  // assemble local mass matrix
  #if FLX_USE_GEGENBAUERPOLY_FOR_KL==0
    FlxMtxSym M(npx*npy);
    {
      FlxMtxSym Mt(npx*npy);
      tdouble* Mtp = Mt.get_VecPointer();
      const tuint pd = (npx+1)*2+((npy+1)*2+1);
      GI->check_GA_polynomialDegree(pd);
      const tuint NGP = GI->pDegree2GPs(pd);
      const tdouble* GP = GI->get_GP(NGP);
      const tdouble* GW = GI->get_GW(NGP);
      for (tuint i=0;i<NGP;++i) {
	XXX_vec(s,GP[i]);				// calculate shape functions
	Mt*=ZERO;
	edg2.KL_integ_2im(npx,npy,Mtp,s,t,st);
	Mt*=GW[i];
	M+=Mt;
      }
    }
  #endif
  // assemble vector
    flxVec f(npx*npy);
    flxVec& h = st;
    {
      flxVec ft(npx*npy);
      flxVec ht(npx*npy);
      const tuint pd = ((DegStd+DegCorr+1>npx+1)?(DegStd+DegCorr+1):(npx+1))+(npx+1);
      GI->check_GA_polynomialDegree(pd);
      const tuint NGP = GI->pDegree2GPs(pd);
      const tdouble* GP = GI->get_GP(NGP);
      const tdouble* GW = GI->get_GW(NGP);
      const tdouble* e1p = &(e1[0]);
      const tdouble* e2p = &(e2[0]);
      const tdouble* e3p = &(e3[0]);
      const tdouble* e4p = &(e4[0]);
      const tdouble dx = 1./KLmesh_o;
      for (tuint m=0;m<KLmesh_o;++m) {
	ft=0.;
	const tdouble x = (2*m+1)*dx-ONE;
	for (tuint i=0;i<NGP;++i) {
	  const tdouble lx = x+GP[i]*dx;
	  XXX_vec(s,lx);				// calculate shape functions
	  flxPoint p = vertex1.get_point()+(vertex2.get_point()-vertex1.get_point())*(lx/2.0+0.5);
	  p.set_global();
	  p.set_global2();
	  const tdouble sD1 = sD->calc();
	  h = 0.;
	  edg2.KL_integ_2iv(npx,npy,s,t,h,ht,sD1,sD,rho,ni,e1p,e2p,e3p,e4p);	  
	  h *= GW[i]*dx;
	  ft+=h;
	}
	f += ft;
      }
    }
  #if FLX_USE_GEGENBAUERPOLY_FOR_KL
    const tdouble* fp = &(f[0]);
    for (tuint i=0;i<npx*npy;++i) tvec_e[i] = fp[i];
  #else
  // solve the system
    tVec& x = h; x = 0.;
    tdouble* xp = &(x[0]);
    tdouble eps = 1e-8;
    tnlong iter = pow2(tnlong(npx*npy));
    M.solve_CG(x,f,eps,iter,tuint(3),false);
    for (tuint i=0;i<npx*npy;++i) tvec_e[i] = xp[i];
  #endif
}

void flxEdge::KL_integ_2iv(const tuint npx, const tuint npy, const flxVec& s, flxVec& t, flxVec& h, flxVec& ht, const tdouble sD1, FlxFunction* sD, FlxFunction* rho, const tdouble* ni, const tdouble* e1p, const tdouble* e2p, const tdouble* e3p, const tdouble* e4p) const
{
  const tuint poi = ((DegStd+DegCorr>npy+1)?(DegStd+DegCorr):(npy+1))+(npy+1);
  GI->check_GA_polynomialDegree(poi);
  const tuint NGP = GI->pDegree2GPs(poi);
  const tdouble* GP = GI->get_GP(NGP);
  const tdouble* GW = GI->get_GW(NGP);
  const tdouble* sp = &(s[0]);
  const tdouble* tp = &(t[0]);
  tdouble* htp = &(ht[0]);
  const tdouble dx = 1./KLmesh_i;
  for (tuint m=0;m<KLmesh_i;++m) {
    ht=0.;
    const tdouble x = (2*m+1)*dx-ONE;
    for (tuint i=0;i<NGP;++i) {
      const tdouble lx = x+GP[i]*dx;
      XXX_vec(t,lx);				// calculate shape functions
      vertex1.get_point().set_global_dist(vertex2.get_point(),lx/2.0+0.5);
      // covariance function
	tdouble a = sD1*sD->calc()*rho->calc();
      // substract linear terms
	a -= ni[0]*sp[0]*tp[0]+ni[1]*sp[1]*tp[0]+ni[2]*sp[1]*tp[1]+ni[3]*sp[0]*tp[1];
      // substract linear-edge terms
	tdouble b = 0.;
	for (tuint j=0;j<npx;++j) {
	  b += e1p[j]*sp[j+2]*tp[0];
	}
	for (tuint j=0;j<npy;++j) {
	  b += e2p[j]*sp[1]*tp[j+2];
	}
	for (tuint j=0;j<npx;++j) {
	  b += e3p[j]*sp[j+2]*tp[1];
	}
	for (tuint j=0;j<npy;++j) {
	  b += e4p[j]*sp[0]*tp[j+2];
	}
	a -= b;
      a *= GW[i];
      for (tuint j=0,c=0;j<npx;++j) {
	for (tuint k=0;k<npy;++k) {
	  htp[c++] += a*sp[j+2]*tp[k+2];
	}
      }
    }
    h += ht;
  }
  h*=dx;
}

void flxEdge::KL_integ_2im(const tuint npx, const tuint npy, tdouble* Mtp, const flxVec& s, flxVec& t, flxVec& st) const 
{
  GI->check_GA_polynomialDegree((npy+1)*2);
  const tuint NGP = GI->pDegree2GPs((npy+1)*2);
  const tdouble* GP = GI->get_GP(NGP);
  const tdouble* GW = GI->get_GW(NGP);
  const tdouble* sp = &(s[0]);
  const tdouble* tp = &(t[0]);
  tdouble* stp = &(st[0]);
  for (tuint i=0;i<NGP;++i) {
    XXX_vec(t,GP[i]);				// calculate shape functions
    for (tuint j=0,c=0;j<npx;++j) {
      for (tuint k=0;k<npy;++k) {
	stp[c++] = sp[j+2]*tp[k+2];
      }
    }
    for (tuint j=0,c=0;j<npx*npy;++j) {
      for (tuint k=0;k<=j;++k) {
	Mtp[c++] += stp[j]*stp[k]*GW[i];
      }
    }
  }
}

flxEdgeList::flxEdgeList()
:locked(false),tmpVec_size(0)
{
  flxEdge::egl = this;
  flxFaceList::set_EdgeList(this);
  #if FLX_DEBUG
    ++instance;
    if (instance>1) throw FlxException_Crude("flxEdgeList::flxEdgeList");
  #endif
}

flxEdgeList::~flxEdgeList()
{
  for (std::map<tuint, flxEdge*>::iterator pos = box.begin(); pos != box.end(); ++pos) {
    delete pos->second;
  }
  #if FLX_DEBUG
    --instance;
  #endif
}

void flxEdgeList::insert(const tuint index, flxEdge* edge, const bool errSerious)
{
  std::pair<tuint, flxEdge*>Element(index, edge);
  if ( ! box.insert(Element).second ) {
    delete edge;
    std::ostringstream ssV;
    ssV << "Edge '" << index << "' has already been defined.";
    FlxError(errSerious, "flxEdgeList::insert", ssV.str() );
  }
}

flxEdge* flxEdgeList::get(const tuint index, const bool throwErr, const bool errSerious) const
{
  std::map<tuint, flxEdge*>::const_iterator pos = box.find(index);
  if ( pos != box.end() ) {
    return pos->second;
  } else {
    if (throwErr) {
      std::ostringstream ssV;
      ssV << "Edge '" << index << "' does not exist.";
      FlxError(errSerious,"flxEdgeList::get_1", ssV.str() ); return NULL; // dummy-return
    } else {
      return NULL;
    }
  }
}

const tuint flxEdgeList::get_index(const flxEdge*const edge) const
{
  for (std::map<tuint, flxEdge*>::const_iterator pos = box.begin(); pos != box.end(); ++pos) {
    if (pos->second == edge) {
      return pos->first;
    }
  }
  std::ostringstream ssV;
  ssV << "The given edge is not in the list.";
  throw FlxException("flxEdgeList::get_index", ssV.str() );
}

flxEdge* flxEdgeList::find(const flxDOFe* DOF, const bool throwErr) const
{
  const FlxDof::dof dofDir = DOF->get_dofDir();
  for (std::map<tuint, flxEdge*>::const_iterator pos = box.begin(); pos != box.end(); ++pos) {
    if ( pos->second->find_dof(dofDir,false,false) == DOF) return pos->second;
  }
  if (throwErr) {
    std::ostringstream ssV;
      ssV << "Edge with requested DOF does not exist.";
      throw FlxException("flxEdgeList::get_2", ssV.str() );
  } else {
    return NULL;
  }  
}

flxEdge* flxEdgeList::find(flxVertex* v1, flxVertex* v2) const
{
  if (v1==v2) throw FlxException_Crude("flxEdgeList::find");
  for (std::map<tuint, flxEdge*>::const_iterator pos = box.begin(); pos != box.end(); ++pos) {
    flxEdge* et = pos->second;
    if (&(et->get_vertex_1()) == v1 || &(et->get_vertex_2()) == v1) {
      if (&(et->get_vertex_1()) == v2 || &(et->get_vertex_2()) == v2) {
	return et;
      }
    }
  }
  return NULL;
}

void flxEdgeList::print(std::ostream& sout) const
{
  for (std::map<tuint, flxEdge*>::const_iterator pos = box.begin(); pos != box.end(); ++pos) {
    sout << "Edge " << pos->first << " " << vl->get_index(&(pos->second->get_vertex_1())) << " " << vl->get_index(&(pos->second->get_vertex_2())) << std::endl;
  }
}

void flxEdgeList::refineMesh()
{
  for (std::map<tuint, flxEdge*>::iterator pos = box.begin(); pos != box.end(); ++pos) {
    pos->second->refineMesh();
  }
}

void flxEdgeList::countDOF(tnlong& DofA, tnlong& FixA, tnlong& preDispA, tnlong& DofSum)
{
  tmpVec_size = 0;
  for (std::map<tuint, flxEdge*>::iterator pos = box.begin(); pos != box.end(); ++pos) {
    pos->second->countDOF(DofA,FixA,preDispA,DofSum,*vl,tmpVec_size);
  }
  if (locked) {
    throw FlxException_Crude("flxEdgeList::countDOF");
  }
  locked = true;
}

void flxEdgeList::assembleDOF(tnlong& DofIndexFree, tnlong& FixPreIndex, tnlong& FixZeroIndex)
{
  for (std::map<tuint, flxEdge*>::iterator pos = box.begin(); pos != box.end(); ++pos) {
    pos->second->assembleDOF(DofIndexFree,FixPreIndex,FixZeroIndex);
  }
}

void flxEdgeList::assembleStf(flxStiffnessMtxBase& stfmtx)
{
  const tuint tS = (pow2(tmpVec_size)+tmpVec_size)/2; 
  flxVec tmpVec((tS==0)?1:tS);
  for (std::map<tuint, flxEdge*>::iterator pos = box.begin(); pos != box.end(); ++pos) {
    flxEdge* eg = pos->second;
    RFbox->set_Current_Element_KL(eg);
    eg->assembleStf(stfmtx,&tmpVec);
  }
}

void flxEdgeList::assembleForces(flxStiffnessMtxBase& stfmtx)
{
  flxVec tmpVec((tmpVec_size==0)?1:tmpVec_size);
  for (std::map<tuint, flxEdge*>::iterator pos = box.begin(); pos != box.end(); ++pos) {
    flxEdge* eg = pos->second;
    RFbox->set_Current_Element_KL(eg);
    eg->assembleForces(stfmtx,&tmpVec);
  }
}

void flxEdgeList::updateDisplVec(const flxStiffnessMtxBase& stfmtx)
{
  for (std::map<tuint, flxEdge*>::iterator pos = box.begin(); pos != box.end(); ++pos) {
    pos->second->updateDisplVec(stfmtx);
  }
}

flxDOFf::flxDOFf(const FlxDof::dof dofDir, const FlxDof::dof_fixed_state state, const bool tensorV, const tuint DOFpOrderLxV, const tuint DOFpOrderLyV, FlxFunDeg* state_valueDV, flxDOFfPtr& start, const flxFaceList& fcl)
: next(NULL),dofDir(dofDir),state(state),internalDOF(true),tensorSpace(tensorV),
DOFpOrderLx(DOFpOrderLxV),DOFpOrderLy(DOFpOrderLyV),EdgeXPOrderY(NULL),EdgeYPOrderX(NULL),
DOFindex(0),state_valueD(state_valueDV)
{
  // check if everything makes sense
    #if FLX_DEBUG
      if (state_valueD==NULL && state!=FlxDof::dof_free ) {
	throw FlxException_Crude("flxDOFf::flxDOFf_1");
      }
    #endif
    if (DOFpOrderLx<1 || DOFpOrderLy<1) {
      throw FlxException("flxDOFf::flxDOFf_2","The order of an face-DOF has to be at least '1'.");
    }
    // make sure that enough shape functions are available
      hLP_check_order(DOFpOrderLx);
      hLP_check_order(DOFpOrderLy);
  // find correct position in list
    if (start) {
      start = start->link2list(this);
    } else {
      start = this;
    }
  // link DOF if it is a linked DOF
    if (state==FlxDof::dof_link) {
      const tuint l = state_valueDV->get_fun()->cast2tuintW0();	// face index
      delete state_valueDV;
      flxFace& fac_Link = *(fcl.get(l,true));
      flxDOFf* d2l_Link = fac_Link.find_dof(dofDir,true);
      if (d2l_Link->state == FlxDof::dof_link) {
	throw FlxException("flxDOFf::flxDOFf_3","A DOF must never be linked to another DOF which is itself linked.");
      }
      state_valueD = d2l_Link;
      internalDOF = false;
      d2l_Link->internalDOF = false;
      tensorSpace = d2l_Link->tensorSpace;
      DOFpOrderLx = d2l_Link->DOFpOrderLx;
      DOFpOrderLy = d2l_Link->DOFpOrderLy;
    }
}

flxDOFf::~flxDOFf()
{
  if (EdgeXPOrderY) delete [] EdgeXPOrderY;
  if (EdgeYPOrderX) delete [] EdgeYPOrderX;
  if (next) delete next;
  if (state_valueD) {
    switch (state) {
      case FlxDof::dof_free:
      case FlxDof::dof_fixed:
	#if FLX_DEBUG
	  throw FlxException_Crude("flxDOFn::~flxDOFn");
	#endif
	break;
      case FlxDof::dof_spring:
      case FlxDof::dof_predispl:
      {
	FlxFunDeg* f = (FlxFunDeg*)state_valueD;
	delete f;
	break;
      }
      case FlxDof::dof_link:
	break;
    }
  }
}

flxDOFf* flxDOFf::link2list(flxDOFf* el2link)
{
  if (el2link->dofDir < this->dofDir) {
    el2link->next = this;
    return el2link;
  } 
  #if FLX_DEBUG
    else if (el2link->dofDir == this->dofDir) {
      throw FlxException_Crude("flxDOFe::link2list");
    } 
  #endif
  if (this->next == NULL) {
    this->next = el2link;
  } else {
    this->next = next->link2list(el2link);
  }
  return this;
}

flxDOFf* flxDOFf::find_dof(const FlxDof::dof dofDirV)
{
  if (dofDir==dofDirV) {
    return this;
  } else if (dofDir<dofDirV) {
    if (next) {
      return next->find_dof(dofDirV);
    }
  }
  return NULL;
}

void flxDOFf::requires_nodal_DOF(const tdouble* Tmtx, bool* reqMap) const
{
  #if FLX_DEBUG
    if (!FlxDof::is_force(dofDir)) throw FlxException_Crude("flxDOFf::requires_nodal_DOF");
  #endif
  const tuint startM = FlxDof::get_dir(dofDir);
  for (tuint i=0; i<3; ++i) {
    if (fabs(Tmtx[startM*3+i])>GlobalVar.TOL()) {
      reqMap[i] = true;
    }
  }
  if (next) next->requires_nodal_DOF(Tmtx, reqMap);
}

void flxDOFf::countDOF(tnlong& DofA, tnlong& FixA, tnlong& preDispA, tnlong& DofSum, tuint& i_DofA, tuint& LinkDofA, tuint& eown_count, const bool condenseDOF, const flxFaceList& fcl, const flxFace& currentFace)
{
  its_face = &currentFace;
  const tuint NinterLx = currentFace.meshIntLx;
  const tuint NinterLy = currentFace.meshIntLy;
  // if is linked dof
    if (state==FlxDof::dof_link) {
      throw FlxException_NotImplemented("flxDOFf::countDOF_1");
    }
  // make sure edge degrees of freedom are fixed if face is fixed AND predisplacements are continuous
    if (state==FlxDof::dof_fixed || state==FlxDof::dof_predispl) {
      throw FlxException_NotImplemented("flxDOFf::countDOF_2");
    }
  // assign the nodal DOFs
    for (tuint i=0;i<4;++i) {
      dofn_link[i] = currentFace.VtxLst[i]->find_dof(FlxDof::get_dof(currentFace.mapL2G[FlxDof::get_dir(dofDir)],true),true);
    }
    
  // count the DOFs   
    // nodes internal
      tuint ti = (NinterLx-1)*(NinterLy-1);
      eown_count+=4;
    // edges internal
      EdgeYPOrderX = new tuint[NinterLx+1];
      dofe_link[3] = currentFace.EdgLst[3]->find_dof(FlxDof::get_dof(currentFace.mapL2G[4*3+FlxDof::get_dir(dofDir)],true),true);
	dofe_link[3]->unset_internal();
	EdgeYPOrderX[0] = dofe_link[3]->get_DOFpOrder();
      dofe_link[1] = currentFace.EdgLst[1]->find_dof(FlxDof::get_dof(currentFace.mapL2G[2*3+FlxDof::get_dir(dofDir)],true),true);
	dofe_link[1]->unset_internal();
	EdgeYPOrderX[NinterLx] = dofe_link[1]->get_DOFpOrder();
      if (NinterLx>1) {
	if (EdgeYPOrderX[0] != EdgeYPOrderX[NinterLx]) {
	  throw FlxException_NotImplemented("flxDOFf::countDOF_3");
	}
      }
      eown_count+= EdgeYPOrderX[0]*NinterLy-1;
      for (tuint i=1;i<NinterLx;++i) {
	EdgeYPOrderX[i] = EdgeYPOrderX[0];
	ti += (EdgeYPOrderX[i]-1)*NinterLy;
      }
      eown_count+= EdgeYPOrderX[NinterLx]*NinterLy-1;
      EdgeXPOrderY = new tuint[NinterLy+1];
      dofe_link[0] = currentFace.EdgLst[0]->find_dof(FlxDof::get_dof(currentFace.mapL2G[1*3+FlxDof::get_dir(dofDir)],true),true);
	dofe_link[0]->unset_internal();
	EdgeXPOrderY[0] = dofe_link[0]->get_DOFpOrder();
      dofe_link[2] = currentFace.EdgLst[2]->find_dof(FlxDof::get_dof(currentFace.mapL2G[3*3+FlxDof::get_dir(dofDir)],true),true);
	dofe_link[2]->unset_internal();
	EdgeXPOrderY[NinterLy] = dofe_link[2]->get_DOFpOrder();
      if (NinterLy>1) {
	if (EdgeXPOrderY[0] != EdgeXPOrderY[NinterLy]) {
	  throw FlxException_NotImplemented("flxDOFf::countDOF_3");
	}
      }
      eown_count+= EdgeXPOrderY[0]*NinterLx-1;
      for (tuint i=1;i<NinterLy;++i) {
	EdgeXPOrderY[i] = EdgeXPOrderY[0];
	ti += (EdgeXPOrderY[i]-1)*NinterLx;
      }
      eown_count+= EdgeXPOrderY[NinterLy]*NinterLx-1;
    // faces internal
      if (tensorSpace) {
	NbubblesPsubEl = (DOFpOrderLx-1)*(DOFpOrderLy-1);
      } else {
	const tuint max = (DOFpOrderLx>=DOFpOrderLy)?DOFpOrderLx:DOFpOrderLy;
	const tuint min = (DOFpOrderLx<=DOFpOrderLy)?DOFpOrderLx:DOFpOrderLy;
	if (min<=1) {
	  NbubblesPsubEl = 0;
	} else {
	  NbubblesPsubEl = (max*max+6-5*max)/2;
	  if (max-min>2) {
	    const tuint m = max-min-2;
	    NbubblesPsubEl -= (m*m+m)/2;
	  }
	}
      }
      ti += NbubblesPsubEl*NinterLx*NinterLy;
    
    if (condenseDOF&&internalDOF) {
      i_DofA+=ti;
    } else {		// no internal DOFs
      switch (state) {
	case FlxDof::dof_free:
	case FlxDof::dof_spring:
	  DofA+=ti;
	  DofSum+=ti;
	  break;
	case FlxDof::dof_fixed:
	  FixA+=ti;
	  DofSum+=ti;
	  break;
	case FlxDof::dof_predispl:
	  FixA+=ti;
	  preDispA+=ti;
	  DofSum+=ti;
	  break;
	case FlxDof::dof_link:
	  LinkDofA+=ti;
	  break;
      }
    }
  // deal with the next edge DOF in the list
    if (next) next->countDOF(DofA,FixA,preDispA,DofSum,i_DofA,LinkDofA,eown_count,condenseDOF,fcl,currentFace);
}

void flxDOFf::assembleDOF(tnlong& DofIndexFree, tnlong& FixPreIndex, tnlong& FixZeroIndex, const bool condenseDOF, tuint& iDOF_ext, tuint& iDOF_internal)
{
  const tuint NinterLx = its_face->meshIntLx;
  const tuint NinterLy = its_face->meshIntLy;
  // deal with DOFs which are owned by other elements
    DOFindex_internal_ow[0] = iDOF_ext;
    iDOF_ext += 4;
    // edge 1
      DOFindex_internal_ow[1] = iDOF_ext;
      iDOF_ext += EdgeXPOrderY[0]*NinterLx-1;;
    // edge 2
      DOFindex_internal_ow[2] = iDOF_ext;
      iDOF_ext += EdgeYPOrderX[NinterLx]*NinterLy-1;
    // edge 3
      DOFindex_internal_ow[3] = iDOF_ext;
      iDOF_ext += EdgeXPOrderY[NinterLy]*NinterLx-1;
    // edge 4
      DOFindex_internal_ow[4] = iDOF_ext;
      iDOF_ext += EdgeYPOrderX[0]*NinterLy-1;
      
  // assign indices (internal; DOFs owned by face)
    if (condenseDOF&&internalDOF) {	// internal DOFs
      DOFindex_internal[0] = iDOF_internal;
    } else {				// external DOFs
      DOFindex_internal[0] = iDOF_ext;
    }
    
  // count internal DOFs
    // nodes
      tuint ti = (NinterLx-1)*(NinterLy-1);
      DOFindex_internal[1] = DOFindex_internal[0] + ti;
    // y-edges in x
      for (tuint i=1;i<NinterLx;++i) {
	ti += (EdgeYPOrderX[i]-1)*NinterLy;
      }
      DOFindex_internal[2] = DOFindex_internal[0] + ti;
    // x-edges in y
      for (tuint i=1;i<NinterLy;++i) {
	ti += (EdgeXPOrderY[i]-1)*NinterLx;
      }
      DOFindex_internal[3] = DOFindex_internal[0] + ti;
    // bubbles
      ti += NbubblesPsubEl*NinterLx*NinterLy;
      NinterSum = ti;
      
  // increase counters (internal; DOFs owned by face)
    if (condenseDOF&&internalDOF) {	// internal DOFs
      iDOF_internal+=ti;
    } else {				// external DOFs
      iDOF_ext+=ti;
      switch (state) {
	case FlxDof::dof_free:
	case FlxDof::dof_spring:
	  DOFindex = DofIndexFree;
	  DofIndexFree+=ti;
	  break;
	case FlxDof::dof_fixed:
	  DOFindex = FixZeroIndex;
	  FixZeroIndex+=ti;
	  break;
	case FlxDof::dof_predispl:
	  DOFindex = FixPreIndex;
	  FixPreIndex+=ti;
	  break;
	case FlxDof::dof_link:
	  break;
      }
    }
    
  if (next) next->assembleDOF(DofIndexFree,FixPreIndex,FixZeroIndex,condenseDOF,iDOF_ext,iDOF_internal);
}

void flxDOFf::assembleStf()
{
  switch (state) {
    case FlxDof::dof_free:
      break;
    case FlxDof::dof_spring:
      throw FlxException_NotImplemented("flxDOFf::assembleStf_1");
      break;
    case FlxDof::dof_fixed:
      break;
    case FlxDof::dof_predispl:
      throw FlxException_NotImplemented("flxDOFf::assembleStf_2");
      break;
    case FlxDof::dof_link:
      throw FlxException_NotImplemented("flxDOFf::assembleStf_3");
      break;
  }
  if (next) next->assembleStf();
}

const tnlong flxDOFf::get_globalIndex(const int ID, tuint& length) const
{
  switch (ID) {
    case 0:
    case 1:
    case 2:
    case 3:
      length = 1;
      return dofn_link[ID]->get_DOFindex();
    case 4:
    case 5:
    case 6:
    case 7:
      length = dofe_link[ID-4]->get_DOFpOrder()*((ID==4||ID==6)?its_face->meshIntLx:its_face->meshIntLy)-1;
      return dofe_link[ID-4]->get_DOFindex();
    case 8:
      if (its_face->condenseDOF&&internalDOF) {
	length = 0;
	return 0;
      } else {
	length = NinterSum;
	return DOFindex;
      }
    default:
      #if FLX_DEBUG
	throw FlxException_Crude("flxDOFf::get_globalIndex");
      #else
	length = 0;
	return 0;
      #endif
  }
}

const bool flxDOFf::get_globalOrientation(const int ID) const
{
  switch (ID) {
    case 0:
    case 1:
    case 2:
    case 3:
      return its_face->mapL2G_Orient[FlxDof::get_dir(dofDir)];
    case 4:
    case 5:
    case 6:
    case 7:
      return its_face->mapL2G_Orient[(ID-3)*3+FlxDof::get_dir(dofDir)];
    case 8:
      return true;
    default:
      #if FLX_DEBUG
	throw FlxException_Crude("flxDOFf::get_globalOrientation");
      #else
	return true;
      #endif
  }
}

const tdouble flxDOFf::get_displacement(const tdouble& lx, const tdouble& ly, const tdouble* dV) const
{
  tuint senX,senY;
  bool phX, phY;
  phX = flxFEMrefine_1D::search_interval(senX,lx,its_face->meshXLx,its_face->meshIntLx);
  phY = flxFEMrefine_1D::search_interval(senY,ly,its_face->meshYLy,its_face->meshIntLy);
  if ( ( phY || (fabs(ly-ONE)<=GlobalVar.TOL()) ) && (fabs(lx-ONE)<=GlobalVar.TOL()) ) {
    senX = its_face->meshIntLx;
    phX = true;
  }
  if ( phX && fabs(ly-ONE)<=GlobalVar.TOL() ) {
    senY = its_face->meshIntLy;
    phY = true;
  }
  if (phX && phY) {	// point !!!
    if ( (senX==0||senX==its_face->meshIntLx) && (senY==0||senY==its_face->meshIntLy) ) {
      tuint id;
      if (senX==0) {
	if (senY==0) id=0;
	else id=3;
      } else {
	if (senY==0) id=1;
	else id=2;
      }
      return dV[DOFindex_internal_ow[0]+id];
    } else if (senX==0||senX==its_face->meshIntLx) {
      if (senX==0) {
	return dV[get_localIndex(senX,senY,0)];
      } else {
	return dV[get_localIndex(senX-1,senY,1)];
      }
    } else if (senY==0||senY==its_face->meshIntLy) {
      if (senY==0) {
	return dV[get_localIndex(senX,senY,1)];
      } else {
	return dV[get_localIndex(senX,senY-1,2)];
      }
    } else {
      return dV[DOFindex_internal[0]+(senY-1)*(its_face->meshIntLx-1)+senX-1];
    }
  }
  // lx/ly of the sub-element
    const tdouble slx = flxFEMrefine_1D::get_lx_sub(senX,lx,its_face->meshXLx);
    const tdouble sly = flxFEMrefine_1D::get_lx_sub(senY,ly,its_face->meshYLy);
  // prepare ...
    tuint pomx = DOFpOrderLx;
    tuint pomy = DOFpOrderLy;
      if (EdgeXPOrderY[senY]>pomx) pomx = EdgeXPOrderY[senY];
      if (EdgeYPOrderX[senX+1]>pomy) pomy = EdgeYPOrderX[senX+1];
      if (EdgeXPOrderY[senY+1]>pomx) pomx = EdgeXPOrderY[senY+1];
      if (EdgeYPOrderX[senX]>pomy) pomy = EdgeYPOrderX[senX];
      flxVec rx(pomx+1);
      flxVec ry(pomy+1);
      hLP_vec(rx,slx); tdouble* rxp = &rx[0];
      hLP_vec(ry,sly); tdouble* ryp = &ry[0];
  // evaluate the shape-functions
    tdouble res=0;
    // nodal DOFs
      res += rxp[0]*ryp[0]*dV[get_localIndex(senX,senY,0)];
      res += rxp[1]*ryp[0]*dV[get_localIndex(senX,senY,1)];
      res += rxp[1]*ryp[1]*dV[get_localIndex(senX,senY,2)];
      res += rxp[0]*ryp[1]*dV[get_localIndex(senX,senY,3)];
    // edge DOFs
      res += get_displacement_help(rxp,ryp[0],&dV[get_localIndex(senX,senY,4)],EdgeXPOrderY[senY],(senY==0?its_face->get_edge_Orient(0):true));
      res += get_displacement_help(ryp,rxp[1],&dV[get_localIndex(senX,senY,5)],EdgeYPOrderX[senX+1],((senX==its_face->meshIntLx-1)?its_face->get_edge_Orient(1):true));
      res += get_displacement_help(rxp,ryp[1],&dV[get_localIndex(senX,senY,6)],EdgeXPOrderY[senY+1],((senY==its_face->meshIntLy-1)?its_face->get_edge_Orient(2):true));
      res += get_displacement_help(ryp,rxp[0],&dV[get_localIndex(senX,senY,7)],EdgeYPOrderX[senX],(senX==0?its_face->get_edge_Orient(3):true));
    // bubble-DOFs
      const tdouble* dVp = &dV[get_localIndex(senX,senY,8)];
      if (tensorSpace) {
	for (tuint i=2;i<=DOFpOrderLx;++i) {
	  for (tuint j=2;j<=DOFpOrderLy;++j) {
	    res += rxp[i]*ryp[j]*(*dVp);
	    ++dVp;
	  }
	}
      } else {
	const tuint MAX = (DOFpOrderLx>=DOFpOrderLy)?DOFpOrderLx:DOFpOrderLy;
	for (tuint i=2;i<=MAX-2 && i<=DOFpOrderLx;++i) {
	  for (tuint j=2;j<=DOFpOrderLy && i+j<=MAX;++j) {
	    res += rxp[i]*ryp[j]*(*dVp);
	    ++dVp;
	  }
	}
      }
  return res;
}

const tdouble flxDOFf::get_displacement_help(const tdouble* r1p, const tdouble r2, const tdouble* dV, const tuint order, const bool orient) const
{
  tdouble res = ZERO;
  if (orient) {
    for (tuint k=2;k<=order;++k) {
      res += r1p[k]*r2*dV[k-2];
    }
  } else {
    for (tuint k=2;k<=order;k+=2) {
      res += r1p[k]*r2*dV[k-2];
    }
    for (tuint k=3;k<=order;k+=2) {
      res += -ONE*r1p[k]*r2*dV[k-2];
    }
  }
  return res;
}

const tuint flxDOFf::get_max_pOrder() const
{
  tuint pOm = 0;
  if (next)pOm = next->get_max_pOrder();
  if (DOFpOrderLx>pOm) pOm = DOFpOrderLx;
  if (DOFpOrderLy>pOm) pOm = DOFpOrderLy;
  return pOm;
}

const tuint flxDOFf::get_localIndex(const tuint elIDx, const tuint elIDy, const int ID) const
{
  const tuint NinterLx = its_face->meshIntLx;
  const tuint NinterLy = its_face->meshIntLy;
  switch (ID) {
    case 0:
      if (elIDx==0 && elIDy==0) {
	return DOFindex_internal_ow[0]+0;
      } else if (elIDx==0) {
	if (its_face->edgOrient[3]) {
	  return DOFindex_internal_ow[4]+elIDy*(EdgeYPOrderX[0])-1;
	} else {
	  return DOFindex_internal_ow[4]+(NinterLy-elIDy)*(EdgeYPOrderX[0])-1;
	}
      } else if (elIDy==0) {
	if (its_face->edgOrient[0]) {
	  return DOFindex_internal_ow[1]+elIDx*(EdgeXPOrderY[0])-1;
	} else {
	  return DOFindex_internal_ow[1]+(NinterLx-elIDx)*(EdgeXPOrderY[0])-1;
	}
      } else {
	return DOFindex_internal[0]+((elIDy-1)*(NinterLx-1)+elIDx-1);
      }
    case 1:
      if (elIDx==(NinterLx-1) && elIDy==0) {
	return DOFindex_internal_ow[0]+1;
      } else if (elIDx==(NinterLx-1)) {
	if (its_face->edgOrient[1]) {
	  return DOFindex_internal_ow[2]+elIDy*(EdgeYPOrderX[NinterLx])-1;
	} else {
	  return DOFindex_internal_ow[2]+(NinterLy-elIDy)*(EdgeYPOrderX[NinterLx])-1;
	}
      } else if (elIDy==0) {
	if (its_face->edgOrient[0]) {
	  return DOFindex_internal_ow[1]+(elIDx+1)*(EdgeXPOrderY[0])-1;
	} else {
	  return DOFindex_internal_ow[1]+(NinterLx-1-elIDx)*(EdgeXPOrderY[0])-1;
	}
      } else {
	return DOFindex_internal[0]+((elIDy-1)*(NinterLx-1)+elIDx);
      }
    case 2:
      if (elIDx==(NinterLx-1) && elIDy==(NinterLy-1)) {
	return DOFindex_internal_ow[0]+2;
      } else if (elIDx==(NinterLx-1)) {
	if (its_face->edgOrient[1]) {
	  return DOFindex_internal_ow[2]+(elIDy+1)*(EdgeYPOrderX[NinterLx])-1;
	} else {
	  return DOFindex_internal_ow[2]+(NinterLy-1-elIDy)*(EdgeYPOrderX[NinterLx])-1;
	}
      } else if (elIDy==(NinterLy-1)) {
	if (its_face->edgOrient[2]) {
	  return DOFindex_internal_ow[3]+(elIDx+1)*(EdgeXPOrderY[NinterLy])-1;
	} else {
	  return DOFindex_internal_ow[3]+(NinterLx-1-elIDx)*(EdgeXPOrderY[NinterLy])-1;
	}
      } else {
	return DOFindex_internal[0]+(elIDy*(NinterLx-1)+elIDx);
      }
    case 3:
      if (elIDx==0 && elIDy==(NinterLy-1)) {
	return DOFindex_internal_ow[0]+3;
      } else if (elIDx==0) {
	if (its_face->edgOrient[3]) {
	  return DOFindex_internal_ow[4]+(elIDy+1)*(EdgeYPOrderX[0])-1;
	} else {
	  return DOFindex_internal_ow[4]+(NinterLy-1-elIDy)*(EdgeYPOrderX[0])-1;
	}
      } else if (elIDy==(NinterLy-1)) {
	if (its_face->edgOrient[2]) {
	  return DOFindex_internal_ow[3]+elIDx*(EdgeXPOrderY[NinterLy])-1;
	} else {
	  return DOFindex_internal_ow[3]+(NinterLx-elIDx)*(EdgeXPOrderY[NinterLy])-1;
	}
      } else {
	return DOFindex_internal[0]+(elIDy*(NinterLx-1)+elIDx-1);
      }
    case 4:
      if (elIDy==0) {
	if (its_face->edgOrient[0]) {
	  return DOFindex_internal_ow[1]+elIDx*(EdgeXPOrderY[0]);
	} else {
	  return DOFindex_internal_ow[1]+(NinterLx-1-elIDx)*(EdgeXPOrderY[0]);
	}
      } else {
	tuint ti = DOFindex_internal[2];
	for (tuint i=1;i<elIDy;++i) {
	  ti += (EdgeXPOrderY[i]-1)*NinterLx;
	}
	ti += (EdgeXPOrderY[elIDy]-1)*elIDx;
	return ti;
      }
    case 5:
      if (elIDx==(NinterLx-1)) {
	if (its_face->edgOrient[1]) {
	  return DOFindex_internal_ow[2]+elIDy*(EdgeYPOrderX[NinterLx]);
	} else {
	  return DOFindex_internal_ow[2]+(NinterLy-1-elIDy)*(EdgeYPOrderX[NinterLx]);
	}
      } else {
	tuint ti = DOFindex_internal[1];
	for (tuint i=1;i<=elIDx;++i) {
	  ti += (EdgeYPOrderX[i]-1)*NinterLy;
	}
	ti += (EdgeYPOrderX[elIDx+1]-1)*elIDy;
	return ti;
      }
    case 6:
      if (elIDy==(NinterLy-1)) {
	if (its_face->edgOrient[2]) {
	  return DOFindex_internal_ow[3]+elIDx*(EdgeXPOrderY[NinterLy]);
	} else {
	  return DOFindex_internal_ow[3]+(NinterLx-1-elIDx)*(EdgeXPOrderY[NinterLy]);
	}
      } else {
	tuint ti = DOFindex_internal[2];
	for (tuint i=1;i<=elIDy;++i) {
	  ti += (EdgeXPOrderY[i]-1)*NinterLx;
	}
	ti += (EdgeXPOrderY[elIDy+1]-1)*elIDx;
	return ti;
      }
    case 7:
      if (elIDx==0) {
	if (its_face->edgOrient[3]) {
	  return DOFindex_internal_ow[4]+elIDy*(EdgeYPOrderX[0]);
	} else {
	  return DOFindex_internal_ow[4]+(NinterLy-1-elIDy)*(EdgeYPOrderX[0]);
	}
      } else {
	tuint ti = DOFindex_internal[1];
	for (tuint i=1;i<elIDx;++i) {
	  ti += (EdgeYPOrderX[i]-1)*NinterLy;
	}
	ti += (EdgeYPOrderX[elIDx]-1)*elIDy;
	return ti;
      }
    case 8:
      return DOFindex_internal[3]+(elIDy*NinterLx+elIDx)*NbubblesPsubEl;
    default:
      #if FLX_DEBUG
	throw FlxException_Crude("flxDOFf::get_globalIndex");
      #else
	return 0;
      #endif
  }
}

void flxDOFf::set_Const(FlxConstantBox& ConstantBox)
{
  LX = ConstantBox.get("lx",true);
}

flxVertex* get_common_vtx(const flxEdge& e1, const flxEdge& e2)
{
  if ( (&e1.get_vertex_1())==(&e2.get_vertex_1()) || (&e1.get_vertex_1())==(&e2.get_vertex_2())) {
    if ((&e1.get_vertex_2())==(&e2.get_vertex_1()) || (&e1.get_vertex_2())==(&e2.get_vertex_2())) {
      throw FlxException("flxFace::get_common_vtx_1","Problem with the geometrical definition of the  face.");
    }
    return &e1.get_vertex_1();
  } else if ((&e1.get_vertex_2())==(&e2.get_vertex_1()) || (&e1.get_vertex_2())==(&e2.get_vertex_2())) {
    return &e1.get_vertex_2();
  } else {
    throw FlxException("flxFace::get_common_vtx_2","Edges do not have a common vertex.");
  }
}

flxFace::flxFace(flxEdge* e1, flxEdge* e2, flxEdge* e3, flxEdge* e4, flxDOFf* dofList, const tuint default_order, const bool default_tensor, const bool condenseDOF, FlxFunction* geo_fun, FlxFunction* cellAreaPoints, const tuint mesh_min, const tuint mesh_max)
: dofList(dofList),elList(NULL),detJ_const(ONE),is_detJ_const(false),isAligned(true),hfemProb(true),condenseDOF(condenseDOF),default_tensor(default_tensor),default_order(default_order),
DOFf_start(0),DOFf_external(0),DOFf_internal(0),fStfMtx(NULL),fForceVec(NULL),fDisplVec(NULL),
mesh_min(mesh_min),mesh_max(mesh_max),meshIntLx(0),meshIntLy(0),meshXLx(NULL),meshYLy(NULL),mesh(NULL),mesh_cc(NULL),geo_fun(geo_fun),cellAreaPoints(cellAreaPoints),
DegStd(0),DegCorr(0)
{
  // check edges
    EdgLst[0]=e1; EdgLst[1]=e2; EdgLst[2]=e3; EdgLst[3]=e4;
    for (tuint i = 0; i<3;++i) {	// check for double-edges
      for (tuint j=i+1;j<4;++j) {
	if (EdgLst[i]==EdgLst[j]) {
	  throw FlxException("flxFace::flxFace_1","Face must be defined by for different edges.");
	}
      }
    }
  // check vertices
    VtxLst[0] = get_common_vtx(*e1,*e4);
    VtxLst[1] = get_common_vtx(*e1,*e2);
    VtxLst[2] = get_common_vtx(*e2,*e3);
    VtxLst[3] = get_common_vtx(*e3,*e4);
    for (tuint i = 0; i<3;++i) {	// check for double-nodes
      for (tuint j=i+1;j<4;++j) {
	if (VtxLst[i]==VtxLst[j]) {
	  throw FlxException("flxFace::flxFace_2","Face must be defined by four different vertices.");
	}
      }
    }
  // determine orientation of edges
    for (tuint i=0;i<4;++i) {
      edgOrient[i] = (&(EdgLst[i]->get_vertex_1())==VtxLst[i]);
    }
    edgOrient[2]= !edgOrient[2];
    edgOrient[3]= !edgOrient[3];
  // check if element is plane
    gVec[0] = VtxLst[0]->get_point();
    gVec[1]= VtxLst[1]->get_point() - gVec[0];
    gVec[2] = VtxLst[3]->get_point() - gVec[0];
    gVec[3] = (gVec[1]%gVec[2]).normalize();
    flxPoint a = VtxLst[3]->get_point() - VtxLst[2]->get_point();
    flxPoint b = VtxLst[1]->get_point() - VtxLst[2]->get_point();
    if (gVec[3]!=(a%b).normalize()) {
      std::ostringstream ssV;
      ssV << "Nodes must be in a plane: " << gVec[0] << ", " << VtxLst[1]->get_point() << ", " << VtxLst[2]->get_point() << ", " << VtxLst[3]->get_point() << ".";
      ssV << std::endl << "n1: " << gVec[3] << "; n2: " << (a%b).normalize();
      throw FlxException("flxFace::flxFace_3", ssV.str() );
    }
  // set up the Jacobian-part2-matrix (for mapping from local coordinates to the in_plane_system)
    flxPoint c = VtxLst[2]->get_point() - gVec[0];
    Jaco_part2[0] = gVec[1].length(); 
    Jaco_part2[1] = ZERO;
    Jaco_part2[2] = std::cos(gVec[1].get_phi(c))*c.length();
    Jaco_part2[3] = std::sin(gVec[1].get_phi(c))*c.length();
    Jaco_part2[4] = std::cos(gVec[1].get_phi(gVec[2]))*gVec[2].length();
    Jaco_part2[5] = std::sin(gVec[1].get_phi(gVec[2]))*gVec[2].length();
  // set up the transformation matrix from the in_plane_system to global coordinates
    a = gVec[1]; a.normalize();
    b = gVec[2]; b.normalize();
    b = b-a*(b*a); b.normalize();
    tdouble J[9];
    for (int i = 0; i < 3; i++) {
      J[0+i] = a[i];
      J[3+i] = b[i];
      J[6+i] = gVec[3][i];
    }
    const tdouble detJinv = ONE/(J[0]*J[4]*J[8]+J[1]*J[5]*J[6]+J[2]*J[3]*J[7]-J[2]*J[4]*J[6]-J[1]*J[3]*J[8]-J[0]*J[5]*J[7]);
    transMtxIP[0]=(J[4]*J[8]-J[5]*J[7])*detJinv;
    transMtxIP[1]=(J[5]*J[6]-J[3]*J[8])*detJinv;
    transMtxIP[2]=(J[3]*J[7]-J[4]*J[6])*detJinv;
    transMtxIP[3]=(J[2]*J[7]-J[1]*J[8])*detJinv;
    transMtxIP[4]=(J[0]*J[8]-J[2]*J[6])*detJinv;
    transMtxIP[5]=(J[1]*J[6]-J[0]*J[7])*detJinv;
    transMtxIP[6]=(J[1]*J[5]-J[2]*J[4])*detJinv;
    transMtxIP[7]=(J[2]*J[3]-J[0]*J[5])*detJinv;
    transMtxIP[8]=(J[0]*J[4]-J[1]*J[3])*detJinv;
  // check if det_J is const
    detJ_const = get_detJ(ZERO,ZERO);
    is_detJ_const = true;
    if (fabs(Jaco_part2[5]-Jaco_part2[3])>GlobalVar.TOL()) is_detJ_const = false;
    if (fabs(Jaco_part2[2]-Jaco_part2[4]-Jaco_part2[0])>GlobalVar.TOL()) is_detJ_const = false;
  // check hFEM-property
    for (tuint i=0;i<4;++i) {
      if (EdgLst[i]->get_parallel_axis()==4) {
	isAligned = false;
      }
    }
    check_hfem();
}

flxFace::~flxFace()
{
  if (dofList) delete dofList;
  if (elList) delete elList;
//   if (loadList) delete loadList;
  if (fStfMtx && DOFf_internal>0) delete fStfMtx;
  if (fForceVec && DOFf_internal>0) delete fForceVec;
  if (fDisplVec) delete fDisplVec;
  if (mesh) {
    for (tuint i=0;i<meshIntLx;++i) {
      for (tuint j=0;j<meshIntLy;++j) {
	delete [] mesh[i*meshIntLy+j];
	delete [] mesh_cc[i*meshIntLy+j];
      }
    }
  }
  if (mesh) delete [] mesh;
  if (mesh_cc) delete [] mesh_cc;
  if (geo_fun) delete geo_fun;
  if (cellAreaPoints) delete cellAreaPoints;
}

void flxFace::get_J(tdouble* J, const tdouble& lx, const tdouble& ly) const
{
  J[0] = (Jaco_part2[0]*(ONE-ly)+(ONE+ly)*(Jaco_part2[2]-Jaco_part2[4]))/4.0;
  J[1] = ((Jaco_part2[3]-Jaco_part2[5])*(ONE+ly))/4.0;
  J[2] = ((Jaco_part2[2]-Jaco_part2[0])*(ONE+lx)+Jaco_part2[4]*(ONE-lx))/4.0;
  J[3] = (Jaco_part2[3]*(ONE+lx)+Jaco_part2[5]*(ONE-lx))/4.0;
}

void flxFace::check_hfem()
{
  if (elList==NULL) return;
  if (!hfemProb) return;
  // number of sub-elements
    if (hfemProb && meshIntLx*meshIntLy>1) hfemProb=false;
  // order of polynomial degree (edges&face)
//     if (hfemProb &&  default_order!=1) hfemProb=false;
    if (hfemProb && dofList) {
      if (dofList->get_max_pOrder()!=1) hfemProb=false;
    }
    for (tuint i=0;i<4&&hfemProb;++i) {
      if (EdgLst[i]->get_max_pOrder()!=1) hfemProb=false;
    }
  // make sure the element fulfills the assumptions
    if (!hfemProb) check_OrthPar();
}

void flxFace::refineMesh()
{
  if (meshXLx) return;
  if (EdgLst[0]->get_geo_Ninter()!=EdgLst[2]->get_geo_Ninter() || EdgLst[1]->get_geo_Ninter()!=EdgLst[3]->get_geo_Ninter()) {
    throw FlxException("flxFace::refineMesh","Number of sub-elements on the edges does not match.");
  }
  meshIntLx = EdgLst[0]->get_geo_Ninter();
  meshIntLy = EdgLst[1]->get_geo_Ninter();
  check_hfem();
  meshXLx = EdgLst[0]->get_geometry();
  meshYLy = EdgLst[1]->get_geometry();
  mesh = new tuint*[meshIntLx*meshIntLy];
  mesh_cc = new tdouble*[meshIntLx*meshIntLy];
  // create the mesh of the sub-elements (for integration only)
    std::vector<tuint> vec;
    std::vector<tdouble> vec_cc;
    for (tuint i=0;i<meshIntLx;++i) {
      for (tuint j=0;j<meshIntLy;++j) {
	vec.clear();
	vec_cc.clear();
	const tdouble dxh = (meshXLx[i+1]-meshXLx[i])/2;
	const tdouble dyh = (meshYLy[j+1]-meshYLy[j])/2;
	refineMesh_help(vec,vec_cc,0,meshXLx[i]+dxh,meshYLy[j]+dyh,dxh,dyh,ZERO,ZERO);
	const size_t S = vec.size();
	tuint* geov = new tuint[S];
	const tuint* vecp = &vec[0];
	for (tuint k=0;k<S;++k) {
	  geov[k] = vecp[k];
	}
	mesh[i*meshIntLy+j] = geov;
	const size_t S_cc = vec_cc.size();
	tdouble* geov_cc = new tdouble[S_cc];
	const tdouble* vecp_cc = &vec_cc[0];
	for (tuint k=0;k<S_cc;++k) {
	  geov_cc[k] = vecp_cc[k];
	}
	mesh_cc[i*meshIntLy+j] = geov_cc;
      }
    }
  // gather mapping information
    if (isAligned) {
    // nodes
      const tdouble lt[3] = {1,2,3};
      tdouble gt;
      for (tuint i=0;i<3;++i) {
	gt = ZERO;
	for (tuint j=0;j<3;++j) {
	  gt += lt[j]*transMtxIP[i*3+j];
	}
	mapL2G_Orient[i] = (gt > ZERO);
	gt = fabs(gt);
	tuint gi = tuint(gt); --gi;
	mapL2G[i] = FlxDof::get_dir(gi);
      }
    // edges
      tdouble tm[9]; 
      tdouble ge[3] = {1,2,3};
      for (tuint k=0;k<4;++k) {
	EdgLst[k]->get_transformationMatrix(tm,true,true);
	for (tuint i=0;i<3;++i) {
	  ge[i] = ZERO;
	  for (tuint j=0;j<3;++j) {
	    ge[i] += lt[j]*tm[i*3+j];
	  }
	}
	for (tuint i=0;i<3;++i) {
	  mapL2G_Orient[(k+1)*3+i] = mapL2G_Orient[i];
	  if ( ge[mapL2G[i]] < ZERO) mapL2G_Orient[(k+1)*3+i] = !mapL2G_Orient[(k+1)*3+i];
	  tuint gi = tuint(fabs(ge[mapL2G[i]])); --gi;
	  mapL2G[(k+1)*3+i] = FlxDof::get_dir(gi);
	}
      }
    }
}

void flxFace::refineMesh_help(std::vector<tuint> &vec, std::vector<tdouble> &vec_cc, const tuint level, const tdouble xm, const tdouble ym, const tdouble dxh, const tdouble dyh, const tdouble lxm, const tdouble lym)
{
  const tdouble dh = ONE/(pow(2.,int(level)));
  bool gb[5];
  bool same=true;
  if (geo_fun) {
    const tdouble dhkx = dxh*0.99;
    const tdouble dhky = dyh*0.99;
    update_globalConst(xm-dhkx,ym-dhky);
      gb[0] = (geo_fun->calc()>=ZERO);
    update_globalConst(xm+dhkx,ym-dhky);
      gb[1] = (geo_fun->calc()>=ZERO);
    update_globalConst(xm+dhkx,ym+dhky);
      gb[2] = (geo_fun->calc()>=ZERO);
    update_globalConst(xm-dhkx,ym+dhky);
      gb[3] = (geo_fun->calc()>=ZERO);
    update_globalConst(xm,ym);
      gb[4] = (geo_fun->calc()>=ZERO);
    for (tuint i=1;i<5;++i) {
      if (gb[0]!=gb[i]) {
	same = false;
	break;
      }
    }
  } else {
    for (tuint i=0;i<5;++i) gb[i] = true;
  }
  if ((mesh_min>level || !same) && mesh_max>level) {
    const tdouble dhh = dh/2;
    const tdouble dhhx = dxh/2;
    const tdouble dhhy = dyh/2;
    vec.push_back(0);		// parent cell
    refineMesh_help(vec,vec_cc,level+1,xm-dhhx,ym-dhhy,dhhx,dhhy,lxm-dhh,lym-dhh);
    refineMesh_help(vec,vec_cc,level+1,xm+dhhx,ym-dhhy,dhhx,dhhy,lxm+dhh,lym-dhh);
    refineMesh_help(vec,vec_cc,level+1,xm+dhhx,ym+dhhy,dhhx,dhhy,lxm+dhh,lym+dhh);
    refineMesh_help(vec,vec_cc,level+1,xm-dhhx,ym+dhhy,dhhx,dhhy,lxm-dhh,lym+dhh);
  } else {
    if (same) {
      if (gb[0]) {
	vec.push_back(3);	// full geometry
      } else {
	vec.push_back(1);	// no geometry
      }
    } else {
      tdouble cmx=lxm,cmy=lym;
      const tdouble A = get_cellarea(dh,dxh,dyh,xm,ym,cmx,cmy);
      if (A<=GlobalVar.TOL()) vec.push_back(1);	// no geometry
      else if (ONE-A<=GlobalVar.TOL()) vec.push_back(3);	// full geometry
      else {
	vec.push_back(2);		// mixed cell
	// add center's
	  vec_cc.push_back(cmx);
	  vec_cc.push_back(cmy);
	vec_cc.push_back(A);
      }
    }
  }
}

const tdouble flxFace::get_cellarea(const tdouble dh, const tdouble dxh, const tdouble dyh, const tdouble xm, const tdouble ym, tdouble& cmx, tdouble& cmy)
{
  const tdouble lxm = cmx;
  const tdouble lym = cmy;
  tuint H=0;
  update_globalConst(xm,ym);
  const tuint N = cellAreaPoints->cast2tuint();
  const tdouble ldx = (dh*2)/N;
  const tdouble gdx = (dxh*2)/N;
  const tdouble gdy = (dyh*2)/N;
  tdouble lx,ly=ldx/2,sx=ZERO,sy=ZERO;
  tdouble gx,gy=gdy/2;
  for (tuint i=0;i<N;++i) {
    lx = ldx/2;
    gx = gdx/2;
    for (tuint j=0;j<N;++j) {
      update_globalConst(xm+(gx-dxh),ym+(gy-dyh));
      if (geo_fun->calc()>=ZERO) {
	++H;
	sx+=lx;
	sy+=ly;
      }
      lx+=ldx;
      gx+=gdx;
    }
    ly+=ldx;
    gy+=gdy;
  }
  if (H>0) {
    cmx = lxm+(sx/H-dh);
    cmy = lym+(sy/H-dh);
  }
  return tdouble(H)/tdouble(N*N);
}

void flxFace::countDOF(tnlong& DofA, tnlong& FixA, tnlong& preDispA, tnlong& DofSum, flxEdgeList& vl, tuint& tmpVec_size)
{
  // make sure we have to do this
    if (!dofList) return;
  // make sure hfem-property still holds
    check_hfem();
  if (isAligned) {
    tuint i_DofA=0;	// internal DOFs -> cannot be FIXED !!!
    tuint LinkDofA=0;	// linked DOFs
    const tnlong DofSum_t = DofSum;
    #if FLX_DEBUG
      if (DOFf_start>0) throw FlxException_Crude("flxFace::countDOF_1");
    #endif
    dofList->countDOF(DofA,FixA,preDispA,DofSum,i_DofA,LinkDofA,DOFf_start,condenseDOF,*fcl,*this);

    DOFf_external = DofSum - DofSum_t + LinkDofA;
    DOFf_internal = i_DofA;  
    
    if (DOFf_internal == 0) {
      const tuint N = DOFf_start+DOFf_external;
      if (N>tmpVec_size) tmpVec_size = N;
    }
  } else {
    // make sure required nodal DOFs exist
      bool reqDOFn[3];
      for (tuint i=0;i<3;++i) reqDOFn[i] = false;
      dofList->requires_nodal_DOF(transMtxIP,reqDOFn);
      for (tuint i=0;i<3;++i) {
	if (reqDOFn[i]) {
	  for (tuint j=0;j<4;++j) {
	    VtxLst[j]->find_dof(FlxDof::get_dof(i),true);
	  }
	}
      }
  }
}

void flxFace::assembleDOF(tnlong& DofIndexFree, tnlong& FixPreIndex, tnlong& FixZeroIndex)
{
  // make sure we have to do this
    if (!dofList) return;
  // make sure hfem-property still holds
    check_hfem();
  // some debug-output
    if (GlobalVar.logLevel>=5) {
      GlobalVar.slogcout(5) << "  Vertices:" << std::endl;
      for (tuint i=0;i<4;++i) {
	GlobalVar.slogcout(5) << "    " << VtxLst[i]->get_point() << std::endl;
      }
      flxVec xVec(meshXLx,meshIntLx+1);
      GlobalVar.slogcout(5) << "    local x resolution: " << xVec << std::endl;
      flxVec yVec(meshYLy,meshIntLy+1);
      GlobalVar.slogcout(5) << "    local y resolution: " << yVec << std::endl;
    }
  if (isAligned) {
    // allocate some memory
      #if FLX_DEBUG
	if (fStfMtx || fForceVec || fDisplVec) {
	  throw FlxException_Crude("flxEdge::countDOF");
	}
      #endif
      const tuint N = DOFf_start+DOFf_external+DOFf_internal;
      if (DOFf_internal>0) {
	fStfMtx = new tVec((N*N+N)/2);
	fForceVec = new tVec(N);
      }
      if (elList) fDisplVec = new tVec(N);
    // do the acutal assembly
      tuint iDOF_ext = 0;
      tuint iDOF_internal = DOFf_start+DOFf_external;
      if (dofList) dofList->assembleDOF(DofIndexFree,FixPreIndex,FixZeroIndex,condenseDOF,iDOF_ext,iDOF_internal);
  }
}

void flxFace::assembleStf(flxStiffnessMtxBase& stfmtx, tVec* tmpVec)
{
  // make sure we have to do this
    if (!dofList) return;
  if (!isAligned) {
    if (dofList) dofList->assembleStf();
    if (elList) elList->assembleStfHfem(stfmtx);
    return;
  }
  // check condensation
    if (DOFf_internal>0 && !FEM->StiffnessMtx->condensation_is_allowed()) {
      std::ostringstream ssV;
      ssV << "Condensation not allowed in SSFEM";
      throw FlxException("flxFace::assembleStf_1", ssV.str() );
    }
  // assign temporary StfMtx
    if (DOFf_internal==0) {
      #if FLX_DEBUG
	if (fStfMtx!=NULL) throw FlxException_Crude("flxFace::assembleStf_2");
      #endif
      fStfMtx = tmpVec;
    }
    
    *fStfMtx=ZERO;	// set all entries to zero
    tdouble* p_eStfMtx = &((*fStfMtx)[0]);
  // assemble the local stiffness matrix
    if (dofList) dofList->assembleStf();
    if (elList) elList->assembleStf(meshIntLx,meshIntLy,mesh,mesh_cc,meshXLx,meshYLy,stfmtx);
  // condense the local stiffness matrix
    const tuint N_ext = DOFf_start+DOFf_external;
    tdouble *kp1, *kp2;
    tdouble alpha;
    for (tuint i=DOFf_start+DOFf_external+DOFf_internal-1;i>=N_ext;--i) {
      kp1 = &p_eStfMtx[(i*i+i)/2];
      if (kp1[i]<=GlobalVar.TOL()) {
	bool skip=true;
	tdouble t = ZERO;
	for (tuint j=0;j<i && skip;++j) {
	  if ( kp1[j] > GlobalVar.TOL()) {
	    skip = false;
	    t = kp1[j];
	  }
	}
	if (skip) continue;
	std::ostringstream ssV;
	ssV << "Check row " << i+1 << ". (" << GlobalVar.Double2String(t) << ")";
	throw FlxException("flxFace::assembleStf_3", "Matrix is not positive definite.", ssV.str() );
      }
      for (tuint j=0; j<i; ++j) {
	if (fabs(kp1[j])>ZERO) {
	  alpha = kp1[j]/kp1[i];
	  kp2 = &p_eStfMtx[(j*j+j)/2];
	  for (tuint k=0;k<=j;++k) {
	    kp2[k] -= alpha*kp1[k];
	  }
	}
      }
    }
    
  // assemble face-DOFs to global StfMtx
    flxDOFf* dptr = dofList; flxDOFf *dptr2;
    tuint vl,vl2,row=0,col;
    while (dptr) {			// rows
      for (tuint i=0;i<=8;++i) {	
	const tnlong dsid = dptr->get_globalIndex(i,vl);
	const bool br = dptr->get_globalOrientation(i);
	for (tuint j=0;j<vl;++j) {
	  dptr2 = dofList;
	  col = 0;
	  while (dptr2 && col<=row) {		// columns
	    for (tuint k=0;k<=8 && col<=row;++k) {
	      const tnlong dsid2 = dptr2->get_globalIndex(k,vl2);
	      const bool bc = dptr2->get_globalOrientation(k);
	      if (vl2==0) continue;	      
	      if ( (br && bc) || (!br && !bc) ) {	
		if (vl2==1) {
		  stfmtx.Stf_set_value(dsid+j,dsid2,*p_eStfMtx);
		  ++p_eStfMtx; 
		  ++col;
		} else {
		  const tuint vl2c = (col+vl2-1>row)?(row+1-col):vl2;
		  stfmtx.Stf_set_value(dsid+j,dsid2,p_eStfMtx,vl2c);
		  p_eStfMtx += vl2c;
		  col+=vl2;
		}
	      } else { 		// change orientation
		for (tuint l=0;l<vl2 && col<=row;++l) {
		  if (col==row) {
		    stfmtx.Stf_set_value(dsid+j,dsid2+l,*p_eStfMtx);
		  } else {
		    const tdouble dpt = (*p_eStfMtx)*-ONE;
		    stfmtx.Stf_set_value(dsid+j,dsid2+l,dpt);
		  }
		  ++p_eStfMtx; 
		  ++col;
		}
	      }
	    }
	    dptr2 = dptr2->next;
	  }
	  ++row;
	}
      }
      dptr = dptr->next;
    }
    #if FLX_DEBUG
      if (row!=DOFf_external+DOFf_start) {
	throw FlxException_Crude("flxFace::assembleStf_4");
      }
    #endif
    
  // redo assign of temporary stiffness matrix
    if (DOFf_internal==0) {
      fStfMtx = NULL;
    }
}

void flxFace::assembleForces(flxStiffnessMtxBase& stfmtx, tVec* tmpVec)
{
  if (!isAligned) {
    return;
  }
  
  // prepare the force vector
    if (DOFf_internal==0) {
      #if FLX_DEBUG
	if (fForceVec) throw FlxException_Crude("flxFace::assembleForces_1");
      #endif
      fForceVec = tmpVec;
    }
    *fForceVec = ZERO;
    tdouble* fp = &((*fForceVec)[0]);
  // perform the actual assembly
    // TODO if (loadList) loadList->assembleForces(*this,meshInterval,meshInterval_size);
  // condensation of the force vector
    const tuint N_ext = DOFf_start+DOFf_external;
    if (DOFf_internal) {	// make sure a local stiffness matrix exists ... and condense force vector
      tdouble* p_fStfMtx = &((*fStfMtx)[0]);
      tdouble *kp1;
      tdouble alpha;
      for (tuint i=DOFf_start+DOFf_external+DOFf_internal-1;i>=N_ext;--i) {
	kp1 = &p_fStfMtx[(i*i+i)/2];
	if (kp1[i]<=GlobalVar.TOL()) {
	  bool skip=true;
	  for (tuint j=0;j<i && skip;++j) {
	    if (kp1[j]>GlobalVar.TOL()) skip = false;
	  }
	  if (skip) continue;
	  std::ostringstream ssV;
	  ssV << "Problem in row " << i+1 << ".";
	  throw FlxException("flxFace::assembleForces_2","Please assemble the stiffness matrix before the condensation of the force vector.",ssV.str());
	}
	for (tuint j=0;j<i;++j) {
	  alpha = kp1[j]/kp1[i];
	  fp[j] -= alpha*fp[i];
	}
      }
    }
  // transform and map nodal part of the force vector
    flxDOFf* dptr = dofList;
    tuint vl,row=0;
    while (dptr) {			// rows
      for (tuint i=0;i<=8;++i) {	
	const tnlong dsid = dptr->get_globalIndex(i,vl);
	const bool br = dptr->get_globalOrientation(i);
	if (vl==0) continue;
	if (br) {
	  if (vl==1) {
	    stfmtx.ForceVec_set_value(dsid,*fp);
	    ++fp;
	    ++row;
	  } else {
	    stfmtx.ForceVec_set_value(dsid,fp,vl);
	    fp += vl;
	    row += vl;
	  }
	} else {	// change orientation
	  for (tuint j=0;j<vl;++j) {
	    const tdouble dft = (*fp)*-ONE;
	    stfmtx.ForceVec_set_value(dsid,dft);
	    ++fp;
	    ++row;
	  }
	}
      }
      dptr = dptr->next;
    }
    #if FLX_DEBUG
      if (row!=DOFf_external+DOFf_start) {
	throw FlxException_Crude("flxFace::assembleForces_3");
      }
    #endif

  // deal with a temporary force vector
    if (DOFf_internal==0) {
      fForceVec = NULL;
    }
}

void flxFace::updateDisplVec(const flxStiffnessMtxBase& stfmtx)
{
  // make sure we have to do this
    if (!elList) return;
    if (!isAligned) {
      return;
    }
    #if FLX_DEBUG
      if (fDisplVec==NULL) throw FlxException_Crude("flxEdge::updateDisplVec_1");
    #endif
  // get the nodal displacements .. and transform them
    tdouble* dv = &((*fDisplVec)[0]);
    flxDOFf* dptr = dofList;
    tuint vl,row=0;
    while (dptr) {			// rows
      for (tuint i=0;i<=8;++i) {	
	const tnlong dsid = dptr->get_globalIndex(i,vl);
	const bool br = dptr->get_globalOrientation(i);
	if (vl==0) continue;
	if (vl==1) {
	  *dv = stfmtx.get_U(dsid);
	} else {
	  stfmtx.get_U(dsid,dv,vl);
	}
	if (!br) {
	  for (tuint j=0;j<vl;++j) {
	    dv[j] *= -ONE;
	  }
	}
	dv += vl;
	row += vl;
      }
      dptr = dptr->next;
    }
    #if FLX_DEBUG
      if (row!=DOFf_external+DOFf_start) {
	throw FlxException_Crude("flxFace::updateDisplVec_2");
      }
    #endif
      
  // undo the condensation for the internal DOFs
    if (DOFf_internal) {
      #if FLX_DEBUG
	if (fStfMtx==NULL) throw FlxException_Crude("flxFace::updateDisplVec_3");
      #endif
      dv = &((*fDisplVec)[0]);
      tdouble *k = &((*fStfMtx)[0]);
      tdouble *f = &((*fForceVec)[0]);
      tdouble s;
      const tuint N = DOFf_start+DOFf_external+DOFf_internal;
      for (tuint i=DOFf_start+DOFf_external;i<N;++i) {
	s = ZERO;
	for (tuint j=0;j<i;++j) {
	  s += k[(i*(i+1))/2+j] * dv[j];
	}
	dv[i] = (f[i]-s)/(k[(i*(i+3))/2]);
      }
    }
}

void flxFace::update_globalConst(const tdouble& lx, const tdouble& ly) const
{
  *LX = lx;
  *(LX-1) = ly;
  tdouble px=ZERO, py=ZERO,t;
  t = 0.25*(1+lx)*(1-ly);
    px+=t*Jaco_part2[0];
//     py+=t*Jaco_part2[1];		// = 0 !!!
  t = 0.25*(1+lx)*(1+ly);
    px+=t*Jaco_part2[2];
    py+=t*Jaco_part2[3];
  t = 0.25*(1-lx)*(1+ly);
    px+=t*Jaco_part2[4];
    py+=t*Jaco_part2[5];
  py/=Jaco_part2[5];
  px=(px-py*Jaco_part2[4])/Jaco_part2[0];
  const flxPoint p = gVec[0]+gVec[1]*px+gVec[2]*py;
  p.set_global();
}

void flxFace::check_OrthPar() const
{
  if (!isAligned) {
    throw FlxException("flxFace::check_OrthPar","Every edge of the face must be parallel to an axis of the global coordinate system.");
  }
}

const tdouble flxFace::get_bovara(const tuint id1, const tuint id2) const
{
  return (EdgLst[1]->calc_length()*(meshYLy[id2+1]-meshYLy[id2]))/(EdgLst[0]->calc_length()*(meshXLx[id1+1]-meshXLx[id1]));
}

void flxFace::set_DegStd(const tuint i)
{
  if (i>DegStd) {
    DegStd=i;
    for (tuint j=0;j<4;++j) EdgLst[j]->set_DegStd(i);
  }
}

void flxFace::set_DegCorr(const tuint i)
{
  if (i>DegCorr) {
    DegCorr=i;
    for (tuint j=0;j<4;++j) EdgLst[j]->set_DegCorr(i);
  }
}

flxDOFf* flxFace::find_dof(const FlxDof::dof dofDir, const bool do_create, const bool throwError)
{
  flxDOFf* dofF = NULL;
  if (dofList) {
    dofF = dofList->find_dof(dofDir);
  }
  if (dofF==NULL) {
    if (do_create) {
      if (fcl->is_locked()) {
	throw FlxException_Crude("flxFace::find_dof_1");
      }
      return new flxDOFf(dofDir,FlxDof::dof_free,default_tensor,default_order,default_order,NULL,dofList,*fcl);
    } else {
      if (throwError) {
	std::ostringstream ssV;
	ssV << "node=" << fcl->get_index(this) << "; DOF=" << FlxDof::get_dof(dofDir);
	throw FlxException("flxFace::find_dof_2","Requested DOF does not exist.");
      } else {
	return NULL;
      }
    }
  }
  return dofF;
}

flxElement_2D_Ptr& flxFace::get_ElPointer()
{
  return elList;
}

void flxFace::update_local_StfMtx(const tuint id1, const tuint id2, const tdouble* value, const tuint N)
{
  if (N==0) return;
  tuint idx1 = id1;
  tuint idx2 = id2;
  if (idx2>idx1) {
    std::swap(idx1,idx2);
    tdouble* sm = &(fStfMtx->operator[](0));
    for (tuint i=0;i<N;++i) {
      sm[((idx1+i)*(idx1+i+1))/2+idx2] += value[i];  
    }
  } else {
    tdouble* sm = &(fStfMtx->operator[]((idx1*(idx1+1))/2+idx2));
    for (tuint i=0;i<N;++i) {
      sm[i] += value[i];  
    }
  }
}

void flxFace::update_local_ForceVec(const tuint elNumb, const flxDOFe*const dofe, const tuint dofID, const tdouble& value)
{
  throw FlxException_NotImplemented("flxFace::update_local_ForceVec_1");
}

void flxFace::update_local_ForceVec(const tuint elNumb, const flxDOFe*const dofe, const tuint dofID, const tdouble* value, const tuint N)
{
  throw FlxException_NotImplemented("flxFace::update_local_ForceVec_2");
}

const tdouble flxFace::get_displacement(const FlxDof::dof dofDir, const tdouble& lx, const tdouble& ly)
{
  if (!isAligned) throw FlxException_NotImplemented("flxFace::get_displacement_1");
  #if FLX_DEBUG
    if (fDisplVec==NULL) throw FlxException_Crude("flxFace::get_displacement_2");
  #endif
  flxDOFf* df = find_dof(dofDir,false,false);
  if (df==NULL) {		// ruled only by global displacement
    tdouble ci[4];
    FlxDof::dof dofDirG = FlxDof::get_dof(mapL2G[FlxDof::get_dir(dofDir)],true);
    for (tuint i=0;i<4;++i) {
      flxDOFn *dn = VtxLst[0]->find_dof(dofDirG,false,false);
      if (dn) {
	ci[i] = FEM->StiffnessMtx->get_U(dn->get_DOFindex());
      } else {
	ci[i] = ZERO;
      }
    }
    const tdouble n1x = dhLP0(lx);
    const tdouble n1y = dhLP0(ly);
    const tdouble n2x = dhLP1(lx);
    const tdouble n2y = dhLP1(ly);
    const tdouble gz = ci[0]*n1x*n1y+ci[1]*n2x*n1y+ci[2]*n2x*n2y+ci[3]*n1x*n2y;
    if (mapL2G_Orient[FlxDof::get_dir(dofDir)]) {
      return gz;
    } else {
      return -gz;
    }
  }
  return df->get_displacement(lx,ly,&((*fDisplVec)[0]));
}

flxElement_KL_base* flxFace::create_KL_element(FlxKLelementProp_2D* KLelProp)
{
  return new flxElement_KL_2D(this,*KLelProp);
}

const bool flxFace::map2local(const flxPoint& p) const
{
//   const flxPoint f = p - gVec[0];
//   if ( fabs(gVec[3]*f)>GlobalVar.TOL() ) return false;
//   flxPoint a_orth = (gVec[2]-gVec[1]*((gVec[1]*gVec[2])/pow2(Jaco_part2[0])));
//   a_orth.normalize();
//   const tdouble px = (gVec[1]*gVec[2])/Jaco_part2[0];
//   const tdouble py = (a_orth*f);
//   
//   
  // see NOTE sheet 2011-11-21-map2local
  
  throw FlxException_NotImplemented("flxFace::map2local");
}

void flxFace::set_FEM(flxFEM* FEMv)
{
  FEM = FEMv;
}

flxFaceList::flxFaceList()
:locked(false),tmpVec_size(0)
{
  flxFace::fcl = this;
  flxBoxList::set_FaceList(this);
  #if FLX_DEBUG
    ++instance;
    if (instance>1) throw FlxException_Crude("flxFaceList::flxFaceList");
  #endif
}

flxFaceList::~flxFaceList()
{
  for (std::map<tuint, flxFace*>::iterator pos = box.begin(); pos != box.end(); ++pos) {
    delete pos->second;
  }
  #if FLX_DEBUG
    --instance;
  #endif
}

void flxFaceList::refineMesh()
{
  for (std::map<tuint, flxFace*>::iterator pos = box.begin(); pos != box.end(); ++pos) {
    pos->second->refineMesh();
  }
}

void flxFaceList::countDOF(tnlong& DofA, tnlong& FixA, tnlong& preDispA, tnlong& DofSum)
{
  tmpVec_size = 0;
  for (std::map<tuint, flxFace*>::iterator pos = box.begin(); pos != box.end(); ++pos) {
    pos->second->countDOF(DofA,FixA,preDispA,DofSum,*el,tmpVec_size);
  }
  if (locked) {
    throw FlxException_Crude("flxFaceList::countDOF");
  }
  locked = true;
}

void flxFaceList::assembleDOF(tnlong& DofIndexFree, tnlong& FixPreIndex, tnlong& FixZeroIndex)
{
  for (std::map<tuint, flxFace*>::iterator pos = box.begin(); pos != box.end(); ++pos) {
    pos->second->assembleDOF(DofIndexFree,FixPreIndex,FixZeroIndex);
  }
}

void flxFaceList::assembleStf(flxStiffnessMtxBase& stfmtx)
{
  tVec tmpVec((pow2(tmpVec_size)+tmpVec_size)/2);
  for (std::map<tuint, flxFace*>::iterator pos = box.begin(); pos != box.end(); ++pos) {
    flxFace* fac = pos->second;
    RFbox->set_Current_Element_KL(fac);
    fac->assembleStf(stfmtx,&tmpVec);
  }
}

void flxFaceList::assembleForces(flxStiffnessMtxBase& stfmtx)
{
  tVec tmpVec(tmpVec_size);
  for (std::map<tuint, flxFace*>::iterator pos = box.begin(); pos != box.end(); ++pos) {
    flxFace* fac = pos->second;
    RFbox->set_Current_Element_KL(fac);
    fac->assembleForces(stfmtx,&tmpVec);
  }
}

void flxFaceList::updateDisplVec(const flxStiffnessMtxBase& stfmtx)
{
  for (std::map<tuint, flxFace*>::iterator pos = box.begin(); pos != box.end(); ++pos) {
    pos->second->updateDisplVec(stfmtx);
  }
}

void flxFaceList::insert(const tuint index, flxFace* face, const bool errSerious)
{
  std::pair<tuint, flxFace*>Element(index, face);
  if ( ! box.insert(Element).second ) {
    delete face;
    std::ostringstream ssV;
    ssV << "Face '" << index << "' has already been defined.";
    FlxError(errSerious, "flxFaceList::insert", ssV.str() );
  }
}

flxFace* flxFaceList::get(const tuint index, const bool throwErr, const bool errSerious) const
{
  std::map<tuint, flxFace*>::const_iterator pos = box.find(index);
  if ( pos != box.end() ) {
    return pos->second;
  } else {
    if (throwErr) {
      std::ostringstream ssV;
      ssV << "Face '" << index << "' does not exist.";
      FlxError(errSerious,"flxFaceList::get_1", ssV.str() ); return NULL; // dummy-return
    } else {
      return NULL;
    }
  }
}

const tuint flxFaceList::get_index(const flxFace*const face) const
{
  for (std::map<tuint, flxFace*>::const_iterator pos = box.begin(); pos != box.end(); ++pos) {
    if (pos->second == face) {
      return pos->first;
    }
  }
  std::ostringstream ssV;
  ssV << "The given face is not in the list.";
  throw FlxException("flxFaceList::get_index", ssV.str() );
}

flxFace* flxFaceList::find(const flxDOFf* DOF, const bool throwErr) const
{
  const FlxDof::dof dofDir = DOF->get_dofDir();
  for (std::map<tuint, flxFace*>::const_iterator pos = box.begin(); pos != box.end(); ++pos) {
    if ( pos->second->find_dof(dofDir,false,false) == DOF) return pos->second;
  }
  if (throwErr) {
    std::ostringstream ssV;
      ssV << "Face with requested DOF does not exist.";
      throw FlxException("flxFaceList::get_2", ssV.str() );
  } else {
    return NULL;
  }
}

void flxFaceList::print(std::ostream& sout) const
{
  for (std::map<tuint, flxFace*>::const_iterator pos = box.begin(); pos != box.end(); ++pos) {
    flxEdge** fel = pos->second->get_EdgLst();
    sout << "Edge " << pos->first;
    for (tuint i=0;i<4;++i) {
      sout << " " << el->get_index(fel[0]);
    }
    sout << std::endl;
  }
}

flxBox::flxBox(flxFace* f0, flxFace* f1, flxFace* f2, flxFace* f3, flxFace* f4, flxFace* f5) : DegStd(0),DegCorr(0)
{
  // face list
    FacLst[0] = f0;
    FacLst[1] = f1;
    FacLst[2] = f2;
    FacLst[3] = f3;
    FacLst[4] = f4;
    FacLst[5] = f5;
  // edge list
    EdgLst[0] = get_common_edg(*f0,*f5);
    EdgLst[1] = get_common_edg(*f0,*f1);
    EdgLst[2] = get_common_edg(*f0,*f4);
    EdgLst[3] = get_common_edg(*f0,*f3);
    EdgLst[4] = get_common_edg(*f2,*f5);
    EdgLst[5] = get_common_edg(*f2,*f1);
    EdgLst[6] = get_common_edg(*f2,*f4);
    EdgLst[7] = get_common_edg(*f2,*f3);
    EdgLst[8] = get_common_edg(*f3,*f5);
    EdgLst[9] = get_common_edg(*f1,*f5);
    EdgLst[10] = get_common_edg(*f1,*f4);
    EdgLst[11] = get_common_edg(*f3,*f4);
  // vertex list
    VtxLst[0] = get_common_vtx(*EdgLst[0],*EdgLst[3]);
    VtxLst[1] = get_common_vtx(*EdgLst[0],*EdgLst[1]);
    VtxLst[2] = get_common_vtx(*EdgLst[2],*EdgLst[1]);
    VtxLst[3] = get_common_vtx(*EdgLst[2],*EdgLst[3]);
    VtxLst[4] = get_common_vtx(*EdgLst[4],*EdgLst[7]);
    VtxLst[5] = get_common_vtx(*EdgLst[4],*EdgLst[5]);
    VtxLst[6] = get_common_vtx(*EdgLst[6],*EdgLst[5]);
    VtxLst[7] = get_common_vtx(*EdgLst[6],*EdgLst[7]);
  // get mid-point of box
    flxPoint pm;
    for (tuint i=0;i<8;++i) {
      pm += (VtxLst[i]->get_point());
    }
    pm*=0.125;
    xm = pm.get_x();
    ym = pm.get_y();
    zm = pm.get_z();
  // check if all edges of the box are parallel to the axis of the coordinate system
    for (tuint i=0;i<4;++i) {
      FacLst[i]->check_OrthPar();
    }
  // determines the transformation matrix
    transmtx[0].dir = EdgLst[0]->get_parallel_axis();
    transmtx[0].pomh = (VtxLst[1]->get_point()-VtxLst[0]->get_point())[transmtx[0].dir]/2.0;
    transmtx[1].dir = EdgLst[3]->get_parallel_axis();
    transmtx[1].pomh = (VtxLst[3]->get_point()-VtxLst[0]->get_point())[transmtx[1].dir]/2.0;
    transmtx[2].dir = EdgLst[8]->get_parallel_axis();
    transmtx[2].pomh = (VtxLst[4]->get_point()-VtxLst[0]->get_point())[transmtx[2].dir]/2.0;
  // compute detJ
    detJ_const = fabs(transmtx[0].pomh*transmtx[1].pomh*transmtx[2].pomh);
}

flxBox::~flxBox()
{

}

flxEdge* flxBox::get_common_edg(const flxFace& f1, const flxFace& f2) const
{
  bool found = false; tuint j,ID=0;
  for (tuint i=0;i<4;++i) {
    const flxEdge* e1 = &f1.get_edge(i);
    for (j=0;j<4;++j) {
      if (e1==&f2.get_edge(j)) break;
    }
    if (j==4) continue;
    if (found) {
      throw FlxException("flxBox::get_common_edg_1","Problem with the geometrical definition of the box.");
    } else {
      found = true;
      ID = i;
    }
  }
  if (!found) {
    throw FlxException("flxBox::get_common_edg_2","Faces do not have a common vertex.");
  }
  return &(f1.get_edge(ID));
}

void flxBox::update_globalConst(flxPoint& lxp) const
{
  *LX = lxp.get_x();
  *(LX-1) = lxp.get_y();
  *(LX-2) = lxp.get_z();
  flxPoint p(xm,ym,zm);
  tdouble* xp = &(p.id_fast(0));
  for (tuint i=0;i<3;++i) {
    xp[transmtx[i].dir] += lxp.id_fast(i)*transmtx[i].pomh;
  }
  p.set_global();
}

void flxBox::set_DegStd(const tuint i)
{
  if (i>DegStd) {
    DegStd=i;
    for (tuint j=0;j<6;++j) FacLst[j]->set_DegStd(i);
  }
}

void flxBox::set_DegCorr(const tuint i)
{
  if (i>DegCorr) {
    DegCorr=i;
    for (tuint j=0;j<6;++j) FacLst[j]->set_DegStd(i);
  }
}

flxElement_KL_base* flxBox::create_KL_element(FlxKLelementProp_3D* KLelProp)
{
  return new flxElement_KL_3D(this,*KLelProp);
}

const bool flxBox::map2local(const flxPoint& p) const
{
  throw FlxException_NotImplemented("flxBox::map2local");
}

void flxBox::set_Const(FlxConstantBox& ConstantBox)
{
  LX = ConstantBox.get("lx",true);
}

flxBoxList::flxBoxList()
{
  #if FLX_DEBUG
    ++instance;
    if (instance>1) throw FlxException_Crude("flxBoxList::flxBoxList");
  #endif
}

flxBoxList::~flxBoxList()
{
  for (std::map<tuint, flxBox*>::iterator pos = box.begin(); pos != box.end(); ++pos) {
    delete pos->second;
  }
  #if FLX_DEBUG
    --instance;
  #endif
}

void flxBoxList::refineMesh()
{
  return;
}

void flxBoxList::countDOF(tnlong& DofA, tnlong& FixA, tnlong& preDispA, tnlong& DofSum)
{
  return;
  if (locked) {
    throw FlxException_Crude("flxFaceList::countDOF");
  }
  locked = true;
}

void flxBoxList::assembleDOF(tnlong& DofIndexFree, tnlong& FixPreIndex, tnlong& FixZeroIndex)
{
  return;
}

void flxBoxList::assembleStf(flxStiffnessMtxBase& stfmtx)
{
  return;
}

void flxBoxList::assembleForces(flxStiffnessMtxBase& stfmtx)
{
  return;
}

void flxBoxList::updateDisplVec(const flxStiffnessMtxBase& stfmtx)
{
  return;
}

void flxBoxList::insert(const tuint index, flxBox* boxV, const bool errSerious)
{
  std::pair<tuint, flxBox*>Element(index, boxV);
  if ( ! box.insert(Element).second ) {
    delete boxV;
    std::ostringstream ssV;
    ssV << "Box '" << index << "' has already been defined.";
    FlxError(errSerious, "flxBoxList::insert", ssV.str() );
  }
}

flxBox* flxBoxList::get(const tuint index, const bool throwErr, const bool errSerious) const
{
  std::map<tuint, flxBox*>::const_iterator pos = box.find(index);
  if ( pos != box.end() ) {
    return pos->second;
  } else {
    if (throwErr) {
      std::ostringstream ssV;
      ssV << "Box '" << index << "' does not exist.";
      FlxError(errSerious,"flxBoxList::get_1", ssV.str() ); return NULL; // dummy-return
    } else {
      return NULL;
    }
  }
}

const tuint flxBoxList::get_index(const flxBox*const boxV) const
{
  for (std::map<tuint, flxBox*>::const_iterator pos = box.begin(); pos != box.end(); ++pos) {
    if (pos->second == boxV) {
      return pos->first;
    }
  }
  std::ostringstream ssV;
  ssV << "The given box is not in the list.";
  throw FlxException("flxBoxList::get_index", ssV.str() );
}

void flxBoxList::print(std::ostream& sout) const
{
  for (std::map<tuint, flxBox*>::const_iterator pos = box.begin(); pos != box.end(); ++pos) {
    flxFace* const* bfl = pos->second->get_FacLst();
    sout << "Face " << pos->first;
    for (tuint i=0;i<6;++i) {
      sout << " " << fl->get_index(bfl[0]);
    }
    sout << std::endl;
  }
}





