Skip to content
Snippets Groups Projects
Select Git revision
  • 3fffec689286a77c4dd634c30d930f4edef3c525
  • master default protected
  • ujwal_21_08_2024
  • dev_mm_pf
  • cyrielle
  • vinayak
  • dev_mm_torchSCRU
  • debug_mm_pf
  • newStructureNonLocal
  • Mohamed_stochasticDMN
  • dev_mm_bench
  • stochdmn
  • revert-351ff7aa
  • ujwal_29April2024
  • dev_mm_ann
  • mohamed_vevp
  • ujwal_debug
  • ujwal_2ndApril2024
  • ujwal_October_2023
  • gabriel
  • SFEM
  • v4.0
  • v3.2.3_multiplePhase
  • v3.5
  • v3.3.2
  • v3.4
  • v3.3
  • ver3.2
  • verJulienWork
  • ver3.1
  • ver2
  • ver1.1.2
  • ver1.1.1
  • ver1.1
34 results

nonLinearMechSolver.cpp

Blame
  • nonLinearMechSolver.cpp 553.63 KiB
    
    //
    //
    // Description: Non linear solver for mechanic problems
    //              quasi-static implicit scheme & dynamic explicit Hulbert-Chung scheme
    //
    // Author:  <Gauthier BECKER>, (C) 2011
    //
    // Copyright: See COPYING file that comes with this distribution
    //
    //
    
    #include <string.h>
    #include <cstdlib>
    #include <ctime>
    #include <time.h>
    #include <cstdio>
    #include <vector>
    #include <sys/stat.h>
    #include "GmshConfig.h"
    #include "meshPartition.h"
    #include "nonLinearMechSolver.h"
    #include "linearSystemCSR.h"
    #include "linearSystemPETSc.h"
    #include "linearSystemGmm.h"
    #include "Numeric.h"
    #include "nlTerms.h"
    #include "solverAlgorithms.h"
    #include "quadratureRules.h"
    #include "MPoint.h"
    #include "ipstate.h"
    #include "ipField.h"
    #include "GModel.h"
    #include "explicitHulbertChungPETSc.h"
    #include "explicitHulbertChungBLAS.h"
    #include "nlsolAlgorithms.h"
    #include "nonLinearMechSolver.h"
    #include "MTriangle.h"
    #include "MQuadrangle.h"
    #include "energyField.h"
    #include "nlsolAlgorithms.h"
    #include "solverAlgorithms.h"
    #include "quadratureRules.h"
    #include "MPoint.h"
    #include "pbcDofManager.h"
    #include "staticDofManager.h"
    #include "dofManagerMultiSystems.h"
    #include "contactTerms.h"
    #include "NonLinearSolverConfig.h"
    #include "currentConfig.h"
    #include "MInterfaceLine.h"
    #include "fragment.h"
    #include "OS.h"
    #include "pathFollowingSystem.h"
    #include "StringUtils.h"
    #include "Context.h"
    #include "HighOrder.h"
    #include "nonLinearSystemGmm.h"
    #include "nonLinearSystemGMRESk.h"
    #include "implicitHulbertChungPETSc.h"
    #include "pathFollowingSystemPETSc.h"
    #include "pbcPathFollowingSystemPETSc.h"
    #include "generalLocalControlBasedPathFollowingSystemPETSc.h"
    #include "eigenSolver.h"
    #include "eigenModeView.h"
    #include "eigenVectorData.h"
    #include "failureDetection.h"
    //#include "linearSystemGMRESk.h"
    #include "restartManager.h"
    #include "geometryRotation.h"
    #include "interfaceReferenceLocalBasis.h"
    #include "numericalMaterial.h"
    #include "criticalTimeStepElements.h"
    #include "generalHyperellipticPathFollowingSystemPETSc.h"
    #if defined(HAVE_PLUGINS)
    #include "PluginManager.h"
    #endif //HAVE_PLUGINS
    #include "periodicBoundaryCondition.h"
    #include "ipFiniteStrain.h"
    
    #if defined(HAVE_POST)
    #include "PView.h"
    #include "PViewData.h"
    #endif
    
    #if defined(HAVE_MPI)
    #include "elementFilterMPI.h"
    #include "nlmpiAlgorithms.h"
    #endif // HAVE_MPI
    
    int clustersData::realClusterSize() const
    {
      int realSize =0;
      for (std::map<int, std::map<int, double> >::const_iterator itm = clusterMap.begin(); itm != clusterMap.end(); itm++)
      {
        int clusterId = itm->first;
        if (clusterId >=0)
        {
          realSize ++;
        }
      }
      return realSize;
    }
    
    void clustersData::loadFromFile(const std::string fileName)
    {
      clusterDataFileName = fileName;
      clusterIndexMap.clear();
      materialMap.clear();
      weightMap.clear();
      clusterMap.clear();
      
      Msg::Info("read clusters data from file: %s",fileName.c_str());
      FILE* fp = fopen(fileName.c_str(),"r");
      if (fp !=NULL) {
        printf("start reading file: %s\n",fileName.c_str());
        if(!feof(fp))
        {
          char what1[256];
          char what2[256];
          char what3[256];
          char what4[256];
          int ok = fscanf(fp, "%s %s %s %s", what1, what2, what3, what4);
          printf("%s %s %s %s\n",what1,what2,what3,what4);
        }
        while (1){
          if(feof(fp))
            break;
          int ele, gp, indx, matNum;
          if (fscanf(fp, "%d %d %d %d", &ele,&gp,&indx,&matNum)==4)
          {
            int type = numericalMaterialBase::createTypeWithTwoInts(ele,gp);
            clusterIndexMap[type] = indx;
            materialMap[type] = matNum;
            //printf("%d %d %d %d\n",ele,gp,indx,matNum);
          }
        }
        printf("done reading file: %s\n",fileName.c_str());
        fclose(fp);
      }
      else
      {
        Msg::Error("File %s does not exist !!!",fileName.c_str());
        Msg::Exit(0);
      }
      Msg::Info("done reading clusters data from file: %s",fileName.c_str());
    }
    
    void AvergageCluster::getAverageValue(const IPField* ipf, std::vector<double>& val) const
    {
      if (clusterData == NULL)
      {
        Msg::Error("cluster data is not available");
        Msg::Exit(0);
      }
      
      int nClusters = clusterData->realClusterSize();
      if (nClusters >0)
      {
        val.resize(nClusters);
        for (std::map<int, std::map<int, double> >::const_iterator itm = clusterData->clusterMap.begin(); itm != clusterData->clusterMap.end(); itm++)
        {
          int cluster = itm->first;
          if (cluster >=0)
          {
            const std::map<int, double>& weight = itm->second;
            val[cluster] = 0;
            double volume = 0.;
            for (std::map<int, double>::const_iterator it = weight.begin(); it != weight.end(); it++)
            {
              int type = it->first;
              double w = it->second;
              int eleNum, gp;
              numericalMaterialBase::getTwoIntsFromType(type,eleNum,gp);
              const AllIPState::ipstateElementContainer *vips = ipf->getAips()->getIPstate(eleNum);
              const IPVariable* ipv = (*vips)[gp]->getState(IPStateBase::current);
              val[cluster] += (w*ipv->get(ipVal));
              volume += w;
            }
            if (volume ==0.) 
            {
              val[cluster] = 0.;
            }
            else
            {
              val[cluster] /= volume;
            }
          }
        }
      }
    };
    
    TimeManager::TimeManager():
    _timeSeries(),_numStepSeries(),
    _startTime(0.), _endTime(0.), _numStep(1), _tol(1e-6),
    _lastTime(0.),
    _lastIterationIndex(0.),
    _timeStep(0.),
    _previousTimeStep(0.),
    _maxTimeStepReached(0.),
    _upperBoundTimeStep(std::numeric_limits<double>::max()),
    _lowerBoundTimeStep(0.),
    _timeStepAdaptation(false),
    _numNROptimal(0),
    _expNROptimal(0.),
    _maxNRite(15),
    _timeStepFactorReduction(2.),
    _maxAttemptStepReduction(6),
    _niteTimeStepIncrease(5),
    _numberTimeStepReduction(0),
    _timeStepIncreaseBecauseOfSolverFailure(false)
    {}
    
    TimeManager::TimeManager(const TimeManager& src):
    _timeSeries(src._timeSeries),_numStepSeries(src._numStepSeries),
    _startTime(src._startTime), _endTime(src._endTime), _numStep(src._numStep),  _tol(src._tol),
    _lastTime(src._lastTime),
    _lastIterationIndex(src._lastIterationIndex),
    _timeStep(src._timeStep),
    _previousTimeStep(src._previousTimeStep),
    _maxTimeStepReached(src._maxTimeStepReached),
    _upperBoundTimeStep(src._upperBoundTimeStep),
    _lowerBoundTimeStep(src._lowerBoundTimeStep),
    _timeStepAdaptation(src._timeStepAdaptation),
    _numNROptimal(src._numNROptimal),
    _expNROptimal(src._expNROptimal),
    _maxNRite(src._maxNRite),
    _timeStepFactorReduction(src._timeStepFactorReduction),
    _maxAttemptStepReduction(src._maxAttemptStepReduction),
    _niteTimeStepIncrease(src._niteTimeStepIncrease),
    _numberTimeStepReduction(src._numberTimeStepReduction),
    _timeStepIncreaseBecauseOfSolverFailure(src._timeStepIncreaseBecauseOfSolverFailure)
    {
      
    }
    void TimeManager::clearNumStepSeries()
    {
      _timeSeries.clear();
      _numStepSeries.clear();
    }
    void TimeManager::setNumStepTimeInterval(double t, int nstep)
    {
      if (t > _endTime) _endTime = t;
      _timeSeries.push_back(t);
      _numStepSeries.push_back(nstep);
    }
    void TimeManager::reset()
    {
      clearNumStepSeries();
      _endTime = _startTime; 
      _numStep = 1;
      // no simulation before
      _lastTime = _startTime;
      _lastIterationIndex = 0; 
      // previous data  
      _previousTimeStep = 0.; 
      _maxTimeStepReached = 0.;
      _timeStep = 0.;
    };
    
    
    void TimeManager::activateTimeStepAdaptation(bool adap, int numNROptimal, double exp, double maximalStep, double minTimeStep)
    {
      if (adap)
      {
        Msg::Info("time step adaptation is activated");
        _timeStepAdaptation = adap;
        _numNROptimal = numNROptimal;
        _expNROptimal = exp;
        _upperBoundTimeStep = maximalStep;
        _lowerBoundTimeStep = minTimeStep;
      };
    };
    
    void TimeManager::setManageTimeStepSafeguard(const int miteNR,const int iteIncreaseTS, const double redfactor, const int maxAttemptRedFactor)
    {
      _maxNRite = miteNR;
      _niteTimeStepIncrease = iteIncreaseTS;
      _timeStepFactorReduction = redfactor;
      _maxAttemptStepReduction = maxAttemptRedFactor;
      if (_timeStepFactorReduction < 1.)
      {
        _timeStepFactorReduction= 1.;
        Msg::Warning("step factor cannot be smaller than 1");
      }
    };
    
    void TimeManager::setMaximalNumberOfFails(int maxnbFails)
    {
      _maxAttemptStepReduction = maxnbFails;
      Msg::Info("set maximal number of fails: %d",_maxAttemptStepReduction);
    }
    
    
    void TimeManager::restart()
    {
      //restartManager::restart(_numNROptimal}; // num optimal
      //restartManager::restart(_numNROptimalLocal}; // number optimal for local
      //restartManager::restart(_expNROptimal};
      //restartManager::restart(_timeStepAdaptation}; // true if path following step is adapted with number NR
      restartManager::restart(_lastTime);
      restartManager::restart(_lastIterationIndex);
    }
    
    bool TimeManager::willArchive(double curtime, int numstep) const {return true;}
    bool TimeManager::reachEndTime() const 
    {
      if (fabs(_lastTime -_endTime) > _tol*_endTime)
      {
        return false;
      }
      else
      {
        return true;
      }
    }
    
    void TimeManager::reduceTimeStep()
    {
      _numberTimeStepReduction ++;
      _timeStep /= _timeStepFactorReduction;
      Msg::Info("Reduced time step =  %e",_timeStep);
    }
    
    void TimeManager::setTimeStep(double dt)
    {
      _timeStep = dt;
      //Msg::Info("modified time step =  %e",_timeStep);
    };
    
    double TimeManager::getStartTime() const 
    { 
      return _startTime;
    }
    
    double TimeManager::getEndTime() const
    {
      return _endTime;
    }
    int TimeManager::getNumSteps() const
    {
      return _numStep;
    };
    
    void TimeManager::setStartTime(double starttime)
    {
    
        _startTime = starttime;
    };
    
    void TimeManager::setEndTime(double endtime)
    {
    
      _endTime = endtime;
    };
    
    void TimeManager::setNumSteps(int st)
    {
      _numStep = st;
    }
    
    double TimeManager::getLastTime() const
    {
      return _lastTime;
    }
    
    int TimeManager::getLastIterationIndex() const 
    {
      return _lastIterationIndex;
    };
    
    double TimeManager::getTimeStep() const 
    {
      return _timeStep;
    };
    
    
    int TimeManager::getMaxNbIterations() const {return _maxNRite;};
    int TimeManager::getMaxNbFails() const{return _maxAttemptStepReduction;};
    int TimeManager::getNbFails() const {return _numberTimeStepReduction;}
    
    
    void TimeManager::computeTimeStepForNextSolving(int niteNR) 
    {
      // from data on _lastTim, _timeStep, 
      int lastTimeInterVal = -1;
      int sizeTimeSeries = _timeSeries.size();
      for (int i=0; i< sizeTimeSeries-1; i++)
      {
        if (fabs(_lastTime-_timeSeries[i]) < _tol*_endTime)
        {
          lastTimeInterVal = i;
          break;
        }
      }
      
      if (lastTimeInterVal == -1)
      {
        if (_timeStepAdaptation)
        {
          // change current time step with optimal number
          _timeStep = _previousTimeStep*pow((double)_numNROptimal/(double)niteNR,_expNROptimal);
    
          if (_timeStep > _upperBoundTimeStep)
          {
            _timeStep = _upperBoundTimeStep;
            Msg::Info("New time step for next solving using max time step =  %e",_timeStep);
          }
          else if (_timeStep < _lowerBoundTimeStep)
          {
            _timeStep = _lowerBoundTimeStep;
            Msg::Info("fNew time step for next solving using min time step =  %e",_timeStep);
          }
          else
          {
            Msg::Info("New time step for next solving by time adaptation = %e",_timeStep);
          }
        }
        else
        {
          // increase time step due to reduce time step because of the solver fail 
          _timeStepIncreaseBecauseOfSolverFailure=false;
          if((niteNR <= _niteTimeStepIncrease) &&  (_numberTimeStepReduction > 0)) // increase time step if convergence in a few iteration
          {
            _timeStepIncreaseBecauseOfSolverFailure = true;
            _numberTimeStepReduction --;
            _timeStep *=_timeStepFactorReduction;
            if (_timeStepFactorReduction > 1.)
            {
              Msg::Info("Time step is increased due to convergence of Newton-Raphson in less than %d",_niteTimeStepIncrease);
            }
          }
        }
      }
      else
      {
        // start with timetep at other time interval
        _timeStep = (_timeSeries[lastTimeInterVal+1]- _timeSeries[lastTimeInterVal])/(_numStepSeries[lastTimeInterVal+1]);
        Msg::Info("start new time step = %e in time interval [%e %e] lastTime = %e",_timeStep,_timeSeries[lastTimeInterVal],_timeSeries[lastTimeInterVal+1],_lastTime);
      }  
      
      for (int i=0; i< sizeTimeSeries; i++)
      {
        if ( _lastTime+_tol*_endTime < _timeSeries[i]  and _lastTime+_timeStep > _timeSeries[i]+_tol*_endTime)
        {
          _timeStep = _timeSeries[i] -  _lastTime;
          Msg::Info("modify time step %e",_timeStep);
        }
      }
      
      // check if end time
      if (_lastTime + _timeStep >_endTime)
      {
        // end time will reach
        _timeStep = _endTime - _lastTime;
      }
    };
    
    bool TimeManager::withTimeStepAdaptation() const
    {
      return _timeStepAdaptation;
    }
    
    void TimeManager::saveTimeHistory() 
    {
      // update last time
      _lastTime += _timeStep;
      _lastIterationIndex ++;
      //
      _previousTimeStep = _timeStep;
      //
      if (_maxTimeStepReached < _timeStep)
      {
        _maxTimeStepReached = _timeStep;
      }
    };
    
    void TimeManager::initializeTimeSteppingPlan()
    {
      // no simulation before
      _lastTime = _startTime;
      _lastIterationIndex = 0;
      
      // previous data  
      _previousTimeStep = 0.; 
      _maxTimeStepReached = 0.;
      //
      _numberTimeStepReduction = 0; // test
      // 
      if (_timeSeries.size() == 0)
      {
        _timeStep = (_endTime-_startTime)/_numStep;
      }
      else
      {
        // modify total number of step;
        _numStep = 0;
        for (int i=0; i< _numStepSeries.size(); i++)
        {
          _numStep += _numStepSeries[i];
        }
        if (_endTime > _timeSeries.back())
        {
          double lastTimeStep = _timeSeries.back()/_numStep;
          _numStep += (int) round((_endTime-_timeSeries.back())/lastTimeStep);
        }
        _timeStep = _timeSeries[0]/_numStepSeries[0]; // start as first interval
      }
    };
    
    PointwiseTimeManager::PointwiseTimeManager(double t):TimeManager(),_savePoints(),_tol(t){}
    
    void PointwiseTimeManager::put(double val)
    {
      _savePoints.push_back(val);
    }
    
    bool PointwiseTimeManager::willArchive(double curtime, int numstep) const
    {
      // the most stupid algorithm
      for (int i=0; i< _savePoints.size(); i++)
      {
        if (fabs(curtime - _savePoints[i]) < _tol)
        {
          printf("saving file: \n");
          return true;
        }
      }
      return false;
    }
    
    StepwiseBeginTimeManager::StepwiseBeginTimeManager(int nstepArchBegin):TimeManager(),_nstepArchBegin(nstepArchBegin){}
    
    bool StepwiseBeginTimeManager::willArchive(double curtime, int numstep) const
    {
    	if (numstep > _nstepArchBegin)
        {
    		printf("saving file: \n");
    		return true;
        } else {
    		return false;
    	}
    }
    
    pathFollowingManager::pathFollowingManager():
    _pathFollowingMethod(GLOBAL_ARC_LENGTH_BASED),
    _controlTypePathFollowing(-1),
    _correctionMethodPathFollowing(-1),
    _tranversalCriterionPathFollowing(-1),
    _solverTypePathFollowing(-1),
    _pathFollowingEqRatio(1.),
    _switchedControlType(false),
    _pathFollowingSwitchCriterion(0.),
    _pathFollowingIncrementType(DISSIPATION_ENERGY),
    _pathFollowingLocation(BULK_INTERFACE),
    
    _localStep(0.),
    _localStepPrev(0.),
    _localStepMinimal(0.),
    _localStepMaximal(1e100),
    _arcLengthStep(0.),
    _arcLengthStepPrev(0.),
    _arcLengthStepMinimal(0.),
    _arcLengthStepMaximal(1e100),
    _pfStepAdaptation(false),
    _numNROptimal(0),
    _expNROptimal(0.),
    _numNROptimalLocal(0)
    {}
    
    pathFollowingManager::pathFollowingManager(const pathFollowingManager &src)
    {
      _pathFollowingMethod = src._pathFollowingMethod;
    
      // if _pathFollowingMethod=GLOBAL_ARC_LENGTH_BASED or HYPERELLIPTIC_BASED is used,
      _controlTypePathFollowing = src._controlTypePathFollowing; // control type
      _correctionMethodPathFollowing = src._correctionMethodPathFollowing; // correction method
      _tranversalCriterionPathFollowing = src._tranversalCriterionPathFollowing; // method to correctly estimate load paramater in the predictor of path following constrain as two solutions exists
      _solverTypePathFollowing = src._solverTypePathFollowing; // solve method two use,
      _pathFollowingEqRatio = src._pathFollowingEqRatio; // equa ratio
      _hyperellipticControlComp = src._hyperellipticControlComp; // comp used in hyperelliptic control
      //
      // if _pathFollowingMethod = LOCAL_BASED
      //bool _switchedControlType;
      _pathFollowingSwitchCriterion = src._pathFollowingSwitchCriterion;
      _pathFollowingIncrementType = src._pathFollowingIncrementType;
      _pathFollowingLocation = src._pathFollowingLocation;
      //
      /*TIME STEP AND PATHFOLLOWING STEP MANAGEMENT*/
    
      // for path following increment (arc-length or local == dissipation increment)
      _localStep = src._localStep; // to local cr control
      _localStepPrev = src._localStepPrev;
      _localStepMinimal = src._localStepMinimal;
      _localStepMaximal = src._localStepMaximal;
    
      _arcLengthStep = src._arcLengthStep; // for arc-length control
      _arcLengthStepPrev = src._arcLengthStepPrev;
      _arcLengthStepMinimal = src._arcLengthStepMinimal;
      _arcLengthStepMaximal = src._arcLengthStepMaximal;
      
      _pfStepAdaptation = src._pfStepAdaptation;
      _numNROptimal = src._numNROptimal;
      _expNROptimal = src._expNROptimal;
      _numNROptimalLocal = src._numNROptimalLocal;
    }
    
    pathFollowingManager& pathFollowingManager::operator = (const pathFollowingManager &src)
    {
      //_pathFollowingMethod holds the method of path following, 0- GLOBAL_ARC_LENGTH_BASED , 1 - LOCAL_BASED, and 2- HYPERELLIPTIC_BASED
      //GLOBAL_ARC_LENGTH_BASED based on the general path following constraint type
      //
      //LOCAL_BASED is a combination of LOAD CONTROL+ DISSIPATION CONTROL after the onset of dissipation
      //
      //HYPERELLIPTIC_BASED uses several particular DOFs insteads of all DOF used in GLOBAL_ARC_LENGTH_BASED to build the path following constraint
      //
      
      
      _pathFollowingMethod = src._pathFollowingMethod;
    
      // if _pathFollowingMethod=GLOBAL_ARC_LENGTH_BASED or HYPERELLIPTIC_BASED is used,
      _controlTypePathFollowing = src._controlTypePathFollowing; // control type
      _correctionMethodPathFollowing = src._correctionMethodPathFollowing; // correction method
      _tranversalCriterionPathFollowing = src._tranversalCriterionPathFollowing; // method to correctly estimate load paramater in the predictor of path following constrain as two solutions exists
      _solverTypePathFollowing = src._solverTypePathFollowing; // solve method two use,
      _pathFollowingEqRatio = src._pathFollowingEqRatio; // equa ratio
      _hyperellipticControlComp = src._hyperellipticControlComp; // comp used in hyperelliptic control
      //
      // if _pathFollowingMethod = LOCAL_BASED
      //bool _switchedControlType;
      _pathFollowingSwitchCriterion = src._pathFollowingSwitchCriterion;
      _pathFollowingIncrementType = src._pathFollowingIncrementType;
      _pathFollowingLocation = src._pathFollowingLocation;
      //
      /*TIME STEP AND PATHFOLLOWING STEP MANAGEMENT*/
    
      // for path following increment (arc-length or local == dissipation increment)
      _localStep = src._localStep; // to local cr control
      _localStepPrev = src._localStepPrev;
      _localStepMinimal = src._localStepMinimal;
      _localStepMaximal = src._localStepMaximal;
    
      _arcLengthStep = src._arcLengthStep; // for arc-length control
      _arcLengthStepPrev = src._arcLengthStepPrev;
      _arcLengthStepMinimal = src._arcLengthStepMinimal;
      _arcLengthStepMaximal = src._arcLengthStepMaximal;
      
      _pfStepAdaptation = src._pfStepAdaptation;
      _numNROptimal = src._numNROptimal;
      _expNROptimal = src._expNROptimal;
      _numNROptimalLocal = src._numNROptimalLocal;
      
      return *this;
    };
    
    void pathFollowingManager::reducePathFollowingStep(const TimeManager* tm)
    {
       // update data for next solve in case of path following
      if (_pathFollowingMethod == LOCAL_BASED )
      {
        if (!_switchedControlType)
        {
          _arcLengthStep /= tm->_timeStepFactorReduction;
          Msg::Info("Reduced path following step step =  %e",_arcLengthStep);
        }
        else
        {
          _localStep /= tm->_timeStepFactorReduction;
          Msg::Info("Reduced path following step step =  %e",_localStep);
        }
      }
      else if (_pathFollowingMethod == GLOBAL_ARC_LENGTH_BASED or _pathFollowingMethod == HYPERELLIPTIC_BASED)
      {
        _arcLengthStep /= tm->_timeStepFactorReduction;
        Msg::Info("Reduced path following step step =  %e",_arcLengthStep);
      }
      else
        Msg::Error("missing case nonLinearMechSolver::oneStepPreSolvePathFollowing");
        
    }
    
    double pathFollowingManager::getCurrentPathFollowingIncrement() const
    {
      double currentPathFollowingIncr = 0;
      if (_pathFollowingMethod == LOCAL_BASED )
      {
        if (!_switchedControlType)
        {
          currentPathFollowingIncr = _arcLengthStep;
        }
        else
        {
          currentPathFollowingIncr = _localStep;
        }
    	}
    	else if (_pathFollowingMethod == GLOBAL_ARC_LENGTH_BASED or _pathFollowingMethod == HYPERELLIPTIC_BASED)
      {
        currentPathFollowingIncr = _arcLengthStep;
    	}
    	else
    		Msg::Error("missing case nonLinearMechSolver::oneStepPreSolvePathFollowing");
      
      return currentPathFollowingIncr;
    };
    
    void pathFollowingManager::computePathFollowingIncrementForNextSolving(const nonLinearMechSolver* solver, int niteNR, const TimeManager* tm)
    {
      // update data for next solve in case of path following
      if (_pathFollowingMethod == LOCAL_BASED )
      {
        // local control always starts with load control
        if (!_switchedControlType)
        {
          if (solver->localPathFollowingSwitching())
          {
            Msg::Info("local control is switched in next solve");
            _switchedControlType = true;
          }
        }
        
        if (_pfStepAdaptation)
        {
          if (!_switchedControlType)
          {
            _arcLengthStep = _arcLengthStepPrev*pow((double)_numNROptimal/(double)niteNR,_expNROptimal);
          }
          else
          {
            _localStep = _localStepPrev*pow((double)_numNROptimalLocal/(double)niteNR,_expNROptimal);
            // check if local step is out of range
            if (_localStep < _localStepMinimal) _localStep = _localStepMinimal;
            else if (_localStep > _localStepMaximal) _localStep = _localStepMaximal;
          }
        }
        else
        {
          
          // increase time step due to reduce time step because of the solver fail 
          if(tm->_timeStepIncreaseBecauseOfSolverFailure) // increase time step if convergence in a few iteration
          {
            if (!_switchedControlType)
            {
              _arcLengthStep *= tm->_timeStepFactorReduction;
            }
            else
            {
              _localStep *= tm->_timeStepFactorReduction;
            }
          }
        }
      }
      else if (_pathFollowingMethod == GLOBAL_ARC_LENGTH_BASED or _pathFollowingMethod == HYPERELLIPTIC_BASED)
      {
        if (_pfStepAdaptation)
        {
          _arcLengthStep = _arcLengthStepPrev*pow((double)_numNROptimal/(double)niteNR,_expNROptimal);
          if (_arcLengthStep < _arcLengthStepMinimal) _arcLengthStep = _arcLengthStepMinimal;
          else if (_arcLengthStep > _arcLengthStepMaximal) _arcLengthStep = _arcLengthStepMaximal;
          
        }
        else
        {
          
          // increase time step due to reduce time step because of the solver fail 
          if(tm->_timeStepIncreaseBecauseOfSolverFailure) // increase time step if convergence in a few iteration
          {
            _arcLengthStep *=tm->_timeStepFactorReduction;
          }
        }
      }
      else
        Msg::Error("missing case nonLinearMechSolver::oneStepPreSolvePathFollowing");
    }
    
    void pathFollowingManager::saveIncrementHistory(const nonLinearMechSolver* solver)
    {
      if ((_pathFollowingMethod == LOCAL_BASED) && (!_switchedControlType) && _pfStepAdaptation)
      {
        // compute local step
        _localStep = solver->computeLocalPathFollowingStep();
      }
      _localStepPrev = _localStep;
      _arcLengthStepPrev = _arcLengthStep;
    }
    
    void pathFollowingManager::restart()
    {
      //restartManager::restart(_pathFollowing); // true if considering pathFollowing
      //int _macroControlTypePathFollowing;
      //int _correctionMethodPathFollowing;
      //int _tranversalCriterionPathFollowing;
      //int _solverTypePathFollowing;
      //double _pathFollowingEqRatio;
    
      //pathFollowingMethod _pathFollowingMethod; // 0- arclength based, 1 -
      restartManager::restart(_localStep); // to local cr control
      restartManager::restart(_localStepPrev); // to local cr control
      //double _localStepMinimal;
      //double _localStepMaximal;
    
      restartManager::restart(_arcLengthStep); // for arc-length control
      restartManager::restart(_arcLengthStepPrev); // previous
    
      //double _pathFollowingSwitchCriterion;
      restartManager::restart(_switchedControlType);
      //pathFollowingLocalIncrementType _pathFollowingIncrementType;
      //pathFollowingLocation _pathFollowingLocation;
    };
    
    NLS_MPI::NLS_MPI()
    {
      #if defined(HAVE_MPI)
      int flag;
      MPI_Initialized(&flag);
      if(!flag) MPI_Init(NULL, NULL);
      MPI_Comm_rank(MPI_COMM_WORLD, &commRank);
      MPI_Comm_size(MPI_COMM_WORLD, &commSize);
      MPI_Comm_set_errhandler(MPI_COMM_WORLD, MPI_ERRORS_RETURN);
      #endif
    };
    
    void NLS_MPI::Init()
    {
      #if defined(HAVE_MPI)
      int flag;
      MPI_Initialized(&flag);
      if(!flag) MPI_Init(NULL, NULL);
      MPI_Comm_rank(MPI_COMM_WORLD, &commRank);
      MPI_Comm_size(MPI_COMM_WORLD, &commSize);
      MPI_Comm_set_errhandler(MPI_COMM_WORLD, MPI_ERRORS_RETURN);
      #endif
    }
    
    int NLS_MPI::commRank = 0;
    int NLS_MPI::commSize = 1;
    int NLS_MPI::GetCommSize() {return commSize;}
    int NLS_MPI::GetCommRank() {return commRank;}
    void NLS_MPI::Barrier()
    {
    #if defined(HAVE_MPI)
      MPI_Barrier(MPI_COMM_WORLD);
    #endif
    }
    
    void NLS_MPI::Finalize()
    {
      #if defined(HAVE_MPI)
      int finalized;
      MPI_Finalized(&finalized);
      if (!finalized)
        MPI_Finalize();
      #endif
    };
    
    StiffnessModificationMonitoring::StiffnessModificationMonitoring(int ws)
    {
      if (ws == 0) 
      {
        _ws = IPStateBase::previous;
        Msg::Info("tangent of the previous converged solution is used");
      }
      else if (ws=1)
      {
        _ws = IPStateBase::current;
         Msg::Info("first iteration tangent of the current solution is used");
      }
      else
      {
        Msg::Error("ws = %s wrong, this value must be 0 or 1",ws);
      }
    }
    
    TimeBasedStiffnessModificationMonitoring::TimeBasedStiffnessModificationMonitoring(int ws) : StiffnessModificationMonitoring(ws)
    {
      _modificationAtTime.push_back(0.);
    };
    TimeBasedStiffnessModificationMonitoring::TimeBasedStiffnessModificationMonitoring(const TimeBasedStiffnessModificationMonitoring& src):
            StiffnessModificationMonitoring(src),_modificationAtTime(src._modificationAtTime){};
    TimeBasedStiffnessModificationMonitoring::~TimeBasedStiffnessModificationMonitoring(){}
    
    void TimeBasedStiffnessModificationMonitoring::modifyStiffnessAtTime(double t)
    {
      _modificationAtTime.push_back(t);
    }
    bool TimeBasedStiffnessModificationMonitoring::willModify(int iter, double time) const
    {
      // check validity
      double tol = 1e-6;
      for (int i=0; i< _modificationAtTime.size(); i++)
      {
        if (fabs(time-_modificationAtTime[i]) < tol*fabs(_modificationAtTime[i]))
        {
          Msg::Info("stiffness will be modified");
          return true;
        }
      }
      return false;
    };
    
    NumIterationBasedStiffnessModificationMonitoring::NumIterationBasedStiffnessModificationMonitoring(int ws) : 
        StiffnessModificationMonitoring(ws), 
        _numIterBetweenTwoModfication(0)
    {
      _modificationAtIteration.push_back(0);
    };
    NumIterationBasedStiffnessModificationMonitoring::NumIterationBasedStiffnessModificationMonitoring(const NumIterationBasedStiffnessModificationMonitoring& src):
            StiffnessModificationMonitoring(src),_modificationAtIteration(src._modificationAtIteration), _numIterBetweenTwoModfication(src._numIterBetweenTwoModfication){};
    NumIterationBasedStiffnessModificationMonitoring::~NumIterationBasedStiffnessModificationMonitoring(){}
    
    void NumIterationBasedStiffnessModificationMonitoring::modifyStiffnessAfterNumIterations(int numIterBetween)
    {
      _numIterBetweenTwoModfication = numIterBetween;
      Msg::Info("stiffness will be modified each %d iterations ",_numIterBetweenTwoModfication);
    }
    void NumIterationBasedStiffnessModificationMonitoring::modifyStiffnessAtIteration(int iter)
    {
      _modificationAtIteration.push_back(iter);
    }
    bool NumIterationBasedStiffnessModificationMonitoring::willModify(int iter, double time) const
    {
      // check validity
      double tol = 1e-6;
      for (int i=0; i< _modificationAtIteration.size(); i++)
      {
        if (fabs(time-(double)_modificationAtIteration[i]) < tol*fabs((double)_modificationAtIteration[i]))
        {
          Msg::Info("stiffness will be modified");
          return true;
        }
      }
      if (_numIterBetweenTwoModfication > 0)
      {
        if (iter%_numIterBetweenTwoModfication == 0)
        {
          Msg::Info("stiffness will be modified");
          return true;
        }
      }
      return false;
    };
    
      
    nonLinearMechSolver::nonLinearMechSolver(int tag) :
    pModel(NULL),
    _dim(0),
    _tag(tag),
    _mpiUserDom(0),
    _workingRank(0),
    _isPartitioned(false),
    _mapRanks(),
    _meshFileName(""),
    _timeManager(new TimeManager()),
    _solver_options(""),
    _GmshOneLabViewNum(0),
    
    pAssembler(NULL),
    _ipf(NULL),
    _ufield(NULL),
    _energField(NULL),
    
    whatSolver(Petsc),
    whatScheme(StaticLinear),
    
    _explicitOpts(),
    _tol(1.e-6),
    _absTol(1.e-12),
    _stiffEstimation(true),
    _stiffnessModification(true),
    _stiffnessModificationMonitoring(NULL),
    _iterativeNR(true),
    _lineSearch(false),
    
    _implicitOpts(),
    
    _pathFollowing(false),
    _pfManager(),
    
    _restartMshFileName("restart_"),
    _resetRestart(false),
    _disableResetRestart(false),
    _crackTrackingName(""),
    _crackTrackingFile(NULL),
    _fragmentationName(""),
    _fragmentationFile(NULL),
    
    _energyComputation(1),
    _fractureEnergyComputation(0),
    nsba(0),
    
    _collection(NULL),
    _previousInit(false),
    _notResetedBC(false),
    _notResetedContact(false),
    
    _strainMap(NULL),
    _endSchemeMonitoringObject(NULL),
    _selectiveObject(NULL),
    
    _bulkElementErosionFlag(false),
    _interfaceElementErosionFlag(false),
    _erosionType(ALL_IP_FAILED),
    _erosionGlobalCriterion(NULL),
    
    _eigOpts(),
    
    _isWriteDeformedMeshToFile(false),
    
    _enumMinus(0),
    _enumPlus(0),
    _gnum(0),
    
    _sucessMicroSolve(false),
    
    _macroTime(1.),
    _macroTimeStep(1.),
    _macroStep(1),
    
    _systemType(MULT_ELIM),
    _controlType(LOAD_CONTROL),
    _microFlag(false),
    _multiscaleFlag(false),
    
    _microBC(NULL),
    _microBCOld(NULL),
    _microFailureBC(NULL),
    
    _stressflag(true),
    _tangentflag(false),
    
    _homogenizeStressMethod(VOLUME),
    _homogenizeTangentMethod(PERTURB),
    _sameStateCriterion(1e-8),
    
    _archive(true),
    _isHommProSaveToFile(true),
    _isHommStrainSaveToFile(true),
    _extractPerturbationToFile(false),
    _messageView(false),
    _tangentPerturbation(1.e-8),
    
    _rho(0.),
    _rveVolume(0.),
    _rveGeoInertia(0.),
    _rveGeometry(0.),
    _currentState(NULL),
    _initialState(NULL),
    _elasticState(NULL),
    
    _pAl(NULL),
    _condensation(NULL),
    _homogenizedFiles(NULL),
    _outputFile(NULL),
    
    _testFlag(false),
    _pbcGroup(NULL),
    
    _batchComputations(false),
    
    _damageIsBlocked(false),
    _solverIsBroken(false),
    _maximalLostEllipticityCriterion(-1.),
    _homogenizedCrackSurface(0.),
    
    _failureBCIsSwitched(false),
    _failureBasedOnPreviousState(true),
    _GModelIsRotated(false),
    _checkFailureOnset(false),
    _checkWithNormal(true),
    _damageToCohesiveJump(false),
    _RVELengthInCohesiveNormal(1.),
    _surfaceReductionRatio(1.),
    _lostSolutionUniquenssTolerance(0.),
    _voidPartInLocalizationBand(0.),
    _extractIrreversibleEnergy(false),
    
    _FdamOnset(0.),
    _lostSolutionUniquenssNormal(0.,0.,0.),
    _homogenizedStressLastActiveDissipation(0.),
    _homogenizedStrainLastActiveDissipation(0.),
    _homogenizedDissipationStrainLastActiveDissipation(0.),
    _homogenizedCohesiveJumpLastActiveDissipation(0.),
    
    _extractElasticTangentOperator(false),
    _elasticDPDFTangentFile(NULL)
    {
      _isPartitioned = false;
    	#if defined(HAVE_MPI)
    	_workingRank = Msg::GetCommRank();
    	#else
    	_workingRank = 0;
    	#endif //HAVE_MPI
    
      // default view (displacement)
      unknownView.emplace_back("displacement",0,nlsField::crude,0,nlsField::val,1);
    
      // initialize c++ function rand (already made in gmsh ?? put elsewhere ??)
      _beginTime = time(NULL);
      srand(_beginTime);
    
      restartManager::Init(2000000000);
      std::ostringstream oss;
      oss << Msg::GetCommRank();
      std::string srank = oss.str();
      _restartMshFileName += oss.str() + ".msh";
    
      _pfManager._hyperellipticControlComp.resize(3);
      _pfManager._hyperellipticControlComp[0] =0;
      _pfManager._hyperellipticControlComp[1] =1;
      _pfManager._hyperellipticControlComp[2] =2;
    }
    
    nonLinearMechSolver::~nonLinearMechSolver(){
      if (pModel) delete pModel;
      if (pAssembler) delete pAssembler;
      if(_ipf != NULL) delete _ipf;
      if (_ufield) delete _ufield;
      if (_energField) delete _energField;
      
      if (_collection) delete _collection;
      if (_timeManager) delete _timeManager;
    
      if (_crackTrackingFile != NULL) fclose(_crackTrackingFile);
      if (_fragmentationFile != NULL) fclose(_fragmentationFile);
    
      if (_strainMap) delete _strainMap;
      if (_endSchemeMonitoringObject != NULL) delete _endSchemeMonitoringObject;
      if (_selectiveObject) delete _selectiveObject;
      if (_erosionGlobalCriterion!=NULL){
        delete _erosionGlobalCriterion;
      }
      if (_stiffnessModificationMonitoring!=NULL)
      {
        delete _stiffnessModificationMonitoring;
      }
    
      if (_microBC){
        delete _microBC;
        _microBC = NULL;
      }
      if (_microBCOld) delete _microBCOld;
      if (_microFailureBC) delete _microFailureBC;
    
      this->resetBoundaryConditions();
      this->resetContactInteraction();
    
    
      if (_pAl) delete _pAl;
      if (_condensation) delete _condensation;
      if (_pbcGroup != NULL) delete _pbcGroup;
      if (_homogenizedFiles !=NULL)
      {
        _homogenizedFiles->closeFiles();
        delete _homogenizedFiles;
      }
    
      if (_elasticDPDFTangentFile) {fclose(_elasticDPDFTangentFile); _elasticDPDFTangentFile = NULL;};
      if (_initialState) {delete _initialState;}
      if (_currentState) {delete _currentState;}
      if (_elasticState) {delete _elasticState;};
    
      if (_outputFile != NULL) fclose(_outputFile);
    
      if((!_microFlag) && (!_batchComputations)) // only on the master solver? -->ok, we need to pass once only to finish everything (PETSc, SLEPc, ...)
        Msg::Exit(0);
    }
    
    nonLinearMechSolver::nonLinearMechSolver(const nonLinearMechSolver& src):
    _dim(src._dim), _tag(src._tag),
    _mpiUserDom(src._mpiUserDom),
    _workingRank(src._workingRank), // current working rank
    _isPartitioned(src._isPartitioned), // true if FE mesh is partitioned
      // the mapRank is created from number of proc available and number of mesh partion,
    _mapRanks(),
    _meshFileName(""), // To transfert the mesh file from one folder to an other one
    _timeManager(new TimeManager()),
        // user solver option given as a string
    _solver_options(src._solver_options),
       // For Onelab display
    _GmshOneLabViewNum(src._GmshOneLabViewNum),
    
    pAssembler(NULL),
    _ipf(NULL),
    _ufield(NULL),
    _energField(NULL),
    
    whatSolver(Petsc),
    whatScheme(StaticLinear),
    
    _tol(1.e-6),
    _absTol(1.e-12),
    _stiffEstimation(true),
    _stiffnessModification(true),
    _stiffnessModificationMonitoring(NULL),
    _iterativeNR(true),
    _lineSearch(false),
    
    _implicitOpts(),
    
    _pathFollowing(false),
    _pfManager(),
    
    _restartMshFileName("restart_"),
    _resetRestart(false),
    _disableResetRestart(false),
    _crackTrackingName(""),
    _crackTrackingFile(NULL),
    _fragmentationName(""),
    _fragmentationFile(NULL),
    
    _energyComputation(1),
    _fractureEnergyComputation(0),
    nsba(0),
    
    _collection(NULL),
    _previousInit(false),
    _notResetedBC(false),
    _notResetedContact(false),
    
    _strainMap(NULL),
    _endSchemeMonitoringObject(NULL),
    _selectiveObject(NULL),
    
    _bulkElementErosionFlag(false),
    _interfaceElementErosionFlag(false),
    _erosionType(ALL_IP_FAILED),
    _erosionGlobalCriterion(NULL),
    
    _eigOpts(),
    
    _isWriteDeformedMeshToFile(false),
    
    _enumMinus(0),
    _enumPlus(0),
    _gnum(0),
    
    _sucessMicroSolve(false),
    
    _macroTime(1.),
    _macroTimeStep(1.),
    _macroStep(1),
    
    _systemType(MULT_ELIM),
    _controlType(LOAD_CONTROL),
    _microFlag(false),
    _multiscaleFlag(false),
    
    _microBC(NULL),
    _microBCOld(NULL),
    _microFailureBC(NULL),
    
    _stressflag(true),
    _tangentflag(false),
    
    _homogenizeStressMethod(VOLUME),
    _homogenizeTangentMethod(PERTURB),
    _sameStateCriterion(1e-8),
    
    _archive(true),
    _isHommProSaveToFile(true),
    _isHommStrainSaveToFile(true),
    _extractPerturbationToFile(false),
    _messageView(false),
    _tangentPerturbation(1.e-8),
    
    _rho(0.),
    _rveVolume(0.),
    _rveGeoInertia(0.),
    _rveGeometry(0.),
    _currentState(NULL),
    _initialState(NULL),
    _elasticState(NULL),
    
    _pAl(NULL),
    _condensation(NULL),
    _homogenizedFiles(NULL),
    _outputFile(NULL),
    
    _testFlag(false),
    _pbcGroup(NULL),
    
    _batchComputations(false),
    
    _damageIsBlocked(false),
    _solverIsBroken(false),
    _maximalLostEllipticityCriterion(-1.),
    _homogenizedCrackSurface(0.),
    
    _failureBCIsSwitched(false),
    _failureBasedOnPreviousState(true),
    _GModelIsRotated(false),
    _checkFailureOnset(false),
    _checkWithNormal(true),
    _damageToCohesiveJump(false),
    _RVELengthInCohesiveNormal(1.),
    _surfaceReductionRatio(1.),
    _lostSolutionUniquenssTolerance(0.),
    _voidPartInLocalizationBand(0.),
    _extractIrreversibleEnergy(false),
    
    _FdamOnset(0.),
    _lostSolutionUniquenssNormal(0.,0.,0.),
    _homogenizedStressLastActiveDissipation(0.),
    _homogenizedStrainLastActiveDissipation(0.),
    _homogenizedDissipationStrainLastActiveDissipation(0.),
    _homogenizedCohesiveJumpLastActiveDissipation(0.),
    
    _extractElasticTangentOperator(false),
    _elasticDPDFTangentFile(NULL)
    {
      src.copyOptionsToOtherSolver(this);
      loadModel(src._meshFileName);
      // domain
      for (int i=0; i<src.domainVector.size(); i++)
      {
        const partDomain* dom = src.domainVector[i];
        const dgPartDomain* dgdom = dynamic_cast<const dgPartDomain*>(dom);
        bool add=false;
        if (dgdom)
        {
          add = true;
          if (dom->elementGroupSize() ==0 and (dgdom->getPlusDomain()->getPhysical() == dgdom->getMinusDomain()->getPhysical()))
          {
            add = false;
          }
        }
        else
        {
          add = true;
        }
    
        if (add)
        {
          addDomain(src.domainVector[i]->clone());
        }
      };
      // material law
      for (std::map<int,materialLaw*>::const_iterator it = src.maplaw.begin(); it!= src.maplaw.end(); it++)
      {
        addMaterialLaw(it->second->clone());
      }
      src.copyBCsToOtherSolver(this);
    };
    
    void nonLinearMechSolver::transferIPField(nonLinearMechSolver* other, const IPDataTransfer& datatransfer)
    {
      if (!(this->isInitialized()))
      {
        Msg::Warning("Solver is not yet initialized");
        if (this->getScheme() == StaticLinear)
        {
          this->init();
          this->init2();
        }
        else if (this->getScheme() == StaticNonLinear)
        {
          this->initializeStaticScheme();
        }
        else if (this->getScheme() == Implicit)
        {
          this->initializeStaticScheme();
        }
        else if (this->getScheme() == Explicit)
        {
          this->initializeExplicitScheme();
        }
        else if (this->getScheme() == Multi)
        {
          this->init();
          this->init2();
        }
        else
        {
          Msg::Error("solverType %d has not implemented",this->getScheme());
        }    
      }
      
      IPField* srcIPF = other->getIPField();
      IPField* destIPF = this->getIPField();
      
      for (int idom = 0; idom < domainVector.size(); idom ++)
      {
        // for bulk
        partDomain* dom = domainVector[idom];
        if (dom->getFormulation())
        {
          Msg::Error("DG domain has not been implemented, please complete !!!");
          Msg::Exit(0);
        }
        int phys =  dom->getPhysical();
        if (datatransfer.withTranfer(phys))
        {
          for (elementGroup::elementContainer::const_iterator ite = dom->element_begin(); ite != dom->element_end(); ite++)
          {
            int eleNum = ite->second->getNum();
            AllIPState::ipstateElementContainer& srcIPs = *(srcIPF->getAips()->getIPstate(eleNum));
            AllIPState::ipstateElementContainer& destIPs = *(destIPF->getAips()->getIPstate(eleNum));
            if (srcIPs.size() != destIPs.size())
            {
              Msg::Error("two ipstates are not compatible in element %d",eleNum);
              Msg::Exit(0);
            }
            else
            {
              int Numstate = srcIPs.size();
              for (int j=0; j< Numstate; j++)
              {
                IPStateBase* src = srcIPs[j];
                IPStateBase* dest = destIPs[j];
                
                std::vector<IPVariable*> allIPsrc, allIPdest;
                src->getAllIPVariable(allIPsrc);
                dest->getAllIPVariable(allIPdest);
                if (allIPsrc.size() != allIPdest.size())
                {
                  Msg::Error("two ipstate is not compatible ele %d gp %d",eleNum,j);
                  Msg::Exit(0);
                }
                else
                {
                  for (int k=0; k< allIPsrc.size(); k++)
                  {
                    datatransfer.transferDataIPv(phys, allIPsrc[k],allIPdest[k]);
                  }
                }
              }
            }
          }
        }
      }
    };
    
    nonLinearMechSolver* nonLinearMechSolver::clone(const std::string mshFile, int tag) const
    {
      nonLinearMechSolver* sv = new nonLinearMechSolver(tag);
      // avoid douplicate views
      sv->unknownView.clear();
      //
      copyOptionsToOtherSolver(sv);
      // load model
      sv->loadModel(mshFile);
      // domain
      for (int i=0; i<domainVector.size(); i++)
      {
        const partDomain* dom = domainVector[i];
        const dgPartDomain* dgdom = dynamic_cast<const dgPartDomain*>(dom);
        bool add=false;
        if (dgdom)
        {
          add = true;
          if (dom->elementGroupSize() ==0 and (dgdom->getPlusDomain()->getPhysical() == dgdom->getMinusDomain()->getPhysical()))
          {
            add = false;
          }
        }
        else
        {
          add = true;
        }
    
        if (add)
        {
          sv->addDomain(domainVector[i]->clone());
        }
      };
      // material law
      for (std::map<int,materialLaw*>::const_iterator it = maplaw.begin(); it!= maplaw.end(); it++)
      {
        sv->addMaterialLaw(it->second->clone());
      }
      copyBCsToOtherSolver(sv);
      return sv;
    };
    
    nonLinearMechSolver* nonLinearMechSolver::clone(int tag, 
                          const std::string mshFile,
                          std::vector<partDomain*>& allDom,
                          std::vector<materialLaw*>& allMat) const
    {
      nonLinearMechSolver* sv = new nonLinearMechSolver(tag);
      //
      // avoid douplicate views
      sv->unknownView.clear();
      //
      copyOptionsToOtherSolver(sv);
      sv->loadModel(mshFile);
      // domain
      for (int i=0; i<allDom.size(); i++)
      {
        sv->addDomain(allDom[i]);
      };
      // material law
      for (int i=0; i< allMat.size(); i++)
      {
        sv->addMaterialLaw(allMat[i]);
      }
      copyBCsToOtherSolver(sv);
      return sv;
    }
    
    void nonLinearMechSolver::copyOptionsToOtherSolver(nonLinearMechSolver* sv) const
    {
      sv->whatSolver = whatSolver; //  Solver used to solve
      sv->whatScheme = whatScheme; // scheme used to solve equation
      //
      // get all Options
      /*TIME SETTING FOR NONLINEAR SOLVER*/
      if (sv->_timeManager) delete sv->_timeManager;
      sv->_timeManager = _timeManager->clone();
    
      /*FOR EXPLICIT SCHEME*/
      sv->_explicitOpts = _explicitOpts;
    
      /* FOR MULTI SYSTEM */
      /* MultiSystems solve */
      sv->_vcompBySys = _vcompBySys;
      sv->_vschemeBySys = _vschemeBySys;
    
      /*FOR QUASI-STATIC AND IMPLICIT SCHEMES*/
      sv->_tol = _tol;
      sv->_absTol = _absTol; // relative and absolute tolerance for iteration
      //sv->_stiffEstimation = _stiffEstimation;
      sv->_stiffnessModification = _stiffnessModification; // true if recalculate stiffness matrix
      if (_stiffnessModificationMonitoring!=NULL)
      {
        sv->_stiffnessModificationMonitoring =_stiffnessModificationMonitoring->clone();
      }
      sv->_iterativeNR = _iterativeNR; // true if using iterative procedure
      sv->_lineSearch = _lineSearch;
    
      /*FOR CH DYNAMIC SCHEME*/
      sv->_implicitOpts = _implicitOpts;
    
      /*FOR PATH FOLLOWING*/
      sv->_pathFollowing = _pathFollowing; // true to activate path following
      sv->_pfManager = _pfManager;
    
      //
      /* data for restart */
      //size_t _beginTime;
      //std::string _restartMshFileName;
      //bool _resetRestart; //to avoid restart when shitfing schemes
      //bool _disableResetRestart; //to allow restat in battery only
    
      /*FOR CRACK*/
       // physical entities that are initialy broken
      sv->initbrokeninter = initbrokeninter;
      sv->initbrokeninterInDomains = initbrokeninterInDomains;
      /* crack tracking */
      //FILE *_crackTrackingFile; // name of files for crack tracking no name == NULL and no track
      if (_crackTrackingFile!=NULL)
      {
        sv->crackTracking(_crackTrackingName);
      }
      /* fragmentation */
      //FILE *_fragmentationFile;
      if (_fragmentationFile!=NULL)
      {
        sv->postproFragment(_fragmentationName);
      }
    
    
      /* FOR ARCHIVING*/
      // archivng internal force at nodes
      //std::list<archiveInternalForce> vafIntenal;
      for (std::list<archiveInternalForce>::const_iterator it =  vafIntenal.begin(); it!= vafIntenal.end(); it++)
      {
        const archiveInternalForce& ar = *it;
        sv->vafIntenal.emplace_back(ar.phys,ar.dim,ar.nstep); 
      }
      
      // std vector to archive a node displacement
      //std::vector<unknownField::archiveNode> anoded;
      for (std::vector<unknownField::archiveNode>::const_iterator it = anoded.begin(); it != anoded.end(); it++)
      {
        const unknownField::archiveNode& ar = *it;
        sv->anoded.emplace_back(ar.physnum,ar.nodenum,ar._comp,ar.wc,ar.nstep);
      };
    
      // std::vector to archive a force
      //std::vector<archiveForce> vaf;
      for (std::vector<archiveForce>::const_iterator it = vaf.begin(); it != vaf.end(); it++)
      {
        const archiveForce& ar = *it;
        sv->vaf.emplace_back(ar.numphys,ar.dim,ar.comp,ar.nstep);
      };
      // std vector to archive ipvariable
      //std::vector<IPField::ip2archive> vaip;
      for (std::vector<IPField::ip2archive>::const_iterator it = vaip.begin(); it != vaip.end(); it++)
      {
        const IPField::ip2archive& ar = *it;
        sv->vaip.emplace_back(ar);
      }
      // list to archive the integral operation in volume
      //std::list<IntegralVolume> _dataVolumeIntegral;
      for (std::list<IntegralVolume>::const_iterator it = _dataVolumeIntegral.begin(); it!=_dataVolumeIntegral.end(); it++)
      {
        const IntegralVolume& ar = *it;
        sv->_dataVolumeIntegral.emplace_back(ar.integType,ar.ipVal,ar.physical,ar.nbArch);
      }
      
      //std::list<clustersData> _clusterData;
      for (std::list<clustersData>::const_iterator it =  _clusterData.begin(); it!= _clusterData.end(); it++)
      {
        sv->loadClustersDataFile(it->clusterDataFileName);
      }
      
      //std::list<AvergageCluster> _averageClusters;
      for (std::list<AvergageCluster>::const_iterator it = _averageClusters.begin(); it != _averageClusters.end(); it++)
      {
        const AvergageCluster& ar = *it;
        sv->_averageClusters.emplace_back(ar.ipVal,ar.nbArch);
      }
      
      // list to archive IP data over physical (data at gauss point with coordinates)
      //std::list<IPDataOnPhysical> _dataOnPhysical;
      // for archiving energy and fracture energy
      sv->_energyComputation = _energyComputation;  // equal to 0 if no energy is saved; >0 if energy is saved with _energyComputation as interval step of archiving
      sv->_fractureEnergyComputation = _fractureEnergyComputation; // equal to 0 if no energy is saved; >0 if energy is saved with _energyComputation as interval step of archiving
    
    
      /*FOR VIEW*/
      // view of unknown --> disp file
      //std::vector<nlsField::dataBuildView> unknownView;
      for (std::vector<nlsField::dataBuildView>::const_iterator it = unknownView.begin(); it!= unknownView.end(); it++)
      {
        const nlsField::dataBuildView& ar = *it;
        sv->unknownView.emplace_back(ar.viewname,ar.comp,ar.ev,ar.gpNum,ar.valtype,ar.nbstepArch);
      }
      // view of ip field --> stress file
      //std::vector<nlsField::dataBuildView> ipView;
      for (std::vector<nlsField::dataBuildView>::const_iterator it = ipView.begin(); it!= ipView.end(); it++)
      {
        const nlsField::dataBuildView& ar = *it;
        sv->ipView.emplace_back(ar.viewname,ar.comp,ar.ev,ar.gpNum,ar.valtype,ar.nbstepArch);
      }
      //std::vector<nlsField::dataBuildView> ipViewInterface;
      for (std::vector<nlsField::dataBuildView>::const_iterator it = ipViewInterface.begin(); it!= ipViewInterface.end(); it++)
      {
        const nlsField::dataBuildView& ar = *it;
        sv->ipViewInterface.emplace_back(ar.viewname,ar.comp,ar.ev,ar.gpNum,ar.valtype,ar.nbstepArch);
      }
      // view of energy field --> energy file, but it is not
      //std::vector<nlsField::dataBuildView> energyView;
      for (std::vector<nlsField::dataBuildView>::const_iterator it = energyView.begin(); it!= energyView.end(); it++)
      {
        const nlsField::dataBuildView& ar = *it;
        sv->energyView.emplace_back(ar.viewname,ar.comp,ar.ev,ar.gpNum,ar.valtype,ar.nbstepArch);
      }
      sv->nsba=nsba; // number of step between two view
    
      /* SWITCH scheme data */
      //bool _previousInit; // To known if the initialization as already be made by an other scheme
      //bool _notResetedBC; // To known if the BC are modified.
      //bool _notResetedContact; // To known if the contact interaction are modified
      //bool _notResetUnknowns; // To known if the unknowns are modified
    
      // strain mapping for foams
      if (_strainMap!=NULL)
      {
        GModel* pm = GModel::current();
        sv->createStrainMapping(_strainMap->getMappingMeshFileName(),_strainMap->getNumStepBetweenTwoSaves());
        GModel::setCurrent(pm);
      }
      // programe monitoring
      if (_endSchemeMonitoringObject!=NULL)
      {
        sv->endSchemeMonitoring(*_endSchemeMonitoringObject);
      }
      // selective update with cohesive crack
      if (_selectiveObject)
      {
        sv->setSelectiveUpdate(*_selectiveObject);
      }
    
      // element erosion control
      if (_bulkElementErosionFlag or _interfaceElementErosionFlag)
      {
        sv->setElementErosion(_bulkElementErosionFlag,_interfaceElementErosionFlag,_erosionType);
      }
      if (_erosionGlobalCriterion)
      {
        sv->setGlobalErosionCheck(true,_erosionGlobalCriterion);
      }
    
      /*EIGEN SOLVER*/
      sv->_eigOpts = _eigOpts;
      //std::vector<nlsField::dataBuildView> _eigview;
      for (std::vector<nlsField::dataBuildView>::const_iterator it = _eigview.begin(); it!= _eigview.end(); it++)
      {
        const nlsField::dataBuildView& ar = *it;
        sv->_eigview.emplace_back(ar.viewname,ar.comp,ar.ev,ar.gpNum,ar.valtype,ar.nbstepArch);
      }
    
      /*DEFORMED MESH TO FILE*/
      sv->_isWriteDeformedMeshToFile = _isWriteDeformedMeshToFile; // write deformed mesh to file
    
    
      /*FOR MULTISCALE ANALYSIS*/
      // Element number and Integration point number
      sv->_enumMinus = _enumMinus;
      sv->_enumPlus = _enumPlus;
      sv->_gnum =_gnum;
    
      sv->_sucessMicroSolve = _sucessMicroSolve;
    
      // time and time step
      sv->_macroTimeStep = _macroTimeStep; // for law which works on increment. (Use only in so no getTimeStep function)
      sv->_macroTime = _macroTime; // To save results vs time
      sv->_macroStep = _macroStep; // current macroscopic step
    
      //for micro flag
      sv->_systemType = _systemType;
      sv->_controlType = _controlType;
      // micro flag--> true if microsolver is used
      sv->_microFlag = _microFlag;
      sv->_multiscaleFlag = _multiscaleFlag; // to know if a multiscale analysis is performed, to be true with both micro and marco solver
    
      //stress flag and tangent flag
      sv->_stressflag = _stressflag; // true if homogenized stress is estimated
      sv->_tangentflag = _tangentflag; // true if homogenizd tangnent is estimated
        // homogenized  method
      sv->_homogenizeTangentMethod = _homogenizeTangentMethod;
      sv->_homogenizeStressMethod = _homogenizeStressMethod;
      sv->_sameStateCriterion = _sameStateCriterion; // this parameter to check if same sate-> economise
      // for archiving
      sv->_archive = _archive;
      // all homogenized filename
      sv->_isHommProSaveToFile = _isHommProSaveToFile; // flag -->save homogenized properties to files
      sv->_isHommStrainSaveToFile = _isHommStrainSaveToFile; //flag --> save homogenized strain to files
      sv->_extractPerturbationToFile = _extractPerturbationToFile; //flag --> save perturbation on RVE boundary to file
      sv->_messageView = _messageView; // flag to archive message to file
      sv->_tangentPerturbation = _tangentPerturbation; // tangent by perturbation
    
      sv->_rho = _rho;  // homogenized density
      sv->_rveVolume = _rveVolume; // rve volume
      sv->_rveGeoInertia = _rveGeoInertia; // rve geometrical inertia
      sv->_rveGeometry = _rveGeometry;
    
      sv->_damageIsBlocked = _damageIsBlocked;
      sv->_solverIsBroken = _solverIsBroken;
      sv->_maximalLostEllipticityCriterion = _maximalLostEllipticityCriterion; // maximal value
      sv->_homogenizedCrackSurface = _homogenizedCrackSurface;
    
      //
      sv->_failureBCIsSwitched = _failureBCIsSwitched; // true if FailureBC is switsched after failure,
      sv->_failureBasedOnPreviousState = _failureBasedOnPreviousState;
      sv->_GModelIsRotated = _GModelIsRotated;
      sv->_checkFailureOnset = _checkFailureOnset; //
      sv->_checkWithNormal = _checkWithNormal; //
      sv->_damageToCohesiveJump = _damageToCohesiveJump; // true if extracting cohsive law
      sv->_RVELengthInCohesiveNormal = _RVELengthInCohesiveNormal; // length perpendicular to cohesive normal
    	sv->_surfaceReductionRatio = _surfaceReductionRatio; // load carrying surface over nominal surface following cohesive normal
      sv->_lostSolutionUniquenssTolerance = _lostSolutionUniquenssTolerance;
    	sv->_voidPartInLocalizationBand = _voidPartInLocalizationBand; // void part in localization band
    	sv->_extractIrreversibleEnergy = _extractIrreversibleEnergy; // flag
    
      //
      sv->_extractElasticTangentOperator = _extractElasticTangentOperator; // true if elastic tangent operator is extracted at the same time as full tangent operator
    };
    
    
    void nonLinearMechSolver::copyBCsToOtherSolver(nonLinearMechSolver* sv) const
    {
       /*FOR CONTACT*/ // TODO
      // contact
      //contactContainer _allContact;
      // defo defo contact BC
      //defoDefoContactContainer _allDefoDefoContact;
    
      /*FOR BOUNDARY CONDITIONS*/
      // neumann BC
      //std::list<nonLinearNeumannBC> allNeumann;
      for (std::list<nonLinearNeumannBC>::const_iterator it = allNeumann.begin(); it!=allNeumann.end(); it++)
      {
        const nonLinearNeumannBC& bc = *it;
        sv->allNeumann.emplace_back();
        nonLinearNeumannBC& neu = sv->allNeumann.back();
        neu.onWhat = bc.onWhat;
        neu._tag = bc._tag;
        neu._NeumannBCType = bc._NeumannBCType;
        neu._f= bc._f;
        neu._comp=bc._comp;
    
        if(neu.onWhat==nonLinearBoundaryCondition::ON_VERTEX)
        {
          neu.g = new elementGroup (0, neu._tag);
        }
        else if(neu.onWhat==nonLinearBoundaryCondition::ON_EDGE)
        {
          neu.g = new elementGroup (1, neu._tag);
        }
        else if(neu.onWhat==nonLinearBoundaryCondition::ON_FACE)
        {
          neu.g = new elementGroup (2, neu._tag);
        }
        else if(neu.onWhat==nonLinearBoundaryCondition::ON_FACE_VERTEX)
        {
          neu.g = new elementGroup(2,neu._tag);
        }
        else if(neu.onWhat==nonLinearBoundaryCondition::ON_VOLUME)
        {
          neu.g = new elementGroup (3, neu._tag);
        }
      }
      // dirichlet BC
      //std::list<nonLinearDirichletBC> allDirichlet;
      for (std::list<nonLinearDirichletBC>::const_iterator it = allDirichlet.begin(); it != allDirichlet.end(); it++)
      {
        const nonLinearDirichletBC& bc = *it;
        sv->allDirichlet.emplace_back();
        nonLinearDirichletBC& diri = sv->allDirichlet.back();
        diri.onWhat = bc.onWhat;
        diri._tag = bc._tag;
        diri._comp = bc._comp;
        diri._f = bc._f;
        diri._mycondition = bc._mycondition;
    
        if(diri.onWhat==nonLinearBoundaryCondition::ON_VERTEX)
        {
          diri.g = new elementGroup (0, diri._tag);
        }
        else if(diri.onWhat==nonLinearBoundaryCondition::ON_EDGE)
        {
          diri.g = new elementGroup (1, diri._tag);
        }
        else if(diri.onWhat==nonLinearBoundaryCondition::ON_FACE)
        {
          diri.g = new elementGroup (2, diri._tag);
        }
        else if(diri.onWhat==nonLinearBoundaryCondition::ON_FACE_VERTEX)
        {
          diri.g = new elementGroup(2,diri._tag);
        }
        else if(diri.onWhat==nonLinearBoundaryCondition::ON_VOLUME)
        {
          diri.g = new elementGroup (3, diri._tag);
        }
    
      }
      // all periodic groups
      //std::list<nonLinearPeriodicBCBetweenTwoGroups> allPeriodic;
      sv->_edgePeriodicPhysicals = _edgePeriodicPhysicals;
      sv->_nodePeriodicPhysicals = _nodePeriodicPhysicals;
      for (std::list<nonLinearPeriodicBCBetweenTwoGroups>::const_iterator it = allPeriodic.begin(); it != allPeriodic.end(); it++)
      {
        const nonLinearPeriodicBCBetweenTwoGroups& bc = *it;
        sv->allPeriodic.emplace_back();
        nonLinearPeriodicBCBetweenTwoGroups& pbc = sv->allPeriodic.back();
        pbc.onWhat=bc.onWhat;
        pbc.phys1 = bc.phys1;
        pbc.phys2 = bc.phys2;
        pbc.physVer1 = bc.physVer1;
        pbc.physVer2 = bc.physVer2;
        pbc.comp = bc.comp;
        if (pbc.onWhat==nonLinearBoundaryCondition::ON_VERTEX)
        {
          pbc.g1 = new elementGroup (0, pbc.phys1);
          pbc.g2 = new elementGroup (0, pbc.phys2);
        }
        else if (pbc.onWhat==nonLinearBoundaryCondition::ON_EDGE)
        {
          pbc.g1 = new elementGroup (1, pbc.phys1);
          pbc.g2 = new elementGroup (1, pbc.phys2);
        }
        else if (pbc.onWhat==nonLinearBoundaryCondition::ON_FACE)
        {
          pbc.g1 = new elementGroup (2, pbc.phys1);
          pbc.g2 = new elementGroup (2, pbc.phys2);
        }
        
        if ((pbc.physVer1 > 0) && (pbc.physVer2 >0))
        {
          elementGroup grv1(0,pbc.physVer1);
          elementGroup grv2(0,pbc.physVer2);
          if (grv1.vsize() > 0 and grv2.vsize() > 0)
          {
            pbc.v1 = (grv1.vbegin()->second);
            pbc.v2 = (grv2.vbegin()->second);
          }
          else{
            Msg::Error("periodic root vertices have not been correctly defined on cloned solver");
            Msg::Exit(0);
          }
        }
        else
        {
          pbc.v1 = NULL;
          pbc.v2 = NULL;
        }
      }
      //all average PBC groups
      //std::list<nonLinearAveragePeriodicBCBetweenTwoGroups> allAveragePeriodic;
      for (std::list<nonLinearAveragePeriodicBCBetweenTwoGroups>::const_iterator it = allAveragePeriodic.begin(); it != allAveragePeriodic.end(); it++)
      {
        const nonLinearAveragePeriodicBCBetweenTwoGroups& bc = *it;
        sv->allAveragePeriodic.emplace_back();
        nonLinearAveragePeriodicBCBetweenTwoGroups& pbc = sv->allAveragePeriodic.back();
        pbc.onWhat=bc.onWhat;
        pbc.phys1 = bc.phys1;
        pbc.phys2 = bc.phys2;
        pbc.physVer1 = bc.physVer1;
        pbc.physVer2 = bc.physVer2;
        pbc.comp = bc.comp;
        if (pbc.onWhat==nonLinearBoundaryCondition::ON_EDGE)
        {
          pbc.g1 = new elementGroup (1, pbc.phys1);
          pbc.g2 = new elementGroup (1, pbc.phys2);
        }
        else if (pbc.onWhat==nonLinearBoundaryCondition::ON_FACE)
        {
          pbc.g1 = new elementGroup (2, pbc.phys1);
          pbc.g2 = new elementGroup (2, pbc.phys2);
        }
        
        if ((pbc.physVer1 > 0) && (pbc.physVer2 >0))
        {
          elementGroup grv1(0,pbc.physVer1);
          elementGroup grv2(0,pbc.physVer2);
          if (grv1.vsize() > 0 and grv2.vsize() > 0)
          {
            pbc.v1 = (grv1.vbegin()->second);
            pbc.v2 = (grv2.vbegin()->second);
          }
          else{
            Msg::Error("average periodic root vertices have not been correctly defined on cloned solver");
            Msg::Exit(0);
          }
        }
        else
        {
          pbc.v1 = NULL;
          pbc.v2 = NULL;
        }
      }
      // all samedisp bc
      //std::list<nonLinearSameDisplacementBC> allSameDisp;
      for (std::list<nonLinearSameDisplacementBC>::const_iterator it = allSameDisp.begin(); it != allSameDisp.end(); it++)
      {
        const nonLinearSameDisplacementBC& bc = *it;
        sv->allSameDisp.emplace_back();
        nonLinearSameDisplacementBC& sameDispBC = sv->allSameDisp.back();
        sameDispBC.onWhat = bc.onWhat;
        sameDispBC._tag = bc._tag;
        sameDispBC.comp = bc.comp;
        sameDispBC.tagRoot = bc.tagRoot;
        sameDispBC.fac = bc.fac;
    
        sameDispBC.gRoot = new elementGroup(0,sameDispBC.tagRoot);
        if(sameDispBC.onWhat==nonLinearBoundaryCondition::ON_VERTEX)
        {
          sameDispBC.g = new elementGroup(0,sameDispBC._tag);
        }
        if(sameDispBC.onWhat==nonLinearBoundaryCondition::ON_EDGE)
        {
          sameDispBC.g = new elementGroup(1,sameDispBC._tag);
        }
        else if (sameDispBC.onWhat==nonLinearBoundaryCondition::ON_FACE)
        {
          sameDispBC.g = new elementGroup(2,sameDispBC._tag);
        }
        else if (sameDispBC.onWhat==nonLinearBoundaryCondition::ON_VOLUME)
        {
          sameDispBC.g = new elementGroup(3,sameDispBC._tag);
        }
      }
    
      // fix on face
      //std::list<nonLinearFixOnFaceBC> allFixAllFace;
      for (std::list<nonLinearFixOnFaceBC>::const_iterator it = allFixAllFace.begin(); it != allFixAllFace.end(); it++)
      {
        const nonLinearFixOnFaceBC& bc = *it;
    
        sv->allFixAllFace.emplace_back(bc.A,bc.B,bc.C,bc.D);
        nonLinearFixOnFaceBC& fixBC = sv->allFixAllFace.back();
        fixBC.onWhat = bc.onWhat;
        fixBC._tag = bc._tag;
        if (fixBC.onWhat == nonLinearBoundaryCondition::ON_VERTEX)
        {
          fixBC.g = new elementGroup(0,fixBC._tag);
        }
        else if (fixBC.onWhat == nonLinearBoundaryCondition::ON_EDGE)
        {
          fixBC.g = new elementGroup(1,fixBC._tag);
        }
        else if (fixBC.onWhat == nonLinearBoundaryCondition::ON_FACE)
        {
          fixBC.g = new elementGroup(2,fixBC._tag);
        }
        else if (fixBC.onWhat == nonLinearBoundaryCondition::ON_VOLUME)
        {
          fixBC.g = new elementGroup(3,fixBC._tag);
        }
      }
      // constraint BC
      //std::list<nonLinearConstraintBC> allConstraint;
      for (std::list<nonLinearConstraintBC>::const_iterator it = allConstraint.begin(); it!= allConstraint.end(); it++)
      {
        const nonLinearConstraintBC& bc = *it;
        sv->allConstraint.emplace_back();
        nonLinearConstraintBC& uc = sv->allConstraint.back();
        uc.onWhat = bc.onWhat;
        uc._tag = bc._tag;
        uc._comp=bc._comp;
        uc._tag=bc._tag;
    
        if (uc.onWhat == nonLinearBoundaryCondition::ON_VERTEX)
        {
          uc.g = new elementGroup (0, uc._tag);
        }
        else if (uc.onWhat == nonLinearBoundaryCondition::ON_EDGE)
        {
          uc.g = new elementGroup (1, uc._tag);
        }
        else if (uc.onWhat == nonLinearBoundaryCondition::ON_FACE)
        {
          uc.g = new elementGroup (2, uc._tag);
        }
        else if (uc.onWhat == nonLinearBoundaryCondition::ON_VOLUME)
        {
          uc.g = new elementGroup (3, uc._tag);
        }
      }
      // when using with microBC, BC can appy on corners of RVE
      //std::list<nonLinearDirichletBCAtCorner> allCornerConstraint;
      for (std::list<nonLinearDirichletBCAtCorner>::const_iterator it = allCornerConstraint.begin(); it !=allCornerConstraint.end(); it++)
      {
        const nonLinearDirichletBCAtCorner& bc = *it;
        sv->allCornerConstraint.emplace_back();
        nonLinearDirichletBCAtCorner& cornerConstraint = sv->allCornerConstraint.back();
        cornerConstraint._tag = bc._tag;
        cornerConstraint._comp = bc._comp;
        cornerConstraint._cornerNumber = bc._cornerNumber;
        cornerConstraint._f = bc._f;
      }
      // force BC
      //std::list<nonLinearNeumannBCAtCorner> allCornerForce;
      for (std::list<nonLinearNeumannBCAtCorner>::const_iterator it =allCornerForce.begin(); it != allCornerForce.end(); it++)
      {
        const nonLinearNeumannBCAtCorner& bc = *it;
        sv->allCornerForce.emplace_back();
        nonLinearNeumannBCAtCorner& cornerConstraint = sv->allCornerForce.back();
        cornerConstraint._tag = bc._tag;
        cornerConstraint._comp = bc._comp;
        cornerConstraint._cornerNumber = bc._cornerNumber;
        cornerConstraint._f = bc._f;
      }
      // initial BC
      //std::list<initialCondition> allinitial;
      for (std::list<initialCondition>::const_iterator it = allinitial.begin(); it!= allinitial.end(); it++)
      {
        const initialCondition& bc = *it;
    
        sv->allinitial.emplace_back(bc._comp,bc._mycondition);
        initialCondition& initc = sv->allinitial.back();
        initc.onWhat = bc.onWhat;
        initc._tag = bc._tag;
        initc._f = bc._f;
        elementFilterTrivial filter;
        initc.g = new elementGroup();
        if (initc.onWhat ==  nonLinearBoundaryCondition::ON_VERTEX)
        {
          initc.g->addPhysical(0,initc._tag,filter);
        }
        else if (initc.onWhat ==  nonLinearBoundaryCondition::ON_EDGE)
        {
          initc.g->addPhysical(1,initc._tag,filter);
        }
        else if (initc.onWhat ==  nonLinearBoundaryCondition::ON_FACE)
        {
          initc.g->addPhysical(2,initc._tag,filter);
        }
        else if (initc.onWhat ==  nonLinearBoundaryCondition::ON_VOLUME)
        {
          initc.g->addPhysical(3,initc._tag,filter);
        }
        else if (initc.onWhat ==  nonLinearBoundaryCondition::RIGIDCONTACT)
        {
    
        }
        else if (initc.onWhat ==  nonLinearBoundaryCondition::UNDEF)
        {
          initc.g->addPhysical(2,initc._tag,filter);
        }
      }
      // neumann BC theta (weak enforcement of rotation) group this with allNeumann ?
      //std::list<nonLinearNeumannBC> allTheta;
      for (std::list<nonLinearNeumannBC>::const_iterator it = allTheta.begin(); it != allTheta.end(); it++)
      {
        const nonLinearNeumannBC& bc = *it;
        sv->allTheta.emplace_back();
        nonLinearNeumannBC& neu = sv->allTheta.back();
    
        neu._tag = bc._tag;
        neu._comp = bc._comp;
        neu.onWhat = nonLinearBoundaryCondition::UNDEF;
        neu.g = new elementGroup(1,neu._tag);
        neu._f = bc._f;
      }
    
      // dirichlet BC on rigid surface (prescribed the motion of gravity center)
      //std::list<rigidContactBC> allContactBC;
      for (std::list<rigidContactBC>::const_iterator it = allContactBC.begin(); it != allContactBC.end(); it++)
      {
        const rigidContactBC& bc = *it;
    
        sv->allContactBC.emplace_back(bc._tag);
        rigidContactBC& diri = sv->allContactBC.back();
        diri.onWhat = nonLinearBoundaryCondition::RIGIDCONTACT;
        diri._comp = bc._comp;
        diri._f = bc._f;
      };
      
      // general linear constrait BC
      //std::list<nonLinearLinearConstraintBCBetweenTwoGroups> allLinearConstraintBC;
      for (std::list<nonLinearLinearConstraintBCBetweenTwoGroups>::const_iterator it = allLinearConstraintBC.begin(); it != allLinearConstraintBC.end(); it++)
      {
        const nonLinearLinearConstraintBCBetweenTwoGroups& oldbc = *it;
        sv->allLinearConstraintBC.emplace_back();
        nonLinearLinearConstraintBCBetweenTwoGroups& bc = sv->allLinearConstraintBC.back();
        
        bc.onWhat = oldbc.onWhat;
        bc.phyMaster = oldbc.phyMaster;
        bc.phySlave = oldbc.phySlave;
        
        int physMater = bc.phyMaster;
        int physSlave  = bc.phySlave;
        
        if(bc.onWhat==nonLinearBoundaryCondition::ON_VERTEX){
          bc.gMaster = new elementGroup (0, physMater);
          if (physSlave > 0)
            bc.gSlave = new elementGroup (0, physSlave);
          else
            bc.gSlave = new elementGroup ();
        }
        else if(bc.onWhat==nonLinearBoundaryCondition::ON_EDGE){
          bc.gMaster = new elementGroup (1, physMater);
          if (physSlave > 0)
            bc.gSlave = new elementGroup (1, physSlave);
          else
            bc.gSlave = new elementGroup ();
        }
        else if(bc.onWhat==nonLinearBoundaryCondition::ON_FACE){
          bc.gMaster = new elementGroup (2, physMater);
          if (physSlave > 0)
            bc.gSlave = new elementGroup (2, physSlave);
          else
            bc.gSlave = new elementGroup ();
        }
        else if(bc.onWhat==nonLinearBoundaryCondition::ON_VOLUME){
          bc.gMaster = new elementGroup (3, physMater);
          if (physSlave > 0)
            bc.gSlave = new elementGroup (3, physSlave);
          else
            bc.gSlave = new elementGroup ();
        }
        
        bc.comp = oldbc.comp;
        if (oldbc.rightHandSide !=NULL)
        {
          bc.rightHandSide = oldbc.rightHandSide->clone();
        }
        else
        {
          Msg::Error("linearCombinationOfVertices must be set");
          Msg::Exit(0);
        }
        if (oldbc.vgOperation!=NULL)
        {
          bc.vgOperation = oldbc.vgOperation->clone();
        }
        else
        {
          bc.vgOperation = new vertexGroupOperation_trivial();
        }
        bc.slaveFactor = oldbc.slaveFactor;
    
         bc.excludedVertices.copyData(oldbc.excludedVertices);
      }
    
      //micro BC
      if (_microBC)
      {
        sv->addMicroBC(_microBC);
      }
      if (_microFailureBC)
      {
        sv->addMicroBCForFailure(_microFailureBC);
      }
      // for test use microBC with macrosolver
      if (_testFlag)
      {
        sv->activateTest(_testFlag);
      }
    
    }
    
    void nonLinearMechSolver::createRestartByTime(const int day,const int hour,const int minute,const int second)
    {
      restartManager::setTimeBetweenCheckpoint(day,hour,minute,second);
    }
    
    void nonLinearMechSolver::createRestartBySteps(const int numstep)
    {
      restartManager::setStepBetweenCheckpoint(numstep);
    }
    
    bool nonLinearMechSolver::mustRestart(const int numstep)
    {
      return restartManager::checkpoint(numstep);
    }
    
    void nonLinearMechSolver::stepBetweenArchiving(const int n)
    {
      if (n > 0)
      {
        nsba = n;
        if (_ipf!=NULL)
        {
          _ipf->stepBetweenArchiving(n);
        }
        if (_ufield != NULL)
        {
          _ufield->stepBetweenArchiving(n);
        }
        if (_energField != NULL)
        {
          _energField->stepBetweenArchiving(n);
        }
    
        // modify all existing view
        for(int i=0;i<ipView.size();i++)
        {
          ipView[i].nbstepArch = n;
        }
        
        // modify all existing view
        for(int i=0;i<ipViewInterface.size();i++)
        {
          ipViewInterface[i].nbstepArch = n;
        }
        
        for(int i=0;i<unknownView.size();i++)
        {
          unknownView[i].nbstepArch = n;
        }
    
        for (int i=0; i< energyView.size(); i++)
        {
          energyView[i].nbstepArch = n;
        }
      }
    }
    
    void nonLinearMechSolver::setFactorOnArchivingFiles(const int fact)
    {
      if (fact > 0)
      {
        // chang factor in solver
        for (int i=0; i< vaip.size(); i++)
        {
          vaip[i].nstep*=fact;
        }
    
        for(int i=0;i<anoded.size();i++)
        {
          anoded[i].nstep*=fact;
        }
        // force
        for(int i=0;i<vaf.size();i++)
        {
          vaf[i].nstep*=fact;
        }
    
        _energyComputation *= fact;
        _fractureEnergyComputation *= fact;
    
        // change factor inside field if exist
        if (_ipf!=NULL)
        {
          _ipf->setFactorOnArchivingFiles(fact);
        }
        if (_ufield!=NULL)
        {
          _ufield->setFactorOnArchivingFiles(fact);
    
        }
        if (_energField !=NULL)
        {
          _energField->setFactorOnArchivingFiles(fact);
        }
      }
    }
    
    // nonLinearMechSolver
    void nonLinearMechSolver::loadModel(const std::string &meshFileName)
    {
      // handle restart case
    /*  if(restartManager::available())
      {
        pModel = new GModel();
        pModel->setFileName(meshFileName);
        pModel->setAsCurrent();
        pModel->readMSH(_restartMshFileName.c_str());
        _dim = pModel->getNumRegions() ? 3 : 2;
        pModel->setFileName(meshFileName);
        pModel->setName(SplitFileName(meshFileName)[1]);
        return;
      }*/
    
      pModel = new GModel();
      pModel->setAsCurrent();
      pModel->readMSH(meshFileName.c_str());
      _meshFileName = meshFileName;
      pModel->setFileName(meshFileName);
      pModel->setName(SplitFileName(meshFileName)[1]);
      this->commonModel();
    }
    
    void nonLinearMechSolver::commonModel()
    {
      _dim = pModel->getNumRegions() ? 3 : 2;
     #if defined(HAVE_MPI)
      int numPartitions = pModel->getNumPartitions();
    
      if (numPartitions >0) {
    		_isPartitioned = true;
    	}
      else
    	{
    		// only first
    		_isPartitioned = false;
    	}
    
      if (_microFlag)
      {
        if (_isPartitioned)
        {
          Msg::Error("micro solver cannot run with partitioned meshes");
          Msg::Exit(0);
        }
      }
      else
      {
        Msg::Info("Number of mesh partition %d for %d cpu(s)",numPartitions,Msg::GetCommSize());
        if(numPartitions > Msg::GetCommSize())
        {
          Msg::Error("Your mesh is wrong partitioned");
          Msg::Exit(0);
        }
        else if(numPartitions !=0 and Msg::GetCommSize() == 1)
        {
          Msg::Error("Cannot perform a serial computation with a partitionned mesh");
          Msg::Exit(0);
        }
        else if(numPartitions > 0 and numPartitions < Msg::GetCommSize())
        {
          Msg::Warning("You have more cpu than mesh partition so some cpus may have no work");
        }
    
        // partitions are numerated from 1 to numPartitions
        std::set<int> mshPart;
        for (int im=1; im < numPartitions+1; im++){
          mshPart.insert(im);
        }
        // int map of ranks
        initMapRanks(mshPart);
      }
     #endif // HAVE_MPI
    }
    
    
    void nonLinearMechSolver::createModel(const std::string &geoFileName, const std::string &outFileName,
                                          const int dim,const int order,const bool incomplete)
    {
      // check reserved name
      //if(!strcmp(outFileName.c_str(),_restartMshFileName.c_str()))
      //{
      //  Msg::Error("The output file name you request to save your mesh is a reserved name! Change it to continue");
      //}
    
      // check restart file and if so use the saved msh file
      //if(restartManager::available())
      //{
      //  this->loadModel(_restartMshFileName);
      //  return;
      //}
    
      // Overwrite options
      CTX::instance()->mesh.order = order;
      CTX::instance()->mesh.secondOrderIncomplete = incomplete;
      // Use version 3 of mesh to avoid a renmbering of the elements
      if (Msg::GetCommSize()>1){
        CTX::instance()->mesh.mshFileVersion = 3.0;
        CTX::instance()->mesh.partitionCreatePhysicals = 0;
      }
      else{
        CTX::instance()->mesh.mshFileVersion = 3.0;
      }
    
      pModel = new GModel();
      pModel->setAsCurrent();
      pModel->setFileName(geoFileName);
      pModel->setName(SplitFileName(geoFileName)[1]);
      pModel->readGEO(geoFileName);
      pModel->mesh(dim);
    
      if((!_microFlag) && Msg::GetCommSize()>1) /// if _microFlag is true, mesh is not partitioned
      {
        CTX::instance()->mesh.numPartitions = Msg::GetCommSize();
        PartitionMesh(pModel, Msg::GetCommSize());
      }
    
      if(!outFileName.empty() && Msg::GetCommRank() == 0){
        pModel->writeMSH(outFileName,
    		     CTX::instance()->mesh.mshFileVersion,CTX::instance()->mesh.binary, CTX::instance()->mesh.saveAll,
                         CTX::instance()->mesh.saveParametric, CTX::instance()->mesh.scalingFactor);
      }
    
      _meshFileName = outFileName;
      commonModel();
    }
    
    void nonLinearMechSolver::createMicroModel(const std::string geoFile, const int dim, const int order,
                            const bool incomplte){
      std::string mshFile = getFileSavingPrefix()+".msh";
      this->createModel(geoFile,mshFile,dim,order,incomplte);
    };
    
    void nonLinearMechSolver::initMapRanks(const std::set<int>& parts){
      _mapRanks.clear();
      std::vector<int> macroProcs;
      int partSize = parts.size();
      for (int i=0; i< partSize; i++){
        macroProcs.push_back(i);
    	}
      if (macroProcs.size() == 0){
    		// only rank 0 has a real solver
        macroProcs.push_back(0);
    		_workingRank = 0;
    	}
      int macroSize = macroProcs.size();
      int allSize = Msg::GetCommSize();
      int distSize = allSize - macroSize;
      int div = distSize/macroSize;
      for (int i=0; i<macroSize; i++){
        int root = macroProcs[i];
        std::set<int> others;
        for (int j=0; j<div; j++){
          int num = macroSize+i*div+j;
          others.insert(num);
        }
        _mapRanks[root] = others;
      }
      int j = macroSize+ div*macroSize;
      while (j< allSize){
        for (int i=0; i< macroSize; i++){
          int root = macroProcs[i];
          std::set<int>& others =_mapRanks[root];
          others.insert(j);
          j++;                                             // check here
          if (j>=allSize) break;
        }
      }
    
      #ifdef _DEBUG
      printf("print map ranks:\n");
      for (std::map<int,std::set<int> >::iterator it = _mapRanks.begin(); it!= _mapRanks.end(); it++){
        if (Msg::GetCommRank() == it->first){
          std::set<int>& others = it->second;
          printf("proc %d -- root rank = %d, other size = %d \n", it->first, it->first,others.size());
          for (std::set<int>::iterator its = others.begin(); its!= others.end(); its++){
            printf("proc %d -- other rank: %d \n",it->first,*its);
          }
        }
      }
      #endif // _DEBUG
    };
    
    int nonLinearMechSolver::getNumRanks() const{
      if (_mapRanks.size() > 0){
        int num=0;
        for (std::map<int,std::set<int> >::const_iterator it = _mapRanks.begin(); it != _mapRanks.end(); it++){
          const std::set<int>& others = it->second;
          num += (1+others.size());
        }
        return num;
    	}
    	else{
        return 1; // in serial case, we have at least 1 proc
    	}
    };
    
    int nonLinearMechSolver::getNumRootRanks() const{
      if (_mapRanks.size() > 0){
        return _mapRanks.size(); //
    	}
    	else{
        return 1; // in serial case, we have at least 1 proc
    	}
    };
    
    bool nonLinearMechSolver::rankOnSolver(const int rank) const{
    	for (std::map<int,std::set<int> >::const_iterator it = _mapRanks.begin(); it != _mapRanks.end(); it++){
    		if (rank == it->first) return true;
    		else{
    			const std::set<int>& others = it->second;
    			if (others.find(rank) != others.end()){
    				return true;
    			}
    
    		}
    	}
    	return false;
    };
    
    void nonLinearMechSolver::getAllRootRanks(std::set<int>& rootRanks) const{
    	rootRanks.clear();
    	for (std::map<int,std::set<int> >::const_iterator it = _mapRanks.begin(); it != _mapRanks.end(); it++){
    		rootRanks.insert(it->first);
    	}
    };
    
    void nonLinearMechSolver::getAllOtherRanks(std::set<int>& otherRanks) const{
      otherRanks.clear();
    	for (std::map<int,std::set<int> >::const_iterator it = _mapRanks.begin(); it != _mapRanks.end(); it++){
    		otherRanks.insert(it->second.begin(), it->second.end());
    	}
    };
    
    void nonLinearMechSolver::getRootRank(const int otherRank, int& rootRank) const{
    	bool found = false;
    	for (std::map<int,std::set<int> >::const_iterator it = _mapRanks.begin(); it != _mapRanks.end(); it++){
    		if (otherRank == it->first){
    			Msg::Error("_mapRanks is wrongly initiated:  wrong rank %d is root in nonLinearMechSolver::getRootRank",otherRank);
    		}
    
    		const std::set<int>& others = it->second;
    		if (others.find(otherRank) != others.end()){
    			rootRank = it->first;
    			found = true;
    			break;
    		}
    	}
    	if (!found){
    		Msg::Error("_mapRanks is wrongly initiated: root of other rank %d can not be found nonLinearMechSolver::getRootRank",otherRank);
    	}
    
    };
    
    void nonLinearMechSolver::getOtherRanks(const int rootRank, std::set<int>& otherRanks) const{
    	otherRanks.clear();
    	std::map<int,std::set<int> >::const_iterator it = _mapRanks.find(rootRank);
    	if (it != _mapRanks.end()){
    		otherRanks = it->second;
    	}
    };
    
    bool nonLinearMechSolver::isInitialized() const
    {
      return _previousInit;
    }
    
    bool nonLinearMechSolver::isMultiscale() const{
      bool isMult = false;
      for (std::map<int,materialLaw*>::const_iterator it = maplaw.begin(); it!= maplaw.end(); it++){
        materialLaw* mlaw = it->second;
        if (mlaw != NULL){
          if (mlaw->isNumeric()){
            isMult = true;
            break;
          }
        }
      }
      return isMult;
    };
    
    bool nonLinearMechSolver::isDgDomain() const{
      for (int i=0; i<domainVector.size(); i++){
        partDomain* dom = domainVector[i];
        if (dom->getFormulation()) return true;
      }
      return false;
    };
    
    bool nonLinearMechSolver::computedUdF() const{
      for (int i=0; i<domainVector.size(); i++){
        partDomain* dom = domainVector[i];
        if (dom->hasBodyForceForHO()) return true;
      }
      return false;
    };
    
    partDomain::BodyForceModuliType  nonLinearMechSolver::useWhichModuliForBF() const{
      partDomain* dom = domainVector[0];    
      return dom->useWhichModuliForBF();
    };
    
    
    
    IPField* nonLinearMechSolver::getIPField() {return _ipf;};
    const IPField* nonLinearMechSolver::getIPField() const {return _ipf;};
    
    unknownField* nonLinearMechSolver::getUnknownField() {return _ufield;};
    const unknownField* nonLinearMechSolver::getUnknownField() const {return _ufield;};
    
    nlsDofManager* nonLinearMechSolver::getDofManager() {return pAssembler;};
    const nlsDofManager* nonLinearMechSolver::getDofManager() const {return pAssembler;};
    
    std::vector<partDomain*>* nonLinearMechSolver::getDomainVector() {return &domainVector;}
    const std::vector<partDomain*>* nonLinearMechSolver::getDomainVector() const {return &domainVector;}
    
    std::vector<partDomain*>* nonLinearMechSolver::getGhostDomainMPI(){return &_ghostDomainMPI;};
    const std::vector<partDomain*>* nonLinearMechSolver::getGhostDomainMPI() const {return &_ghostDomainMPI;};
    
    nonLinearMechSolver::contactContainer* nonLinearMechSolver::getAllContactDomain(){return &_allContact;};
    const nonLinearMechSolver::contactContainer* nonLinearMechSolver::getAllContactDomain() const{return &_allContact;};
    nonLinearMechSolver::defoDefoContactContainer* nonLinearMechSolver::getAllDefoDefoContactDomain(){return &_allDefoDefoContact;};
    const nonLinearMechSolver::defoDefoContactContainer* nonLinearMechSolver::getAllDefoDefoContactDomain() const{return &_allDefoDefoContact;};
    
    GModel* nonLinearMechSolver::getGModel(){return pModel;};
    const GModel* nonLinearMechSolver::getGModel() const {return pModel;};
    
    void nonLinearMechSolver::init(){
    
      if(!_previousInit)
      {
        // Set the material law for each domain and the gauss integration rule
        for(std::vector<partDomain*>::iterator it = domainVector.begin(); it!=domainVector.end(); ++it){
          partDomain *dom = *it;
          dom->setMaterialLaw(maplaw);
        }
    
    		#if defined(HAVE_MPI)
    		// remove all unecessary domains in other ranks
    		if (this->getNumRanks() > 1){
          bool OneDomRemove = true;
          while (OneDomRemove){
            OneDomRemove = false;
            for(std::vector<partDomain*>::iterator it = domainVector.begin(); it!=domainVector.end(); ++it){
              partDomain *dom = *it;
              dgPartDomain* dgdom =  NULL;
              if (dom->IsInterfaceTerms()){
                dgdom = static_cast<dgPartDomain*>(dom);
              }
              std::set<int> otherRanks;
              int rootRank = dom->getRootRank();
              dom->getOtherRanks(otherRanks);
    
              if (otherRanks.size()> 0){
                bool isNumMat = false;
                if (dom->getMaterialLaw()){
                  if (dom->getMaterialLaw()->isNumeric()) isNumMat = true;
                }
                if (!isNumMat){
                  if (dom->IsInterfaceTerms()){
                    if (dgdom->getMaterialLawMinus()->isNumeric() or (dgdom->getMaterialLawPlus()->isNumeric())) {
                      isNumMat = true;
                    }
                  }
                }
                if (!isNumMat){
                  // if material is not numerical
                  if (otherRanks.find(Msg::GetCommRank()) != otherRanks.end()){
                    // in other rank
                    if (dom->elementGroupSize() > 0)
                      Msg::Error("Remove wrong partDomain %d",dom->getPhysical());
                    else if (dom->IsInterfaceTerms()){
                      if (dgdom->gi->size()> 0)
                        Msg::Error("Remove wrong dgPartDomain %d",dom->getPhysical());
                    }
                    domainVector.erase(it);
                    OneDomRemove = true;
                    break;
                  }
                  else if (rootRank == Msg::GetCommRank()){
                    // in root rank, clear other ranks
                    dom->clearOtherRanks();
                  }
                }
              }
            }
          }
    		}
    		#endif // HAVE_MPI
    
        // create interfaceElement
        this->createInterfaceElement();
    
        for(std::vector<partDomain*>::iterator it = domainVector.begin(); it!=domainVector.end(); ++it){
          partDomain *dom = *it;
          dom->setGaussIntegrationRule();
    			// set Solver
    			dom->setMacroSolver(this); // some options in domaian come from solver
        }
    
    		// add create new material cerate inside solver
    		for(std::vector<partDomain*>::iterator it = domainVector.begin(); it!=domainVector.end(); ++it){
    			dgPartDomain *dom = dynamic_cast<dgPartDomain*>(*it);
    			if (dom != NULL){
    				materialLaw* mlawMinus = dom->getMaterialLawMinus();
    				if (maplaw.find(mlawMinus->getNum()) == maplaw.end()){
    					maplaw[mlawMinus->getNum()] = mlawMinus;
    				}
    
    				materialLaw* mlawPlus = dom->getMaterialLawPlus();
    				if (maplaw.find(mlawPlus->getNum()) == maplaw.end()){
    					maplaw[mlawPlus->getNum()] = mlawPlus;
    				}
    			}
    		};
    		for (std::map<int,materialLaw*>::iterator it = maplaw.begin(); it!= maplaw.end(); it++){
    			it->second->setMacroSolver(this);
    		}
    
    		// at root ranks
    		#if defined(HAVE_MPI)
    		if (this->getNumRanks() > 1){
          Msg::Barrier(); // wait to finish all previous mesages
    		}
    		#endif // HAVE_MPI
    
    		if (isDgDomain()){
          _interfaceElements.clear();
          //printf("begin filling reference local basis\n");
          for (int idom= 0; idom < domainVector.size(); idom++){
            const partDomain* dom = domainVector[idom];
            if (dom->IsInterfaceTerms()){
              const dgPartDomain* dgdom = static_cast<const dgPartDomain*>(dom);
              for (elementGroup::elementContainer::const_iterator ite = dgdom->gi->begin(); ite != dgdom->gi->end(); ite++){
                MElement* ele = ite->second;
                MInterfaceElement* iele = dynamic_cast<MInterfaceElement*>(ele);
                TwoNum tn(iele->getElem(0)->getNum(),iele->getElem(1)->getNum());
                _interfaceElements[tn] = ele->getNum();
                _interfaceElementsInverseMap[ele->getNum()] = tn;
              }
            }
          }
    
          if (_interfaceElementsInverseMap.size() > 0){
            FILE* interFile = NULL;
            if (Msg::GetCommSize() > 1){
              std::ostringstream oss;
              oss<<Msg::GetCommRank();
              std::string partNum = oss.str();
              std::string filename = getFileSavingPrefix()+"InterfaceElement_part"+partNum+".csv";
              interFile = fopen(filename.c_str(),"w");
            }
            else{
              std::string filename = getFileSavingPrefix()+ "InterfaceElement.csv";
              interFile = fopen(filename.c_str(),"w");
            }
            for (std::map<int,TwoNum>::const_iterator itm = _interfaceElementsInverseMap.begin(); itm != _interfaceElementsInverseMap.end(); itm++){
              fprintf(interFile,"%d %d %d\n",itm->first,itm->second.small,itm->second.large);
            }
            fclose(interFile);
          }
    
          #if defined(HAVE_MPI)
          if (this->getNumRanks() > 1){
            std::set<int> allRootRanks, allOtherRanks;
            this->getAllRootRanks(allRootRanks);
            this->getAllOtherRanks(allOtherRanks);
    
            if (allRootRanks.find(Msg::GetCommRank()) != allRootRanks.end()){
              std::set<int> otherRanks;
              this->getOtherRanks(Msg::GetCommRank(),otherRanks);
              if (otherRanks.size() > 0){
                // send to other rank
                int interfaceSize = _interfaceElements.size();
                for (std::set<int>::const_iterator itR = otherRanks.begin(); itR != otherRanks.end(); itR++){
                  int oRank  = *itR;
                  int tag = numericalMaterialBase::createTypeWithTwoInts(Msg::GetCommRank(),1);
                  MPI_Send(&interfaceSize,1,MPI_INT,oRank,tag,MPI_COMM_WORLD);
                  if (interfaceSize > 0){
                    int bufferSize = interfaceSize*3;
                    std::vector<int> buffer(bufferSize);
                    int index = 0;
                    for (std::map<TwoNum,int>::const_iterator itm = _interfaceElements.begin(); itm != _interfaceElements.end(); itm++){
                      buffer[index] = itm->first.small;
                      buffer[index+1] = itm->first.large;
                      buffer[index+2] = itm->second;
                      index += 3;
                    }
                    tag = numericalMaterialBase::createTypeWithTwoInts(Msg::GetCommRank(),2);
                    MPI_Send(&buffer[0],bufferSize,MPI_INT,oRank,tag,MPI_COMM_WORLD);
                    buffer.clear();
                  }
                }
              }
            }
            else if (allOtherRanks.find(Msg::GetCommRank()) != allOtherRanks.end()){
              // receive data from root ranks
              int rootRank;
              this->getRootRank(Msg::GetCommRank(),rootRank);
              // receive message
              MPI_Status status;
              int tag = numericalMaterialBase::createTypeWithTwoInts(rootRank,1);
              int interfaceSize=0;
              MPI_Recv(&interfaceSize,1,MPI_INT,rootRank,tag,MPI_COMM_WORLD,&status);
              if (interfaceSize > 0){
                int bufferSize = 3*interfaceSize;
                tag = numericalMaterialBase::createTypeWithTwoInts(rootRank,2);
                std::vector<int> buffer(bufferSize);
                MPI_Recv(&buffer[0],bufferSize,MPI_INT,rootRank,tag,MPI_COMM_WORLD,&status);
    
                int idex = 0;
                for (int i=0; i< interfaceSize; i++){
                  TwoNum tn(buffer[idex],buffer[idex+1]);
                  _interfaceElements[tn] = buffer[idex+2];
                  _interfaceElementsInverseMap[buffer[idex+2]] = tn;
                  idex += 3;
                }
              }
            }
          }
          #endif // HAVE_MPI
        }
    
        // fill reference system axis of interface GPs
    		if (isMultiscale()){
          this->fillLocalReferenceBasisForAllInterfaceElement();
    
          #if defined(HAVE_MPI)
          if (getNumRanks() > 1){
            std::set<int> allRootRanks, allOtherRanks;
            this->getAllRootRanks(allRootRanks);
            this->getAllOtherRanks(allOtherRanks);
    
            if (allRootRanks.find(Msg::GetCommRank()) != allRootRanks.end()){
              std::set<int> otherRanks;
              this->getOtherRanks(Msg::GetCommRank(),otherRanks);
              if (otherRanks.size() > 0){
                int sizeOfInterfaceLocalBasis = _allInterfaceLocalBasis.size();
                for (std::set<int>::const_iterator itR = otherRanks.begin(); itR != otherRanks.end(); itR++){
                  int oRank  = *itR;
                  int tag = numericalMaterialBase::createTypeWithTwoInts(Msg::GetCommRank(),3);
                  MPI_Send(&sizeOfInterfaceLocalBasis,1,MPI_INT,oRank,tag,MPI_COMM_WORLD);
                  if (sizeOfInterfaceLocalBasis > 0){
                    int sizeBufferEleNum = sizeOfInterfaceLocalBasis;
                    int sizeBufferLocalBasis = 9*sizeBufferEleNum;
    
                    std::vector<int> bufferEleNum(sizeBufferEleNum);
                    std::vector<double> bufferLocalBasis(sizeBufferLocalBasis);
                    int rowEleNum =0;
                    for (std::map<int,STensor3>::const_iterator it = _allInterfaceLocalBasis.begin(); it != _allInterfaceLocalBasis.end(); it++){
                      bufferEleNum[rowEleNum] = it->first;
                      const STensor3& mat = it->second;
                      for (int index = 0; index< 9; index++){
                        int i,j;
                        Tensor23::getIntsFromIndex(index,i,j);
                        bufferLocalBasis[9*rowEleNum + index] = mat(i,j);
                      }
                      rowEleNum++;
                    }
                    tag = numericalMaterialBase::createTypeWithTwoInts(Msg::GetCommRank(),4);
                    MPI_Send(&bufferEleNum[0],sizeBufferEleNum,MPI_INT,oRank,tag,MPI_COMM_WORLD);
                    tag = numericalMaterialBase::createTypeWithTwoInts(Msg::GetCommRank(),5);
                    MPI_Send(&bufferLocalBasis[0],sizeBufferLocalBasis,MPI_DOUBLE,oRank,tag,MPI_COMM_WORLD);
                  }
                };
              }
            }
            else if (allOtherRanks.find(Msg::GetCommRank()) != allOtherRanks.end()){
              // receive data from root ranks
              int rootRank;
              this->getRootRank(Msg::GetCommRank(),rootRank);
              int bufferSize=0;
              MPI_Status status;
              int tag = numericalMaterialBase::createTypeWithTwoInts(rootRank,3);
              MPI_Recv(&bufferSize,1,MPI_INT,rootRank,tag,MPI_COMM_WORLD,&status);
              if (bufferSize > 0){
                int sizeBufferEleNum = bufferSize;
                int sizeBufferLocalBasis = 9*bufferSize;
                std::vector<int> bufferEleNum(sizeBufferEleNum);
                std::vector<double> bufferLocalBasis(sizeBufferLocalBasis);
    
                tag = numericalMaterialBase::createTypeWithTwoInts(rootRank,4);
                MPI_Recv(&bufferEleNum[0],sizeBufferEleNum,MPI_INT,rootRank,tag,MPI_COMM_WORLD,&status);
                tag = numericalMaterialBase::createTypeWithTwoInts(rootRank,5);
                MPI_Recv(&bufferLocalBasis[0],sizeBufferLocalBasis,MPI_DOUBLE,rootRank,tag,MPI_COMM_WORLD,&status);
                // set data
                for (int id=0; id< bufferSize; id++){
                  int type = bufferEleNum[id];
                  STensor3 mat(0.);
                  for (int index = 0; index< 9; index++){
                    int i,j;
                    Tensor23::getIntsFromIndex(index,i,j);
                    mat(i,j) = bufferLocalBasis[9*id+index];
                  }
                  _allInterfaceLocalBasis[type] = mat;
                }
              }
            }
          }
          #endif // HAVE_MPI
    		}
    
    		#if defined(HAVE_MPI)
        if (this->getNumRanks() > 1){
          Msg::Barrier(); // wait to finish all previous mesages
    		}
        #endif // HAVE_MPI
      }
      else  // move all archiving data in a folder to start a new analysis
            // keep only the previous one ( --> not valid if 2 switches)
            // Linux only ?? (implementation MAC and Windows)
      {
        moveFiles("previousScheme","mv");
        if(!_disableResetRestart) _resetRestart=true; //to avoid restart when shitfing schemes
      }
    
    
      if(whatScheme==Explicit or whatScheme==Multi)
        this->initMapMElementToComputeTimeStep();
      // find the domain associated to a physical slave number of a rigid contact interaction
      // WARNING a contact interaction cannot be defined on two domains in the same time
      // a physical group has to be define by domain
      if(!_notResetedContact){
        this->initContactInteraction();
        _notResetedContact =true;
      }
    
      if(!_notResetedBC){
        this->setPairSpaceElementForBoundaryConditions();
        _notResetedBC = true;
      }
    
      // initialisation of archiving force
      if (!_previousInit)
      {
        this->initArchiveForce();
        this->initPathFollowingArchiving();
        this->initArchiveInternalForce();
        this->initAverageCluster();
      }
      else
      {
        // for force
        for (std::vector<archiveForce>::iterator it = vaf.begin(); it!= vaf.end(); it++)
        {
          it->clear();
        }
        this->initArchiveForce();
        
        // for 
        for (std::list<archiveInternalForce>::iterator it = vafIntenal.begin(); it!= vafIntenal.end(); it++)
        {
          it->clear();
        }
        this->initArchiveInternalForce();
    
        for (std::list<IntegralVolume>::iterator it = _dataVolumeIntegral.begin(); it!= _dataVolumeIntegral.end(); it++)
        {
          it->resetArchiving(getFileSavingPrefix());
        }
        for (std::list<IPDataOnPhysical>::iterator it= _dataOnPhysical.begin(); it!= _dataOnPhysical.end(); it++)
        {
          it->resetArchiving();
        }
        this->initPathFollowingArchiving();
        for (std::list<AvergageCluster>::iterator it= _averageClusters.begin(); it!= _averageClusters.end(); it++)
        {
          it->resetArchiving(getFileSavingPrefix());
        }
      }
    }
    
    
    void nonLinearMechSolver::moveFiles(const std::string &dirName, const std::string &cm)
    {
       #if defined(HAVE_MPI)
        if(Msg::GetCommSize() > 1)
        {
           // get the host name of each rank
           const char* myhostname = (GetEnvironmentVar("HOSTNAME")).c_str();
           if(myhostname==NULL)
           {
             //Msg::Info("Hostname does not exist use rank");
             //std::ostringstream strm;
             //strm<<Msg::GetCommRank();
             //myhostname= strm.str().c_str();
             Msg::Info("Hostname does not exist use rank");
             myhostname= "DEFAULTNAME";
           }
    
           int* vsizename = new int[Msg::GetCommSize()];
           int* vallsizename = new int[Msg::GetCommSize()];
           for(int i=0;i<Msg::GetCommSize();i++)
           {
             vallsizename[i] = 0;
             vsizename[i] = 0;
           }
           vsizename[Msg::GetCommRank()] = (int) strlen(myhostname);
           MPI_Allreduce(vsizename,vallsizename,Msg::GetCommSize(),MPI_INT,MPI_SUM,MPI_COMM_WORLD);
           delete[] vsizename;
    
           char** vhostname = new char*[Msg::GetCommSize()];
           for(int i=0;i<Msg::GetCommSize();i++)
           {
               vhostname[i] = new char[vallsizename[i]];
               if(Msg::GetCommRank()==i)
               {
                 vhostname[i] = (char*)myhostname;
               }
               MPI_Bcast(vhostname[i],vallsizename[i],MPI_CHAR,i,MPI_COMM_WORLD);
           }
    
           // At this point all processes have an array with the host name of all processes
           // if the host name of the rank doesn't match lower rank files are deleted
           bool firsthostname=true;
           for(int i=0;i<Msg::GetCommRank();i++){
             if(!strcmp(myhostname,vhostname[i]))
             {
                firsthostname = false;
                break;
             }
           }
           if(firsthostname)
           {
             Msg::Warning("File are moved on rank %d, hostname %s",Msg::GetCommRank(),vhostname[Msg::GetCommRank()]);
             std::string rmdir= "rm -rf " + dirName;
             int oks = system(rmdir.c_str());
             std::string mdir= "mkdir " + dirName;
             oks = system(mdir.c_str());
             std::string mvcsv= cm + " *.csv " + dirName +"/";
             oks = system(mvcsv.c_str());
             std::string mvdisp= cm + " "+ getFileSavingPrefix()+ "disp*.msh " + dirName +"/";
             oks = system(mvdisp.c_str());
             std::string mvstr= cm + " "+getFileSavingPrefix()+"stress*.msh " + dirName +"/";
             oks = system(mvstr.c_str());
             std::string mvint= cm + " "+getFileSavingPrefix()+"interfaceStress*.msh " + dirName +"/";
             oks = system(mvint.c_str());
    	 if(!_disableResetRestart){
               struct stat st;
               if(stat("nonLinearMechSolver0.ckp",&st) == 0)
               {
                 std::string mvstr= cm + " *.ckp " + dirName +"/";
                 oks = system(mvstr.c_str());
               }
    	 }
             // get back the mesh file
             //if(!_meshFileName.empty())
             //{
             //  std::string backMesh = "cp previousScheme/" + _meshFileName + " .";
             //  system(backMesh.c_str());
             //}
           }
           else
           {
             Msg::Warning("Nothing moved on rank %d, hostname %s",Msg::GetCommRank(),vhostname[Msg::GetCommRank()]);
           }
           if (getNumRanks() > 1)
            Msg::Barrier();
           // delete array
           delete[] vhostname;
           delete[] vallsizename;
        }
        else
       #endif // HAVE_MPI
        {
          std::string rmdir= "rm -rf " + dirName;
          int oks = system(rmdir.c_str());
          std::string mdir= "mkdir " + dirName;
          oks =system(mdir.c_str());
          std::string mvcsv= cm + " *.csv " + dirName +"/";
          oks =system(mvcsv.c_str());
          std::string mvdisp= cm + " "+getFileSavingPrefix()+"disp*.msh " + dirName +"/";
          oks =system(mvdisp.c_str());
          std::string mvstr= cm + " "+getFileSavingPrefix()+"stress*.msh " + dirName +"/";
          oks =system(mvstr.c_str());
          std::string mvint= cm + " "+getFileSavingPrefix()+"interfaceStress*.msh " + dirName +"/";
          oks = system(mvint.c_str());
          if(!_disableResetRestart){
            struct stat st;
            if(stat("nonLinearMechSolver0.ckp",&st) == 0)
            {
              std::string mvstr= cm + " *.ckp " + dirName +"/";
              oks =system(mvstr.c_str());
            }
          }
    
          // get back the mesh file
          //if(!_meshFileName.empty())
          //{
          //  std::string backMesh = "cp previousScheme/" + _meshFileName + " .";
          //  system(backMesh.c_str());
          //}
        }
    }
    
    
    
    void nonLinearMechSolver::initAllBCsOnDofs(){
      if(whatScheme!=StaticLinear)
       this->setTimeForBC(0.);
      else
       this->setTimeForBC(1.);
    
      this->fixNodalDofs();
    
    	#if defined(HAVE_MPI)
    	if (Msg::GetCommRank() == _workingRank)
    	#endif //HAVE_MPI
    	{
    		// Boundary Conditions and Dof creation
    		this->fillConstraintDof();
    		this->applyConstraintBC();
    
    		// same disp BC
    		this->applySameDispBC();
    		//
    		this->applyFixOnFace();
        //periodic BC
    		this->applyPBCBetweenTwoGroups();
        
        this->applyLinearConstraintBetweenTwoGroups();
    
    		// for pbc
    		if (_testFlag){
    		  Msg::Info("initialize test with micro BC");
    		  this->initialize_test();
    		  this->initCornerBC();
    		}
    	}
    };
    
    void nonLinearMechSolver::init2()
    {
      // initialize the system and dofManager
      this->createSystem();
    
      this->initAllBCsOnDofs();
    
      Msg::Info("Fix and number Dofs");
      this->numberDofs();
      printf("total number of unknown Dofs = %d\n",pAssembler->sizeOfR());
      printf("total number of fixed Dofs = %d\n",pAssembler->sizeOfF());
      Msg::Info("Dofs are fixed and numbered");
    
      /* MPI init */
      #if defined(HAVE_MPI)
      if(whatScheme!=StaticLinear and Msg::GetCommSize()>1)
      {
        Msg::Info("MPI initialization begins");
        pAssembler->systemMPIComm(); // To initiate the MPI Communication
        Msg::Info("MPI initialization OK");
      }
      #endif // HAVE_MPI
     
      // make data as previous  if exists
      this->updateDataFromPreviousSolver();
      
     /* initialization of Dof value */
      // so now initial condition can be given to system
      this->setInitialCondition();
    
      #if defined(HAVE_MPI)
      if (Msg::GetCommSize()>1){
        bool isRootRank =false;
        for (int i=0; i<domainVector.size(); i++){
          partDomain* dom = domainVector[i];
          if (dom->getRootRank() == Msg::GetCommRank()){
            isRootRank = true;
            break;
          }
        }
        if (!isRootRank){
          // no view and energy computation
          unknownView.clear();
          ipView.clear();
          ipViewInterface.clear();
          energyView.clear();
          _energyComputation = 0;
          _fractureEnergyComputation = 0;
        }
      }
      #endif // HAVE_MPI
    
      bool stiffcomputation=true;
      if(whatScheme==Explicit) stiffcomputation=false;
      if(!_previousInit){
        /* fields creation */
        if(_ufield) delete _ufield;
        _ufield = new unknownField(this,3,anoded,unknownView);
    
        Msg::Info("Creation of IPVariable");
        _ipf = new IPField(this,vaip,ipView,ipViewInterface); // Field for GaussPoint
        Msg::Info("IPVariable are created");
         
        _ipf->compute1state(IPStateBase::initial,stiffcomputation);
        _ipf->initialBroken(pModel, initbrokeninter);
        _ipf->initialBrokenDomain(pModel,initbrokeninterInDomains);
        // starting state
        _ipf->copy(IPStateBase::initial,IPStateBase::previous);
        _ipf->copy(IPStateBase::initial,IPStateBase::current);
        if(_energField) delete _energField;
        _energField = new energeticField(this, energyView,_energyComputation,_fractureEnergyComputation,getScheme());
    
        //
        #if defined(HAVE_MPI)
        if (Msg::GetCommSize() > 1){
          if (_dataVolumeIntegral.size() > 0){
            // because all average values are computed independently in each procs
            // the volume in chech procs must be estimated
            std::string fname = getFileSavingPrefix()+"VolumeOnProc";
            std::ostringstream oss;
            oss << Msg::GetCommRank();
            fname += oss.str();
            fname += ".csv";
            FILE* fileVolumeProc = fopen(fname.c_str(),"w");
            double volume = 0.;
            for (int i=0; i<domainVector.size(); i++){
              volume += domainVector[i]->computeVolumeDomain(_ipf);
            };
            fprintf(fileVolumeProc,"%e",volume);
            fclose(fileVolumeProc);
          }
        }
        #endif //HAVE_MPI
      }
      else
      {
        _ipf->resetArchiving();
        if (_ufield) delete _ufield;
        _ufield = new unknownField(this,3,anoded,unknownView);
        if (_energField)
        {
          _energField->resetArchiving();
        }
      }
      
      /* terms initialization */
      this->initTerms();
      _previousInit = true;
      Msg::Info("End of initialization");
     #if defined(HAVE_MPI) && defined(_DEBUG)
      if (getNumRanks() > 1)
      {
        printf("End init OK from rank %d wait all in debug\n",Msg::GetCommRank());
        Msg::Barrier();
      }
     #endif // HAVE_MPI
    }
    
    void nonLinearMechSolver::endOfScheme(const double curtime, const int step)
    {
      this->postproFragment();
    
      if (_strainMap !=NULL)
      {
        _strainMap->buildDisplacementViewAndStrainView(step,true);
      }
    
      _ufield->archive(curtime,step,true);
      _ipf->archive(curtime,step,true);
      _energField->archive(curtime,step,true);
    
      forceArchiving(curtime,step,true);
      internalForceArchiving(curtime,step,true);
      this->IPVolumeIntegralArchiving(curtime,step,true);
      this->IPDataOnPhysicalArchiving(curtime,step,true);
      this->averageClusterArchiving(curtime,step,true);
    
     #if defined(HAVE_MPI)
      if (getNumRanks() > 1)
        Msg::Barrier(); // Wait all before new computation (MPI problem ??)
     #endif // HAVE_MPI
    }
    
    void nonLinearMechSolver::oneStepPreSolve(const double curtime, const double timestep, const int numstep)
    {
      this->setTimeForBC(curtime);
      this->setTimeForLaw(curtime,timestep,numstep);
      //for periodic BC
      this->fixNodalDofs();
      if (_testFlag){
        if (_pbcGroup != NULL){
          this->fixDofCorner();
        }
      }
    }
    
    
    void nonLinearMechSolver::oneStepPreSolvePathFollowing(const double curtime, const double timestep, const int numstep){
      static std::string name = "A";
    	linearSystem<double>* lsys =pAssembler->getLinearSystem(name);
    	pathFollowingSystem<double>* pathsys = dynamic_cast<pathFollowingSystem<double>*>(lsys);
      
      if (_pfManager._pathFollowingMethod == pathFollowingManager::LOCAL_BASED )
      {
        if (!_pfManager._switchedControlType)
        {
          pathsys->setControlType(pathFollowingSystemBase::LOAD_CONTROL);
        }
        else
        {
          pathsys->setControlType(pathFollowingSystemBase::LOCAL_CONTROL);
        }
      }
        
    	// if
    	double currentPathFollowingIncr =  _pfManager.getCurrentPathFollowingIncrement();
      //
    	pathsys->setPathFollowingIncrement(currentPathFollowingIncr);
      Msg::Info("path following step: %e  ",currentPathFollowingIncr);
    
    
    	double loadParam = pathsys->getControlParameter();
    	double dloadParam = fabs(pathsys->getControlParameterStep());
    	this->oneStepPreSolve(loadParam,dloadParam, numstep);
    }
    
    
    void nonLinearMechSolver::oneStepPostSolve(const double curtime, const int numstep)
    {
      // must be check first
      this->checkElementErosion(IPStateBase::current);
      if (_bulkElementErosionFlag or _interfaceElementErosionFlag)
      {
        _elementErosionFilter.print();
      }
    
      /*writing deformed mesh */
      if (_isWriteDeformedMeshToFile){
      	this->writeDeformedMesh(numstep);
      }
    
      this->crackTracking(curtime);
      /* Archiving */
      if (_timeManager->willArchive(curtime,numstep))
      {
        _ufield->archive(curtime,numstep,false);
        _ipf->archive(curtime,numstep,false);
        _energField->archive(curtime,numstep,false);
    
      
        this->forceArchiving(curtime,numstep,false);   // Edge force value;
        this->internalForceArchiving(curtime,numstep,false);
        this->pathFollowingArchiving(curtime,numstep);
    
        this->IPVolumeIntegralArchiving(curtime,numstep,false);
        this->IPDataOnPhysicalArchiving(curtime,numstep,false);
        this->averageClusterArchiving(curtime,numstep,false);
      }
    
    	if (_strainMap !=NULL){
        _strainMap->buildDisplacementViewAndStrainView(numstep,false);
    	}
    
      if (_microFlag)
      {
        // copy current to initial state
        (*_initialState) = (*_currentState);
    
        this->extractAverageProperties(_tangentflag);
        if (getDamageToCohesiveJumpFlag() and !_multiscaleFlag){
          this->checkFailureOnset();
        }
    
        if (_microBC->getTotalNumberOfMechanicalDofs() > 0){
          if (_stressflag){
            _currentState->getHomogenizedStress().print("Stress ");
            if (_microBC->getOrder() == 2)
              _currentState->getHomogenizedSecondOrderStress().print("hostress ");
          }
        }
    
        for (int index=0; index < _microBC->getTotalNumberOfConDofs(); index++){
          _currentState->getHomogenizedConstitutiveExtraDofFlux(index).print("con extra dof flux");
           printf("double con extra dof %d internal energy %f\n",index,_currentState->getHomogenizedConstitutiveExtraDofInternalEnergy(index));
        }
    
        if (_tangentflag){
           _currentState->getHomogenizedTangentOperator_F_F().print("tangent");
        }
    
        if (_extractPerturbationToFile){
          _pAl->getPBCConstraintGroup()->writePertBoundaryToFile(numstep);
        }
    
        if (_isWriteDeformedMeshToFile){
          this->writeDeformedMesh(numstep);
        }
    
        if (withEnergyDissipation()){
          _ipf->checkActiveDissipation();
          if (_ipf->getNumOfActiveDissipationIPsCurrent() > 0){
            // store historical value
            _homogenizedStressLastActiveDissipation = this->getHomogenizationState(IPStateBase::current)->getHomogenizedStress();
            _homogenizedStrainLastActiveDissipation = this->getMicroBC()->getFirstOrderKinematicalVariable();
            _homogenizedDissipationStrainLastActiveDissipation = this->getHomogenizationState(IPStateBase::current)->getHomogenizedActiveDissipationDeformationGradient();
            _homogenizedCohesiveJumpLastActiveDissipation = this->getHomogenizationState(IPStateBase::current)->getHomogenizedCohesiveJump();
    
          }
        }
        // save files
        this->homogenizedDataToFile(curtime);
      }
      
      //
      if (!_stiffnessModification)
      {
        if (_stiffnessModificationMonitoring->willModify(numstep,curtime))
        {
          _stiffEstimation = true;
        }
      }
      else
      {
        _stiffEstimation = true; // alway estimating stiffness matrix at the begining of the NR step
      }
    
      // next step
      this->nextStep(curtime,numstep);
    
      // restart at end if not need to redo next step when reading restart
      if(this->mustRestart(numstep))
      {
        // special operation for the GModel to avoid libGmsh modifications
        pModel->writeMSH(_restartMshFileName, CTX::instance()->mesh.mshFileVersion,CTX::instance()->mesh.binary, CTX::instance()->mesh.saveAll,
                          CTX::instance()->mesh.saveParametric, CTX::instance()->mesh.scalingFactor);
        if (getNumRanks() > 1)
          Msg::Barrier(); // To wait rank0 before end
        restartManager::createRestartFile("nonLinearMechSolver");
        restartManager::restart(this);
        //
        restartManager::closeRestartFile();
        //move output file for restart (if not the stress and disp files have duplicated steps)
        moveFiles("beforeRestart","cp");
        if (getNumRanks() > 1)
          Msg::Barrier(); // To wait rank0 before end
      }
    
    }
    
    void nonLinearMechSolver::checkElementErosion(const IPStateBase::whichState ws){
      if (_bulkElementErosionFlag or _interfaceElementErosionFlag){
        elementGroup* allg =  new elementGroup();
    
        // add element to check
        for (int idom=0; idom< domainVector.size(); idom++){
    			partDomain* dom = domainVector[idom];
          if (_bulkElementErosionFlag){
            for (elementGroup::elementContainer::const_iterator it = dom->element_begin(); it!= dom->element_end(); it++){
              allg->insert(it->second);
            };
          }
    
          if (_interfaceElementErosionFlag){
            if (dom->getFormulation()){
              dgPartDomain* dgdom = static_cast<dgPartDomain*>(dom);
              for (elementGroup::elementContainer::const_iterator it = dgdom->gi->begin(); it!= dgdom->gi->end(); it++){
                allg->insert(it->second);
              };
            }
          }
        }
        //
        for (elementGroup::elementContainer::const_iterator it = allg->begin(); it!= allg->end(); it++){
          MElement* ele = it->second;
          AllIPState::ipstateElementContainer* ips = _ipf->getAips()->getIPstate(ele->getNum());
    
          if (_erosionType == FIRST_IP_FAILED){
            bool willBeEroded = false;
    
            for (int ip=0; ip < (*ips).size(); ip++){
              const IPVariable* ipvprev = (*ips)[ip]->getState(IPStateBase::previous);
              const IPVariable* ipv = (*ips)[ip]->getState(ws);
              // check element
              if (_erosionGlobalCriterion!=NULL){
                // global check
                if (_erosionGlobalCriterion->isFailed(ipvprev,ipv)){
                  willBeEroded = true;
                  break;
                }
              }
              else if (ipv->isDeleted()){
                // local criterion
                willBeEroded = true;
                break;
              }
            }
    
            if (willBeEroded){
              // add element
              _elementErosionFilter.addErodedElement(ele);
              // delete all remaining IP
              for (int ip=0; ip < (*ips).size(); ip++){
                IPVariable* ipv = (*ips)[ip]->getState(ws);
                if (!ipv->isDeleted()){
                  ipv->Delete(true);
                }
              }
            }
          }
          else if (_erosionType == ALL_IP_FAILED){
            int numberNoFailedIP= 0;
            for (int ip=0; ip < (*ips).size(); ip++){
              const IPVariable* ipv = (*ips)[ip]->getState(ws);
              const IPVariable* ipvprev = (*ips)[ip]->getState(IPStateBase::previous);
              if (_erosionGlobalCriterion!=NULL){
                if (!_erosionGlobalCriterion->isFailed(ipvprev,ipv)){
                  numberNoFailedIP++;
                  break;
                }
              }
              else if (!ipv->isDeleted()){
                numberNoFailedIP++;
                break;
              }
            }
            if (numberNoFailedIP == 0){
              for (int ip=0; ip < (*ips).size(); ip++){
                IPVariable* ipv = (*ips)[ip]->getState(ws);
                if (!ipv->isDeleted()){
                  ipv->Delete(true);
                }
              }
              _elementErosionFilter.addErodedElement(ele);
            }
          }
          else{
            Msg::Error("_erosionType = %d is not defined nonLinearMechSolver::checkElementErosion",_erosionType);
          }
        }
    
        delete allg;
      }
    };
    
    void nonLinearMechSolver::restart()
    {
      int newtag = _tag;
      restartManager::restart(newtag);
      restartErosion();
      if(_tag != newtag) Msg::Error("The solver tag has change so restart cannot be done!");
      restartManager::restart(_ufield);
      restartManager::restart(_ipf);
      restartManager::restart(_energField);
      pAssembler->restart();
      //
      int newSolver = (int)whatSolver;
      restartManager::restart(newSolver);
      if(newSolver != (int)whatSolver) Msg::Error("You cannot change the solver at restart!");
      int newScheme = (int)whatScheme;
      restartManager::restart(newScheme);
      if(newScheme != (int)whatScheme) Msg::Error("You cannot change the scheme at restart!");
      
      _timeManager->restart();
      _pfManager.restart();
      restartDefoDefoContact();
      return;
    }
    
    void nonLinearMechSolver::restartErosion()
    {
      //restartManager::restart(_maxAllowedNewlyBrokenElement);  // Max number of allowed newly cracked elements during one step (for implicit case)
      // Erosion control
      //restartManager::restart(_bulkElementErosionFlag); // true if bulk element erosion
      //restartManager::restart(_interfaceElementErosionFlag); // true if interface element erosion
      //restartManager::restart(_erosionType); // all ip failed
      //FailureCriterionBase* _erosionGlobalCriterion;
      _elementErosionFilter.restart(); // a set consists of all erosion elements
      _elementErosionFilter.print();
    }
    void nonLinearMechSolver::restartDefoDefoContact()
    {
      for(defoDefoContactContainer::iterator it = _allDefoDefoContact.begin(); it!=_allDefoDefoContact.end(); ++it){
        defoDefoContactDomain *cdom = *it;
        cdom->restart();
      }
    }
    
    void nonLinearMechSolver::saveInternalState(const std::vector<int> tag)
    {
        if (!tag.empty()) Msg::Info("Saving Internal State");
        //if (tag.empty())
        //{
        //    Msg::Error("Domain tag not initialized for saving InternalState during restart!");
        //}
    
        if (getNumRanks() > 1)
            Msg::Barrier(); // To wait rank0 before end
    
        // if file existing, open and append???
        if(tag.size()>0)
            restartManager::createInternalStateRestartFile("restartInternalState");
    
        // to check element ordering between
        // two different meshses for same domain
        // outputing elem number and vertex coords
        //std::ofstream outfile;
        //outfile.open("Elem_order_"+std::to_string(this->getDomainVector()->size())+".txt");
    
        for (int t = 0; t < tag.size(); ++t)
        {
            std::vector<bool> flagFoundDomain(tag.size(),false);
            std::vector<partDomain*>* domainVec = this->getDomainVector();
            for(std::vector<partDomain*>::iterator it = domainVec->begin(); it!=domainVec->end(); ++it)
            {
                const partDomain *dom = *it;
                if(dom->getPhysical() == tag[t])
                {
                    AllIPState * aips = _ipf->getAips();
                    dom->restartIPState(aips);
                    flagFoundDomain[t] = true;
    
                    /*
                    const elementGroup* elems = dom->elements();
                    for (elementGroup::elementContainer::const_iterator eleIt = elems->begin(); eleIt != elems->end(); ++eleIt)
                    {
                        MElement *e = eleIt->second;
                        outfile << "Elem num: " << e->getNum() << std::endl;
                        const int numVertices = e->getNumVertices();
                        outfile << "Vertices coords: " << std::endl;
                        for(unsigned  int v = 0; v < numVertices; ++v)
                        {
                            outfile << e->getVertex(v)->x() << ", " << e->getVertex(v)->y() << ", " << e->getVertex(v)->z() << std::endl;
                        }
                    }
                    */
                }
            }
            if(!flagFoundDomain[t])
            {
                Msg::Error("Cannot find the parsed domain tag in the domain vector!");
            }
        }
        if (tag.size() > 0)
            restartManager::closeRestartFile();
    
        if (getNumRanks() > 1)
            Msg::Barrier(); // To wait rank0 before end
    
        //outfile.close();
    
        if (!tag.empty()) Msg::Info("Done saving Internal State");
    }
    
    void nonLinearMechSolver::loadInternalState(const std::vector<int> tag)
    {
        Msg::Info("Loading Internal State");
        if(tag.empty())
        {
            Msg::Error("Domain tag not initialized for loading InternalState during restart!");
        }
    
        if (getNumRanks() > 1)
            Msg::Barrier(); // To wait rank0 before end
        const std::string fname("restartInternalState");
        if (tag.size() > 0)
            restartManager::openInternalStateRestartFile(fname);
    
        // to check element ordering between
        // two different meshses for same domain
        // outputing elem number and vertex coords
        //std::ofstream outfile;
        //outfile.open("Elem_order_"+std::to_string(this->getDomainVector()->size())+".txt");
    
        for (int t = 0; t < tag.size(); ++t)
        {
            std::vector<bool> flagFoundDomain(tag.size(),false);
            std::vector<partDomain*>* domainVec = this->getDomainVector();
            for(std::vector<partDomain*>::iterator it = domainVec->begin(); it!=domainVec->end(); ++it)
            {
                const partDomain *dom = *it;
                if (dom->getPhysical() == tag[t])
                {
    
                    AllIPState * aips = _ipf->getAips();
                    dom->restartIPState(aips);
                    flagFoundDomain[t] = true;
    
                    /*
                    const elementGroup* elems = dom->elements();
                    for (elementGroup::elementContainer::const_iterator eleIt = elems->begin(); eleIt != elems->end(); ++eleIt)
                    {
                        MElement *e = eleIt->second;
                        outfile << "Elem num: " << e->getNum() << std::endl;
                        const int numVertices = e->getNumVertices();
                        outfile << "Vertices coords: " << std::endl;
                        for(unsigned  int v = 0; v < numVertices; ++v)
                        {
                            outfile << e->getVertex(v)->x() << ", " << e->getVertex(v)->y() << ", " << e->getVertex(v)->z() << std::endl;
                        }
                    }
                     */
                }
            }
            if(!flagFoundDomain[t])
            {
                Msg::Info("Cannot find the parsed domain tag in the domain vector!");
            }
        }
        if(tag.size() > 0)
            restartManager::closeRestartFile();
        if (getNumRanks() > 1)
            Msg::Barrier(); // To wait rank0 before end
    
        //outfile.close();
    
        Msg::Info("Done loading Internal State");
    }
    
    void nonLinearMechSolver::initArchiveInternalForce()
    {
      for (std::list<archiveInternalForce>::iterator itf = vafIntenal.begin(); itf != vafIntenal.end(); itf++)
      {
        archiveInternalForce& af =  *itf;
        printf("init archiving internal force at physical %d dim %d\n",af.phys,af.dim);
        elementGroup g(af.dim, af.phys);
        if (g.vsize() > 0)
        {
          af.openFile(getFileSavingPrefix());
          fprintf(af.FP,"Time");
        }
        for (elementGroup::vertexContainer::const_iterator itv = g.vbegin(); itv != g.vend(); itv++)
        {
          MVertex* v  = itv->second;
          std::map<archiveInternalForce::elementDomain,int>& sharedElements = af.sharedElements[v->getNum()];
          fprintf(af.FP,";F_%ld_X;F_%ld_Y;F_%ld_Z",v->getNum(),v->getNum(),v->getNum());
          
          for (int i=0; i< domainVector.size(); i++)
          {
            partDomain* dom = domainVector[i];
            for (elementGroup::elementContainer::const_iterator ite = dom->element_begin(); ite != dom->element_end(); ite++)
            {
              MElement* e = ite->second;
              for (int iv=0; iv < e->getNumVertices(); iv++)
              {
                if (e->getVertex(iv)->getNum() == v->getNum())
                {
                  sharedElements.insert(std::pair<archiveInternalForce::elementDomain,int>(archiveInternalForce::elementDomain(dom,e),iv));
                }
              } 
            }
          }
    
        }
        if (g.vsize() > 0)
        {
          fprintf(af.FP,"\n");
        }
      }
    }
    
    void nonLinearMechSolver::internalForceArchiving(const double curtime,const int numstep, const bool forceSave)
    {
      for (std::list<archiveInternalForce>::iterator itf = vafIntenal.begin(); itf != vafIntenal.end(); itf++)
      {
        archiveInternalForce& af =  *itf;
        if (numstep > af.lastSaveStep)
        {
          if((numstep%(af.nstep)==0 or forceSave))
          {
            af.lastSaveStep = numstep;
            fprintf(af.FP,"%e",curtime);
            fprintf(af.FPTotal,"%e",curtime);
            // getData
            double FXtol = 0.;
            double FYtol = 0.;
            double FZtol = 0.;
            for (std::map<int,std::map<archiveInternalForce::elementDomain,int> >::iterator its = af.sharedElements.begin(); its != af.sharedElements.end(); its++)
            {
              double FX(0.), FY(0.), FZ(0.);
              int vnum = its->first;
              std::map<archiveInternalForce::elementDomain,int>&  eleDom = its->second;
              for (std::map<archiveInternalForce::elementDomain,int>::iterator iteled = eleDom.begin(); iteled != eleDom.end(); iteled++)
              {
                partDomain* dom = iteled->first.dom;
                MElement* ele = iteled->first.ele;
                int vertexPos = iteled->second;
                
                std::vector<Dof> R;
                dom->getFunctionSpace()->getKeys(ele,R);
                IntPt *GP;
                int npts = dom->getBulkGaussIntegrationRule()->getIntPoints(ele, &GP);
                
                fullVector<double> val;
                dom->getLinearBulkTerm()->get(ele,npts,GP,val);
                
                int nbFF = ele->getNumShapeFunctions();
                FX += val(vertexPos);
                FY += val(vertexPos+nbFF);
                FZ += val(vertexPos+2*nbFF);
              }
              
              FXtol += FX;
              FYtol += FY;
              FZtol += FZ;
              //printf("internal force at node %d is FX = %e FY = %e FZ = %e\n",vnum,FX,FY,FZ);
              fprintf(af.FP,";%.16g;%.16g;%.16g",FX,FY,FZ);
            }
            fprintf(af.FP,"\n");
            fflush(af.FP);
            fprintf(af.FPTotal,";%.16g;%.16g;%.16g\n",FXtol,FYtol,FZtol);
            fflush(af.FPTotal);
          }
        }
      }
    };
    
    
    std::string nonLinearMechSolver::getFileSavingPrefix() const
    {
      if (_microFlag)
      {
        if (_enumMinus == _enumPlus)
        {
          return "E_"+int2str(_enumMinus)+"_GP_"+int2str(_gnum)+"_";
        }
        else
        {
          return "E_Interface_"+int2str(_enumMinus)+"_"+int2str(_enumPlus)+"_GP_"+int2str(_gnum)+"_";
        }
      }
      else
      {
        return "";
      }
    }
    
    void nonLinearMechSolver::initContactInteraction(){
      // find a common element between slave group of element and the group of element of partdomain
      bool flagfind;
      for(contactContainer::iterator it = _allContact.begin(); it!=_allContact.end(); ++it){
        contactDomain *cdom = *it;
        // get physical of domain
        int physSlave = cdom->getPhysSlave();
        flagfind=false;
        for(int kk=0;kk<_mpiUserDom; kk++){
          partDomain *dom = domainVector[kk];
          if(dom->getPhysical() == physSlave){
            flagfind =true;
            cdom->setDomainAndFunctionSpace(dom);
            break;
          }
        }
        if(!flagfind){
         #if defined(HAVE_MPI)
          // find the domain in the Ghosted domain (the search on an empty group OK)
          for(int kk=0; kk<_ghostDomainMPI.size(); kk++)
          {
            partDomain *dom = _ghostDomainMPI[kk];
            if(dom->getPhysical() == physSlave){
              flagfind =true;
              cdom->setDomainAndFunctionSpace(dom);
              break;
            }
          }
          if(!flagfind)
         #endif // HAVE_MPI
          {
            Msg::Error("The contact interaction %d can't be initiated because the domain is not find on rank %d",cdom->getPhys(),Msg::GetCommRank());
          }
        }
      }
      // now init the contact boundary conditions
      //for(rigidContactBC &diri : allContactBC){
      for(std::list<rigidContactBC>::iterator diri = allContactBC.begin(); diri!=allContactBC.end(); ++diri){
        if(diri->onWhat == nonLinearBoundaryCondition::RIGIDCONTACT){
          for(contactContainer::iterator itcdom = _allContact.begin(); itcdom!=_allContact.end(); ++itcdom){
            contactDomain *cdom = *itcdom;
            if(cdom->getPhys() == diri->_tag){ // we can have some slave domain with the same master --> don't put a break in the loop !
              rigidContactSpaceBase *sp = static_cast<rigidContactSpaceBase*>(cdom->getSpace());
              diri->setSpace(sp);
              diri->_filter = cdom->getDomain()->createFilterDof(diri->_comp);
            }
          }
        }
      }
    
      // Idem for defo defo contact. MPI not implemented
      // for parallel we need to define the master faces as ghost faces in all the other partitions
      for(defoDefoContactContainer::iterator it = _allDefoDefoContact.begin(); it!=_allDefoDefoContact.end(); ++it){
        defoDefoContactDomain *cdom = (*it);
    
        bool domfindSlave=false;
        int physSlave = cdom->getPhysSlave();
        elementGroup gSlave(cdom->getDimSlave(), physSlave); //will not be insterted, so delete it at the end
        // map
        std::map<partDomain*,std::vector<MElement*> > mapfindSlave;
        for(std::vector<partDomain*>::iterator itdom=domainVector.begin(); itdom!=domainVector.end(); ++itdom){
          partDomain *dom = *itdom;
          if(physSlave == dom->getPhysical()){
            domfindSlave = true;
            for(elementGroup::elementContainer::const_iterator it2=dom->element_begin(); it2!=dom->element_end(); ++it2)
              mapfindSlave[dom].push_back(it2->second);
            break;
          }
        }
        #if defined(HAVE_MPI)
        // look if the BC is applied on a vertex
        if(!domfindSlave){
          if(cdom->getDimSlave() == 1){
            // loop on dof of other partition (if the dof is included only on this partition it will be find after)
            elementGroup::vertexContainer::const_iterator itvBC = gSlave.vbegin();
            MVertex *verBC = NULL;
            if (gSlave.vsize() > 0)
              verBC = itvBC->second;
            for(std::map<int,std::vector<elementGroup> >::iterator it=_mapOtherDomain.begin(); it!=_mapOtherDomain.end(); ++it){
              std::vector<elementGroup> & groupOtherPart = it->second;
              for(int j=0;j<Msg::GetCommRank(); j++){ // look on domain with a lower partition number
                for(elementGroup::vertexContainer::const_iterator itv = groupOtherPart[j].vbegin(); itv!=groupOtherPart[j].vend(); ++itv){
                  MVertex *ver = itv->second;
                  if(verBC == ver){
                    domfindSlave=true; // the BC will be applied on an other partition
                    break;
                  }
                }
              }
            }
          }
        }
        #endif //HAVE_MPI
        if(!domfindSlave){
          // loop on element
          std::map<partDomain*,MElement*> mapelemtype; // need for gauss quadrature rule TODO group with mapfind in a structure but no time benefit
          for(elementGroup::elementContainer::const_iterator it=gSlave.begin(); it!=gSlave.end();++it){
            MElement *e = it->second;
            bool flagfind = false;
            // Loop on all Domain to find on which domain the BC is applied
            for(int j=0; j<_mpiUserDom; j++){
              // if Dimensions are the same the element can be find with a comparison to element
              partDomain *dom = domainVector[j];
              // otherwise the vertex must be identified separately
              MElement *etest = NULL;
              if (dom->elementGroupSize() > 0)
              {
                etest = dom->element_begin()->second;
                mapelemtype[dom] = etest;
              }
              if( (etest != NULL) and (etest->getDim() == e->getDim())){
                for(elementGroup::elementContainer::const_iterator it2=dom->element_begin(); it2!=dom->element_end(); ++it2){
                  if(it2->second == e){ // The Element is contained in the domain
                    mapfindSlave[dom].push_back(e);
                    flagfind = true;
                    break;
                  }
                }
              }
              else{ // loop on Vertex identify the BC with first vertex of e
                     // an element is included in one domain --> one vertex of Element is OK
                     // in mpi the BC can be included in another partition
                #if defined(HAVE_MPI)
                for(int j=_mpiUserDom; j<domainVector.size(); j++){ // loop only on interface domain "added by mpi"
                  dgPartDomain *dgdom = static_cast<dgPartDomain*>(domainVector[j]);
                  // get the two partition number
                  // Neumann BC cannot be applied on ghost element (will not be assembled in the end)
                  // do not applied on ghost element AS THEY WILL NOT BE ABLE TO BE ASSEMBLED!
                  // loop over ghost element and see if there is a match
                  for(elementGroup::elementContainer::const_iterator iteleDom = dgdom->gi->begin(); iteleDom != dgdom->gi->end();++iteleDom)
                  {
                    MInterfaceElement *ie = dynamic_cast<MInterfaceElement*>(iteleDom->second);
                    int numMinus = ie->getElem(0)->getPartition();
                    int numPlus = ie->getElem(1)->getPartition();
                    // identify the Ghost
                    MElement* eleGhost = (numMinus == Msg::GetCommRank()+1) ? ie->getElem(1) : ie->getElem(0);
                    // loop over nodes to see if they match
                    int nmatch = 0;
                    for(int i=0;i<eleGhost->getNumVertices();++i)
                    {
                      MVertex* verGhost = eleGhost->getVertex(i);
                      for(int jj=0;jj<e->getNumVertices();jj++){
                        MVertex *verBC = e->getVertex(jj);
                        if(verBC == verGhost)
                        {
                          nmatch++;
                          if(nmatch == e->getNumVertices())
                          {
                            flagfind = true; // the BC will be applied on another partition as on ghost here
                            break;
                          }
                        }
                      }
                      if(flagfind) break;
                    }
                    if(flagfind) break;
                  }
                }
                #endif // HAVE_MPI
                if(!flagfind){
                  // check all vertex of the BC (node 0 could be on another partition)
                  int ncount = 0;
                  for(elementGroup::vertexContainer::const_iterator itv=dom->vertex_begin(); itv!=dom->vertex_end(); ++itv){
                    for(int jj=0; jj<e->getNumVertices();++jj)
                    {
                      MVertex *ver = e->getVertex(jj);
                      if((itv->second) == ver){
                        ncount ++;
                        if(ncount == e->getNumVertices())
                        {
                          mapfindSlave[dom].push_back(e);
                          flagfind = true;
                          break;
                        }
                      }
                    }
                    if(flagfind) break;
                  }
                }
              }
            if(flagfind)
              break;
            }
          }
        }
    
    
    
        bool domfindMaster=false;
        int physMaster = cdom->getPhysMaster();
        //elementGroup *gMaster = new elementGroup (cdom->getDimMaster(), physMaster);
        elementGroup gMaster(cdom->getDimMaster(), physMaster); //will not be insterted, so delete it at the end
        // map
        std::map<partDomain*,std::vector<MElement*> > mapfindMaster;
        for(std::vector<partDomain*>::iterator itdom=domainVector.begin(); itdom!=domainVector.end(); ++itdom){
          partDomain *dom = *itdom;
          if(physMaster == dom->getPhysical()){
            domfindMaster = true;
            for(elementGroup::elementContainer::const_iterator it2=dom->element_begin(); it2!=dom->element_end(); ++it2)
              mapfindMaster[dom].push_back(it2->second);
            break;
          }
        }
        #if defined(HAVE_MPI)
        // look if the BC is applied on a vertex
        if(!domfindMaster){
          if(cdom->getDimMaster() == 1){
            // loop on dof of other partition (if the dof is included only on this partition it will be find after)
            elementGroup::vertexContainer::const_iterator itvBC = gMaster.vbegin();
            MVertex *verBC = NULL;
            if (gMaster.vsize() > 0)
              verBC = itvBC->second;
            for(std::map<int,std::vector<elementGroup> >::iterator it=_mapOtherDomain.begin(); it!=_mapOtherDomain.end(); ++it){
              std::vector<elementGroup> & groupOtherPart = it->second;
              for(int j=0;j<Msg::GetCommRank(); j++){ // look on domain with a lower partition number
                for(elementGroup::vertexContainer::const_iterator itv = groupOtherPart[j].vbegin(); itv!=groupOtherPart[j].vend(); ++itv){
                  MVertex *ver = itv->second;
                  if(verBC == ver){
                    domfindMaster=true; // the BC will be applied on an other partition
                    break;
                  }
                }
              }
            }
          }
        }
        #endif //HAVE_MPI
        if(!domfindMaster){
          // loop on element
          std::map<partDomain*,MElement*> mapelemtype; // need for gauss quadrature rule TODO group with mapfind in a structure but no time benefit
          for(elementGroup::elementContainer::const_iterator it=gMaster.begin(); it!=gMaster.end();++it){
            MElement *e = it->second;
            bool flagfind = false;
            // Loop on all Domain to find on which domain the BC is applied
            for(int j=0; j<_mpiUserDom; j++){
              // if Dimensions are the same the element can be find with a comparison to element
              partDomain *dom = domainVector[j];
              // otherwise the vertex must be identified separately
              MElement *etest = NULL;
              if (dom->elementGroupSize() > 0)
              {
                etest = (dom->element_begin()->second);
                mapelemtype[dom] = etest;            
              }
              if( (etest != NULL) and (etest->getDim() == e->getDim())){
                for(elementGroup::elementContainer::const_iterator it2=dom->element_begin(); it2!=dom->element_end(); ++it2){
                  if(it2->second == e){ // The Element is contained in the domain
                    mapfindMaster[dom].push_back(e);
                    flagfind = true;
                    break;
                  }
                }
              }
              else{ // loop on Vertex identify the BC with first vertex of e
                     // an element is included in one domain --> one vertex of Element is OK
                     // in mpi the BC can be included in another partition
                #if defined(HAVE_MPI)
                for(int j=_mpiUserDom; j<domainVector.size(); j++){ // loop only on interface domain "added by mpi"
                  dgPartDomain *dgdom = static_cast<dgPartDomain*>(domainVector[j]);
                  // get the two partition number
                  // Neumann BC cannot be applied on ghost element (will not be assembled in the end)
                  // do not applied on ghost element AS THEY WILL NOT BE ABLE TO BE ASSEMBLED!
                  // loop over ghost element and see if there is a match
                  for(elementGroup::elementContainer::const_iterator iteleDom = dgdom->gi->begin(); iteleDom != dgdom->gi->end();++iteleDom)
                  {
                    MInterfaceElement *ie = dynamic_cast<MInterfaceElement*>(iteleDom->second);
                    int numMinus = ie->getElem(0)->getPartition();
                    int numPlus = ie->getElem(1)->getPartition();
                    // identify the Ghost
                    MElement* eleGhost = (numMinus == Msg::GetCommRank()+1) ? ie->getElem(1) : ie->getElem(0);
                    // loop over nodes to see if they match
                    int nmatch = 0;
                    for(int i=0;i<eleGhost->getNumVertices();++i)
                    {
                      MVertex* verGhost = eleGhost->getVertex(i);
                      for(int jj=0;jj<e->getNumVertices();jj++){
                        MVertex *verBC = e->getVertex(jj);
                        if(verBC == verGhost)
                        {
                          nmatch++;
                          if(nmatch == e->getNumVertices())
                          {
                            flagfind = true; // the BC will be applied on another partition as on ghost here
                            break;
                          }
                        }
                      }
                      if(flagfind) break;
                    }
                    if(flagfind) break;
                  }
                }
                #endif // HAVE_MPI
                if(!flagfind){
                  // check all vertex of the BC (node 0 could be on another partition)
                  int ncount = 0;
                  for(elementGroup::vertexContainer::const_iterator itv=dom->vertex_begin(); itv!=dom->vertex_end(); ++itv){
                    for(int jj=0; jj<e->getNumVertices();++jj)
                    {
                      MVertex *ver = e->getVertex(jj);
                      if((itv->second) == ver){
                        ncount ++;
                        if(ncount == e->getNumVertices())
                        {
                          mapfindMaster[dom].push_back(e);
                          flagfind = true;
                          break;
                        }
                      }
                    }
                    if(flagfind) break;
                  }
                }
              }
              if(flagfind)
                break;
            }
          }
        }
        // now the map Space Element is filled for the BC
        for(std::map<partDomain*,std::vector<MElement*> >::iterator it=mapfindMaster.begin(); it!=mapfindMaster.end(); ++it)
        {
          elementGroup mast(it->second);
          for(std::map<partDomain*,std::vector<MElement*> >::iterator itslave=mapfindSlave.begin(); itslave!=mapfindSlave.end(); ++itslave)
          {
            elementGroup slav(itslave->second);
            cdom->insertSlaveMasterDomainsAndFunctionSpace(itslave->first,&slav,it->first,&mast);
          }
        }
      }
    }
    
    void nonLinearMechSolver::setPairSpaceElementForBoundaryConditions()
    {
      // The current GModel has to be selected to create the BC
      GModel* save_current_model = GModel::current();
      GModel::setCurrent(pModel);
    
      // loop on BC
      // BC which are prescribed weakly (the interfaceElement has to be finded in vinter)
      // to prescribed the BC
      // Theta (normally on Boundary only --> impossible that the condition be on 2 partitions)
      for(std::list<nonLinearNeumannBC>::iterator ittheta=allTheta.begin(); ittheta!=allTheta.end(); ++ittheta){
         for(elementGroup::elementContainer::const_iterator ite=ittheta->g->begin(); ite!=ittheta->g->end(); ++ite){
           MVertex *vv = (ite->second)->getVertex(2);
           for(int k=0;k<domainVector.size(); k++){
             partDomain *dom = domainVector[k];
             if(dom->IsInterfaceTerms()){ // change this (virtual interface has to be defined on domain)
               // Ok because 2nd degree min BoundaryInterfaceElement is created only for this vertex
               dgPartDomain *dgdom = static_cast<dgPartDomain*>(dom);
               elementGroup *gvi = dgdom->giv;
               for(elementGroup::elementContainer::const_iterator it_inter = gvi->begin(); it_inter!=gvi->end();++it_inter){
                 MElement *minter = it_inter->second;
                 //loop on component of map_edge
                 int numvertexminus1 = minter->getNumVertices()-1;
                 if((vv == minter->getVertex(2)) or (vv == minter->getVertex(numvertexminus1))){
                   dgdom->gib->insert(minter);
                   break;
                 }
               }
             }
           }
         }
       }
    
    #if 1
      //Dirichlet (can be applied on each domain look on ghost element to prescribed BC on them avoid a search in unknown map at each iteration)
      for(std::list<nonLinearDirichletBC>::iterator itbc = allDirichlet.begin(); itbc != allDirichlet.end();++itbc){
        // first look if the BC is not applied on whole domain
        bool domfind=false;
        for(std::vector<partDomain*>::iterator itdom=domainVector.begin(); itdom!=domainVector.end(); ++itdom){
          partDomain *dom = *itdom;
          if(itbc->_tag == dom->getPhysical()){
            domfind = true;
            if(dom->_groupGhostMPI->size() > 0) // save BC to applied an other groupOfElement
            {
              FunctionSpaceBase *spdbc = dom->getSpaceForBC(nonLinearBoundaryCondition::DIRICHLET,itbc->onWhat, nonLinearNeumannBC::NEUMANN_UNDEF,
                                                            itbc->_comp,itbc->getDofType(),dom->_groupGhostMPI);
              FilterDof *fdof = dom->createFilterDof(itbc->_comp);
              itbc->_vspace.push_back(spdbc);
              itbc->_vgroup.push_back(dom->_groupGhostMPI);
              itbc->_vfilter.push_back(fdof);
              itbc->_vdg.push_back(dom->getFormulation());
            }
            FunctionSpaceBase *spdbc = dom->getSpaceForBC(nonLinearBoundaryCondition::DIRICHLET,itbc->onWhat, nonLinearNeumannBC::NEUMANN_UNDEF,
                                                            itbc->_comp,itbc->getDofType(),dom->elements());
            FilterDof *fdof = dom->createFilterDof(itbc->_comp);
            itbc->_vspace.push_back(spdbc);
            itbc->_vgroup.push_back(dom->elements());
            itbc->_vfilter.push_back(fdof);
            itbc->_vdg.push_back(dom->getFormulation());
            break;
          }
        }
        #if defined(HAVE_MPI)
        // same on ghost domain (be aware copy past of loop on domain !!)
        bool ghostdomfind=false;
        for(std::vector<partDomain*>::iterator itdom=_ghostDomainMPI.begin(); itdom!=_ghostDomainMPI.end(); ++itdom){
          partDomain *dom = *itdom;
          if(itbc->_tag == dom->getPhysical() and dom->_groupGhostMPI->size()>0){
            ghostdomfind = true;
    	    FunctionSpaceBase *spdbc = dom->getSpaceForBC(nonLinearBoundaryCondition::DIRICHLET,itbc->onWhat, nonLinearNeumannBC::NEUMANN_UNDEF,
                                                            itbc->_comp,itbc->getDofType(),dom->_groupGhostMPI);
    	    FilterDof* fdof = dom->createFilterDof(itbc->_comp);
            itbc->_vspace.push_back(spdbc);
            itbc->_vgroup.push_back(dom->_groupGhostMPI);
            itbc->_vfilter.push_back(fdof);
            itbc->_vdg.push_back(dom->getFormulation());
            break;
          }
        }
        #endif // HAVE_MPI
        if(!domfind){
          // loop on element
          // map
          std::map<partDomain*,std::vector<MElement*> > mapfind;
          std::map<partDomain*,std::vector<MElement*> > mapfindGhost;
          for(elementGroup::elementContainer::const_iterator it=itbc->g->begin(); it!=itbc->g->end();++it){
            MElement *e = it->second;
            // Loop on all Domain to find on which domain the BC is applied
            for(std::vector<partDomain*>::iterator itdom=domainVector.begin(); itdom!=domainVector.end(); ++itdom){
              // if Dimensions are the same the element can be find with a comparison to element
              partDomain *dom = *itdom;
              // otherwise the vertex must be identified separately
              const MElement *etest = NULL;
              if (dom->elementGroupSize() > 0)
              {
                etest = (dom->element_begin()->second);
              }
              if( (etest !=NULL) and (etest->getDim() == e->getDim())){
                for(elementGroup::elementContainer::const_iterator it2=dom->element_begin(); it2!=dom->element_end(); ++it2){
                  if(it2->second == e){ // The Element is contained in the domain
                    mapfind[dom].push_back(e);
                    break;
                  }
                }
                #if defined(HAVE_MPI)
                for(elementGroup::elementContainer::const_iterator it2=dom->_groupGhostMPI->begin(); it2!=dom->_groupGhostMPI->end(); ++it2){
                  if(it2->second == e){ // The Element is contained in the domain
                    mapfind[dom].push_back(e);
                    break;
                  }
                }
                #endif // HAVE_MPI
              }
              else{
                // an element is included in one domain --> one vertex of Element is OK
                // On interface the BC is put on the first met domain
                fillMapOfInterfaceElementsInOneDomain(e, mapfind[(*itdom)], dom->elements());
                #if defined(HAVE_MPI)
                // change map in mapFindGhost???
                fillMapOfInterfaceElementsInOneDomain(e, mapfind[(*itdom)], dom->_groupGhostMPI);
                // check on element which are not included in _groupGhostMPI as there is no interface element with the element
                for(int j=0;j<Msg::GetCommSize();j++)
                {
                  if(j!=Msg::GetCommRank() and !(*itdom)->getFormulation() ) // no ghost with itself
                    fillMapOfInterfaceElementsInOneDomain(e, mapfindGhost[(*itdom)], &(_mapOtherDomain[(*itdom)->getPhysical()][j]));
                }
    
                #endif // HAVE_MPI
              }
            }
          }
          // now the space element map of the BC is filled
          for(std::map<partDomain*,std::vector<MElement*> >::iterator it=mapfind.begin(); it!=mapfind.end(); ++it){
            if(it->second.size() !=0){
              elementGroup* dirig = new elementGroup(it->second);
              FunctionSpaceBase *spdbc = it->first->getSpaceForBC(nonLinearBoundaryCondition::DIRICHLET,itbc->onWhat, nonLinearNeumannBC::NEUMANN_UNDEF,
                                                            itbc->_comp,itbc->getDofType(),dirig);
              FilterDof *fdof = it->first->createFilterDof(itbc->_comp);
              itbc->_vspace.push_back(spdbc);
              itbc->_vgroup.push_back(dirig);
              itbc->_vfilter.push_back(fdof);
              itbc->_vdg.push_back(it->first->getFormulation());
            }
          }
          // same with mapfindGhost
          #if defined(HAVE_MPI)
          for(std::map<partDomain*,std::vector<MElement*> >::iterator it=mapfindGhost.begin(); it!=mapfindGhost.end(); ++it){
            if(it->second.size() !=0){
              elementGroup* dirig = new elementGroup(it->second);
              FunctionSpaceBase *spdbc = it->first->getSpaceForBC(nonLinearBoundaryCondition::DIRICHLET,itbc->onWhat, nonLinearNeumannBC::NEUMANN_UNDEF,
                                                                  itbc->_comp,itbc->getDofType(),dirig);
              FilterDof *fdof = it->first->createFilterDof(itbc->_comp);
              itbc->_vspace.push_back(spdbc);
              itbc->_vgroup.push_back(dirig);
              itbc->_vfilter.push_back(fdof);
              itbc->_vdg.push_back(it->first->getFormulation());
            }
          }
          #endif // HAVE_MPI
        }
    
        #if defined(HAVE_MPI)
        // same on ghostDomain (be aware copy past of loop on domain !!)
        if(!ghostdomfind){
          // loop on element
          // map
          std::map<partDomain*,std::vector<MElement*> > ghostmapfind;
          for(elementGroup::elementContainer::const_iterator it=itbc->g->begin(); it!=itbc->g->end();++it){
            MElement *e = it->second;
            // Loop on all Domain to find on which domain the BC is applied
            for(std::vector<partDomain*>::iterator itdom=_ghostDomainMPI.begin(); itdom!=_ghostDomainMPI.end(); ++itdom){
              // if Dimensions are the same the element can be find with a comparison to element
              partDomain *dom = *itdom;
              // otherwise the vertex must be identified separately
              MElement *etest = NULL;
              if (dom->elementGroupSize() > 0)
              {
                  etest = (dom->element_begin()->second);
              }
              if( (etest !=NULL) and (etest->getDim() == e->getDim())){
                for(elementGroup::elementContainer::const_iterator it2=dom->_groupGhostMPI->begin(); it2!=dom->_groupGhostMPI->end(); ++it2){
                  if(it2->second == e){ // The Element is contained in the domain
                    ghostmapfind[dom].push_back(e);
                    break;
                  }
                }
              }
              else
              {
                // an element is included in one domain --> one vertex of Element is OK
                // On interface the BC is put on the first met domain
                fillMapOfInterfaceElementsInOneDomain(e, ghostmapfind[(*itdom)], dom->_groupGhostMPI);
                // check on element which are not included in _groupGhostMPI as there is no interface element with the element
                for(int j=0;j<Msg::GetCommSize();j++)
                {
                  if(j!=Msg::GetCommRank() and !(*itdom)->getFormulation() ) // no ghost with itself
                    fillMapOfInterfaceElementsInOneDomain(e, ghostmapfind[(*itdom)], &(_mapOtherDomain[(*itdom)->getPhysical()][j]));
                }
    
              }
            }
          }
          // now the space element of the BC is filled
          for(std::map<partDomain*,std::vector<MElement*> >::iterator it=ghostmapfind.begin(); it!=ghostmapfind.end(); ++it){
            if(it->second.size() !=0){
              elementGroup *dirig = new elementGroup(it->second);
              FunctionSpaceBase *spdbc = it->first->getSpaceForBC(nonLinearBoundaryCondition::DIRICHLET,itbc->onWhat, nonLinearNeumannBC::NEUMANN_UNDEF,
                                                            itbc->_comp,itbc->getDofType(),dirig);
              FilterDof* fdof = it->first->createFilterDof(itbc->_comp);
              itbc->_vspace.push_back(spdbc);
              itbc->_vgroup.push_back(dirig);
              itbc->_vfilter.push_back(fdof);
              itbc->_vdg.push_back(it->first->getFormulation());
            }
          }
        }
        #endif // HAVE_MPI
      }
    
      // Idem for Neumann BC APPLY ON FIRST FOUNDED DOM. MPI problem the condition has be applied once (on the partition with the lowest number)
      for(std::list<nonLinearNeumannBC>::iterator itbc = allNeumann.begin(); itbc!=allNeumann.end();++itbc){
        // first look if the BC is not applied on whole domain
        bool domfind=false;
        for(std::vector<partDomain*>::iterator itdom=domainVector.begin(); itdom!=domainVector.end(); ++itdom){
          partDomain *dom = *itdom;
          if(itbc->_tag == dom->getPhysical()){
            domfind = true;
            FunctionSpaceBase *spnbc = dom->getSpaceForBC(nonLinearBoundaryCondition::NEUMANN,itbc->onWhat, itbc->_NeumannBCType,
                                                            itbc->_comp,itbc->getDofType(),dom->elements());
            QuadratureBase *integ = dom->getQuadratureRulesForNeumannBC(*itbc);
            itbc->_vspace.push_back(spnbc);
            itbc->_vgroup.push_back(dom->elements());
            itbc->_vquadrature.push_back(integ);
            itbc->_vdom.push_back(dom);
            break;
          }
        }
        // map
        std::map<partDomain*,std::vector<MElement*> > mapfind;
        #if defined(HAVE_MPI)
        // look if the BC is applied on a vertex
        if(!domfind){
          if(itbc->g->vsize() == 1){
            // loop on dof of other partition (if the dof is included only on this partition it will be find after)
            elementGroup::vertexContainer::const_iterator itvBC = itbc->g->vbegin();
            MVertex *verBC = NULL;
            if (itbc->g->vsize() > 0)
            { 
              verBC = itvBC->second;
            }
            for(std::map<int,std::vector<elementGroup> >::iterator it=_mapOtherDomain.begin(); it!=_mapOtherDomain.end(); ++it){
              std::vector<elementGroup> & groupOtherPart = it->second;
              for(int j=0;j<Msg::GetCommRank(); j++){ // look on domain with a lower partition number
                for(elementGroup::vertexContainer::const_iterator itv = groupOtherPart[j].vbegin(); itv!=groupOtherPart[j].vend(); ++itv){
                  MVertex *ver = itv->second;
                  if(verBC == ver){
                    domfind=true; // the BC will be applied on an other partition
                    break;
                  }
                }
              }
            }
          }
        }
        #endif //HAVE_MPI
        if(!domfind){
          // loop on element
          std::map<partDomain*,MElement*> mapelemtype; // need for gauss quadrature rule TODO group with mapfind in a structure but no time benefit
          for(elementGroup::elementContainer::const_iterator it=itbc->g->begin(); it!=itbc->g->end();++it){
            MElement *e = it->second;
            bool flagfind = false;
            // Loop on all Domain to find on which domain the BC is applied
            for(int j=0; j<_mpiUserDom; j++){
              // if Dimensions are the same the element can be find with a comparison to element
              partDomain *dom = domainVector[j];
              // otherwise the vertex must be identified separately
              MElement *etest = NULL;
              if (dom->elementGroupSize() > 0)
              {
                etest = (dom->element_begin()->second);
                mapelemtype[dom] = etest;
              }
              if( (etest != NULL) and (etest->getDim() == e->getDim())){
                for(elementGroup::elementContainer::const_iterator it2=dom->element_begin(); it2!=dom->element_end(); ++it2){
                  if(it2->second == e){ // The Element is contained in the domain
                    mapfind[dom].push_back(e);
                    flagfind = true;
                    break;
                  }
                }
              }
              else{ // loop on Vertex identify the BC with first vertex of e
                     // an element is included in one domain --> one vertex of Element is OK
                     // in mpi the BC can be included in another partition
                #if defined(HAVE_MPI)
                for(int j=_mpiUserDom; j<domainVector.size(); j++){ // loop only on interface domain "added by mpi"
                  dgPartDomain *dgdom = static_cast<dgPartDomain*>(domainVector[j]);
                  // get the two partition number
                  // Neumann BC cannot be applied on ghost element (will not be assembled in the end)
                  // do not applied on ghost element AS THEY WILL NOT BE ABLE TO BE ASSEMBLED!
    #if 0 // old code
                  elementGroup::elementContainer::const_iterator iteleDom = dgdom->gi->begin();
                  MInterfaceElement *ie = dynamic_cast<MInterfaceElement*>(*iteleDom);
                  int numPlus = ie->getElem(1)->getPartition();
                  // if this partition is the one of numPlus it is the partition with the highest number and
                  // it has been look  if the BC appear in the other partition
                  if(Msg::GetCommRank()+1 == numPlus){
                    // loop on element of the domain
                    for(elementGroup::vertexContainer::const_iterator itv=dgdom->gi->vbegin(); itv!=dgdom->gi->vend(); ++itv){
                      MVertex *verDom = *itv;
                      for(int jj=0;jj<e->getNumVertices();jj++){
                        MVertex *verBC = e->getVertex(jj);
                        if(verBC == verDom){
                          flagfind = true; // the BC will be applied on another partition
                          break;
                        }
                      }
                      if(flagfind) break;
                    }
                  }
    #else
                  // loop over ghost element and see if there is a match
                  for(elementGroup::elementContainer::const_iterator iteleDom = dgdom->gi->begin(); iteleDom != dgdom->gi->end();++iteleDom)
                  {
                    MInterfaceElement *ie = dynamic_cast<MInterfaceElement*>(iteleDom->second);
                    int numMinus = ie->getElem(0)->getPartition();
                    int numPlus = ie->getElem(1)->getPartition();
                    // identify the Ghost
                    MElement* eleGhost = (numMinus == Msg::GetCommRank()+1) ? ie->getElem(1) : ie->getElem(0);
                    // loop over nodes to see if they match
                    int nmatch = 0;
                    for(int i=0;i<eleGhost->getNumVertices();++i)
                    {
                      MVertex* verGhost = eleGhost->getVertex(i);
                      for(int jj=0;jj<e->getNumVertices();jj++){
                        MVertex *verBC = e->getVertex(jj);
                        if(verBC == verGhost)
                        {
                          nmatch++;
                          if(nmatch == e->getNumVertices())
                          {
                            flagfind = true; // the BC will be applied on another partition as on ghost here
                            break;
                          }
                        }
                      }
                      if(flagfind) break;
                    }
                    if(flagfind) break;
                  }
    #endif //0
                }
                #endif // HAVE_MPI
                if(!flagfind){
                  // check all vertex of the BC (node 0 could be on another partition)
                  int ncount = 0;
                  for(elementGroup::vertexContainer::const_iterator itv=dom->vertex_begin(); itv!=dom->vertex_end(); ++itv){
                    for(int jj=0; jj<e->getNumVertices();++jj)
                    {
                      MVertex *ver = e->getVertex(jj);
                      if((itv->second) == ver){
                        ncount ++;
                        if(ncount == e->getNumVertices())
                        {
                          mapfind[dom].push_back(e);
                          flagfind = true;
                          break;
                        }
                      }
                    }
                    if(flagfind) break;
                  }
                }
              }
            if(flagfind)
              break;
            }
          }
          // now the map Space Element is filled for the BC
          for(std::map<partDomain*,std::vector<MElement*> >::iterator it=mapfind.begin(); it!=mapfind.end(); ++it){
            //nonLinearNeumannBC *neu = new nonLinearNeumannBC(allNeumann[i]);
            elementGroup *neug = new elementGroup(it->second);
            FunctionSpaceBase *spnbc = it->first->getSpaceForBC(nonLinearBoundaryCondition::NEUMANN,itbc->onWhat, itbc->_NeumannBCType,
                                                                itbc->_comp,itbc->getDofType(),neug);
            QuadratureBase *integ = it->first->getQuadratureRulesForNeumannBC(*itbc);
            itbc->_vspace.push_back(spnbc);
            itbc->_vgroup.push_back(neug);
            itbc->_vquadrature.push_back(integ);
            itbc->_vdom.push_back(it->first);
          }
        }
      }
    
      for(std::list<initialCondition>::iterator itic = allinitial.begin(); itic != allinitial.end();++itic){
        // first look if the BC is not applied on whole domain
        bool domfind=false;
        for(std::vector<partDomain*>::iterator itdom=domainVector.begin(); itdom!=domainVector.end(); ++itdom){
          partDomain *dom = *itdom;
          if(itic->_tag == dom->getPhysical()){
            domfind = true;
            if(dom->_groupGhostMPI->size() > 0) // save BC to applied an other groupOfElement
            {
              FunctionSpaceBase *spdbc = dom->getSpaceForBC(nonLinearBoundaryCondition::INITIAL,itic->onWhat, nonLinearNeumannBC::NEUMANN_UNDEF,
                                                            itic->_comp,itic->getDofType(),dom->_groupGhostMPI);
              FilterDof *fdof = dom->createFilterDof(itic->_comp);
              itic->_vspace.push_back(spdbc);
              itic->_vgroup.push_back(dom->_groupGhostMPI);
              itic->_vfilter.push_back(fdof);
              itic->_vdg.push_back(dom->getFormulation());
            }
            FunctionSpaceBase *spdbc = dom->getSpaceForBC(nonLinearBoundaryCondition::INITIAL,itic->onWhat, nonLinearNeumannBC::NEUMANN_UNDEF,
                                                          itic->_comp,itic->getDofType(),dom->elements());
            FilterDof *fdof = dom->createFilterDof(itic->_comp);
            itic->_vspace.push_back(spdbc);
            itic->_vgroup.push_back(dom->elements());
            itic->_vfilter.push_back(fdof);
            itic->_vdg.push_back(dom->getFormulation());
            break;
          }
        }
        #if defined(HAVE_MPI)
        // same on ghost domain (be aware copy past of loop on domain !!)
        bool ghostdomfind=false;
        for(std::vector<partDomain*>::iterator itdom=_ghostDomainMPI.begin(); itdom!=_ghostDomainMPI.end(); ++itdom){
          partDomain *dom = *itdom;
          if(itic->_tag == dom->getPhysical() and dom->_groupGhostMPI->size()>0){
            ghostdomfind = true;
    	    FunctionSpaceBase *spdbc = dom->getSpaceForBC(nonLinearBoundaryCondition::INITIAL,itic->onWhat, nonLinearNeumannBC::NEUMANN_UNDEF,
                                                            itic->_comp,itic->getDofType(),dom->_groupGhostMPI);
    	    FilterDof* fdof = dom->createFilterDof(itic->_comp);
            itic->_vspace.push_back(spdbc);
            itic->_vgroup.push_back(dom->_groupGhostMPI);
            itic->_vfilter.push_back(fdof);
            itic->_vdg.push_back(dom->getFormulation());
            break;
          }
        }
        #endif // HAVE_MPI
        if(!domfind){
          // loop on element
          // map
          std::map<partDomain*,std::vector<MElement*> > mapfind;
          std::map<partDomain*,std::vector<MElement*> > mapfindGhost;
          for(elementGroup::elementContainer::const_iterator it=itic->g->begin(); it!=itic->g->end();++it){
            MElement *e = it->second;
            // Loop on all Domain to find on which domain the BC is applied
            for(std::vector<partDomain*>::iterator itdom=domainVector.begin(); itdom!=domainVector.end(); ++itdom){
              // if Dimensions are the same the element can be find with a comparison to element
              partDomain *dom = *itdom;
              // otherwise the vertex must be identified separately
              MElement *etest = NULL;
              if (dom->elementGroupSize() > 0)
              {
                etest = (dom->element_begin()->second);
              }
              
              if( (etest !=NULL) and (etest->getDim() == e->getDim())){
                for(elementGroup::elementContainer::const_iterator it2=dom->element_begin(); it2!=dom->element_end(); ++it2){
                  if(it2->second == e){ // The Element is contained in the domain
                    mapfind[dom].push_back(e);
                    break;
                  }
                }
                #if defined(HAVE_MPI)
                for(elementGroup::elementContainer::const_iterator it2=dom->_groupGhostMPI->begin(); it2!=dom->_groupGhostMPI->end(); ++it2){
                  if(it2->second == e){ // The Element is contained in the domain
                    mapfind[dom].push_back(e);
                    break;
                  }
                }
                #endif // HAVE_MPI
              }
              else{
                     // an element is included in one domain --> one vertex of Element is OK
                     // On interface the BC is put on the first met domain
                fillMapOfInterfaceElementsInOneDomain(e, mapfind[(*itdom)], dom->elements());
                #if defined(HAVE_MPI)
                // change map in mapFindGhost???
                fillMapOfInterfaceElementsInOneDomain(e, mapfind[(*itdom)], dom->_groupGhostMPI);
                // check on element which are not included in _groupGhostMPI as there is no interface element with the element
                for(int j=0;j<Msg::GetCommSize();j++)
                {
                  if(j!=Msg::GetCommRank() and !(*itdom)->getFormulation() ) // no ghost with itself
                    fillMapOfInterfaceElementsInOneDomain(e, mapfindGhost[(*itdom)], &(_mapOtherDomain[(*itdom)->getPhysical()][j]));
                }
    
                #endif // HAVE_MPI
              }
            }
          }
          // now the space element map of the BC is filled
          for(std::map<partDomain*,std::vector<MElement*> >::iterator it=mapfind.begin(); it!=mapfind.end(); ++it){
            if(it->second.size() !=0){
              elementGroup* dirig = new elementGroup(it->second);
              FunctionSpaceBase *spdbc = it->first->getSpaceForBC(nonLinearBoundaryCondition::INITIAL,itic->onWhat, nonLinearNeumannBC::NEUMANN_UNDEF,
                                                                  itic->_comp,itic->getDofType(),dirig);
              FilterDof *fdof = it->first->createFilterDof(itic->_comp);
              itic->_vspace.push_back(spdbc);
              itic->_vgroup.push_back(dirig);
              itic->_vfilter.push_back(fdof);
              itic->_vdg.push_back(it->first->getFormulation());
            }
          }
          // same with mapfindGhost
          #if defined(HAVE_MPI)
          for(std::map<partDomain*,std::vector<MElement*> >::iterator it=mapfindGhost.begin(); it!=mapfindGhost.end(); ++it){
            if(it->second.size() !=0){
              elementGroup* dirig = new elementGroup(it->second);
              FunctionSpaceBase *spdbc = it->first->getSpaceForBC(nonLinearBoundaryCondition::INITIAL,itic->onWhat, nonLinearNeumannBC::NEUMANN_UNDEF,
                                                                  itic->_comp,itic->getDofType(),dirig);
              FilterDof *fdof = it->first->createFilterDof(itic->_comp);
              itic->_vspace.push_back(spdbc);
              itic->_vgroup.push_back(dirig);
              itic->_vfilter.push_back(fdof);
              itic->_vdg.push_back(it->first->getFormulation());
            }
          }
          #endif // HAVE_MPI
        }
    
        #if defined(HAVE_MPI)
        // same on ghostDomain (be aware copy past of loop on domain !!)
        if(!ghostdomfind){
          // loop on element
          // map
          std::map<partDomain*,std::vector<MElement*> > ghostmapfind;
          for(elementGroup::elementContainer::const_iterator it=itic->g->begin(); it!=itic->g->end();++it){
            MElement *e = it->second;
            // Loop on all Domain to find on which domain the BC is applied
            for(std::vector<partDomain*>::iterator itdom=_ghostDomainMPI.begin(); itdom!=_ghostDomainMPI.end(); ++itdom){
              // if Dimensions are the same the element can be find with a comparison to element
              partDomain *dom = *itdom;
              // otherwise the vertex must be identified separately
              const MElement *etest = NULL;
              if (dom->elementGroupSize() >0)
              {
                etest =(dom->element_begin()->second);
              }
              if( (etest !=NULL) and (etest->getDim() == e->getDim())){
                for(elementGroup::elementContainer::const_iterator it2=dom->_groupGhostMPI->begin(); it2!=dom->_groupGhostMPI->end(); ++it2){
                  if(it2->second == e){ // The Element is contained in the domain
                    ghostmapfind[dom].push_back(e);
                    break;
                  }
                }
              }
              else
              {
                     // an element is included in one domain --> one vertex of Element is OK
                     // On interface the BC is put on the first met domain
                fillMapOfInterfaceElementsInOneDomain(e, ghostmapfind[(*itdom)], dom->_groupGhostMPI);
              }
            }
          }
          // now the space element of the BC is filled
          for(std::map<partDomain*,std::vector<MElement*> >::iterator it=ghostmapfind.begin(); it!=ghostmapfind.end(); ++it){
            if(it->second.size() !=0){
              elementGroup *dirig = new elementGroup(it->second);
              FunctionSpaceBase *spdbc = it->first->getSpaceForBC(nonLinearBoundaryCondition::INITIAL,itic->onWhat, nonLinearNeumannBC::NEUMANN_UNDEF,
                                                            itic->_comp,itic->getDofType(),dirig);
              FilterDof* fdof = it->first->createFilterDof(itic->_comp);
              itic->_vspace.push_back(spdbc);
              itic->_vgroup.push_back(dirig);
              itic->_vfilter.push_back(fdof);
              itic->_vdg.push_back(it->first->getFormulation());
            }
          }
        }
        #endif // HAVE_MPI
      }
    #endif // 0
    
      // put back the curent GModel
      GModel::setCurrent(save_current_model);
    
    };
    
    
    void nonLinearMechSolver::setTimeForBC(double time){
      for(std::list<nonLinearDirichletBC>::iterator it = allDirichlet.begin(); it != allDirichlet.end();++it)
      {
        it->_f->setTime(time);
      }
    
      for(std::list<nonLinearNeumannBC>::iterator it = allNeumann.begin(); it!= allNeumann.end();++it)
      {
        it->_f->setTime(time);
      }
    
      // set time for rigid contact BC
      for(std::list<rigidContactBC>::iterator it = allContactBC.begin(); it!=allContactBC.end(); ++it){
        it->_f->setTime(time);
      }
    
      if (_testFlag){
        for (std::list<nonLinearDirichletBCAtCorner>::iterator it = allCornerConstraint.begin(); it != allCornerConstraint.end(); it++){
          it->_f->setTime(time);
        }
    
        for (std::list<nonLinearNeumannBCAtCorner>::iterator it = allCornerForce.begin(); it != allCornerForce.end(); it++){
          it->_f->setTime(time);
        }
      }
    
    	// for periodic BC
      this->setTimeForMicroBC(time);
    
    }
    
    void nonLinearMechSolver::setTimeForLaw(const double t,const double dt, const int numstep)
    {
      for(std::map<int,materialLaw*>::iterator it=maplaw.begin(); it!=maplaw.end();++it)
      {
        if (_microFlag){
          double mtime = _macroTime- (1-t)*_macroTimeStep;
          double tstep = dt*_macroTimeStep; // because
          it->second->setTime(mtime,tstep);
        }
        else{
          it->second->setTime(t,dt);
        }
      }
    };
    
    void nonLinearMechSolver::fixNodalDofs()
    {
      // Fixation (prescribed displacement)
      
      for(std::list<nonLinearDirichletBC>::iterator it = allDirichlet.begin(); it!=allDirichlet.end();++it )
      {
        nonLinearDirichletBC &diri = *it;
        // select the system if required
        nlsDofManager* currentManager = pAssembler->getManagerByComp(diri._comp);
        if(diri._mycondition==nonLinearBoundaryCondition::position)
        {
          for(int j=0; j<diri._vspace.size();j++)
          {
            FunctionSpaceBase *spdiri = diri._vspace[j];
            const elementGroup *gdiri = diri._vgroup[j];
            FilterDof* fdofdiri = diri._vfilter[j];
            bool fdg = diri._vdg[j];
            FixNodalDofs(spdiri,gdiri->begin(),gdiri->end(),*currentManager,*diri._f,*(fdofdiri),fdg,diri._dofType);
          }
        }
        else
        {
          for(int j=0;j<diri._vspace.size();j++){
            // allow to prescribed a velocity or acceleration over time using a dirichlet BC
            SetInitialDofs(diri._vspace[j],diri._vgroup[j]->begin(),diri._vgroup[j]->end(),diri._mycondition,*currentManager,diri._f,*(diri._vfilter[j]),diri._vdg[j],diri._dofType);
          }
        }
      }
    
      // prescribed displacement of rigid bodies (CG)
      //for(rigidContactBC &rcbc : allContactBC){
      for(std::list<rigidContactBC>::iterator rcbc = allContactBC.begin(); rcbc!=allContactBC.end(); ++rcbc)
      {
        nlsDofManager* currentManager = pAssembler->getManagerByComp(rcbc->_comp);
        FixNodalDofs(rcbc->space,*rcbc->_f,*(rcbc->_filter),*currentManager);
      }
    }
    
    void nonLinearMechSolver::updateDataFromPreviousSolver()
    {
      // solver must be used previously
      if (_previousInit and _resetRestart)
      {
        if (_collection==NULL)
        {
          Msg::Warning("previous system is used but data is not collected");
        }
        else
        {
          pAssembler->updateDataFromCollection(*_collection);
        }
      }
    }
    
    void nonLinearMechSolver::numberDofs(){
      // we number the dofs : when a dof is numbered, it cannot be numbered
      // again with another number.
    
      for(std::vector<partDomain*>::iterator itdom = domainVector.begin(); itdom!=domainVector.end(); ++itdom)
      {
        partDomain *dom = *itdom;
        if(dom->elementGroupSize() > 0)
        {
          NumberDofsByVector(*(dom->getFunctionSpace()), dom->element_begin(), dom->element_end(),*pAssembler);
        }
        #if defined(HAVE_MPI)
        // create the mpi communication on interface domain
        // no element in g --> interfaceDomain
        else if(Msg::GetCommSize()>1)
        {
          dgPartDomain *dgdom = dynamic_cast<dgPartDomain*>(dom);
          if (dgdom)
          {
            NumberInterfaceDofsMPI(dom->getFunctionSpace(), dgdom->gi->begin(), dgdom->gi->end(), pAssembler);
          }
        }
        #endif // HAVE_MPI
      }
      if (_testFlag and (_pbcGroup != NULL)){
        // number new dof coming from microBC if existing
        _pbcGroup->numberDof_virtualVertices(pAssembler);
      }
      
      // contact
      pAssembler->setFirstRigidContactUnknowns(); // store the number of usual Dof in ech system
      // NumberDofs of Rigid Contact Entities (Dofs for GC)
      for(contactContainer::iterator it = _allContact.begin(); it!=_allContact.end(); ++it)
      {
        contactDomain* cdom = *it;
        if(cdom->isRigid())
        {
          rigidContactSpaceBase *rcspace = static_cast<rigidContactSpaceBase*>(cdom->getSpace());
          NumberDofs(*rcspace,*pAssembler);
        }
      }
      //HERE DO WE NEED SOMETHING FOR DEFODEFO? SHOULD NOT BE BECAUSE IT S FOR CG
    
      
      // allocate system
      if (whatScheme == Eigen)
      {
        int nunk = pAssembler->sizeOfR();
        static std::string A("A");
        pAssembler->getLinearSystem(A)->allocate(nunk);
        if (_eigOpts.type == eigenSolverOptions::Dynamic)
        {
    	    static std::string B("B");
        	pAssembler->getLinearSystem(B)->allocate(nunk);
        };
      }
      else
      {
        pAssembler->allocateSystem();
      }
      
      // Try to fill the matrix sparsity if needed
      // this has to be done after the system allocation
      for(int i=0; i<_mpiUserDom;++i)
      {
        partDomain *dom = domainVector[i];
        if(dom->elementGroupSize() > 0)
        {
          SparsityDofs(*(dom->getFunctionSpace()), dom->element_begin(), dom->element_end(),*pAssembler);
        }
        // account also interface
        dgPartDomain *dgdom = dynamic_cast<dgPartDomain*>(dom);
        if (dgdom)
        {
          #if defined(HAVE_MPI)
          if(Msg::GetCommSize()>1 && dom->elementGroupSize() == 0)
            SparsityInterfaceDofsMPI(dgdom->getFunctionSpace(), dgdom->gi->begin(), dgdom->gi->end(),pAssembler);
          #endif
          SparsityDofs(*(dgdom->getFunctionSpace()), dgdom->gi->begin(), dgdom->gi->end(),*pAssembler);
        }
      }
      #if defined(HAVE_MPI)
       // MPI interface sparsity
      if(Msg::GetCommSize()>1)
      {
        for(int i=_mpiUserDom; i<domainVector.size();++i)
        {
          dgPartDomain *dgdom = dynamic_cast<dgPartDomain*>(domainVector[i]);
          if (dgdom)
          {
            SparsityInterfaceDofsMPI(dgdom->getFunctionSpace(), dgdom->gi->begin(), dgdom->gi->end(),pAssembler);
          }
        }
      }
      #endif //HAVE_MPI
      
      // pre allocate entry
      if (whatScheme == Eigen)
      {
        // allocate system
        static std::string A("A");
        pAssembler->getLinearSystem(A)->preAllocateEntries();
          
        if (_eigOpts.type == eigenSolverOptions::Dynamic)
        {
          static std::string B("B");
          pAssembler->setCurrentMatrix(B);
          
          //
          for(int i=0; i<_mpiUserDom;++i)
          {
            partDomain *dom = domainVector[i];
            if(dom->elementGroupSize() > 0)
            {
              SparsityDofs(*(dom->getFunctionSpace()), dom->element_begin(), dom->element_end(),*pAssembler);
            }
            // account also interface
            dgPartDomain *dgdom = dynamic_cast<dgPartDomain*>(dom);
            if (dgdom)
            {
              #if defined(HAVE_MPI)
              if(Msg::GetCommSize()>1 && dom->elementGroupSize() == 0)
                SparsityInterfaceDofsMPI(dgdom->getFunctionSpace(), dgdom->gi->begin(), dgdom->gi->end(),pAssembler);
              #endif
              SparsityDofs(*(dgdom->getFunctionSpace()), dgdom->gi->begin(), dgdom->gi->end(),*pAssembler);
            }
          }
          #if defined(HAVE_MPI)
           // MPI interface sparsity
          if(Msg::GetCommSize()>1)
          {
            for(int i=_mpiUserDom; i<domainVector.size();++i)
            {
              dgPartDomain *dgdom = dynamic_cast<dgPartDomain*>(domainVector[i]);
              if (dgdom)
              {
                SparsityInterfaceDofsMPI(dgdom->getFunctionSpace(), dgdom->gi->begin(), dgdom->gi->end(),pAssembler);
              }
            }
          }
          #endif //HAVE_MPI
          //
          pAssembler->getLinearSystem(B)->preAllocateEntries();
          pAssembler->setCurrentMatrix(A);
        };
      }
      else
      {
        pAssembler->preAllocateEntries();
      }
      
      //
      if (_pathFollowing and (_pfManager._pathFollowingMethod == pathFollowingManager::HYPERELLIPTIC_BASED))
      {
        static std::string A("A");
        pathFollowingSystemBase* pfsysBase = dynamic_cast<pathFollowingSystemBase*>(pAssembler->getLinearSystem(A));
        if (pfsysBase != NULL){
          for (int i=0; i< _pfManager._hyperellipticControlComp.size(); i++){
    
            int comp = _pfManager._hyperellipticControlComp[i];
            for(std::vector<partDomain*>::iterator itdom = domainVector.begin(); itdom!=domainVector.end(); ++itdom)
            {
              partDomain *dom = *itdom;
              if (dom->getAccountPathFollowingFlag()){
                FunctionSpaceBase* space = dom->getFunctionSpace();
                FilterDof* filter = dom->createFilterDof(comp);
                for (elementGroup::elementContainer::const_iterator ite = dom->element_begin(); ite != dom->element_end(); ite++){
                  MElement* ele = ite->second;
                  std::vector<Dof> keys;
                  space->getKeys(ele,keys);
                  for (int k=0; k< keys.size(); k++){
                    if ((pAssembler->isAnUnknown(keys[k])) and (filter->operator ()(keys[k]))){
                      int num = pAssembler->getDofNumber(keys[k]);
                      if (num >=0){
                        pfsysBase->addToHyperellipticMatrix(num,num,1.);
                      }
                    }
                  }
                }
                delete filter;
              }
            }
    
          }
        }
        else{
          Msg::Error("path following system must be used");
        }
      };
    
      return;
    }
    
    void nonLinearMechSolver::addDomain(partDomain* dom){
      pModel->setAsCurrent();
      // initialization of ElementGroup
      int dim = dom->getDim();
      int phys = dom->getPhysical();
    #if defined(HAVE_MPI)
      std::vector<elementGroup> groupOtherPart(Msg::GetCommSize());
      elementFilterMPI elemfil(&groupOtherPart) ;
      dom->addPhysicalGroup(dim,phys,elemfil);
      // add to map (to construct later interface domain)
      _mapOtherDomain.insert(std::pair<int,std::vector<elementGroup> >(phys,groupOtherPart));
      if (_microFlag == false){
        if (dom->isDistributedOtherRanks()){
          // if multiscale material is used
          // at root ranks
          if (_mapRanks.find(Msg::GetCommRank()) != _mapRanks.end()){
            int addFlag = 0;
            if(dom->elementGroupSize()!= 0){ // otherwise the domain is include totally on an other partition
              dom->setWorkingRanks(Msg::GetCommRank(),_mapRanks[Msg::GetCommRank()]);
              domainVector.push_back(dom);
              addFlag= 1;
            }
            else if(dom->IsInterfaceTerms()){ // take into account for pure interface domain which have dom->g.size()=0
              dgPartDomain *dgdom = static_cast<dgPartDomain*>(dom);
              if(dgdom->getMinusDomain()->getPhysical() != dgdom->getPlusDomain()->getPhysical()){ // pure interface --> add
                dom->setWorkingRanks(Msg::GetCommRank(),_mapRanks[Msg::GetCommRank()]);
                domainVector.push_back(dom);
                addFlag = 2;
              }
              else{ // add the domain in ghostDomain vector to prescribed Dirichlet BC (applied on all cpu)
                _ghostDomainMPI.push_back(dom);
                addFlag = 3;
              }
            }
    
            std::set<int>& otherRanks = _mapRanks[Msg::GetCommRank()];
            for (std::set<int>::iterator its = otherRanks.begin(); its != otherRanks.end(); its++)
              MPI_Send(&addFlag,1,MPI_INT,*its,*its,MPI_COMM_WORLD);
    
          }
          // in other ranks
          else{
            for (std::map<int,std::set<int> >::iterator it = _mapRanks.begin(); it!= _mapRanks.end(); it++){
              std::set<int>& other = it->second;
              if (other.find(Msg::GetCommRank()) != other.end()){
                int rank = it->first;
                int addFlag = 0;
                MPI_Status status;
                MPI_Recv(&addFlag,1,MPI_INT,rank,Msg::GetCommRank(),MPI_COMM_WORLD,&status);
                if (addFlag == 1 or addFlag == 2){
                  domainVector.push_back(dom);
                  dom->setWorkingRanks(rank,other);
                  dom->clearElementGroup();
                }
              }
            }
          }
        }
        else{
          // if normal Domain is used
          if (_mapRanks.find(Msg::GetCommRank()) != _mapRanks.end()){
            std::set<int> nullSet;
            if(dom->elementGroupSize() != 0){ // otherwise the domain is include totally on an other partition
              dom->setWorkingRanks(Msg::GetCommRank(),nullSet);
              domainVector.push_back(dom);
            }
            else if(dom->IsInterfaceTerms()){ // take into account for pure interface domain which have dom->g.size()=0
              dgPartDomain *dgdom = static_cast<dgPartDomain*>(dom);
              if(dgdom->getMinusDomain()->getPhysical() != dgdom->getPlusDomain()->getPhysical()){ // pure interface --> add
                dom->setWorkingRanks(Msg::GetCommRank(),nullSet);
                domainVector.push_back(dom);
              }
              else{ // add the domain in ghostDomain vector to prescribed Dirichlet BC (applied on all cpu)
                _ghostDomainMPI.push_back(dom);
              }
            }
          }
    
        }
      }
      else {
        if(dom->elementGroupSize() != 0){ // otherwise the domain is include totally on an other partition
          domainVector.push_back(dom);
        }
        else if(dom->IsInterfaceTerms()){ // take into account for pure interface domain which have dom->g.size()=0
          dgPartDomain *dgdom = static_cast<dgPartDomain*>(dom);
          if(dgdom->getMinusDomain()->getPhysical() != dgdom->getPlusDomain()->getPhysical()){ // pure interface --> add
            domainVector.push_back(dom);
          }
          else{ // add the domain in ghostDomain vector to prescribed Dirichlet BC (applied on all cpu)
            _ghostDomainMPI.push_back(dom);
          }
        }
      }
    
    
    #else
      elementFilterTrivial elemfil;
      dom->addPhysicalGroup(dim,phys,elemfil);
      domainVector.push_back(dom);
    #endif // HAVE_MPI
    }
    
    void nonLinearMechSolver::addMaterialLaw(materialLaw* mlaw){
      maplaw.insert(std::pair<int,materialLaw*>(mlaw->getNum(),mlaw));
    }
    
    materialLaw* nonLinearMechSolver::getMaterialLaw(const int num){
      return (maplaw.find(num)->second);
    }
    
    void nonLinearMechSolver::thetaBC(const int numphys){
      elementGroup *goe = new elementGroup(1,numphys);
      this->insertTheta(numphys,goe);
    }
    void nonLinearMechSolver::stiffnessModification(const bool flag){
      _stiffnessModification = flag;
      if (!_stiffnessModification)
      {
        _stiffnessModificationMonitoring = new StiffnessModificationMonitoring(0);
      }
    };
    
    
    void nonLinearMechSolver::stiffnessModification(const StiffnessModificationMonitoring& stiffModifMonitor)
    {
      _stiffnessModification = false;
      if (_stiffnessModificationMonitoring) delete _stiffnessModificationMonitoring;
      _stiffnessModificationMonitoring = stiffModifMonitor.clone();
    };
    
    void nonLinearMechSolver::iterativeProcedure(const bool flag){
      _iterativeNR = flag;
    };
    void nonLinearMechSolver::lineSearch(const bool lin){
      _lineSearch = lin;
    }
    void nonLinearMechSolver::pathFollowing(const bool p, const int method){
      _pathFollowing = p;
    	_pfManager._pathFollowingMethod = (pathFollowingManager::pathFollowingMethod)method;
    }
    
    void nonLinearMechSolver::setPathFollowingIncrementAdaptation(const bool stepAdap, const int numNROptimal, const double exp)
    {
      _pfManager._numNROptimal = numNROptimal;
      _pfManager._numNROptimalLocal = numNROptimal;
      _pfManager._expNROptimal = exp;
      _pfManager._pfStepAdaptation = stepAdap;
      if (_pfManager._pfStepAdaptation) {
        Msg::Info("path following increment is adapted with number of NR iterations");
      }
    };
    
    void nonLinearMechSolver::setPathFollowingIncrementAdaptationLocal(const bool stepAdap, const int numNROptimalLoad, const int numNROptimalLocal, const double exp){
      _pfManager._numNROptimal = numNROptimalLoad;
      _pfManager._numNROptimalLocal = numNROptimalLocal;
      _pfManager._expNROptimal = exp;
      _pfManager._pfStepAdaptation = stepAdap;
      if (_pfManager._pfStepAdaptation) {
         Msg::Info("path following increment is adapted with number of NR iterations");
      }
    };
    
    void nonLinearMechSolver::setPathFollowingControlType(const int i){
      _pfManager._controlTypePathFollowing = i;
    }
    void nonLinearMechSolver::setPathFollowingCorrectionMethod(const int i){
    	_pfManager._correctionMethodPathFollowing = i;
    };
    void nonLinearMechSolver::setPathFollowingTranversalCriterion(const int i){
    	_pfManager._tranversalCriterionPathFollowing = i;
    };
    void nonLinearMechSolver::setPathFollowingSolverType( const int i, const double eqRatio){
    	_pfManager._solverTypePathFollowing = i;
    	_pfManager._pathFollowingEqRatio = eqRatio;
    };
    
    void nonLinearMechSolver::setPathFollowingLocalSteps(const double loadStep, const double localCrStep){
    	_pfManager._arcLengthStep = loadStep;
      _pfManager._arcLengthStepPrev = loadStep;
    	_pfManager._localStep = localCrStep;
      _pfManager._localStepPrev = localCrStep;
    }
    
    void nonLinearMechSolver::setPathFollowingArcLengthStep(const double arcStep){
    	_pfManager._arcLengthStep = arcStep;
      _pfManager._arcLengthStepPrev = arcStep;
    };
    
    void nonLinearMechSolver::setBoundsOfPathFollowingLocalSteps(const double lowerBound, const double upperBound){
      _pfManager._localStepMinimal = lowerBound;
      _pfManager._localStepMaximal = upperBound;
      Msg::Info("path following local step is bounded by lowerbound %e and by upper bound %e",lowerBound,upperBound);
    };
    
    void nonLinearMechSolver::setBoundsOfPathFollowingLoadSteps(const double lowerBound, const double upperBound){
      _pfManager._arcLengthStepMinimal = lowerBound;
      _pfManager._arcLengthStepMaximal = upperBound;
      Msg::Info("path following load step is bounded by lowerbound %e and by upper bound %e",lowerBound,upperBound);
    };
    
    void nonLinearMechSolver::setBoundsOfPathFollowingArcLengthSteps(const double lowerBound, const double upperBound){
      _pfManager._arcLengthStepMinimal = lowerBound;
      _pfManager._arcLengthStepMaximal = upperBound;
      Msg::Info("path following local step is bounded by lowerbound %e and by upper bound %e",lowerBound,upperBound);
    };
    
    void nonLinearMechSolver::setPathFollowingLocalIncrementType(const int i){
      _pfManager._pathFollowingIncrementType = (pathFollowingManager::pathFollowingLocalIncrementType)i;
      if (i == 0) {
        Msg::Info("pf increment with defo energy");
      }
      else if (i == 1){
       Msg::Info("pf increment with dissipation energy");
      }
      else if (i == 2){
        Msg::Info("pf increment with plastic energy");
      }
      else if (i == 3){
        Msg::Info("pf increment with damage energy");
      }
      else{
        Msg::Error("missing case nonLinearMechSolver::setPathFollowingLocalIncrementType %d",i);
      }
    };
    
    void nonLinearMechSolver::setPathFollowingLocation(const int loc){
      _pfManager._pathFollowingLocation = (pathFollowingManager::pathFollowingLocation)loc;
      if (_pfManager._pathFollowingLocation == pathFollowingManager::BULK_INTERFACE){
        Msg::Info("path following constraint is contructed from bulk and interfaces elements");
      }
      else if (_pfManager._pathFollowingLocation == pathFollowingManager::BULK){
        Msg::Info("path following constraint is contructed from bulk elements");
      }
      else if (_pfManager._pathFollowingLocation == pathFollowingManager::INTERFACE){
        Msg::Info("path following constraint is contructed from interfaces elements");
      }
      else{
        Msg::Error("case missing %d nonLinearMechSolver::setPathFollowingLocation ",loc);
      }
    
    };
    
    void nonLinearMechSolver::setPathFollowingSwitchCriterion(const double cr){
      _pfManager._pathFollowingSwitchCriterion = cr;
    };
    
    void nonLinearMechSolver::clearAllHyperellipticControlComp(){
      _pfManager._hyperellipticControlComp.resize(0);
    };
    
    void nonLinearMechSolver::setHyperellipticControlComp(const int comp){
      _pfManager._hyperellipticControlComp.push_back(comp);
      Msg::Info("path following with comp %d",comp);
    };
    
    /*
    void nonLinearMechSolver::setInitOrRestartFileName(const std::string fname){
      initOrResartfname =fname;
      _restart = true;
    }
    
    void nonLinearMechSolver::restart(unknownField *ufield){
      if(!_restart) // no file to restart
        return;
      // create a map between elem number and adress
      std::map<long int,std::pair<partDomain*,MElement*> > allelem;
      for(std::vector<partDomain*>::iterator itdom = domainVector.begin(); itdom!=domainVector.end(); ++itdom){
        partDomain *dom = *itdom;
        for(elementGroup::elementContainer::iterator ite = dom->g->begin(); ite!=dom->g->end(); ++ite){
          MElement *ele = *ite;
          std::pair<partDomain*,MElement*> pa(dom,ele);
          allelem.insert(std::pair<long int,std::pair<partDomain*,MElement*> >(ele->getNum(),pa));
        }
      }
    
      // read data in file
      FILE *f = fopen(initOrResartfname.c_str(), "r");
      char what[256];
      while(!feof(f)){
        fscanf(f, "%s", what);
        if(!strcmp(what,"$ElementNodeData")){
          int nstring;
          fscanf(f,"%d",&nstring);
          int i=0;
          char what2[256];
          while(i<nstring){
            fscanf(f,"%s",&what2);
            i++;
          }
          int nint, n;
          fscanf(f,"%d",&nint);
          i=0;
          while(i<nint){
            fscanf(f,"%d",&n);
            i++;
          }
          fscanf(f,"%d",&nint);
          if(nint != 4)
          {
            Msg::Error("Your restart file hasn't the good format since the number of int (!=4) is not good. The number of element is taken from the third one!");
          }
          int nelem;
          i=0;
          while(i<nint){
            fscanf(f,"%d",&n);
            if(i == 2)
              nelem = n;
            i++;
          }
          int elemnum,elemtype,ndofs,nvertex,ncomp;
          i = 0;
          int n2 = nelem; // because n is modified by fscanf(f,"%ld %d",&elemnum,&elemtype); WHY ??
          while(i<n2){
            fscanf(f,"%d %d",&elemnum,&elemtype);
            //MElement *ele = allelem[elemnum].second;
            //partDomain *dom = allelem[elem]
            std::map<long int,std::pair<partDomain*,MElement*> >::iterator it = allelem.find(elemnum);
            MElement *ele = (it->second).second;
            partDomain *dom = (it->second).first;
            FunctionSpaceBase *space = dom->getFunctionSpace();
            std::vector<Dof> R;
            space->getKeys(ele,R);
            ndofs = space->getNumKeys(ele);
            nvertex = ele->getNumVertices();
            ncomp = ndofs/nvertex;
            int j=0;
            std::vector<double> disp;
            disp.resize(ndofs);
            //double temp;
            while(j<nvertex){
              for(int k=0;k<ncomp;k++){
                fscanf(f,"%lf",&disp[j+k*nvertex]);
              }
              j++;
            }
            // set displacement in displacement field
            ufield->setInitial(R,disp);
            i++;
          }
        }
      }
      fclose(f);
      ufield->buildAllView(domainVector,0.,0);
    }
    */
    void nonLinearMechSolver::insertTheta(const int numphys, elementGroup *goe){
      allTheta.emplace_back();
      nonLinearNeumannBC& neu = allTheta.back();
      neu.g = goe;
      neu.onWhat = nonLinearBoundaryCondition::UNDEF;
      neu._tag = numphys;
      neu._f = new simpleFunctionTimeWithConstant<double>(0.);
    }
    
    void nonLinearMechSolver::physInitBroken(const int dim, const int numphys){
      initbrokeninter.push_back(std::pair<int,int>(dim,numphys));
    };
    
    void nonLinearMechSolver::initialBrokenDomain(const int phys)
    {
      initbrokeninterInDomains.push_back(std::pair<int,int>(phys,phys));
    }
    void nonLinearMechSolver::initialBrokenInterfaceDomain(const int physMinus, const int phyPlus)
    {
      initbrokeninterInDomains.push_back(std::pair<int,int>(physMinus,phyPlus));
    }
    
    void nonLinearMechSolver::createInterfaceElement(){
      // Compute and add interface element to the model
      // Loop on mesh element and store each edge (which will be distributed between InterfaceElement, Boundary InterfaceElement and VirtualInterfaceElement)
      // A tag including the vertex number is used to identified the edge common to two elements. In this case a interface element is created
      // manage creation of interface between 2 domains
      pModel->setAsCurrent();
      #if defined(HAVE_MPI)
      if (getNumRanks() > 1 && ipViewInterface.size() > 0)
      {
        Msg::Barrier();
        if (Msg::GetCommRank() > 0)
        {
          int maximalNumberEle = 0;
          MPI_Status status;
          MPI_Recv(&maximalNumberEle,1,MPI_INT,Msg::GetCommRank()-1,Msg::GetCommRank(),MPI_COMM_WORLD,&status);
          pModel->setMaxElementNumber(maximalNumberEle);
        }
      }
      #endif //HAVE_MPI
      
      manageInterface maninter(&domainVector);
      // loop on element
      for(std::vector<partDomain*>::iterator itdom=domainVector.begin(); itdom !=domainVector.end(); ++itdom)
      {
        partDomain *dom = *itdom;
        dom->createInterface(maninter);
    
       // remove interface element if domain is not DG ??
       // there is other way do that
        dgPartDomain* dgdom = dynamic_cast<dgPartDomain*>(dom);
        if (dgdom){
          if (!dgdom->IsInterfaceTerms()){
            dgdom->gi->clearAll();
          }
        }
      }
    
    
      // virtual interface
      for(std::vector<partDomain*>::iterator itdom = domainVector.begin(); itdom!=domainVector.end(); ++itdom){
        partDomain *dom = *itdom;
        int phys = dom->getPhysical();
        for(manageInterface::IelementContainer::iterator it=maninter.begin(); it!=maninter.end(); ++it){
          IElement *ie = it->second;
          if(ie->getPhys() == phys){
            dgPartDomain *dgdom = static_cast<dgPartDomain*>(dom);
            MElement* interel = dom->createVirtualInterface(ie);
            dgdom->giv->insert(interel);
          }
        }
      }
      _mpiUserDom = domainVector.size();
    
    
    #if defined(HAVE_MPI)
      if(Msg::GetCommSize() > 1 and (_mapRanks.find(Msg::GetCommRank()) != _mapRanks.end())){
        // first add interface to the interdomain defined by the user
        for(std::vector<partDomain*>::iterator itdom = domainVector.begin(); itdom != domainVector.end(); ++itdom){
          // find interdomain (g->size==0)
          partDomain *dom = *itdom;
          if(dom->elementGroupSize()==0){
            int phys1,phys2;
            dgPartDomain *dgdom = static_cast<dgPartDomain*>(dom);
            manageInterface::getPhysDom(dom->getPhysical(),phys1,phys2);
            // find both domains
            partDomain *dom1=NULL;
            partDomain *dom2=NULL;
            for(std::vector<partDomain*>::iterator itdom = domainVector.begin(); itdom != domainVector.end(); ++itdom){
              partDomain* domtmp = *itdom;
              if(domtmp->getPhysical() == phys1) dom1=domtmp;
              else if(domtmp->getPhysical() == phys2) dom2=domtmp;
              if((dom1!=NULL) and(dom2!=NULL)) break;
            }
            if(dom1!=NULL){ // otherwise no element of dom1 on the partition
              // find the group of ghosted element of dom2
              std::map<int,std::vector<elementGroup> >::iterator itg= _mapOtherDomain.find(phys2);
              if(itg!=_mapOtherDomain.end()){
                for(int i=0; i< itg->second.size(); i++){
                  elementGroup &group = itg->second[i];
                  if(group.size()!=0)
                    dom1->createInterfaceMPI(i,group,dgdom);
                }
              }
            }
            if(dom2!=NULL){ // otherwise no element of dom1 on partition
              // find the group of ghosted element of dom1
              std::map<int,std::vector<elementGroup> >::iterator itg= _mapOtherDomain.find(phys1);
              if(itg!=_mapOtherDomain.end()){
                for(int i=0; i< itg->second.size(); i++){
                  elementGroup &group = itg->second[i];
                  if(group.size()!=0)
                    dom2->createInterfaceMPI(i,group,dgdom);
                }
              }
            }
          }
        }
    
        // create pure interface domain for MPI
        for(std::map<int,std::vector<elementGroup> >::iterator it=_mapOtherDomain.begin(); it!=_mapOtherDomain.end(); ++it){
          int physdom = it->first;
          // get the domain
          partDomain *dom = NULL;
          for(std::vector<partDomain*>::iterator itdom = domainVector.begin(); itdom != domainVector.end(); ++itdom){
            if(physdom == (*itdom)->getPhysical()){
              dom = *itdom;
              break;
            }
          }
          if(dom != NULL){
            std::vector<elementGroup> & groupOtherPart = it->second;
            dgPartDomain* newInterDom = NULL;
            int gsize = groupOtherPart.size();
            for(int i=0; i< gsize; i++){
              if(i!=Msg::GetCommRank()){
                elementGroup &group = groupOtherPart[i];
                if(group.size() != 0){ // otherwise no domain to create
                  dom->createInterfaceMPI(i,group,newInterDom);
                }
              }
            }
            if((newInterDom!=NULL) and (newInterDom->gi->size() > 0)){
              // law are set before (change this ??)
              newInterDom->setMaterialLaw(maplaw);
              domainVector.push_back(newInterDom); // otherwise no interface element
            }
            else{
             if(newInterDom!=NULL) delete newInterDom;
            }
          }
        }
      }
    #endif // HAVE_MPI
    
      // save to file
      if (ipViewInterface.size() > 0)
      {
       elementGroup gr;
       for (int i=0; i< domainVector.size(); i++)
       {
         dgPartDomain* dom = dynamic_cast<dgPartDomain*>(domainVector[i]);
         if (dom != NULL)
         {
           for (elementGroup::elementContainer::const_iterator it = dom->gi->begin(); it != dom->gi->end(); it++)
           {
             gr.insert(it->second);
           }
         }
       }
       if (gr.size() > 0)
       {
         std::string  fname = "allInterfaceElements.msh";
         #if defined(HAVE_MPI)
         if (getNumRanks() > 1)
         {
           fname = "allInterfaceElements_part"+std::to_string(Msg::GetCommRank())+".msh";
         }
         #endif //HAVE_MPI
         InterpolationOperations::write_MSH2(&gr,fname);
       };
      }
      #if defined(HAVE_MPI)
      if (getNumRanks() > 1 && (ipViewInterface.size() > 0))
      {
        if (Msg::GetCommRank() < getNumRanks()-1)
        {
          int maximalNumberEle = pModel->getMaxElementNumber();
          MPI_Send(&maximalNumberEle,1,MPI_INT,Msg::GetCommRank()+1,Msg::GetCommRank()+1,MPI_COMM_WORLD);
        }
        printf("rankd %d numximal element id = %ld \n",Msg::GetCommRank(),pModel->getMaxElementNumber());
      }
      #endif //HAVE_MPI  
    };
    
    void nonLinearMechSolver::createInterfaceElement_2()
    {
      // The contructor of dgGroupCollection create automatically the GroupsOfElements
    /*  _groups = dgGroupCollection(this->pModel,this->_dim,1); // TODO ?? Add parameter to model to store order
      _groups.buildGroupsOfInterfaces();
      // Affichage temporaire pour vérification
      int nn = _groups.getNbFaceGroups();
      printf("Number of group of faces : %d\n",nn);
      for(int i=0;i<nn;i++){
        printf("Group of face number %d\n",i);
        dgGroupOfFaces *inter = _groups.getFaceGroup(i);
        int nnn = inter->getNbGroupOfConnections();
        printf("Number of connection group %d \n",nnn);
        for(int j=0;j<nnn;j++){
          const dgGroupOfConnections connec = inter->getGroupOfConnections(j);
          printf("Connection's group number %d\n",j);
          int nnnn = connec.getNbClosures();
          printf("Number of closures %d\n",nnnn);
          for(int k=0;k<nnnn;k++){
            printf("Closure number %d\n",k);
            std::vector<int> vec = connec.getClosure(k);
            for(int kk=0;kk<vec.size();kk++){
              printf(" %d ",vec[kk]);
            }
            printf("\n");
          }
        }
      }*/
    }
    
    void nonLinearMechSolver::rotateModel(const SVector3& n1, const SVector3& n2, const SVector3& n3){
      if (!_GModelIsRotated){
        _GModelIsRotated = true;
        GeometryRotation::rotateGModel(getFileSavingPrefix(),pModel,n1,n2,n3);
      }
    };
    
    int nonLinearMechSolver::getInterfaceElementNumber(const int em, const int ep) const{
      TwoNum tn(em,ep);
      std::map<TwoNum,int>::const_iterator itF = _interfaceElements.find(tn);
      if ( itF != _interfaceElements.end())
      {
        return itF->second;
      }
      else{
        Msg::Error("interface element with negative %d positive %d is not found nonLinearMechSolver::getInterfaceElementNumber!",em,ep);
      }
      return 0;
    };
    
    TwoNum nonLinearMechSolver::getMinusAndPlusElementNumber(const int elnum) const{
      std::map<int,TwoNum>::const_iterator itF = _interfaceElementsInverseMap.find(elnum);
      if (itF != _interfaceElementsInverseMap.end()){
        return itF->second;
      }
      else{
        #if defined (HAVE_MPI)
        if (Msg::GetCommSize() > 1)
          Msg::Error("interface element %d is not found nonLinearMechSolver::getMinusAndPlusElementNumber at rank %d",elnum,Msg::GetCommRank());
        else
        #endif // HAVE_MPI
          Msg::Error("interface element %d is not found nonLinearMechSolver::getMinusAndPlusElementNumber",elnum);
      }
      return TwoNum(0,0);
    };
    
    void nonLinearMechSolver::initTerms(){
      // resize _vterm in NeumannBC
      if(_previousInit){ // if previous computation delete first the terms
        // BC
        for(std::list<nonLinearNeumannBC>::iterator itbc = allNeumann.begin(); itbc != allNeumann.end();++itbc)
        {
          for(int j=0;j<itbc->_vterm.size();j++)
          {
            delete itbc->_vterm[j];
          }
        }
        // domain
        for(std::vector<partDomain*>::iterator itdom = domainVector.begin(); itdom!=domainVector.end(); ++itdom){
          partDomain *dom = *itdom;
          if(dom->getBilinearBulkTerm() != NULL) delete dom->getBilinearBulkTerm();
          if(dom->getBilinearMassTerm() != NULL) delete dom->getBilinearMassTerm();
          if(dom->getLinearBulkTerm() != NULL) delete dom->getLinearBulkTerm();
          if(dom->IsInterfaceTerms()){
            dgPartDomain *dgdom = static_cast<dgPartDomain*>(dom);
            if(dgdom->getBilinearInterfaceTerm() != NULL) delete dgdom->getBilinearInterfaceTerm();
            if(dgdom->getBilinearVirtualInterfaceTerm() !=NULL) delete dgdom->getBilinearVirtualInterfaceTerm();
            if(dgdom->getLinearInterfaceTerm() != NULL) delete dgdom->getLinearInterfaceTerm();
            if(dgdom->getLinearVirtualInterfaceTerm() != NULL) delete dgdom->getLinearVirtualInterfaceTerm();
          }
        }
      }
      for(std::list<nonLinearNeumannBC>::iterator itbc = allNeumann.begin(); itbc != allNeumann.end();++itbc)
      {
        nonLinearNeumannBC &neu = *itbc;
        int nsize = neu._vspace.size();
        neu._vterm.resize(nsize);
        neu._vmatrix_term.resize(nsize);
        for(int j=0;j<nsize;++j)
        {
          neu._vterm[j] = neu._vdom[j]->createNeumannTerm(static_cast<FunctionSpace<double>*>(neu._vspace[j]),  neu._vgroup[j],
                                                          neu._f,_ufield, _ipf,neu.onWhat,neu._NeumannBCType, neu._comp);
          neu._vmatrix_term[j] = neu._vdom[j]->createNeumannMatrixTerm(static_cast<FunctionSpace<double>*>(neu._vspace[j]),  neu._vgroup[j],
                                                                 neu._f,_ufield, _ipf,neu.onWhat,neu._NeumannBCType, neu._comp);
        }
      }
    
      for(std::vector<partDomain*>::iterator itdom = domainVector.begin(); itdom!=domainVector.end(); ++itdom){
        partDomain *dom = *itdom;
        dom->initializeTerms(_ufield,_ipf);
      }
    
      // contact domain
      for(contactContainer::iterator it = _allContact.begin(); it!=_allContact.end(); ++it){
        contactDomain *cdom = *it;
          cdom->initializeTerms(_ufield);
      }
      // contact domain
      for(defoDefoContactContainer::iterator it = _allDefoDefoContact.begin(); it!=_allDefoDefoContact.end(); ++it){
        defoDefoContactDomain *cdom = *it;
          cdom->initializeTerms(_ufield);
      }
    
    }
    
    double nonLinearMechSolver::solveStaticLinear()
    {
      this->init();
      this->init2();
    
      /* time initialization */
      double curtime = 1.;
      double timestep = 1.;
      int step = 1;
    
      /* solving */
      this->setTimeForLaw(curtime,timestep,step);
      std::cout <<  "Neumann BC"<< std::endl;
      double tsystresol = Cpu();
      this->computeExternalForces();
      this->computeStiffMatrix();
      printf("-- done assembling!\n");
      pAssembler->systemSolve();
      printf("-- done solving!\n");
      
      /* compute stress after solve */
      _ipf->compute1state(IPStateBase::current,true);
      double normRHS = computeRightHandSide(); // to make all archiving possible
      /* end of scheme */
      tsystresol = Cpu() - tsystresol;
      Msg::Info("Time of resolution: %f",tsystresol);
      this->endOfScheme(curtime,step);
      Msg::Info("StaticLinear OK");
      return curtime;
    }
    
    double nonLinearMechSolver::solveEigen()
    {
      // no view
      unknownView.clear();
      ipView.clear();
      _energyComputation = 0;
      _fractureEnergyComputation = 0;
    
      /* init data */
      this->init();
      this->init2();
    
      /* time initialization */
      double curtime = 1.;
      double timestep = 1.;
      int step = 1;
    
      /* solving */
      this->setTimeForLaw(curtime,timestep,step);
      printf("--begin assembling \n");
      double tsystresol = Cpu();
      if (_eigOpts.type == eigenSolverOptions::Dynamic)
      {
       this->computeMassMatrix();
      }
      this->computeStiffMatrix();
      printf("-- done assembling!\n");
    
      if (_eigOpts.MKToFile){
        #if defined(HAVE_PETSC)
         std::string Aname("A");
         linearSystem<double>* lsysA = pAssembler->getLinearSystem(Aname);
         std::string Bname("B");
         linearSystem<double>* lsysB = pAssembler->getLinearSystem(Bname);
    
         linearSystemPETSc<double>* Apet = dynamic_cast<linearSystemPETSc<double>*>(lsysA);
         if (Apet!= NULL) {
            functionPETSc::MatToFile(Apet->getMatrix(),"Mat_Stiffness.txt");
    
         linearSystemPETSc<double>* Bpet = dynamic_cast<linearSystemPETSc<double>*>(lsysB);
         if (Bpet!= NULL) {
            functionPETSc::MatToFile(Bpet->getMatrix(),"Mat_Mass.txt");
          };
        }
         #endif
      }
    
      #if defined (HAVE_SLEPC)
      eigenSolver* eigSolver = this->eigenSolve(step);
      eigenVectorField eigfield(this,eigSolver,3,_eigview,"eigenMode");
      eigfield.archive(curtime,step,false);
    
      // save perturbe file
      if (_eigOpts.isPerturbedEigenMode)
      {
        if (eigSolver!=NULL)
        {
          writeDisturbedMeshByEigenVector(*eigSolver,_eigOpts.numberPerturbedMode,_eigOpts.valPerturbedMode);
        }
      }
      delete eigSolver;
      #else
      Msg::Error("Slepc is not intalled");
      #endif // HAVE_SLEPC
      printf("-- done solving!\n");
    
      /* end of scheme */
      this->endOfScheme(curtime,step);
      tsystresol = Cpu() - tsystresol;
      Msg::Info("Time of Eigen Value resolution resolution: %f",tsystresol);
      Msg::Info("EigenSolve OK");
      return curtime;
    }
    
    void nonLinearMechSolver::computeLoadVector()
    {
      this->setTimeForBC(1.);
      for(std::list<nonLinearNeumannBC>::iterator itbc = allNeumann.begin(); itbc != allNeumann.end();++itbc)
      {
        nonLinearNeumannBC &neu = *itbc;
        // select the system if required
        for(int j=0;j<neu._vspace.size();j++)
        {
          FunctionSpaceBase *spn = neu._vspace[j];
          const elementGroup *gneu = neu._vgroup[j];
          QuadratureBase *qneu = neu._vquadrature[j];
          Assemble(neu._vterm[j],*spn,gneu->begin(),
                 gneu->end(),*(qneu),_ufield,*pAssembler,nonLinearSystemBase::LoadVector, _elementErosionFilter);
        }
      }
      //
      if (_testFlag){
        if (_pbcGroup != NULL)
        {
          for (std::list<nonLinearNeumannBCAtCorner>::iterator itc = allCornerForce.begin(); itc != allCornerForce.end();++itc){
            nonLinearNeumannBCAtCorner& con = *itc;
            // select the system if required
            std::vector<Dof>& allDof = con.constraintDofs;
            int size = allDof.size();
            MVertex* v = _pbcGroup->getCornerVertex(con._cornerNumber);
            fullVector<double> F;
            F.resize(size,true);
            double val = con._f->operator ()(v->x(),v->y(),v->z());
            F.setAll(val);
            pAssembler->assemble(allDof,F,nonLinearSystemBase::LoadVector);
          };
        };
      };
      this->setTimeForBC(0.);
    }
    
    
    // for path following on RVE 
    void nonLinearMechSolver::computeBodyForceVector(){
        _pAl->zeroBodyForceVector();
        static STensor33 G0;
        static STensor33 G1;
        double curtime = this->getMicroBC()->getTime();
        this->getMicroBC()->setTime(0.); 
        G0 = this->getMicroBC()->getSecondOrderKinematicalVariable();
        this->getMicroBC()->setTime(1.); 
        G1 = this->getMicroBC()->getSecondOrderKinematicalVariable();
        this->getMicroBC()->setTime(curtime);
        
        G1 -=G0;
        for (int idom=0; idom<domainVector.size(); idom++){
            partDomain* dom = domainVector[idom];
            dom->setSecondOrderKinematic(G1);
            
    	if (dom->elementGroupSize()>0){
    	   AssembleBodyForceVector(*dom->getLinearTermPFBodyForce(),*dom->getFunctionSpace(),dom->element_begin(),
               dom->element_end(),*dom->getBulkGaussIntegrationRule(),*pAssembler,_elementErosionFilter);
    	}
        }
    }
    
    void nonLinearMechSolver::setHomogeneousTangentToDomain(homogenizedData* homoData){
        static STensor43 Homogeneous_K;
        Homogeneous_K = homoData->getHomogenizedTangentOperator_F_F();
        
        double volume = 0.;
        for (int i=0; i<domainVector.size(); i++){
           volume += domainVector[i]->computeVolumeDomain(_ipf);
        };
        Homogeneous_K *= _rveVolume/volume;
        for (int idom=0; idom<domainVector.size(); idom++){
            partDomain* dom = domainVector[idom];
            dom->setHomogeneousTangent(Homogeneous_K);
        }
    }
    
    void nonLinearMechSolver::computePathFollowingConstraint()
    {
    	
    	for(std::vector<partDomain*>::iterator itdom=domainVector.begin(); itdom!=domainVector.end();++itdom)
      {
        partDomain *dom = *itdom;
        if (dom->getMaterialLaw() != NULL){
          if (dom->getMaterialLaw()->withEnergyDissipation()){
            AssemblerPathFollowingConstraint(*(dom->getScalarTermPFBulk()),dom->element_begin(),dom->element_end(),*(dom->getBulkGaussIntegrationRule()),*pAssembler,_elementErosionFilter);
          }
        }
    
    		if (dom->IsInterfaceTerms()){
    			dgPartDomain* dgDom = static_cast<dgPartDomain*>(dom);
          if (dgDom->getMaterialLawMinus()->withEnergyDissipation() or dgDom->getMaterialLawPlus()->withEnergyDissipation()){
            AssemblerPathFollowingConstraint(*(dgDom->getScalarTermPFBound()),dgDom->gi->begin(),dgDom->gi->end(),
    									*(dgDom->getInterfaceGaussIntegrationRule()),*pAssembler,_elementErosionFilter);
    
          }
    		}
    	}
    };
    
    void nonLinearMechSolver::computeDPathFollowingConstraintDUnknowns(){
    	for(std::vector<partDomain*>::iterator itdom=domainVector.begin(); itdom!=domainVector.end();++itdom)
      {
        partDomain *dom = *itdom;
        if (dom->getMaterialLaw() != NULL){
          if (dom->getMaterialLaw()->withEnergyDissipation()){
            AssemblerDPathFollowingConstraintDUnknowns(*(dom->getLinearTermPFBulk()),*(dom->getFunctionSpace()),
    						 dom->element_begin(),dom->element_end(),*(dom->getBulkGaussIntegrationRule()),*pAssembler,_elementErosionFilter);
          }
        }
    		if (dom->IsInterfaceTerms())
        {
    			dgPartDomain* dgDom = static_cast<dgPartDomain*>(dom);
          if (dgDom->getMaterialLawMinus()->withEnergyDissipation() or dgDom->getMaterialLawPlus()->withEnergyDissipation()){
            AssemblerDPathFollowingConstraintDUnknowns(*(dgDom->getLinearTermPFBound()),*(dgDom->getFunctionSpace()),
    						 dgDom->gi->begin(),dgDom->gi->end(),*(dgDom->getInterfaceGaussIntegrationRule()),*pAssembler,_elementErosionFilter);
          }
    		}
    	}
    
    };
    
    void nonLinearMechSolver::computeExternalForces()
    {
      if(this->getScheme() != StaticLinear)
      {
        for (std::list<nonLinearNeumannBC>::iterator itbc = allNeumann.begin(); itbc != allNeumann.end();++itbc)
        {
          nonLinearNeumannBC &neu = *itbc;
          for(int j=0;j<neu._vspace.size();j++)
          {
            FunctionSpaceBase *spn = neu._vspace[j];
            const elementGroup *gneu = neu._vgroup[j];
            QuadratureBase *qneu = neu._vquadrature[j];
            Assemble(neu._vterm[j],*spn,gneu->begin(),
                   gneu->end(),*(qneu),_ufield,*pAssembler,nonLinearSystemBase::Fext,_elementErosionFilter);
          }
        }
        //
        if (_testFlag)
        {
          if (_pbcGroup != NULL)
          {
            for (std::list<nonLinearNeumannBCAtCorner>::iterator itc = allCornerForce.begin(); itc != allCornerForce.end();++itc)
            {
              nonLinearNeumannBCAtCorner& con = *itc;
              // select the system if required
              std::vector<Dof>& allDof = con.constraintDofs;
              int size = allDof.size();
              MVertex* v = _pbcGroup->getCornerVertex(con._cornerNumber);
              fullVector<double> F;
              F.resize(size,true);
              double val = con._f->operator ()(v->x(),v->y(),v->z());
              F.setAll(val);
              pAssembler->assemble(allDof,F,nonLinearSystemBase::Fext);
            };
    
          };
        };
      }
      else{
        for(std::list<nonLinearNeumannBC>::iterator itbc = allNeumann.begin(); itbc != allNeumann.end();++itbc)
        {
          nonLinearNeumannBC &neu = *itbc;
          for(int j=0;j<neu._vspace.size();j++)
          {
            FunctionSpaceBase *spn = neu._vspace[j];
            const elementGroup *gneu = neu._vgroup[j];
            QuadratureBase *qneu = neu._vquadrature[j];
            AssembleItMap(*(neu._vterm[j]),*spn,gneu->begin(),gneu->end(),*(qneu),*pAssembler,_elementErosionFilter);
          }
        }
    
        if (_testFlag)
        {
          if (_pbcGroup != NULL){
            for (std::list<nonLinearNeumannBCAtCorner>::iterator itc = allCornerForce.begin(); itc != allCornerForce.end();++itc){
              nonLinearNeumannBCAtCorner& con = *itc;
              std::vector<Dof>& allDof = con.constraintDofs;
              int size = allDof.size();
              MVertex* v = _pbcGroup->getCornerVertex(con._cornerNumber);
              fullVector<double> F;
              F.resize(size,true);
              double val = con._f->operator ()(v->x(),v->y(),v->z());
              F.setAll(val);
              pAssembler->assemble(allDof,F);
            };
          };
        };
      }
    }
    
    void nonLinearMechSolver::snlData(const int ns, const double et, const double reltol, const double absTol){
      if(whatScheme==StaticNonLinear or whatScheme==Multi or whatScheme== Implicit)
      {
        _timeManager->setEndTime(et);
        _timeManager->setNumSteps(ns);
        // tolerance
        _tol=reltol;
        _absTol=absTol;
        if (whatScheme==Multi)
        {
          // if implicit-explict scheme is used
          _explicitOpts.numstepImpl = ns;
        }
      }
      else
      {
        Msg::Error("Impossible to set data for Static Non Linear scheme because another is chosen to solve the problem");
        _timeManager->setEndTime(et);
        _timeManager->setNumSteps(1);
      }
    };
    
    void nonLinearMechSolver::clearNumStepSeries()
    {
      _timeManager->clearNumStepSeries();
    }
    void nonLinearMechSolver::setNumStepTimeInterval(double t, int nstep)
    {
      if (whatScheme==Multi || whatScheme==Explicit)
      {
        Msg::Error("setNumStepTimeInterval is applicable in case of Static Non Linear scheme");
      }
      else
      {
        _timeManager->setNumStepTimeInterval(t,nstep);
      }
    }
    
    void nonLinearMechSolver::clearMultiSystem()
    {
      _vcompBySys.clear();
      _vschemeBySys.clear();
    }
    
    void nonLinearMechSolver::addSystem(const int comp,const int s)
    {
      _vcompBySys.push_back(comp);
      _vschemeBySys.push_back((scheme)s);
    }
    
    void nonLinearMechSolver::switchSystem(const int sysindex,const int s)
    {
      _vschemeBySys[sysindex] = (scheme)s;
    }
    
    void nonLinearMechSolver::setTimeIncrementAdaptation(const bool adap, const int numNROptimal, const double exp, const double maximalStep, const double minTimeStep, const int maxFailtIn)
    {
      _timeManager->activateTimeStepAdaptation(adap,numNROptimal,exp,maximalStep,minTimeStep);
      _timeManager->setMaximalNumberOfFails(maxFailtIn);
    }
    
    void nonLinearMechSolver::snlManageTimeStep(const int miteNR,const int iteIncreaseTS, const double redfactor, const int maxAttemptRedFactor)
    {
      _timeManager->setManageTimeStepSafeguard(miteNR,iteIncreaseTS,redfactor,maxAttemptRedFactor);
    }
    
    void nonLinearMechSolver::explicitData(const double ftime, const double gams, const double beta, const double gamma, const double alpham,const bool benson){
      //
      _timeManager->setEndTime(ftime);
      _explicitOpts.timeStepByBenson = benson;
      _explicitOpts.beta=beta;
      _explicitOpts.gamma=gamma;
      _explicitOpts.alpham = alpham;
      // depends on numerical damping
      double _rhoinfty = (alpham+1.)/(2.-alpham);
      double omegas = sqrt((12.*(1.+_rhoinfty)*(1.+_rhoinfty)*(1.+_rhoinfty)*(2.-_rhoinfty))/(10.+15.*_rhoinfty-_rhoinfty*_rhoinfty+_rhoinfty*_rhoinfty*_rhoinfty-_rhoinfty*_rhoinfty*_rhoinfty*_rhoinfty));
      _explicitOpts.gammas = gams*omegas;
    }
    
    void nonLinearMechSolver::implicitData(const double beta, const double gamma, const double alpham , const double alphaf){
      _implicitOpts.beta = beta;
      _implicitOpts.gamma = gamma;
      _implicitOpts.alphaf = alphaf;
      _implicitOpts.alpham = alpham;
    };
    
    void nonLinearMechSolver::explicitSpectralRadius(const double ftime,const double gams, const double rho,const bool benson){
      _timeManager->setEndTime(ftime); // set end time
      _explicitOpts.timeStepByBenson = benson;
      double _rhoinfty=rho;
      _explicitOpts.beta = (5.-3.*_rhoinfty)/((1.+_rhoinfty)*(1.+_rhoinfty)*(2.-_rhoinfty));
      _explicitOpts.alpham = (2.*_rhoinfty-1.)/(1.+_rhoinfty);
      _explicitOpts.gamma = 1.5-_explicitOpts.alpham;
      double omegas = sqrt((12.*(1.+_rhoinfty)*(1.+_rhoinfty)*(1.+_rhoinfty)*(2.-_rhoinfty))/(10.+15.*_rhoinfty-_rhoinfty*_rhoinfty+_rhoinfty*_rhoinfty*_rhoinfty-_rhoinfty*_rhoinfty*_rhoinfty*_rhoinfty));
      _explicitOpts.gammas = gams*omegas;
    }
    
    void nonLinearMechSolver::options(const std::string &options)
    {
      if(!strcmp("petsc_direct",options.c_str()))
      {
        _solver_options = std::string("-pc_type lu -ksp_type preonly");
      }
      else if(!strcmp("petsc_gmres",options.c_str()))
      {
        _solver_options = std::string("-pc_type jacobi -ksp_gmres_restart 200");
      }
      else if(!strcmp("petsc_mumps",options.c_str()))
      {
        _solver_options = std::string("-pc_mat_solver_package mumps");
      }
      else
      {
        _solver_options = std::string(options);
      }
      return;
    }
    
    void nonLinearMechSolver::implicitSpectralRadius(const double rho){
      double _rhoinfty = rho;
      _implicitOpts.alpham = (2.*_rhoinfty-1.)/(1.+_rhoinfty);
      _implicitOpts.alphaf = _rhoinfty/(_rhoinfty +1.);
      _implicitOpts.beta = 1./((_rhoinfty +1.)*(_rhoinfty +1.));
      _implicitOpts.gamma = (3.-_rhoinfty)/(2.+2.*_rhoinfty);
    };
    
    void nonLinearMechSolver::explicitTimeStepEvaluation(const int nst){
      _explicitOpts.numstepExpl = nst;
    }
    
    double nonLinearMechSolver::solve(){
      double tstart=Cpu();
      double reachtime=0.;
      if (_microFlag == false){
        switch(whatScheme){
          case StaticLinear :
              reachtime = this->solveStaticLinear();
            break;
          case StaticNonLinear :
              reachtime = this->solveSNL();
            break;
          case Implicit:
              reachtime = this->solveSNL();
            break;
          case Explicit:
            reachtime = this->solveExplicit();
            break;
          case Multi:
            reachtime = this->solveMulti();
    		break;
    	  case Eigen:
    		reachtime = this->solveEigen();
    		break;
        }
      }
      else{
        switch(whatScheme){
          case StaticLinear:
            reachtime = this->microSolveStaticLinear();
            break;
          case StaticNonLinear :
            reachtime = this->microSolveSNL();
    		break;
        }
      };
      if (getNumRanks() > 1)
        Msg::Barrier(); // wait all before end (for MPI)
      _notResetedBC = true;
      _notResetedContact = true;
      _explicitOpts.dynamicRelaxation = false;
      Msg::Info("Total time consuming %.6f (s) ",Cpu()-tstart);
      return reachtime;
    }
    
    void nonLinearMechSolver::resetBoundaryConditions()
    {
      for(std::list<nonLinearDirichletBC>::iterator itbc = allDirichlet.begin(); itbc != allDirichlet.end();++itbc)
      {
        itbc->clear();
      }
      allDirichlet.clear();
      for(std::list<nonLinearNeumannBC>::iterator itbc = allNeumann.begin(); itbc != allNeumann.end();++itbc)
      {
        itbc->clear();
      }
      allNeumann.clear();
      for(std::list<initialCondition>::iterator itic = allinitial.begin(); itic !=allinitial.end();++itic)
      {
        itic->clear();
      }
      allinitial.clear();
    
      allCornerConstraint.clear();
      allCornerForce.clear();
    
      // clear all other BC
      // as objects created, destructor leading to clean the objects
      _nodePeriodicPhysicals.clear();
      _edgePeriodicPhysicals.clear();
      allPeriodic.clear();
      allAveragePeriodic.clear();
      allSameDisp.clear();
      allFixAllFace.clear();
      allConstraint.clear();
    
      _testFlag = false;
      if (_pbcGroup) {
        delete _pbcGroup;
        _pbcGroup = NULL;
      }
      _notResetedBC = false;
    }
    
    void nonLinearMechSolver::resetContactInteraction()
    {
     // for(contactContainer::iterator it = _allContact.begin(); it!=_allContact.end(); ++it){
     //   contactDomain *cdom = *it;
     //   delete cdom;
     // }
      _allContact.clear();
      //HERE NOT SURE WE NEED TO RESET THE DEFO DEFO CONTACT; DO ANOTHER FUNCTION?
      _notResetedContact = false;
    }
    void nonLinearMechSolver::disableResetRestart() //to avoid restart at a change of solver
    {
      _disableResetRestart = true;
    }
    
    // to set tag of domain that needs restart to save/load InternalState
    void nonLinearMechSolver::setRestartDomainTag(const int tag)
    {
        _restartDomainTag.push_back(tag);
    }
    
    // get domain tag for restart InternalState set by user
    const std::vector<int> nonLinearMechSolver::getRestartDomainTag()
    {
        return _restartDomainTag;
    }
    
    void nonLinearMechSolver::dynamicRelaxation(const double gams,const double ftime,const double tol,const int wsolver,const bool benson)
    {
      _explicitOpts.timeStepByBenson = true;
      _explicitOpts.dynamicRelaxation = true;
      whatScheme = Explicit;
      if(wsolver == 2)
        whatSolver = Petsc;
      _tol = tol;
      this->explicitSpectralRadius(ftime,gams,0.99); //rho_infty == 0 for dynamic relaxtion (no influence ??)
    }
    
    double nonLinearMechSolver::getCoordinatesOfNodePhysical(const int numphys, const int comp)
    {
      elementGroup gr(0,numphys);
      if (gr.vsize() != 1)
      {
        Msg::Error("physical %d consisting of %d nodes",gr.vsize());
      }
      else
      {
        MVertex* v = gr.vbegin()->second;
        if (comp ==0) return v->x();
        else if (comp == 1) return v->y();
        else if (comp == 2) return v->z();
        else
          Msg::Error("comp %d does not exist",comp);
      }
      return 0.;
    };
    
    void nonLinearMechSolver::displacementBC(std::string onwhat, const int numphys, const int comp, const double value)
    {
      simpleFunctionTime<double> *fct = new simpleFunctionTime<double>(value);
      elementFilterTrivial filter = elementFilterTrivial();
      this->displacementBC(onwhat,numphys,comp,&filter,fct,mixedFunctionSpaceBase::DOF_STANDARD);
    }
    
    void nonLinearMechSolver::displacementBC(std::string onwhat, const int numphys, const int comp, const double valueDiff, const double valueInit){
      simpleFunctionTime<double> *fct = new simpleFunctionTimeWithConstant<double>(valueDiff,true,1.,valueInit);
      elementFilterTrivial filter = elementFilterTrivial();
      this->displacementBC(onwhat,numphys,comp,&filter,fct,mixedFunctionSpaceBase::DOF_STANDARD);
    };
    
    void nonLinearMechSolver::velocityBC(std::string onwhat, const int numphys, const int comp, const double value)
    {
      this->displacementBC(onwhat,numphys,comp,value);
      allDirichlet.back()._mycondition = nonLinearBoundaryCondition::velocity;
    }
    
    void nonLinearMechSolver::accelerationBC(std::string onwhat, const int numphys, const int comp, const double value)
    {
      this->displacementBC(onwhat,numphys,comp,value);
      allDirichlet.back()._mycondition = nonLinearBoundaryCondition::acceleration;
    }
    
    void nonLinearMechSolver::displacementBC(std::string onwhat, const int numphys, const int comp, const double value, elementFilter *filter)
    {
      simpleFunctionTime<double> *fct = new simpleFunctionTime<double>(value);
      this->displacementBC(onwhat,numphys,comp,filter,fct,mixedFunctionSpaceBase::DOF_STANDARD);
    }
    
    void nonLinearMechSolver::velocityBC(std::string onwhat, const int numphys, const int comp, const double value, elementFilter *filter)
    {
      this->displacementBC(onwhat,numphys,comp,value,filter);
      allDirichlet.back()._mycondition = nonLinearBoundaryCondition::velocity;
    }
    
    void nonLinearMechSolver::displacementBC(std::string onwhat, const int numphys, const int comp,simpleFunctionTime<double> *fct)
    {
      elementFilterTrivial filter = elementFilterTrivial();
      this->displacementBC(onwhat,numphys,comp,&filter,fct,mixedFunctionSpaceBase::DOF_STANDARD);
    }
    
    void nonLinearMechSolver::velocityBC(std::string onwhat, const int numphys, const int comp,simpleFunctionTime<double> *fct)
    {
      this->displacementBC(onwhat,numphys,comp,fct);
      allDirichlet.back()._mycondition = nonLinearBoundaryCondition::velocity;
    }
    
    void nonLinearMechSolver::curlDisplacementBC(std::string onwhat, const int numphys, const int comp, const double value)
    {
      simpleFunctionTime<double> *fct = new simpleFunctionTime<double>(value);
      elementFilterTrivial filter = elementFilterTrivial();
      this->displacementBC(onwhat,numphys,comp,&filter,fct,mixedFunctionSpaceBase::DOF_CURL);
    }
    
    void nonLinearMechSolver::displacementBC(std::string onwhat, const int numphys, const int comp, elementFilter *filter,
                                             simpleFunctionTime<double> *fct, const mixedFunctionSpaceBase::DofType dofType)
    {
      allDirichlet.emplace_back();
      nonLinearDirichletBC& diri = allDirichlet.back();
      const std::string node("Node");
      const std::string edge("Edge");
      const std::string face("Face");
      const std::string volume("Volume");
      diri.g = new elementGroup();
      if(onwhat==node){
        if(dofType !=mixedFunctionSpaceBase::DOF_STANDARD)
           Msg::Error("nonLinearMechSolver::displacementBC: impossible to constrain a non vertex dof on a node");
        diri.g->addPhysical(0, numphys,*filter);
        diri.onWhat=nonLinearBoundaryCondition::ON_VERTEX;
      }
      else if(onwhat==edge){
        diri.g->addPhysical(1, numphys,*filter);
        diri.onWhat=nonLinearBoundaryCondition::ON_EDGE;
      }
      else if(onwhat==face){
        diri.g->addPhysical(2, numphys,*filter);
        diri.onWhat=nonLinearBoundaryCondition::ON_FACE;
      }
      else if(onwhat==volume){
        diri.g->addPhysical(3, numphys,*filter);
        diri.onWhat=nonLinearBoundaryCondition::ON_VOLUME;
      }
      else{
        Msg::Error("Impossible to prescribe a displacement on a %s\n",onwhat.c_str());
        delete diri.g;
        return;
      }
      diri._f= static_cast<simpleFunctionTime<double>*>(fct);
      diri._comp=comp;
      diri._tag=numphys;
      diri._dofType=dofType;
    };
    
    
    void nonLinearMechSolver::velocityBC(std::string onwhat, const int numphys, const int comp, elementFilter *filter,
                                             simpleFunctionTime<double> *fct)
    {
      this->displacementBC(onwhat,numphys,comp,filter,fct,mixedFunctionSpaceBase::DOF_STANDARD);
      allDirichlet.back()._mycondition = nonLinearBoundaryCondition::velocity;
    }
    
    void nonLinearMechSolver::displacementRigidContactBC(const int numphys, const int comp_, const double value)
    {
      simpleFunctionTime<double> *fct = new simpleFunctionTime<double>(value);
      this->displacementRigidContactBC(numphys,comp_,fct);
    }
    void nonLinearMechSolver::displacementRigidContactBC(const int numphys, const int comp_, simpleFunctionTime<double> *fct)
    {
      allContactBC.emplace_back(numphys);
      rigidContactBC& diri = allContactBC.back();
      diri.onWhat = nonLinearBoundaryCondition::RIGIDCONTACT;
      diri._comp = comp_;
      diri._f = static_cast<simpleFunctionTime<double>*>(fct);
    }
    
    void nonLinearMechSolver::initialBC(std::string onwhat, std::string whichC, const int numphys,
                                              const int comp, const double value)
    {
      elementFilterTrivial filter = elementFilterTrivial();
      this->initialBC(onwhat,whichC,numphys,comp,value,&filter);
    }
    
    void nonLinearMechSolver::initialBC(std::string onwhat, std::string whichC, const int numphys,
                                              const int comp, const double value,
                                              elementFilter *filter)
    {
      simpleFunctionTime<double>* sfunction = new simpleFunctionTime<double>(value,false,0.);
      this->initialBC(onwhat,whichC,numphys,comp,sfunction,filter);
    }
    
    void nonLinearMechSolver::initialBC(std::string onwhat, std::string whichC, const int numphys, const int comp,
                                        simpleFunctionTime<double> *fct,elementFilter* filter)
    {
      bool nullfilter=false;
      if(filter==NULL)
      {
        filter = new elementFilterTrivial();
        nullfilter = true;
      }
    
      const std::string node("Node");
      const std::string edge("Edge");
      const std::string face("Face");
      const std::string volume("Volume");
      const std::string rigid("Rigid");
      const std::string position("Position");
      const std::string velocity("Velocity");
      const std::string acceleration("Acceleration");
    
      nonLinearBoundaryCondition::location ow;
      elementGroup *gr = new elementGroup();
      if(onwhat == node){
        ow = nonLinearBoundaryCondition::ON_VERTEX;
        gr->addPhysical(0,numphys,*filter);
      }
      else if(onwhat == edge){
        ow = nonLinearBoundaryCondition::ON_EDGE;
        gr->addPhysical(1,numphys,*filter);
      }
      else if(onwhat == face){
        ow = nonLinearBoundaryCondition::ON_FACE;
        gr->addPhysical(2,numphys,*filter);
      }
      else if(onwhat == volume){
        ow = nonLinearBoundaryCondition::ON_VOLUME;
        gr->addPhysical(3,numphys,*filter);
      }
      else if(onwhat == rigid)
      {
        ow = nonLinearBoundaryCondition::RIGIDCONTACT;
        // empty group insert gravity center later
      }
      else{
        ow = nonLinearBoundaryCondition::UNDEF;
        gr->addPhysical(2,numphys,*filter);
      }
    
      nonLinearBoundaryCondition::whichCondition wc;
      if(whichC == position)
        wc = nonLinearBoundaryCondition::position;
      else if(whichC == velocity)
        wc = nonLinearBoundaryCondition::velocity;
      else if(whichC == acceleration)
        wc = nonLinearBoundaryCondition::acceleration;
      else{
        Msg::Warning("Impossible to prescribed an initial condition %d",numphys);
        //return;
      }
      allinitial.emplace_back(comp,wc);
      initialCondition& initc = allinitial.back();
      initc.onWhat = ow;
      initc.g = gr;
      initc._tag = numphys;
      initc._f = fct;
    
    
    
      if(nullfilter)
      {
        delete filter;
      }
    }
    
    void nonLinearMechSolver::initialDCBVeloBC(std::string onwhat,const int numphys,const int axiscomp, const int deflcomp,const double length, const double value)
    {
      const std::string edge("Edge");
      const std::string face("Face");
      const std::string volume("Volume");
      allinitial.emplace_back(deflcomp,nonLinearBoundaryCondition::velocity);
      initialCondition initc = allinitial.back();
      initc._f = new functionVelocityDCB(value,axiscomp,length);
      initc.g = new elementGroup();
      initc._tag = numphys;
      if(onwhat == edge){
        initc.onWhat = nonLinearBoundaryCondition::ON_EDGE;
        initc.g->addPhysical(1,numphys);
      }
      else if(onwhat == face){
        initc.onWhat = nonLinearBoundaryCondition::ON_FACE;
        initc.g->addPhysical(2,numphys);
      }
      else if(onwhat == volume){
        initc.onWhat  = nonLinearBoundaryCondition::ON_VOLUME;
        initc.g->addPhysical(3,numphys);
      }
      else{
        Msg::Error("The initial velocity BC can be applied only on an edge or on a face");
      }
    }
    
    void nonLinearMechSolver::independentDisplacementBC(std::string onwhat, const int numphys, const int comp, const double value){
      allDirichlet.emplace_back();
      nonLinearDirichletBC& diri = allDirichlet.back();
      const std::string node("Node");
      const std::string edge("Edge");
      const std::string face("Face");
      const std::string volume("Volume");
      if(onwhat==node){
        diri.g = new elementGroup (0, numphys);
        diri.onWhat=nonLinearBoundaryCondition::ON_VERTEX;
      }
      else if(onwhat==edge){
        diri.g = new elementGroup (1, numphys);
        diri.onWhat=nonLinearBoundaryCondition::ON_EDGE;
      }
      else if(onwhat==face){
        diri.g = new elementGroup (2, numphys);
        diri.onWhat=nonLinearBoundaryCondition::ON_FACE;
      }
      else if(onwhat==volume){
        diri.g = new elementGroup (3, numphys);
        diri.onWhat=nonLinearBoundaryCondition::ON_VOLUME;
      }
      else Msg::Error("Impossible to prescribe a displacement on a %s\n",onwhat.c_str());
      diri._f= new simpleFunctionTime<double>(value,false);
      diri._comp=comp;
      diri._tag=numphys;
    }
    
    void nonLinearMechSolver::forceBC(std::string onwhat, const int numphys, const int comp, const double val)
    {
      simpleFunctionTime<double> *fct = new simpleFunctionTime<double>(val,true,0.);
      this->forceBC(onwhat,numphys,comp,fct);
    }
    void nonLinearMechSolver::forceBC(std::string onwhat, const int numphys, const int comp, simpleFunctionTime<double> *fct)
    {
       // in general, forceBC is applied for the flux of and arbitrary unknown field
      // (force in case of displacement, extradof field in case of extraDof, ...) in REFERENCE CONFIGURATION.
      // in case of using CURRENT CONFIGURATION, as pressure or scalar flux,
      // pressureOnPhysicalGroupBC for presurre or
      // scalarFluxBC for scalar flux  should be used
    
      const std::string node("Node");
      const std::string edge("Edge");
      const std::string face("Face");
      const std::string volume("Volume");
      const std::string facevertex("FaceVertex");
    
      allNeumann.emplace_back();
      nonLinearNeumannBC& neu = allNeumann.back();
    
      if(onwhat==node){
        neu.g = new elementGroup (0, numphys);
        neu.onWhat=nonLinearBoundaryCondition::ON_VERTEX;
      }
      else if(onwhat==edge){
        neu.g = new elementGroup (1, numphys);
        neu.onWhat=nonLinearBoundaryCondition::ON_EDGE;
      }
      else if(onwhat==face){
        neu.g = new elementGroup (2, numphys);
        neu.onWhat=nonLinearBoundaryCondition::ON_FACE;
      }
      else if(onwhat==facevertex)
      {
        neu.g = new elementGroup(2,numphys);
        neu.onWhat=nonLinearBoundaryCondition::ON_FACE_VERTEX;
      }
      else if(onwhat==volume){
        neu.g = new elementGroup (3, numphys);
        neu.onWhat=nonLinearBoundaryCondition::ON_VOLUME;
      }
      else  {
        Msg::Error("Impossible to prescribe a force on a %s\n",onwhat.c_str());
        if (onwhat == "Pressure"){
          Msg::Error("pressureOnPhysicalGroupBC should be used instead of forceBC when applying pressure on a BC \n Applying pressure by this function has been removed");
        }
        else if (onwhat == "Flux"){
          Msg::Error("scalarFluxBC should be used instead of forceBC when applying scalar flux on a BC \n Applying scalar flux by this function has been removed");
        }
      };
    
      neu._NeumannBCType = nonLinearNeumannBC::FORCE;
      neu._f= fct;
      neu._tag=numphys;
      neu._comp=comp;
    };
    
    void nonLinearMechSolver::scalarFluxBC(std::string onwhat, const int numphys, const int comp, const double val){
      simpleFunctionTime<double> *fct = new simpleFunctionTime<double>(val,true,0.);
      this->scalarFluxBC(onwhat,numphys,comp,fct);
    };
    void nonLinearMechSolver::scalarFluxBC(std::string onwhat, const int numphys, const int comp, simpleFunctionTime<double> *fct){
      const std::string edge("Edge");
      const std::string face("Face");
    
      allNeumann.emplace_back();
      nonLinearNeumannBC& neu = allNeumann.back();
      if(onwhat==edge){
        neu.g = new elementGroup (1, numphys);
        neu.onWhat=nonLinearBoundaryCondition::ON_EDGE;
      }
      else if(onwhat==face){
        neu.g = new elementGroup (2, numphys);
        neu.onWhat=nonLinearBoundaryCondition::ON_FACE;
      }
      else  Msg::Error("Impossible to prescribe a scalar flux on a %s\n",onwhat.c_str());
    
      neu._NeumannBCType = nonLinearNeumannBC::SCALAR_FLUX;
      neu._f= fct;
      neu._tag=numphys;
      neu._comp=comp;
    };
    
    void nonLinearMechSolver::independentForceBC(std::string onwhat, const int numphys, const int comp, const double val){
      allNeumann.emplace_back();
      nonLinearNeumannBC& neu = allNeumann.back();
      const std::string node("Node");
      const std::string edge("Edge");
      const std::string face("Face");
      const std::string volume("Volume");
      if(onwhat==node){
        neu.g = new elementGroup (0, numphys);
        neu.onWhat=nonLinearBoundaryCondition::ON_VERTEX;
      }
      else if(onwhat==edge){
        neu.g = new elementGroup (1, numphys);
        neu.onWhat=nonLinearBoundaryCondition::ON_EDGE;
      }
      else if(onwhat==face){
        neu.g = new elementGroup (2, numphys);
        neu.onWhat=nonLinearBoundaryCondition::ON_FACE;
      }
      else if(onwhat==volume){
        neu.g = new elementGroup (3, numphys);
        neu.onWhat=nonLinearBoundaryCondition::ON_VOLUME;
      }
      else  Msg::Error("Impossible to prescribe a force on a %s\n",onwhat.c_str());
      neu._NeumannBCType = nonLinearNeumannBC::FORCE;
      neu._f= new simpleFunctionTimeWithConstant<double>(val,false);
      neu._tag=numphys;
      neu._comp = comp;
    }
    
    void nonLinearMechSolver::pressureOnPhysicalGroupBC(const int numphys, const double press, const double p0){
      Msg::Warning("applying pressure on surface");
      this->pressureOnPhysicalGroupBC("Face",numphys,press,p0);
    }
    
    void nonLinearMechSolver::pressureOnPhysicalGroupBC(const int numphys, simpleFunctionTime<double> *fct){
      Msg::Warning("applying pressure on surface");
      this->pressureOnPhysicalGroupBC("Face",numphys,fct);
    };
    
    void nonLinearMechSolver::pressureOnPhysicalGroupBC(std::string onwhat, const int numphys, const double pressDiff, const double p0){
      simpleFunctionTime<double> *fct = new simpleFunctionTimeWithConstant<double>(pressDiff,true,1.,p0);
      this->pressureOnPhysicalGroupBC(onwhat,numphys,fct);
    };
    
    
    void nonLinearMechSolver::pressureOnPhysicalGroupBC(std::string onwhat, const int numphys, simpleFunctionTime<double> *fct){
      allNeumann.emplace_back();
      nonLinearNeumannBC& neu = allNeumann.back();
    
      const std::string edge("Edge");
      const std::string face("Face");
      if (onwhat == face){
        neu.g = new elementGroup(2,numphys);
        neu.onWhat = nonLinearBoundaryCondition::ON_FACE;
      }
      else if (onwhat == edge){
        neu.g = new elementGroup(1,numphys);
        neu.onWhat = nonLinearBoundaryCondition::ON_EDGE;
      }
      else{
        Msg::Error("Impossible to prescribe a pressure on a %s\n",onwhat.c_str());
      }
      neu._NeumannBCType = nonLinearNeumannBC::PRESSURE;
      neu._f = fct;
      neu._tag=numphys;
    };
    
    void nonLinearMechSolver::blastPressureBC(std::string onwhat, const int numphys,const double p0,const double p1, const double plexp,
                                           const double t0, const double t1){
      simpleFunctionTime<double> *fct = new powerDecreasingFunctionTime(p0,p1,plexp,t0,t1,_timeManager->getEndTime());
      this->pressureOnPhysicalGroupBC(onwhat,numphys,fct);
    };
    
    
    void nonLinearMechSolver::blastPressureBC(const int numphys,const double p0,const double p1, const double plexp,
                                           const double t0, const double t1){
      Msg::Warning("applying pressure on surface");
      this->blastPressureBC("Face",numphys,p0,p1,plexp,t0,t1);
    };
    
    void nonLinearMechSolver::archivingInternalForceOnPhysicalGroup(const std::string onwhat, const int numphys,const int nstep)
    {
      static std::string node("Node");
      static std::string edge("Edge");
      static std::string face("Face");
      static std::string volu("Volume");
      int dim=0;
      if(onwhat == node )
        dim = 0;
      else if(onwhat == edge)
        dim = 1;
      else if(onwhat == face)
        dim = 2;
      else if(onwhat == volu)
        dim = 3;
      int ns = nstep;
      if (nstep <1) ns = 1;
      vafIntenal.emplace_back(numphys,dim,ns);
    }
    
    void nonLinearMechSolver::archivingForceOnPhysicalGroup(const std::string onwhat, const int numphys, const int comp,const int nstep){
      // get the node of the edge
      static std::string node("Node");
      static std::string edge("Edge");
      static std::string face("Face");
      static std::string volu("Volume");
      int dim=0;
      if(onwhat == node )
        dim = 0;
      else if(onwhat == edge)
        dim = 1;
      else if(onwhat == face)
        dim = 2;
      else if(onwhat == volu)
        dim = 3;
    
      // check if
      bool found = false;
      for (int i=0; i< vaf.size(); i++)
      {
        if ((vaf[i].numphys == numphys) and (vaf[i].comp == comp))
        {
          found = true;
          break;
        }
      }
      if (!found)
      {
        vaf.emplace_back(numphys,dim,comp,nstep);
      }
    }
    
    void nonLinearMechSolver::archivingRigidContactForce(const int numphys, const int comp,const int nstep){
      // check if
      bool found = false;
      for (int i=0; i< vaf.size(); i++)
      {
        if ((vaf[i].numphys == numphys) and (vaf[i].comp == comp))
        {
          found = true;
          break;
        }
      }
    
      if (!found)
      {
        vaf.emplace_back(numphys,0,comp,nstep);
      }
    }
    
    void nonLinearMechSolver::initArchiveReactionForceOnFixedPhysical(archiveForce& aforce){
      if(_previousInit) {
        aforce.vdof.clear();
      };
      std::vector<MVertex*> vv;
      pModel->getMeshVerticesForPhysicalGroup(aforce.dim,aforce.numphys,vv);
    
      if(vv.size() > 0) {
        elementGroup grForce(aforce.dim,aforce.numphys);
        for(int idom = 0; idom < _mpiUserDom;++idom){ // Do not consider Ghost domain when archiving forces
          partDomain *dom = domainVector[idom];
          
          if (dom->getFunctionSpaceType() == functionSpaceType::Hierarchical)
          {
            HierarchicalFunctionSpace *hasp = dynamic_cast<HierarchicalFunctionSpace*>(dom->getFunctionSpace());
            std::vector<int> forcecomp(1,aforce.comp);
            std::vector<Dof> R;
            for (elementGroup::elementContainer::const_iterator iteF = grForce.begin(); iteF != grForce.end(); iteF++)
            {
              MElement* eleF = iteF->second;
              for(elementGroup::elementContainer::const_iterator ite = dom->element_begin(); ite!=dom->element_end(); ++ite)
              {
                MElement *ele = ite->second;
                if (checkInterfaceElementBelongToBulkElement(eleF,ele))
                {
                  //hasp->getKeysOnLowerDimensionPart(ele,eleF,forcecomp,R);
                  for (int iev=0; iev< eleF->getNumVertices(); iev++)
                  {
                    hasp->getKeysOnVertex(ele,eleF->getVertex(iev),forcecomp,R);
                  }
                  break;
                }
              }
              
              
            }
            for (int ir=0; ir< R.size(); ir++)
            {
              aforce.vdof.insert(R[ir]);
            }
          }
          else
          {
            FunctionSpaceBase *sp = dom->getFunctionSpace();
            std::vector<Dof> R;
            for(elementGroup::elementContainer::const_iterator ite = dom->element_begin(); ite!=dom->element_end(); ++ite){
              MElement *ele = ite->second;
              int nbvertex = ele->getNumVertices();
              sp->getKeys(ele,R);
              for(std::vector<MVertex*>::iterator it=vv.begin(); it!=vv.end(); ++it){
                for(int j=0;j<nbvertex; j++){
                  if(ele->getVertex(j) == *it){
                    aforce.vdof.insert(R[j+nbvertex*aforce.comp]);
                    break;
                  }
                }
              }
              R.clear();
            }
          }
        }
      }
    };
    
    void nonLinearMechSolver::initArchiveForce(){
     #if defined(HAVE_MPI)
      std::vector<int> has_this_contact(Msg::GetCommSize(),0);
     #endif // HAVE_MPI
      for(std::vector<archiveForce>::iterator itf=vaf.begin(); itf!=vaf.end(); ++itf){
        archiveForce &af = *itf;
        if(af.vdof.size()==0){// otherwise it has already be initialized before in a previous computation
          std::vector<MVertex*> vv;
          pModel->getMeshVerticesForPhysicalGroup(af.dim,af.numphys,vv);
          // loop on domain (to find the domain where the force is applied)
          if(vv.size() > 0) 
          {// otherwise rigid contact force
            elementGroup grForce(af.dim,af.numphys);
            for(int idom = 0; idom < _mpiUserDom;++idom){ // Do not consider Ghost domain when archiving forces
              partDomain *dom = domainVector[idom];
              if (dom->getFunctionSpaceType() == functionSpaceType::Hierarchical)
              {
                HierarchicalFunctionSpace *hasp = dynamic_cast<HierarchicalFunctionSpace*>(dom->getFunctionSpace());
                std::vector<int> forcecomp(1,af.comp);
                std::vector<Dof> R;
                for (elementGroup::elementContainer::const_iterator iteF = grForce.begin(); iteF != grForce.end(); iteF++)
                {
                  MElement* eleF = iteF->second;
                  
                  for(elementGroup::elementContainer::const_iterator ite = dom->element_begin(); ite!=dom->element_end(); ++ite)
                  {
                    MElement *ele = ite->second;
                    if (checkInterfaceElementBelongToBulkElement(eleF,ele))
                    {
                      //hasp->getKeysOnLowerDimensionPart(ele,eleF,forcecomp,R);
                      for (int iev=0; iev< eleF->getNumVertices(); iev++)
                      {
                        hasp->getKeysOnVertex(ele,eleF->getVertex(iev),forcecomp,R);
                      }
                      break;
                    }
                  }
                }
                for (int ir=0; ir< R.size(); ir++)
                {
                  af.vdof.insert(R[ir]);
                }
              }
              else
              {
                FunctionSpaceBase *sp = dom->getFunctionSpace();
                std::vector<Dof> R;
                for(elementGroup::elementContainer::const_iterator ite = dom->element_begin(); ite!=dom->element_end(); ++ite){
                  MElement *ele = ite->second;
                  int nbvertex = ele->getNumVertices();
                  sp->getKeys(ele,R);
                  for(std::vector<MVertex*>::iterator it=vv.begin(); it!=vv.end(); ++it){
                    for(int j=0;j<nbvertex; j++){
                      if(ele->getVertex(j) == *it){
                        af.vdof.insert(R[j+nbvertex*af.comp]);
                        break;
                      }
                    }
                  }
                  R.clear();
                }
              }
            }
          }
          else {
            for(nonLinearMechSolver::contactContainer::const_iterator itc = _allContact.begin(); itc!=_allContact.end(); ++itc){
              contactDomain *cdom = *itc;
              if(cdom->getPhys() != af.numphys)
                continue;
              partDomain* domf=NULL;
              int physDom = cdom->getPhysSlave();
              for(std::vector<partDomain*>::iterator itdom = domainVector.begin(); itdom!=domainVector.end(); ++itdom){
                partDomain *dom = *itdom;
                if(dom->getPhysical() == physDom){
                  domf = dom;
                  break;
                }
              }
            #if defined(HAVE_MPI)
              if(false) // domf == NULL)
              {
                for(std::vector<partDomain*>::iterator itdom = _ghostDomainMPI.begin(); itdom!=_ghostDomainMPI.end(); ++itdom)
                {
                  partDomain *dom = *itdom;
                  if(dom->getPhysical() == physDom)
                  {
                    domf = dom;
                    break;
                  }
                }
              }
            #endif // HAVE_MPI
              if(domf==NULL)
                continue;
    
              std::vector<Dof> R;
              (const_cast<rigidContactSpaceBase*>(static_cast<const rigidContactSpaceBase*>(cdom->getSpace())))->getKeysOfGravityCenter(R);
    
              FilterDof *fdof = domf->createFilterDof(af.comp);
              for(int j=0;j<R.size(); j++) {
                if(fdof->operator()(R[j])){
                  af.vdof.insert(R[j]);
                  break;   // only 1 dof allow...
                }
              }
              delete fdof;
            }
          #if defined(HAVE_MPI)
            if(!af.vdof.empty()){
               has_this_contact[Msg::GetCommRank()] = 1;
            }
            MPI_Allreduce(MPI_IN_PLACE,has_this_contact.data(),Msg::GetCommSize(),MPI_INT,MPI_SUM,MPI_COMM_WORLD);
            int root_rank = 0;
            for(; root_rank< Msg::GetCommSize();++root_rank)
            {
                if(has_this_contact[root_rank]==1)
                    break;
            }
            if(root_rank == Msg::GetCommSize())
                Msg::Error("cannot find a contact on any of the partition...");
            af.contact_root = root_rank;
            std::fill(has_this_contact.begin(),has_this_contact.end(),0);
          #endif // HAVE_MPI
          }
        }
        if(!af.vdof.empty() && (af.contact_root == -1 || af.contact_root==Msg::GetCommRank()))
          af.openFile(getFileSavingPrefix());
      }
    }
    
    void nonLinearMechSolver::initAverageCluster()
    {
      if (_clusterData.size() > 0)
      {
        Msg::Info("init initAverageCluster");
        // initialize cluster data
        for (std::list<clustersData>::iterator itclData = _clusterData.begin(); itclData != _clusterData.end(); itclData++)
        {
          clustersData& clData = *itclData;
          if (clData.clusterIndexMap.size() ==0)
          {
            Msg::Error("clusters data were not corrrectly loaded");
          }
          
          if (!clData.isInitialized && clData.clusterIndexMap.size() > 0)
          {
            clData.isInitialized = true;
            for (int i=0; i< domainVector.size(); i++)
            {
              IntPt* GP;
              partDomain* dom = domainVector[i];
              for (elementGroup::elementContainer::const_iterator ite = dom->element_begin(); ite!= dom->element_end(); ite++)
              {
                MElement* ele = ite->second;
                int npts = dom->getBulkGaussIntegrationRule()->getIntPoints(ele,&GP);
                for (int j=0; j< npts; j++)
                {
                  double u = GP[j].pt[0]; double v = GP[j].pt[1]; double w = GP[j].pt[2];
                  double weight = GP[j].weight;
                  double detJ = ele->getJacobianDeterminant(u,v,w);
                  int type = numericalMaterialBase::createTypeWithTwoInts(ele->getNum(),j);
                  clData.weightMap[type] = weight*detJ;
                }
              }
            }
            //
            for (std::map<int,int>::const_iterator itm=clData.clusterIndexMap.begin(); itm != clData.clusterIndexMap.end(); itm++)
            {
              // first- eleNum + gpNum
              // second- clusterIndex
              std::map<int,double>& cluster_i = clData.clusterMap[itm->second];
              // in cluster_i
              // key is eleNum + gpNum
              // value is weight
              cluster_i[itm->first] = clData.weightMap[itm->first];
            }
          }
        }
        
        std::list<AvergageCluster> averageClustersFromOptions(_averageClusters.begin(),_averageClusters.end());
        _averageClusters.clear();
        for (std::list<clustersData>::const_iterator itclData = _clusterData.begin(); itclData != _clusterData.end(); itclData++)
        {
          const clustersData& clusterData = *itclData;
          if (clusterData.isInitialized)
          {
            // creat save file for each cluster
            for (std::list<AvergageCluster>::const_iterator it = averageClustersFromOptions.begin(); it!= averageClustersFromOptions.end(); it++)
            {
              const AvergageCluster& opts = *it;
              _averageClusters.emplace_back(opts.ipVal,opts.nbArch);
              AvergageCluster& cur = _averageClusters.back();
              cur.clusterData = &clusterData;
              cur.openFile(getFileSavingPrefix());
            }
          }
        }
        Msg::Info("done initAverageCluster");
      }
    };
    
    void nonLinearMechSolver::averageClusterArchiving(const double curtime, const int numstep, const bool forceSave)
    {
      for (std::list<AvergageCluster>::iterator it = _averageClusters.begin(); it != _averageClusters.end(); it++)
      {
        AvergageCluster& ac = *it;
        if (numstep > ac.lastSaveStep)
        {
          if (numstep%ac.nbArch==0 or forceSave)
          {
            ac.lastSaveStep = numstep;
            std::vector<double> val;
            ac.getAverageValue(getIPField(),val);
            if (ac.fp == NULL) ac.openFile(getFileSavingPrefix());
            fprintf(ac.fp,"%e",curtime);
            for (int i=0; i< val.size(); i++)
            {
              fprintf(ac.fp,";%e",val[i]);
            }
            fprintf(ac.fp,"\n");
            fflush(ac.fp);
          }
        }
      }
    }
    
    
    void nonLinearMechSolver::initPathFollowingArchiving()
    {
      if (_pathFollowing)
      {
        #if defined(HAVE_MPI)
        if (Msg::GetCommRank() == 0)
        #endif // HAVE_MPI
        {
          std::string filename = getFileSavingPrefix()+"pathResult_Time_ControlParameter_StateParameter.csv";
          FILE* file = fopen(filename.c_str(),"w");
          fclose(file);
        }
      }
    }
    
    void nonLinearMechSolver::pathFollowingArchiving(const double curtime, const int numstep){
      if (_pathFollowing){
        std::string name = "A";
        linearSystem<double>* lsys = pAssembler->getLinearSystem(name);
        nonLinearSystem<double>* nsys = dynamic_cast<nonLinearSystem<double>*>(lsys);
        pathFollowingSystem<double>* psys = dynamic_cast<pathFollowingSystem<double>*>(lsys);
        double control = psys->getControlParameter();
        double state = psys->getStateParameter();
    
        #if defined(HAVE_MPI)
        if (Msg::GetCommRank() == 0)
        #endif // HAVE_MPI
        {
          std::string filename = getFileSavingPrefix()+"pathResult_Time_ControlParameter_StateParameter.csv";
          FILE* file = fopen(filename.c_str(),"a");
          fprintf(file,"%e; %e; %e; \n",curtime,control,state);
          fclose(file);
        }
      }
    };
    
    void nonLinearMechSolver::archivingNodeDisplacement(const int numphys, const int comp,const int nstep){
      this->archivingNode(numphys,comp,nonLinearBoundaryCondition::position,nstep);
    }
    
    void nonLinearMechSolver::archivingNodeDisplacementOnControlNode(const int pos, const int comp,const int nstep){
      anoded.push_back(unknownField::archiveNode(pos,0,comp,nonLinearBoundaryCondition::position,nstep));
    }
    
    void nonLinearMechSolver::archivingMeshVertexDisplacement(const int num, const int comp,const int nstep){
      // no distinction between cG/dG and full Dg formulation. class Displacement Field manage it
      MVertex* v = NULL;
      v = pModel->getMeshVertexByTag(num);
    
      nonLinearBoundaryCondition::whichCondition wc = nonLinearBoundaryCondition::position;
    
      if(v != NULL){
        anoded.push_back(unknownField::archiveNode(0,v->getNum(),comp,wc,nstep));
      }
      else{
        Msg::Warning("No mesh vertex %d So it is impossible to archive nodal displacement",num);
      }
    
    };
    
    void nonLinearMechSolver::archivingVolumeIntegralValue(const int num, const int nstep)
    {
      _dataVolumeIntegral.emplace_back(IntegralVolume::VolumeIntegral,num,-1,nstep);
    };
    
    void nonLinearMechSolver::archivingVolumeIntegralValueOnPhysical(const int phy, const int num, const int nstep)
    {
      _dataVolumeIntegral.emplace_back(IntegralVolume::VolumeIntegral,num,phy,nstep);
    };
    
    void nonLinearMechSolver::archivingAverageValue(const int num, const int nstep){
      _dataVolumeIntegral.emplace_back(IntegralVolume::VolumeAverage,num,-1,nstep);
    };
    
    void nonLinearMechSolver::archivingAverageValueOnPhysical(const int phy, const int num, const int nstep){
      _dataVolumeIntegral.emplace_back(IntegralVolume::VolumeAverage,num,phy,nstep);
    };
    
    void nonLinearMechSolver::archivingAverageValueActiveDissipation(const int num, const int nstep){
      _dataVolumeIntegral.emplace_back(IntegralVolume::ActiveDissipationAverage,num,-1,nstep);
    };
    void nonLinearMechSolver::archivingIncrementBasedAverageValueActiveDissipation(const int num, const int nstep){
      _dataVolumeIntegral.emplace_back(IntegralVolume::ActiveDissipationIncrementAverage,num,-1,nstep);
    };
    
    
    
    void nonLinearMechSolver::loadClustersDataFile(const std::string fileName)
    {
      _clusterData.emplace_back();
      clustersData& ob = _clusterData.back();
      ob.loadFromFile(fileName);
    };
    
    void nonLinearMechSolver::archivingAverageValueOverClusters(const int num, const int nstep)
    {
      _averageClusters.emplace_back(num,nstep);
    };
    
    
    void nonLinearMechSolver::IPVolumeIntegralArchiving(const double curtime, const int numstep, const bool forceSave)
    {
      for (std::list<IntegralVolume>::iterator it = _dataVolumeIntegral.begin(); it != _dataVolumeIntegral.end(); it++)
      {
        IntegralVolume& iv = *it;
        if (numstep > iv.lastSaveStep)
        {
          if (numstep%iv.nbArch==0 or forceSave)
          {
            iv.lastSaveStep = numstep;
            double& val= iv.value;
            if (iv.integType == IntegralVolume::VolumeIntegral)
            {
              if (iv.physical > 0)
              {
                this->getTotalIntegralByVolumeIntegralOnPhysical(iv.physical,iv.ipVal,val);
              }
              else
              {
                this->getTotalIntegralByVolumeIntegral(iv.ipVal,val);
              }
            }
            else if (iv.integType == IntegralVolume::VolumeAverage)
            {
              if (iv.physical > 0)
              {
                this->getHomogenizedPropertyByVolumeIntegralOnPhysical(iv.physical,iv.ipVal,val);
              }
              else
              {
                this->getHomogenizedPropertyByVolumeIntegral(iv.ipVal,val);
              }
            }
            else if (iv.integType == IntegralVolume::ActiveDissipationAverage)
            {
              this->getHomogenizedPropertyByVolumeIntegralOnActiveDissipationDomain(iv.ipVal,val,IPStateBase::current);
            }
            else if (iv.integType == IntegralVolume::ActiveDissipationIncrementAverage)
            {
              double incrementVal = 0.;
              this->getHomogenizedIncrementalPropertyByVolumeIntegralOnActiveDissipationDomain(iv.ipVal,incrementVal);
              val += incrementVal;
            }
            else
            {
              Msg::Error("integType has not been defined");
            }
            if (iv.fp == NULL) iv.openFile(getFileSavingPrefix());
            fprintf(iv.fp,"%.16g;%.16g\n",curtime,val);
            fflush(iv.fp);
          }
        }
      }
    }
    
    void nonLinearMechSolver::IPDataOnPhysicalArchiving(const double curtime, const int numstep, const bool forceSave)
    {
      for (std::list<IPDataOnPhysical>::iterator idt = _dataOnPhysical.begin(); idt != _dataOnPhysical.end(); idt++)
      {
        IPDataOnPhysical& ipdata = *idt;
        int dim = ipdata.dim;
        int phys = ipdata.physical;
        int ipval = ipdata.ipValue;
        
        // file name
        std::string savefname = getFileSavingPrefix()+"IPDdata_"+ IPField::ToString(ipval)+ "_OnPhysical_"+int2str(phys);
        #if defined(HAVE_MPI)
        if ( getNumRanks() > 1){
          savefname += "_part"+int2str(Msg::GetCommRank());
        }
        #endif //HAVE_MPIim
        if (ipdata.oneFileForAllStep)
        {
          savefname += ".csv";
        }
        else
        {
          savefname += "_step"+int2str(numstep)+".csv";
        }
    
    
        if (!ipdata.isInitialized)
        {
          FILE* savefile=NULL;
          if (ipdata.oneFileForAllStep)
          {
            savefile = fopen(savefname.c_str(),"w");
            fprintf(savefile,"Time");
          }
          
          ipdata.isInitialized = true;
          elementGroup g;
          #if defined(HAVE_MPI)
          elementFilterMPI elemfil(NULL);
          g.addPhysical(dim,phys,elemfil);
          #else
          g.addPhysical(dim,phys);
          #endif // HAVE_MPI
    
    
          std::string fname = getFileSavingPrefix()+"coordinate_"+ IPField::ToString(ipval)+ "_OnPhysical_"+int2str(phys);
          #if defined(HAVE_MPI)
          if (this->getNumRanks() > 1){
            fname += "_part"+int2str(Msg::GetCommRank());
          }
          #endif //HAVE_MPIim
          fname += ".csv";
    
    
          FILE* coordinateFile = fopen(fname.c_str(),"w");
          ipdata.element.resize(g.size());
          int index= 0;
          SPoint3 p;
          for (elementGroup::elementContainer::const_iterator it = g.begin(); it!= g.end(); it++)
          {
            MElement* ele = it->second;
            ipdata.element[index] = ele->getNum(); index++;
            for (int idom = 0; idom < domainVector.size(); idom++){
              partDomain* dom = domainVector[idom];
              if (dom->g_find(ele)){
                IntPt* GP;
                int npts = dom->getBulkGaussIntegrationRule()->getIntPoints(ele,&GP);
                for (int j=0; j< npts; j++){
                  double u = GP[j].pt[0]; double v = GP[j].pt[1]; double w = GP[j].pt[2];
                  double weight = GP[j].weight;
                  double detJ = ele->getJacobianDeterminant(u,v,w);
                  ele->pnt(u,v,w,p);
                  fprintf(coordinateFile,"%ld; %e; %e; %e; %d; %e\n",ele->getNum(),p.x(),p.y(),p.z(),j,weight*detJ);
                  
                  bool willSave = false;
                  if (ipdata.ipIndex == -1 or ipdata.ipIndex==j)
                  {
                    willSave = true;
                  } 
                  if (willSave)
                  {                
                    if (ipdata.oneFileForAllStep)
                    {
                      fprintf(savefile,";%s_P%dE%ldG%d",IPField::ToString(ipval).c_str(), phys,ele->getNum(),j);
                    }   
                  }
                }
                break;
              }
            }
          }
          fclose(coordinateFile);
          if (ipdata.oneFileForAllStep)
          {
            fprintf(savefile,"\n");
            fclose(savefile);
          }
        }
    
        if (numstep > ipdata.lastSaveStep)
        {
          if (numstep%ipdata.nbArch==0 or forceSave)
          {
            ipdata.lastSaveStep = numstep;
            //
    
            FILE* file =NULL;
            if (ipdata.oneFileForAllStep)
            {
              file = fopen(savefname.c_str(),"a");
              // add time
              fprintf(file,"%e",curtime);
              
              for (int i=0; i< ipdata.element.size(); i++){
                int elnum = ipdata.element[i];
                const std::vector<IPStateBase*>& ips = (*(_ipf->getAips()->getIPstate(elnum)));
                for (int j=0; j< ips.size(); j++)
                {
                  bool willSave = false;
                  if (ipdata.ipIndex == -1 or ipdata.ipIndex==j)
                  {
                    willSave = true;
                  } 
                  if (willSave)
                  {
                    double val = ips[j]->getState(IPStateBase::current)->get(IPField::Output(ipval));
                    fprintf(file,";%e",val);
                  }
                }
              }
              fprintf(file,"\n");
              fclose(file);
            }
            else
            {
              file = fopen(savefname.c_str(),"w");
              for (int i=0; i< ipdata.element.size(); i++){
                int elnum = ipdata.element[i];
                const std::vector<IPStateBase*>& ips = (*(_ipf->getAips()->getIPstate(elnum)));
                for (int j=0; j< ips.size(); j++)
                {
                  bool willSave = false;
                  if (ipdata.ipIndex == -1 or ipdata.ipIndex==j)
                  {
                    willSave= true;
                  } 
                  if (willSave)
                  {
                    double val = ips[j]->getState(IPStateBase::current)->get(IPField::Output(ipval));
                    fprintf(file,"%e\n",val);
                  }
                }
              }
              fclose(file);
            }
          }
        }
      }
    };
    
    void nonLinearMechSolver::archivingIPDataOnPhysical(const int ipval, const int phys, const int dim, const int nstep, bool oneFileForAllStep, int ipLoc){
      if (oneFileForAllStep) 
      {
        Msg::Info("archivingIPDataOnPhysical all steps are stored in one file");
      }
      if (ipLoc >= 0)
      {
        Msg::Info("archivingIPDataOnPhysical ip %d is stored",ipLoc);
      }
      else if (ipLoc == -1)
      {
        Msg::Info("archivingIPDataOnPhysical all IPs are stored");
      }
      else
      {
        Msg::Error("archivingIPDataOnPhysical IP number %s does not exist",ipLoc);
      }
      _dataOnPhysical.emplace_back(ipval,phys,dim,nstep,oneFileForAllStep,ipLoc);
    };
    
    void nonLinearMechSolver::archivingNodeDisplacementOnPhysical(const int dim, const int physical, const int comp,const int nstep){
      nonLinearBoundaryCondition::whichCondition wc = nonLinearBoundaryCondition::position;
      archivingNode(physical,dim,comp,wc,nstep);
    };
    
    void nonLinearMechSolver::archivingNodeVelocity(const int numphys, const int comp,const int nstep){
      this->archivingNode(numphys,comp,nonLinearBoundaryCondition::velocity,nstep);
    }
    
    void nonLinearMechSolver::archivingNodeAcceleration(const int numphys, const int comp, const int nstep){
      this->archivingNode(numphys,comp,nonLinearBoundaryCondition::acceleration,nstep);
    }
    void nonLinearMechSolver::constraintBC(std::string onwhat, const int numphys, const int comp)
    {
      allConstraint.emplace_back();
      nonLinearConstraintBC& uc = allConstraint.back();
      const std::string node("Node");
      const std::string edge("Edge");
      const std::string face("Face");
      const std::string volume("Volume");
      if(onwhat==node){
        uc.g = new elementGroup (0, numphys);
        uc.onWhat=nonLinearBoundaryCondition::ON_VERTEX;
      }
      else if(onwhat==edge){
        uc.g = new elementGroup (1, numphys);
        uc.onWhat=nonLinearBoundaryCondition::ON_EDGE;
      }
      else if(onwhat==face){
        uc.g = new elementGroup (2, numphys);
        uc.onWhat=nonLinearBoundaryCondition::ON_FACE;
      }
      else if(onwhat==volume){
        uc.g = new elementGroup(3, numphys);
        uc.onWhat=nonLinearBoundaryCondition::ON_VOLUME;
      }
      else Msg::Error("Impossible to prescribe a constraint on a %s in constraintBC\n",onwhat.c_str());
      uc._comp=comp;
      uc._tag=numphys;
    
    }
    
    void nonLinearMechSolver::createTreeForBC(const std::string &PhysicalCurve, const std::string &PhysicalSurface, const std::string &PhysicalVolume, const int OutputPhysical)
    {
      #if defined(HAVE_PLUGINS)
      try
      {
        PluginManager::instance()->setPluginOption("SpanningTree", "PhysicalCurves", PhysicalCurve);
        PluginManager::instance()->setPluginOption("SpanningTree", "PhysicalSurfaces", PhysicalSurface);
        PluginManager::instance()->setPluginOption("SpanningTree", "PhysicalVolumes", PhysicalVolume);
        PluginManager::instance()->setPluginOption("SpanningTree", "OutputPhysical", OutputPhysical);
        PluginManager::instance()->action("SpanningTree", "Run", 0);
      }
      catch(...)
      {
        Msg::Error("Failure in running the GMSH plugin: SpanningTree in nonLinearMechSolver::createTreeForBC()");
        throw -1;
      }  
      #else
      Msg::Error("HAVE_PLUGINS must be activated");
      #endif //HAVE_PLUGINS
    }
    
    void nonLinearMechSolver::linearConstraintBCBetweenTwoGroups(std::string onwhat, const int physMater, const int physSlave, const int comp, const double slFactor,
                                                        const linearCombinationOfVertices* rh,
                                                        const vertexGroupOperation* op,
                                                        const setInt* exclVertices)
    {
      Msg::Info("linear constraint BC comp %d on physical %d physical %d",comp,physMater,physSlave);
      allLinearConstraintBC.emplace_back();
      
      nonLinearLinearConstraintBCBetweenTwoGroups& bc = allLinearConstraintBC.back();
      
      bc.phyMaster = physMater;
      bc.phySlave = physSlave;
      
      if(onwhat=="Node"){
        bc.gMaster = new elementGroup (0, physMater);
        if (physSlave > 0)
          bc.gSlave = new elementGroup (0, physSlave);
        else
          bc.gSlave = new elementGroup ();
        bc.onWhat=nonLinearBoundaryCondition::ON_VERTEX;
      }
      else if(onwhat=="Edge"){
        bc.gMaster = new elementGroup (1, physMater);
        if (physSlave > 0)
          bc.gSlave = new elementGroup (1, physSlave);
        else
          bc.gSlave = new elementGroup ();
        bc.onWhat=nonLinearBoundaryCondition::ON_EDGE;
      }
      else if(onwhat=="Face"){
        bc.gMaster = new elementGroup (2, physMater);
        if (physSlave > 0)
          bc.gSlave = new elementGroup (2, physSlave);
        else
          bc.gSlave = new elementGroup ();
        bc.onWhat=nonLinearBoundaryCondition::ON_FACE;
      }
      else if(onwhat=="Volume"){
        bc.gMaster = new elementGroup (3, physMater);
        if (physSlave > 0)
          bc.gSlave = new elementGroup (3, physSlave);
        else
          bc.gSlave = new elementGroup ();
        bc.onWhat=nonLinearBoundaryCondition::ON_VOLUME;
      }
      else Msg::Error("Impossible to prescribe a constraint on a %s in linearConstraintBCBetweenTwoGroups\n",onwhat.c_str());
      
      bc.comp = comp;
      if (rh !=NULL)
      {
        bc.rightHandSide = rh->clone();
      }
      else
      {
        Msg::Error("linearCombinationOfVertices must be set");
        Msg::Exit(0);
      }
      if (op!=NULL)
      {
        bc.vgOperation = op->clone();
      }
      else
      {
        bc.vgOperation = new vertexGroupOperation_trivial();
      }
      bc.slaveFactor = slFactor;
      if (exclVertices != NULL)
      {
        bc.excludedVertices.copyData(*exclVertices);
      }
    }
    
    void nonLinearMechSolver::linearConstraintBCBetweenTwoGroups(std::string onwhat, const int physMater, const int comp, 
                                                        const linearCombinationOfVertices* rh,
                                                        const setInt* exclVertices)
    {  
      linearConstraintBCBetweenTwoGroups(onwhat,physMater,0,comp,0.,rh,NULL,exclVertices);
    }
    
    void nonLinearMechSolver::addPeriodicEdgePhysicals(int edge)
    {
      _edgePeriodicPhysicals.push_back(edge);
      
    }
    void nonLinearMechSolver::addPeriodicNodePhysicals(int node)
    {
      _nodePeriodicPhysicals.push_back(node);
    }
      
    void nonLinearMechSolver::periodicBC(std::string onwhat, const int phys1, const int phys2, const int v1, const int v2, const int comp)
    {
      if (v1 > 0 && v2 > 0)
      {
        Msg::Info("Periodic BC comp %d on physical %d physical %d  versur vertex %d %d",comp,phys1,phys2,v1,v2);
      }
      else
      {
        Msg::Info("Displacement comp %d on physical %d is the same as on physical %d",comp,phys1,phys2);
      }
      
      const std::string node("Node");
      const std::string edge("Edge");
      const std::string face("Face");
      nonLinearBoundaryCondition::location loc;
      if(onwhat==node){
        loc=nonLinearBoundaryCondition::ON_VERTEX;
      }
      else if(onwhat==edge){
        loc=nonLinearBoundaryCondition::ON_EDGE;
      }
      else if(onwhat==face){
        loc=nonLinearBoundaryCondition::ON_FACE;
      }
      bool found = false;
      for (std::list<nonLinearPeriodicBCBetweenTwoGroups>::iterator pbctg=allPeriodic.begin(); pbctg!=allPeriodic.end(); pbctg++)
      {
        nonLinearPeriodicBCBetweenTwoGroups& bc = *pbctg;
        if ((bc.onWhat == loc) && (bc.phys1 == phys1) && (bc.phys2 == phys2) && (bc.physVer1 == v1) && (bc.physVer2 == v2))
        {
          bc.comp.push_back(comp);
          found = true;
          break;
        }
      }
      
      if (!found)
      {
        allPeriodic.emplace_back();
        nonLinearPeriodicBCBetweenTwoGroups& pbc = allPeriodic.back();
        if(onwhat==node){
          pbc.g1 = new elementGroup (0, phys1);
          pbc.g2 = new elementGroup (0, phys2);
          pbc.onWhat=nonLinearBoundaryCondition::ON_VERTEX;
        }
        else if(onwhat==edge){
          pbc.g1 = new elementGroup (1, phys1);
          pbc.g2 = new elementGroup (1, phys2);
          pbc.onWhat=nonLinearBoundaryCondition::ON_EDGE;
        }
        else if(onwhat==face){
          pbc.g1 = new elementGroup (2, phys1);
          pbc.g2 = new elementGroup (2, phys2);
          pbc.onWhat=nonLinearBoundaryCondition::ON_FACE;
        }
        else Msg::Error("Impossible to prescribe a constraint on a %s in periodicBC\n",onwhat.c_str());
        
        if (v1 > 0 && v2 > 0)
        {
          elementGroup grv1(0,v1);
          elementGroup grv2(0,v2);
          if (grv1.vsize() > 0 and grv2.vsize() > 0)
          {
            pbc.v1 = (grv1.vbegin()->second);
            pbc.v2 = (grv2.vbegin()->second);
          }
          else
          {
            Msg::Error("periodic root vertices have not been correctly defined");
            pbc.v1 = NULL;
            pbc.v2 = NULL;
          }
        }
        else
        {
          pbc.v1 = NULL;
          pbc.v2 = NULL;
        }
        pbc.comp.push_back(comp);
        pbc.phys1 = phys1;
        pbc.phys2 = phys2;
        pbc.physVer1 = v1;
        pbc.physVer2 = v2;
      }
    };
    
    void nonLinearMechSolver::averagePeriodicBC(std::string onwhat, const int phys1, const int phys2, const int v1, const int v2, const int comp)
    {
      if (v1 > 0 && v2 > 0)
      {
        Msg::Info("average periodic BC comp %d on physical %d physical %d  versur vertex %d %d",comp,phys1,phys2,v1,v2);
      }
      else
      {
        Msg::Info("average comp %d on physical %d is the same as on physical %d",comp,phys1,phys2);
      }
      const std::string edge("Edge");
      const std::string face("Face");
      nonLinearBoundaryCondition::location loc;
      if(onwhat==edge)
      {
        loc=nonLinearBoundaryCondition::ON_EDGE;
      }
      else if(onwhat==face)
      {
        loc=nonLinearBoundaryCondition::ON_FACE;
      }
      bool found = false;
      for (std::list<nonLinearAveragePeriodicBCBetweenTwoGroups>::iterator pbctg=allAveragePeriodic.begin(); pbctg!=allAveragePeriodic.end(); pbctg++)
      {
        nonLinearAveragePeriodicBCBetweenTwoGroups& bc = *pbctg; 
        if ((bc.onWhat == loc) && (bc.phys1 == phys1) && (bc.phys2 == phys2) && (bc.physVer1 == v1) && (bc.physVer2 == v2))
        {
          bc.comp.push_back(comp);
          found = true;
          break;
        }
      }
      
      if (!found)
      {
        allAveragePeriodic.emplace_back();
        nonLinearAveragePeriodicBCBetweenTwoGroups& pbc = allAveragePeriodic.back();
        if(onwhat==edge){
          pbc.g1 = new elementGroup (1, phys1);
          pbc.g2 = new elementGroup (1, phys2);
          pbc.onWhat=nonLinearBoundaryCondition::ON_EDGE;
        }
        else if(onwhat==face){
          pbc.g1 = new elementGroup (2, phys1);
          pbc.g2 = new elementGroup (2, phys2);
          pbc.onWhat=nonLinearBoundaryCondition::ON_FACE;
        }
        else Msg::Error("Impossible to prescribe an average PBC constraint on a %s\n",onwhat.c_str());
        
        if (v1 > 0 && v2 > 0)
        {
          elementGroup grv1(0,v1);
          elementGroup grv2(0,v2);
          if (grv1.vsize() > 0 and grv2.vsize() > 0){
            pbc.v1 = (grv1.vbegin()->second);
            pbc.v2 = (grv2.vbegin()->second);
          }
          else{
            Msg::Error("periodic root vertices have not been correctly defined");
            pbc.v1 = NULL;
            pbc.v2 = NULL;
          }
        }
        else
        {
          pbc.v1 = NULL;
          pbc.v2 = NULL;
        }
        pbc.comp.push_back(comp);
        pbc.phys1 = phys1;
        pbc.phys2 = phys2;
        pbc.physVer1 = v1;
        pbc.physVer2 = v2;
      }
    };
    
    void nonLinearMechSolver::sameDisplacementBC(std::string onwhat, const int phy, const int rootphy, const int comp, const double fac){
      Msg::Info("Displacement comp %d on physical %d is the same as on node %d",comp,phy,rootphy);
      allSameDisp.emplace_back();
      nonLinearSameDisplacementBC& bc = allSameDisp.back();
    
      bc.comp = comp;
      bc.tagRoot = rootphy;
      bc.gRoot = new elementGroup(0,rootphy);
      bc._tag = phy;
      bc.fac = fac;
    
      const std::string node("Node");
      const std::string edge("Edge");
      const std::string face("Face");
      const std::string volume("Volume");
    
      if(onwhat==node){
        bc.onWhat=nonLinearBoundaryCondition::ON_VERTEX;
        bc.g = new elementGroup(0,phy);
      }
      else if (onwhat == edge){
        bc.onWhat=nonLinearBoundaryCondition::ON_EDGE;
        bc.g = new elementGroup(1,phy);
      }
      else if (onwhat == face){
        bc.onWhat=nonLinearBoundaryCondition::ON_FACE;
        bc.g = new elementGroup(2,phy);
      }
      else if (onwhat == volume){
        bc.onWhat=nonLinearBoundaryCondition::ON_VOLUME;
        bc.g = new elementGroup(3,phy);
      }
      else{
        Msg::Error("Impossible to prescribe a constraint on a %s in sameDisplacementBC\n",onwhat.c_str());
      }
    }
    
    void nonLinearMechSolver::sameDisplacementBCBetweenTwoGroups(std::string onwhat, const int phy1, const int phy2, const int comp){
      periodicBC(onwhat,phy1,phy2,0,0,comp);
    };
    
    void nonLinearMechSolver::sameAverageDisplacementBCBetweenTwoGroups(std::string onwhat, const int phy1, const int phy2, const int comp)
    {
      averagePeriodicBC(onwhat,phy1,phy2,0,0,comp);
    }
    
    void nonLinearMechSolver::fixOnFace(std::string onwhat, const int phy, const double A, const double B, const double C, const double D){
      allFixAllFace.emplace_back(A,B,C,D);
      nonLinearFixOnFaceBC& bc = allFixAllFace.back();
      const std::string node("Node");
      const std::string edge("Edge");
      const std::string face("Face");
      const std::string volume("Volume");
    
      bc._tag = phy;
    
      if(onwhat==node){
        bc.onWhat=nonLinearBoundaryCondition::ON_VERTEX;
        bc.g = new elementGroup(0,phy);
      }
      else if (onwhat == edge){
        bc.onWhat=nonLinearBoundaryCondition::ON_EDGE;
        bc.g = new elementGroup(1,phy);
      }
      else if (onwhat == face){
        bc.onWhat=nonLinearBoundaryCondition::ON_FACE;
        bc.g = new elementGroup(2,phy);
      }
      else if (onwhat == volume){
        bc.onWhat=nonLinearBoundaryCondition::ON_VOLUME;
        bc.g = new elementGroup(3,phy);
      }
      else{
        Msg::Error("Impossible to prescribe a constraint on a %s in fixOnFace\n",onwhat.c_str());
      }
    
    
    };
    
    void nonLinearMechSolver::symetryBC(std::string onwhat, const int phy){
      allFixAllFace.emplace_back();
      nonLinearFixOnFaceBC& bc = allFixAllFace.back();
      const std::string face("Face"); // for 3D
    
      bc._tag = phy;
      if (onwhat == face){
        bc.onWhat=nonLinearBoundaryCondition::ON_FACE;
        bc.g = new elementGroup(2,phy);
        
        MElement* ele  = NULL;
        if (bc.g->size() == 0) 
          Msg::Error("physical %d has not element",phy);
        else
          ele = (bc.g->begin()->second);
    
        SPoint3 p1 = ele->getVertex(0)->point();
        SPoint3 p2 = ele->getVertex(1)->point();
        SVector3 u12(p2,p1);
        SPoint3 p3 = ele->getVertex(2)->point();
        SVector3 u13(p3,p1);
    
        SVector3 n = crossprod(u12,u13);
        if (n.norm() <= 0.) Msg::Info("zero normal to fixed face");
        n.normalize();
    
        bc.A = n(0);
        bc.B = n(1);
        bc.C = n(2);
        bc.D = -(n(0)*p1.x()+n(1)*p1.y()+n(2)*p1.z());
      }
      else{
        Msg::Error("Impossible to prescribe a constraint on a %s in symetryBC\n",onwhat.c_str());
      }
    };
    
    void nonLinearMechSolver::applySameDispBC(){
      for(std::list<nonLinearSameDisplacementBC>::const_iterator sdbc=allSameDisp.begin(); sdbc!=allSameDisp.end(); ++sdbc){
    		if (isDgDomain()){
          MElement* e = NULL;
          if (sdbc->gRoot->size() ==0)
          {
            Msg::Error("applySameDispBC has no gRoot");
            Msg::Exit(0);
          }
          else
            e = (sdbc->gRoot->begin()->second);
          MVertex* vroot = e->getVertex(0);
    
          MElement* eleRoot = NULL;
          FunctionSpaceBase* spaceRoot = NULL;
          int vpos = 0.;
    
          for (int i=0; i< domainVector.size(); i++){
            partDomain* dom = domainVector[i];
            for (elementGroup::elementContainer::const_iterator it = dom->element_begin(); it != dom->element_end(); it++){
              MElement* ele = it->second;
              for (int j=0; j< ele->getNumVertices(); j++){
                MVertex* vj = ele->getVertex(j);
                if (vj->getNum() == vroot->getNum()){
                   eleRoot = ele;
                   spaceRoot = dom->getFunctionSpace();
                   vpos = j;
                   Msg::Info("found root element %d for root node %d vpos = %d",eleRoot->getNum(),vroot->getNum(),vpos);
                   break;
                }
              }
              if (eleRoot != NULL) break;
            }
            if (eleRoot != NULL) break;
          }
    
          if (eleRoot == NULL) Msg::Error("root vertex is wrongly defined in nonLinearMechSolver::applySameDispBC");
          std::vector<Dof> RRoot;
          std::vector<Dof> dofRoot;
          spaceRoot->getKeys(eleRoot,RRoot);
          dofRoot.push_back(RRoot[vpos+eleRoot->getNumVertices()*sdbc->comp]);
    
          // fill domain
          std::set<Dof> others;
          for (elementGroup::elementContainer::const_iterator itg = sdbc->g->begin(); itg!=sdbc->g->end(); itg++){
            MElement* eleBound = itg->second;
            std::vector<int> vvBound(eleBound->getNumPrimaryVertices());
            for (int iv=0; iv < eleBound->getNumPrimaryVertices(); iv++){
              vvBound[iv] = eleBound->getVertex(iv)->getNum();
            }
    
            // find bulk element
    
            MElement* eleBulk = NULL;
            FunctionSpaceBase* spaceBulk = NULL;
    
            for (int i=0; i< domainVector.size(); i++){
              partDomain* dom = domainVector[i];
              for (elementGroup::elementContainer::const_iterator it = dom->element_begin(); it != dom->element_end(); it++){
                MElement* eleb = it->second;
                std::set<int> vvb;
                for (int iv=0; iv < eleb->getNumPrimaryVertices(); iv++){
                  vvb.insert(eleb->getVertex(iv)->getNum());
                }
    
                bool isFound = true;
                for (int iv=0; iv< vvBound.size(); iv++){
                  if (vvb.find(vvBound[iv]) == vvb.end()){
                    isFound = false;
                    break;
                  }
                }
    
                if (isFound){
                  eleBulk = eleb;
                  spaceBulk = dom->getFunctionSpace();
                  break;
                }
    
              }
              if (eleBulk != NULL) break;
            }
    
            if (eleBulk == NULL) Msg::Error("nonLinearSameDisplacementBC is wrongly defined nonLinearMechSolver::applySameDispBC");
            std::vector<Dof> R;
            spaceBulk->getKeys(eleBulk,R);
            int nbFFb = eleBulk->getNumShapeFunctions();
    
            std::vector<int> positionBulk;
            for (int iv=0; iv< eleBound->getNumVertices(); iv++){
              MVertex* vi = eleBound->getVertex(iv);
              //
              if (vi->getNum() != vroot->getNum()){
                for (int jv=0; jv < eleBulk->getNumVertices(); jv++){
                  MVertex* vj = eleBulk->getVertex(jv);
                  if (vi->getNum() == vj->getNum()){
                    positionBulk.push_back(jv);
                    break;
                  }
                }
              }
            }
    
            for (int k=0; k<positionBulk.size(); k++){
              Dof*  D = &R[positionBulk[k]+nbFFb*sdbc->comp];
              if (others.find(*D) == others.end()){
                others.insert(*D);
              }
            }
          }
    
          DofAffineConstraint<double> dof;
          dof.linear.push_back(std::pair<Dof,double>(dofRoot[0],sdbc->fac));
          dof.shift = 0.;
    
    
          for (std::set<Dof>::iterator itd = others.begin(); itd != others.end(); itd++){
    				if (!pAssembler->isConstrained(*itd) and !pAssembler->isFixed(*itd)){
            	pAssembler->setLinearConstraint(*itd,dof);
    				}
          };
    
    		}
        else{
          // because of CG
          FunctionSpaceBase* spb = domainVector[0]->getFunctionSpace();
          FilterDof* fiterDof = domainVector[0]->createFilterDof(sdbc->comp);
    
          // get Root Dof
          MElement* e = (sdbc->gRoot->begin()->second);
          MVertex* vroot = e->getVertex(0);
          std::vector<Dof> R, dofRoot;
          spb->getKeys(e,R);
          for (int idof=0; idof<R.size(); idof++){
            if (fiterDof->operator()(R[idof])){
              dofRoot.push_back(R[idof]);
              break;
            }
          }
    
          std::vector<Dof> others;
          // get constraint Dof
          for (elementGroup::vertexContainer::const_iterator it = sdbc->g->vbegin(); it!=sdbc->g->vend(); it++){
            MVertex* vv = it->second;
            if (vv->getNum() != vroot->getNum()){
              MPoint point(it->second);
              R.clear();
              spb->getKeys(&point,R);
              for (int idof=0; idof<R.size(); idof++){
                if (fiterDof->operator()(R[idof])){
                  others.push_back(R[idof]);
                  break;
                }
              }
            }
          }
    
          DofAffineConstraint<double> dof;
          dof.linear.push_back(std::pair<Dof,double>(dofRoot[0],sdbc->fac));
          dof.shift = 0.;
    
          for (int j =0; j<others.size(); j++){
    				if (!pAssembler->isConstrained(others[j]) and !pAssembler->isFixed(others[j])){
            	pAssembler->setLinearConstraint(others[j],dof);
    				}
          };
        };
      };
    
    };
    
    void nonLinearMechSolver::applyFixOnFace(){
      if (allFixAllFace.size() > 0){
        Msg::Info("begin applying fix  on face BC");
      }
      if (isDgDomain()){
        //for (const nonLinearFixOnFaceBC & fofbc : allFixAllFace){
        for(std::list<nonLinearFixOnFaceBC>::const_iterator fofbc=allFixAllFace.begin(); fofbc!=allFixAllFace.end(); fofbc++){
          double A = fofbc->A;
          double B = fofbc->B;
          double C = fofbc->C;
          double D = fofbc->D;
    
          for (elementGroup::elementContainer::const_iterator it = fofbc->g->begin();
                    it!= fofbc->g->end(); it++){
            MElement* ele = it->second;
            MElement* eleFound = NULL;
            partDomain* domFound = NULL;
            bool found = false;
    
            std::vector<MVertex*> vvMaster;
            ele->getVertices(vvMaster);
    
            for (int idom =0; idom< domainVector.size(); idom ++){
              partDomain* aldo = domainVector[idom];
              for (elementGroup::elementContainer::const_iterator itedom = aldo->element_begin(); itedom != aldo->element_end(); itedom++){
                MElement* receiveEle = itedom->second;
                int numFace = receiveEle->getNumFaces();
                for (int kk=0; kk<numFace; kk++){
                  std::vector<MVertex*> faceVer;
                  receiveEle->getFaceVertices(kk,faceVer);
                  if (ele->getType() == TYPE_TRI){
                    if ((vvMaster[0] == faceVer[0] and vvMaster[1] == faceVer[1] and vvMaster[2] == faceVer[2]) or
                        (vvMaster[0] == faceVer[0] and vvMaster[1] == faceVer[2] and vvMaster[2] == faceVer[1]) or
                        (vvMaster[0] == faceVer[1] and vvMaster[1] == faceVer[0] and vvMaster[2] == faceVer[2]) or
                        (vvMaster[0] == faceVer[1] and vvMaster[1] == faceVer[2] and vvMaster[2] == faceVer[0]) or
                        (vvMaster[0] == faceVer[2] and vvMaster[1] == faceVer[1] and vvMaster[2] == faceVer[0]) or
                        (vvMaster[0] == faceVer[2] and vvMaster[1] == faceVer[0] and vvMaster[2] == faceVer[1])){
                      found = true;
                      eleFound = receiveEle;
                      domFound = aldo;
                      break;
                    }
                  }
                  else if (ele->getType() == TYPE_QUA){
                    if ((vvMaster[0] == faceVer[0] and vvMaster[1] == faceVer[1] and vvMaster[2] == faceVer[2]) or
                            (vvMaster[0] == faceVer[1] and vvMaster[1] == faceVer[2] and vvMaster[2] == faceVer[3]) or
                            (vvMaster[0] == faceVer[2] and vvMaster[1] == faceVer[3] and vvMaster[2] == faceVer[0]) or
                            (vvMaster[0] == faceVer[3] and vvMaster[1] == faceVer[0] and vvMaster[2] == faceVer[1]) or
                            (vvMaster[0] == faceVer[0] and vvMaster[1] == faceVer[3] and vvMaster[2] == faceVer[2]) or
                            (vvMaster[0] == faceVer[3] and vvMaster[1] == faceVer[2] and vvMaster[2] == faceVer[1]) or
                            (vvMaster[0] == faceVer[2] and vvMaster[1] == faceVer[1] and vvMaster[2] == faceVer[0]) or
                            (vvMaster[0] == faceVer[1] and vvMaster[1] == faceVer[0] and vvMaster[2] == faceVer[3])){
    
                      found = true;
                      eleFound = receiveEle;
                      domFound = aldo;
                      break;
                    };
                  }
                  else{
                    Msg::Warning("Element type %d is not implemented",ele->getType());
                  }
                }
    
                if (found)  break;
              }
              if (found) break;
            };
    
            if (!found) Msg::Error("ele %d is not found",ele->getNum());
    
            int nbFF = eleFound->getNumShapeFunctions();
            std::vector<Dof> keys;
            domFound->getFunctionSpace()->getKeys(eleFound,keys);
    
            for (int iver = 0; iver < vvMaster.size(); iver++){
              MVertex* v = vvMaster[iver];
              int idex = -1;
              for (int ii=0; ii < eleFound->getNumVertices(); ii++){
                if (eleFound->getVertex(ii)->getNum() == v->getNum()){
                  idex = ii;
                  break;
                }
              }
              std::vector<Dof>  R;
              R.push_back(keys[idex]);
              R.push_back(keys[idex+nbFF]);
              R.push_back(keys[idex+2*nbFF]);
    
              DofAffineConstraint<double> dof;
              double val = -1.*(A*v->x()+B*v->y()+C*v->z()+D);
    
              if (fabs(A) > 1e-10 and !pAssembler->isFixed(R[0])){
                if (!pAssembler->isFixed(R[1]) or !pAssembler->isFixed(R[2])){
                  dof.shift = val/A;
                  dof.linear.push_back(std::pair<Dof,double>(R[1],-B/A));
                  dof.linear.push_back(std::pair<Dof,double>(R[2],-C/A));
                  pAssembler->setLinearConstraint(R[0],dof);
                }
    
              }
              else if (fabs(B) > 1e-10 and !pAssembler->isFixed(R[1])){
                if (!pAssembler->isFixed(R[0]) or !pAssembler->isFixed(R[2])){
                  dof.shift = val/B;
                  dof.linear.push_back(std::pair<Dof,double>(R[0],-A/B));
                  dof.linear.push_back(std::pair<Dof,double>(R[2],-C/B));
                  pAssembler->setLinearConstraint(R[1],dof);
                }
    
              }
              else if (fabs(C) > 1e-10 and !pAssembler->isFixed(R[2])){
                if (!pAssembler->isFixed(R[0]) or !pAssembler->isFixed(R[1])){
                  dof.shift = val/C;
                  dof.linear.push_back(std::pair<Dof,double>(R[0],-A/C));
                  dof.linear.push_back(std::pair<Dof,double>(R[1],-B/C));
                  pAssembler->setLinearConstraint(R[2],dof);
                }
              }
              //else
              //  Msg::Error("error in nonLinearMechSolver::applyFixOnFace");
            }
          }
        }
      }
      else{
        for(std::list<nonLinearFixOnFaceBC>::const_iterator fofBC=allFixAllFace.begin(); fofBC!=allFixAllFace.end(); fofBC++){
          double A = fofBC->A;
          double B = fofBC->B;
          double C = fofBC->C;
          double D = fofBC->D;
    
          // because of CG
          for (elementGroup::vertexContainer::const_iterator it = fofBC->g->vbegin();
                    it!= fofBC->g->vend(); it++){
            MVertex* v = it->second;
            int idex = -1;
            MElement* ele = NULL;
            partDomain* dom = NULL;
            bool found = false;
    
            for (int idom =0; idom< domainVector.size(); idom ++){
              partDomain* aldo = domainVector[idom];
              for (elementGroup::elementContainer::const_iterator itedom = aldo->element_begin(); itedom != aldo->element_end(); itedom++){
                MElement* etemp = itedom->second;
                for (int iv = 0; iv < etemp->getNumVertices(); iv++){
                  if (etemp->getVertex(iv)->getNum() == v->getNum()){
                    found = true;
                    ele = etemp;
                    dom = aldo;
                    idex = iv;
                    break;
                  }
                }
                if (found)  break;
              }
              if (found) break;
            };
    
            if (!found) Msg::Error("vertex %d is not found",v->getNum());
    
            int nbFF = ele->getNumShapeFunctions();
            std::vector<Dof> keys;
            dom->getFunctionSpace()->getKeys(ele,keys);
            std::vector<Dof>  R;
            R.push_back(keys[idex]);
            R.push_back(keys[idex+nbFF]);
            R.push_back(keys[idex+2*nbFF]);
    
            DofAffineConstraint<double> dof;
            double val = -1.*(A*v->x()+B*v->y()+C*v->z()+D);
    
            if (fabs(A) > 1e-10 and !pAssembler->isFixed(R[0])){
              if (!pAssembler->isFixed(R[1]) or !pAssembler->isFixed(R[2])){
                dof.shift = val/A;
                dof.linear.push_back(std::pair<Dof,double>(R[1],-B/A));
                dof.linear.push_back(std::pair<Dof,double>(R[2],-C/A));
                pAssembler->setLinearConstraint(R[0],dof);
              }
    
            }
            else if (fabs(B) > 1e-10 and !pAssembler->isFixed(R[1])){
              if (!pAssembler->isFixed(R[0]) or !pAssembler->isFixed(R[2])){
                dof.shift = val/B;
                dof.linear.push_back(std::pair<Dof,double>(R[0],-A/B));
                dof.linear.push_back(std::pair<Dof,double>(R[2],-C/B));
                pAssembler->setLinearConstraint(R[1],dof);
              }
    
            }
            else if (fabs(C) > 1e-10 and !pAssembler->isFixed(R[2])){
              if (!pAssembler->isFixed(R[0]) or !pAssembler->isFixed(R[1])){
                dof.shift = val/C;
                dof.linear.push_back(std::pair<Dof,double>(R[0],-A/C));
                dof.linear.push_back(std::pair<Dof,double>(R[1],-B/C));
                pAssembler->setLinearConstraint(R[2],dof);
              }
            }
    
          }
    
        };
      };
    
      if (allFixAllFace.size() > 0){
        Msg::Info("end applying fix  on face BC");
      }
    };
    
    void nonLinearMechSolver::archivingNode(const int numphys, const int comp, nonLinearBoundaryCondition::whichCondition wc,const int nstep){
      // no distinction between cG/dG and full Dg formulation. class Displacement Field manage it
    
      elementGroup g(0,numphys);
    
      if(g.vsize() >0){
        for (elementGroup::vertexContainer::const_iterator it =g.vbegin(); it!= g.vend(); it++){
          MVertex* v = it->second;
          bool found = false;
          for (int i=0; i< anoded.size(); i++)
          {
            if (anoded[i].physnum == numphys and anoded[i].nodenum == v->getNum() and anoded[i]._comp == comp and anoded[i].wc == wc)
            {
              found = true;
              break;
            }
          }
    
          if (!found)
          {
            anoded.emplace_back(numphys,v->getNum(),comp,wc,nstep);
          }
        }
    
      }
      else{
        Msg::Warning("No physical group %d So it is impossible to archive nodal displacement",numphys);
      }
    }
    
    void nonLinearMechSolver::archivingNode(const int numpphys, const int dim, const int comp, nonLinearBoundaryCondition::whichCondition wc,const int nstep){
      elementGroup g(dim,numpphys);
      FILE* file = NULL;
      #if defined(HAVE_MPI)
      if (Msg::GetCommRank() == 0)
      #endif
      {
        std::ostringstream ossnumver;
        ossnumver << numpphys;
        std::string ss = ossnumver.str();
        std::string fnam = getFileSavingPrefix()+ "numver" + ss+ ".csv";
        file = fopen(fnam.c_str(),"w");
      }
    
      for (elementGroup::vertexContainer::const_iterator it =g.vbegin(); it!= g.vend(); it++){
        MVertex* vv = it->second;
        bool found = false;
        for (int i=0; i< anoded.size(); i++)
        {
          if (anoded[i].physnum == numpphys and anoded[i].nodenum == vv->getNum() and anoded[i]._comp == comp and anoded[i].wc == wc)
          {
            found = true;
            break;
          }
        }
    
        if (!found)
        {
          anoded.emplace_back(numpphys,vv->getNum(),comp,wc,nstep);
        }
        #if defined(HAVE_MPI)
        if (Msg::GetCommRank() == 0)
        #endif
        {
          fprintf(file,"%ld ;%f;%f;%f\n",vv->getNum(),vv->x(),vv->y(),vv->z());
        }
      }
      #if defined(HAVE_MPI)
      if (Msg::GetCommRank() == 0)
      #endif
      {
        fclose(file);
      }
    };
    
    
    void nonLinearMechSolver::archivingRigidContact(const int numphys, const int comp, const int wc,const int nstep){
      // For rigid contact the value are disponible on all rank --> archiving only on last rank
      #if defined(HAVE_MPI)
      if(Msg::GetCommRank() == Msg::GetCommSize() -1)
      #endif// HAVE_MPI
      {
        nonLinearBoundaryCondition::whichCondition whc;
        switch(wc){
         case 0:
          whc = nonLinearBoundaryCondition::position;
          break;
         case 1:
          whc = nonLinearBoundaryCondition::velocity;
          break;
         case 2:
          whc = nonLinearBoundaryCondition::acceleration;
          break;
         default:
          whc = nonLinearBoundaryCondition::position;
        }
    
        bool found = false;
        for (int i=0; i< anoded.size(); i++)
        {
          if (anoded[i].physnum == numphys and anoded[i].nodenum == numphys and
              anoded[i]._comp == comp and anoded[i].wc == whc and
              anoded[i].iscontact)
          {
            found = true;
            break;
          }
        }
        if (!found)
        {
          anoded.emplace_back(numphys,numphys,comp,whc,nstep,true);
        }
      }
    }
    
    void nonLinearMechSolver::archivingNodeIP(const int numphys, const int ipval,const int elemval,const int nstep){
      vaip.push_back(IPField::ip2archive(0, numphys,ipval,elemval,nstep));
    }
    
    void nonLinearMechSolver::archivingNodeIPOnPhysicalGroup(const int dim, const int numphys, const int ipval, const int nstep){
      elementGroup g(dim,numphys);
      if (g.vsize() > 0){
        FILE* ff = NULL;
        #if defined(HAVE_MPI)
        if (Msg::GetCommRank() ==0)
        #endif // HAVE_MPI
        {
          std::string filename = getFileSavingPrefix()+"archivingNodeIPOnPhysicalGroup_vertexList.csv";
          ff = fopen(filename.c_str(),"w");
          fprintf(ff,"Num;X;Y;Z\n");
        }
        for (elementGroup::vertexContainer::const_iterator it = g.vbegin(); it!= g.vend(); it++){
          MVertex* v = it->second;
          vaip.push_back(IPField::ip2archive(ipval,nstep,v));
          if (ff != NULL){
            fprintf(ff,"%ld;%.16g;%.16g;%.16g\n",v->getNum(),v->x(),v->y(),v->z());
          }
        }
        if (ff != NULL){
          fclose(ff);
        }
      }
    };
    
    /**
     * @brief Archiving a value IP on an interface element. The result is stored under a csv file.
     * @param elenumMinus, elenumPlus: interface between two bulk elements
     * @param ipval: a quantity specified in IPField::Ouput
     * @param elemval: an operation to perform: 1 - mean value, 2 - min value, 3 - max value, and aribtrary another value - value at a specific Gausspoint specifyied by numip
     * @param elemval: an operation to perform: 1 - mean value, 2 - min value, 3 - max value, and aribtrary another value - value at a specific Gausspoint specifyied by numip
     * @param numip (0 by default): specifying a IP to archive, ranging from 0 to npts-1. This optiton is considered if elemval is not 1, 2, or 3
     * @param nstep (1 by default): archiving frequence after each nstep time steps
     */
    void nonLinearMechSolver::archivingInterfaceElementIP(const int elenumMinus,const int elenumPlus, const int ipval,const int elemval, const int numip, const int nstep){
      vaip.push_back(IPField::ip2archive(-1, elenumMinus, elenumPlus,ipval,elemval,nstep,numip));
    };
    
    
    /**
     * @brief Archiving a value IP on a bulk element. . The result is stored under a csv file.
     * @param elenum: element number
     * @param ipval: a quantity specified in IPField::Ouput
     * @param elemval: an operation to perform between 1 - mean value, 2 - min value, 3 - max value, and aribtrary another value - the value at a specific Gausspoint specifyied by numip
     * @param numip (0 by default): specifying a IP to archive, ranging from 0 to npts-1. This optiton is considered if elemval is not 1, 2, or 3
     * @param nstep (1 by default): archiving frequence after each nstep time steps
     */
    void nonLinearMechSolver::archivingElementIP(const int elenum,const int ipval,const int elemval, const int numip, const int nstep){
      // save data on numip on elemement elenum
      vaip.push_back(IPField::ip2archive(-1, elenum, elenum,ipval,elemval,nstep,numip));
    };
    
    /**
     * @brief Archiving a value on a physical (only mean, min, max over this physical are available).
     * @param onwhat: entity identification between Node, Edge, Face, or Volume
     * @param numphys: physical number of the entity
     * @param ipval: a quantity specified in IPField::Ouput
     * @param elemval: an operation to perform between 1 - mean value, 2 - min value, 3 - max value
     * @param nstep (1 by default): archiving frequence after each nstep time steps
     */
    void nonLinearMechSolver::archivingIPOnPhysicalGroup(std::string onwhat, const int numphys, const int ipval,const int elemval, const int nstep){
    
      const std::string node("Node");
      const std::string edge("Edge");
      const std::string face("Face");
      const std::string volume("Volume");
      int dim= 0;
      if(onwhat==node){
         dim =0;
      }
      else if(onwhat==edge){
         dim =1;
      }
      else if(onwhat==face){
         dim =2;
      }
      else if(onwhat==volume){
         dim =3;
      }
      else Msg::Error("Impossible to archive on a %s\n",onwhat.c_str());
      vaip.push_back(IPField::ip2archive(dim, numphys,ipval,elemval,nstep));
    }
    
    
    void nonLinearMechSolver::archivingNodeIPShell(int loc, const int numphys, const int ipval,const int elemval,const int nstep){
      vaip.push_back(IPField::ip2archive(0, numphys,ipval,elemval,nstep));
      vaip.back().setTag(loc);
    }
    
    void nonLinearMechSolver::archivingNodeIPShellOnPhysicalGroup(int loc, const int dim, const int numphys, const int ipval, const int nstep){
      elementGroup g(dim,numphys);
      if (g.vsize() > 0){
        FILE* ff = NULL;
        #if defined(HAVE_MPI)
        if (Msg::GetCommRank() ==0)
        #endif // HAVE_MPI
        {
          std::string filename = getFileSavingPrefix()+"archivingNodeIPOnPhysicalGroup_vertexList.csv";
          ff = fopen(filename.c_str(),"w");
          fprintf(ff,"Num;X;Y;Z\n");
        }
        for (elementGroup::vertexContainer::const_iterator it = g.vbegin(); it!= g.vend(); it++){
          MVertex* v = it->second;
          vaip.push_back(IPField::ip2archive(ipval,nstep,v));
          vaip.back().setTag(loc);
          if (ff != NULL){
            fprintf(ff,"%ld;%.16g;%.16g;%.16g\n",v->getNum(),v->x(),v->y(),v->z());
          }
        }
        if (ff != NULL){
          fclose(ff);
        }
      }
    };
    
    void nonLinearMechSolver::archivingInterfaceElementIPShell(int loc, const int elenumMinus,const int elenumPlus, const int ipval,const int elemval, const int numip, const int nstep){
      vaip.push_back(IPField::ip2archive(-1, elenumMinus, elenumPlus,ipval,elemval,nstep,numip));
      vaip.back().setTag(loc);
    };
    
    void nonLinearMechSolver::archivingElementIPShell(int loc, const int elenum,const int ipval,const int elemval, const int numip, const int nstep){
      // save data on numip on elemement elenum
      vaip.push_back(IPField::ip2archive(-1, elenum, elenum,ipval,elemval,nstep,numip));
      vaip.back().setTag(loc);
    };
    
    void nonLinearMechSolver::archivingIPShellOnPhysicalGroup(int loc,  std::string onwhat, const int numphys, const int ipval,const int elemval, const int nstep){
    
      const std::string node("Node");
      const std::string edge("Edge");
      const std::string face("Face");
      const std::string volume("Volume");
      int dim= 0;
      if(onwhat==node){
         dim =0;
      }
      else if(onwhat==edge){
         dim =1;
      }
      else if(onwhat==face){
         dim =2;
      }
      else if(onwhat==volume){
         dim =3;
      }
      else Msg::Error("Impossible to archive on a %s\n",onwhat.c_str());
      vaip.push_back(IPField::ip2archive(dim, numphys,ipval,elemval,nstep));
      vaip.back().setTag(loc);
    }
    
    void nonLinearMechSolver::contactInteraction(contactDomain *cdom){
      _allContact.insert(cdom);
    
    }
    void nonLinearMechSolver::defoDefoContactInteraction(defoDefoContactDomain *cdom){
      _allDefoDefoContact.insert(cdom);
    
    }
    
    
    // create an element for each element type
    // which will be used to compute the time step
    void nonLinearMechSolver::initMapMElementToComputeTimeStep()
    {
      MElementFactory factory;
      std::vector<MVertex*> vver;
      std::map<int,MElement*>::iterator ite;
      for(int i=0;i< _mpiUserDom;i++)
      {
        partDomain * dom = domainVector[i];
        for(elementGroup::elementContainer::const_iterator it=dom->element_begin(); it!=dom->element_end(); ++it){
          MElement *ele = it->second;
          ite = _mapMElementTmp.find(ele->getTypeForMSH());
          if(ite==_mapMElementTmp.end()) // create one new element all vertex are in (0,0,0) They will be filled later
          {
            vver.clear();
            for(int j=0;j<ele->getNumVertices();j++)
            {
              vver.push_back(new MVertex(0.,0.,0.));
            }
            _mapMElementTmp.insert(std::pair<int,MElement*>(ele->getTypeForMSH(),factory.create(ele->getTypeForMSH(),vver)));
          }
          ite = _mapMElementFirstOrder.find(ele->getType());
          if(ite == _mapMElementFirstOrder.end())
          {
            vver.clear();
            for(int j=0;j<ele->getNumPrimaryVertices();j++)
            {
              vver.push_back(new MVertex(0.,0.,0.));
            }
            MElement *eletmp;
            switch(ele->getType())
            {
             case TYPE_PNT:
              eletmp = new MPoint(vver[0]); break;
             case TYPE_LIN:
              eletmp = factory.create(MSH_LIN_2,vver); break;
             case TYPE_TRI:
              eletmp = factory.create(MSH_TRI_3,vver); break;
             case TYPE_QUA:
              eletmp = factory.create(MSH_QUA_4,vver); break;
             case TYPE_TET:
              eletmp = factory.create(MSH_TET_4,vver); break;
             case TYPE_PYR:
              eletmp = factory.create(MSH_PYR_5,vver); break;
             case TYPE_PRI:
              eletmp = factory.create(MSH_PRI_6,vver); break;
             case TYPE_HEX:
              eletmp = factory.create(MSH_HEX_8,vver); break;
             case TYPE_POLYG:
              eletmp = factory.create(MSH_POLYG_,vver); break;
             case TYPE_POLYH:
              eletmp = factory.create(MSH_POLYH_,vver); break;
             default:
              Msg::Error("unknown element type %d",ele->getType());
            }
            _mapMElementFirstOrder.insert(std::pair<int,MElement*>(ele->getType(),eletmp));
          }
        }
      }
    }
    
    double nonLinearMechSolver::initialCriticalExplicitTimeStep(){
    	// evaluate the critical time step carachteristic length = hs
      double hs;
      double dtmin=1.e100; // Initialization to infinity
      for(int i=0; i< _mpiUserDom; i++){
        partDomain *dom = domainVector[i];
        if(dom->elementGroupSize() > 0){ // otherwise no element (interface domain and no need to compute time step)
          double hsmin= 1.e100; // Initialization to infinity
          FunctionSpaceBase *spdom = dom->getFunctionSpace();
          std::vector<Dof> R;
          std::vector<double> disp;
          materialLaw *mlt = dom->getMaterialLaw();
          double celerity = mlt->soundSpeed();
          double scaleTimeStep = dom->scaleTimeStep();
          for(elementGroup::elementContainer::const_iterator it=dom->element_begin(); it!=dom->element_end(); ++it){
    				MElement *e = it->second;
           // nodal displacement 3 per nodes FIX THIS ASSUMPTION
    
    				spdom->getKeys(e,R);
    				int nkeys = spdom->getNumKeys(e);
    				int nbvertex = e->getNumVertices();
    
    				disp.resize(R.size());
    				for (int iR = 0; iR < R.size(); iR++){
    					disp[iR] = 0.;
    				}
    				MElement *etmp = NULL;
    				if((e->getTypeForMSH()!=MSH_TET_10) and (e->getTypeForMSH()!=MSH_TET_20) and (e->getTypeForMSH()!=MSH_HEX_27)) // some functions are undefined in 3D so cannot use new method!
    				{
    					etmp = _mapMElementTmp[e->getTypeForMSH()];
              for(int j=0;j<nbvertex;j++)
    					{
    						//for(int jj=0;jj<nkeyspernodes;jj++){}
    						MVertex *ver = e->getVertex(j);
    						MVertex *vertmp = etmp->getVertex(j);
    						vertmp->x() = ver->x();
    						vertmp->y() = ver->y();
    						vertmp->z() = ver->z();
    					}
    				}
    
            // Unfortunally, computation of characteristic length depends on the element type
            // ADD THIS FUNCTIONS directly in gmsh (method of MELEMENT)
            // Problem as inner radius is defined only for 1st order element
            switch(e->getTypeForMSH()){
             case MSH_QUA_9 :
              hs = quadrangle2orderCharacteristicSize(etmp,_mapMElementFirstOrder);
              break;
             case MSH_QUA_16:
              hs = quadrangle3orderCharacteristicSize(etmp,_mapMElementFirstOrder);
              break;
             case MSH_TRI_6 :
              hs = triangular2orderCharacteristicSize(etmp,_mapMElementFirstOrder);
              break;
             case MSH_TRI_10:
              hs = triangular3orderCharacteristicSize(etmp,_mapMElementFirstOrder);
              break;
             case MSH_TET_10:
              hs = tetrahedron2orderCharacteristicSize(e,disp);
              break;
             case MSH_TET_20:
              hs = tetrahedron3orderCharacteristicSize(e,disp);
              break;
             case MSH_HEX_27:
              hs = hexahedron2orderCharacteristicSize(e,disp);
              break;
             default :
              //default criteria innerRadius()/polynomialOrder() as innerRAdius is defined for first order only !!
              // keep divide by polynomialOrder to be general for undefined case and add 2 for security
              hs = etmp->getInnerRadius()/e->getPolynomialOrder()/2.;
            }
            if(hs<0)
            {
              Msg::Error("Negative characteristic size for element %d on rank %d",e->getNum(),Msg::GetCommRank());
            }
            if(hs<hsmin) hsmin = hs;
            R.clear(); disp.clear();
          }
          double dt = scaleTimeStep*hsmin/celerity;
          if(dt < dtmin ) dtmin = dt;
        }
      }
      return dtmin;
    };
    
    bool nonLinearMechSolver::considerForTimeStep(const partDomain* dom, MElement* ele) const
    {
      if (!getElementErosionFilter()(ele)) return false; // if element already eliminate--> do not account for
      bool cons = true;  
      if (dom->getMaterialLaw()->withEnergyDissipation())
      {
        cons = false;
        const AllIPState::ipstateElementContainer *vips = _ipf->getAips()->getIPstate(ele->getNum()); // if one damage element is still work
        for(int j=0;j<vips->size();j++){
          IPVariable *ipv = (*vips)[j]->getState(IPStateBase::current);
          if(ipv->get(IPField::DAMAGE)<0.9)
          {
            cons = true;
            break;
          }
        }
      }
      return cons;
    };
    
    double nonLinearMechSolver::criticalExplicitTimeStep(){
      // evaluate the critical time step carachteristic length = hs
      double hs;
      double dtmin=1.e100; // Initialization to infinity
      for(int i=0; i< _mpiUserDom; i++){
        partDomain *dom = domainVector[i];
        if(dom->elementGroupSize() > 0){ // otherwise no element (interface domain and no need to compute time step)
          double hsmin= 1.e100; // Initialization to infinity
          FunctionSpaceBase *spdom = dom->getFunctionSpace();
          std::vector<Dof> R;
          std::vector<double> disp;
          materialLaw *mlt = dom->getMaterialLaw();
          double celerity = mlt->soundSpeed();
          double scaleTimeStep = dom->scaleTimeStep();
          for(elementGroup::elementContainer::const_iterator it=dom->element_begin(); it!=dom->element_end(); ++it){
           MElement *e = it->second;
           // nodal displacement 3 per nodes FIX THIS ASSUMPTION
           if(this->considerForTimeStep(dom,e))
           {
    				spdom->getKeys(e,R);
    				int nkeys = spdom->getNumKeys(e);
    				int nbvertex = e->getNumVertices();
    
    				_ufield->get(R,disp);
    
    				MElement *etmp = NULL;
    				if((e->getTypeForMSH()!=MSH_TET_10) and (e->getTypeForMSH()!=MSH_TET_20) and (e->getTypeForMSH()!=MSH_HEX_27)) // some functions are undefined in 3D so cannot use new method!
    				{
    					etmp = _mapMElementTmp[e->getTypeForMSH()];
    					for(int j=0;j<nbvertex;j++)
    					{
    						//for(int jj=0;jj<nkeyspernodes;jj++){}
    						MVertex *ver = e->getVertex(j);
    						MVertex *vertmp = etmp->getVertex(j);
    						vertmp->x() = ver->x() + disp[j];
    						vertmp->y() = ver->y() + disp[j+nbvertex];
    						vertmp->z() = ver->z() + disp[j+nbvertex+nbvertex];
    					}
    				}
    
            // Unfortunally, computation of characteristic length depends on the element type
            // ADD THIS FUNCTIONS directly in gmsh (method of MELEMENT)
            // Problem as inner radius is defined only for 1st order element
            switch(e->getTypeForMSH()){
             case MSH_QUA_9 :
              hs = quadrangle2orderCharacteristicSize(etmp,_mapMElementFirstOrder);
              break;
             case MSH_QUA_16:
              hs = quadrangle3orderCharacteristicSize(etmp,_mapMElementFirstOrder);
              break;
             case MSH_TRI_6 :
              hs = triangular2orderCharacteristicSize(etmp,_mapMElementFirstOrder);
              break;
             case MSH_TRI_10:
              hs = triangular3orderCharacteristicSize(etmp,_mapMElementFirstOrder);
              break;
             case MSH_TET_10:
              hs = tetrahedron2orderCharacteristicSize(e,disp);
              break;
             case MSH_TET_20:
              hs = tetrahedron3orderCharacteristicSize(e,disp);
              break;
             case MSH_HEX_27:
              hs = hexahedron2orderCharacteristicSize(e,disp);
              break;
             default :
              //default criteria innerRadius()/polynomialOrder() as innerRAdius is defined for first order only !!
              // keep divide by polynomialOrder to be general for undefined case and add 2 for security
              hs = etmp->getInnerRadius()/e->getPolynomialOrder()/2.;
            }
            if(hs<0)
            {
              Msg::Error("Negative characteristic size for element %d on rank %d",e->getNum(),Msg::GetCommRank());
            }
            if(hs<hsmin) hsmin = hs;
            R.clear(); disp.clear();
           }
          }
          double dt = scaleTimeStep*hsmin/celerity;
          if(dt < dtmin ) dtmin = dt;
        }
      }
      return dtmin;
    }
    
    void nonLinearMechSolver::setInitialCondition(){
      
      for(std::list<initialCondition>::iterator itic = allinitial.begin() ; itic != allinitial.end(); ++itic){
        initialCondition &initC = *itic;
        if(initC._comp != -1){
          // select the system if required
          nlsDofManager* currentManager = pAssembler->getManagerByComp(initC._comp);
          for(int j=0;j<initC._vspace.size();j++)
          {
            SetInitialDofs(initC._vspace[j],initC._vgroup[j]->begin(),initC._vgroup[j]->end(),initC._mycondition,*currentManager,initC._f,*(initC._vfilter[j]),initC._vdg[j],initC._dofType);
          }
        }
        else
        {
          // contact if exist always at first system
          nlsDofManager* currentManager = pAssembler->getManager(0);
          for(int j=0;j<initC._vspace.size();j++){
            elementGroup *groupDom = NULL;
            if(initC._vgroup[j] != initC._vDomGroup[j])
            {
              groupDom = initC._vDomGroup[j];
            }
            SetNormalInitialDofs(initC._vspace[j],initC._vgroup[j]->begin(),initC._vgroup[j]->end(),initC._mycondition,*currentManager,initC._f,*(initC._vfilter[j]),initC._vdg[j],groupDom);
          }
        }
      }
    }
    
    double nonLinearMechSolver::solveExplicit(){
      _timeManager->initializeTimeSteppingPlan();
      this->initializeExplicitScheme();
      //
      bool forceTimeStepEvaluation=false; // if restart, the time step is forced to be re-estimated
      if(restartManager::available() and !_resetRestart)
      {
        if (getNumRanks() > 1)
          Msg::Barrier(); // To wait rank0 before end
        restartManager::openRestartFile("nonLinearMechSolver");
        restartManager::restart(this);
        // restart takes only nonm const argument! So convert numstep and curtime
        //
        restartManager::closeRestartFile();
        Msg::Barrier(); // To wait rank0 before end
    
        restartManager::InitFromRestartFile();
        Msg::Barrier(); // To wait rank0 before end
        struct stat st;
        if(stat("beforeRestart",&st) == 0)
        {
          std::ostringstream oss;
          oss << _timeManager->getLastIterationIndex();
          std::string cpDir="mv beforeRestart restart"+oss.str();
          int oks =system(cpDir.c_str());
        }
        forceTimeStepEvaluation=true;
      }
      _resetRestart=false;
      if (getNumRanks() > 1)
        Msg::Barrier(); // To wait rank0 before end
    
      /* time loop */
      double curtime = _timeManager->getLastTime();
      while(curtime<_timeManager->getEndTime()){ // end via break for dynamic relaxation
        curtime = this->oneExplicitStep(curtime,forceTimeStepEvaluation);
      }
    
      /* end of scheme */
      return this->finalizeExplicitScheme(curtime);
    }
    
    void nonLinearMechSolver::initializeExplicitScheme()
    {
      Msg::Info("Explicit Data: endtime = %e beta=%f gamma=%f alpha_m=%f",_timeManager->getEndTime(), this->_explicitOpts.beta, this->_explicitOpts.gamma, this->_explicitOpts.alpham);
    
      /* init data */
      this->init();
      this->init2();
    
      /* mass matrix computation outside the loop */
      for(std::vector<partDomain*>::iterator it = domainVector.begin(); it!=domainVector.end(); ++it){
        partDomain *dom = *it;
        AssembleMass(dom->getBilinearMassTerm(),*(dom->getFunctionSpace()),dom->element_begin(),dom->element_end(),
                   *(dom->getBulkGaussIntegrationRule()),*pAssembler);
      }
      if(_notResetedContact){
        for(nonLinearMechSolver::contactContainer::iterator itc = _allContact.begin(); itc!= _allContact.end(); ++itc){
          contactDomain *cdom = *itc;
          if(cdom->isRigid()){
            rigidContactSpaceBase *rcsp = static_cast<rigidContactSpaceBase*>(cdom->getSpace());
            AssembleMass(cdom->getMassTerm(),rcsp,pAssembler);
          }
        }
      }
    
      /* time initialization */
      pAssembler->nextStep();
    };
    
    double nonLinearMechSolver::oneExplicitStep(const double curtime, bool &forceTimeStepEvaluation)
    {
      // Make these to variables static as they have to be kept
      // betwwen 2 calls to this function
      static double timeStep=0.;
    
      // compute time step and current time
      if(_timeManager->getLastIterationIndex()%_explicitOpts.numstepExpl ==0 || forceTimeStepEvaluation){
        timeStep = _explicitOpts.gammas * this->criticalExplicitTimeStep();
       #if defined(HAVE_MPI)
        if(Msg::GetCommSize()>1){
          // in case of mpi the time step is choosen as the minimal value compute on each partition
          double mpiTimeStep;
          MPI_Allreduce(&timeStep,&mpiTimeStep,1,MPI_DOUBLE,MPI_MIN,MPI_COMM_WORLD);
          timeStep = mpiTimeStep;
        }
       #endif // HAVE_MPI
        Msg::Info("time step value: %e at time %e",timeStep,curtime);
        pAssembler->setTimeStep(timeStep);
        _timeManager->setTimeStep(timeStep);
        forceTimeStepEvaluation=false;
      }
      double mytime= curtime + timeStep;
    
      if(mytime>_timeManager->getEndTime())
      {
         mytime = _timeManager->getEndTime();
         timeStep = mytime - curtime;
         pAssembler->setTimeStep(timeStep);
        _timeManager->setTimeStep(timeStep);
      }
    
      this->oneStepPreSolve(mytime,timeStep,_timeManager->getLastIterationIndex()+1);
    
      // solve via assembler to perform mpi operation (communication before and after systemSolve
      pAssembler->systemSolve();
    
      // compute the new forces
      _ipf->compute1state(IPStateBase::current,false);
      double normRHS = this->computeRightHandSide();
      if(std::isnan(normRHS))
      {
        Msg::Error("Nan force value --> end");
        return _timeManager->getEndTime(); // To finish the loop
      }
      if(_explicitOpts.dynamicRelaxation){
        double norm0 = pAssembler->norm0Inf();
        if(_timeManager->getLastIterationIndex()%_explicitOpts.numstepExpl ==0 ) Msg::Info("Rel norm for dynamic relaxation %e tol = %e",normRHS/norm0,_tol);
        if(normRHS/norm0 < _tol){
          Msg::Info("Dynamic relaxation procedure is converged. Return endtime to finish the loop");
          return _timeManager->getEndTime();
        }
        if(mytime == _timeManager->getEndTime())
          Msg::Info("Dynamic relaxation ended as the final time is reached. But convergence is not achieved!");
      }
      
      _timeManager->saveTimeHistory(); // time and step increase
      this->oneStepPostSolve(mytime,_timeManager->getLastIterationIndex());
      // current step
      return mytime;
    }
    
    double nonLinearMechSolver::finalizeExplicitScheme(const double curtime)
    {
      pAssembler->nextStep(); // to make last current state at last explicit step
      this->endOfScheme(curtime,_timeManager->getLastIterationIndex());
      Msg::Info("Explicit OK");
      return curtime;
    }
    
    void nonLinearMechSolver::initializeStaticScheme()
    {
      /* init data */
      this->init();
      this->init2();
      // iterative scheme (loop on timestep)
      std::string name = "A";
      linearSystem<double>* lsys =pAssembler->getLinearSystem(name);
      nonLinearSystem<double>* nlsys = dynamic_cast<nonLinearSystem<double>*>(lsys);
    
      if (whatScheme == Implicit){
         Msg::Info("begin assembling mass matrix");
        /* mass matrix computation outside the loop */
        // zero matrix first
        nlsys->zeroMatrix(nonLinearSystemBase::mass);
    
        for(std::vector<partDomain*>::iterator it = domainVector.begin(); it!=domainVector.end(); ++it){
          partDomain *dom = *it;
          AssembleMass(dom->getBilinearMassTerm(),*(dom->getFunctionSpace()),dom->element_begin(),dom->element_end(),
                     *(dom->getBulkGaussIntegrationRule()),*pAssembler);
        }
        if(_notResetedContact){
          for(nonLinearMechSolver::contactContainer::iterator itc = _allContact.begin(); itc!= _allContact.end(); ++itc){
            contactDomain *cdom = *itc;
            if(cdom->isRigid()){
              rigidContactSpaceBase *rcsp = static_cast<rigidContactSpaceBase*>(cdom->getSpace());
              AssembleMass(cdom->getMassTerm(),rcsp,pAssembler);
            }
          }
        }
        Msg::Info("done assembling mass matrix");
      }
    
    
    
      if (whatScheme == Implicit){ //now only for implicit and dgdomain
        if (_implicitOpts.noMassPredictorWithoutVelocityAndAcceleration)
        {
          Msg::Info("start no mass DOFs collecting");
          // collect all no mass DOFs
          std::set<Dof> nomassDOf;
          /// Loop on all domains
          for(std::vector<partDomain*>::iterator it=domainVector.begin(); it!=domainVector.end(); ++it){
            partDomain *dom = *it;
            // take back the keys of dofs
            dom->getNoMassDofs(nomassDOf);
          }
          Msg::Info("done collecting no mass DOF, total %d DOFs ",nomassDOf.size());
          std::vector<int>  noMasPosInGlobalVec(nomassDOf.size(),0);
          int idex = 0;
          for (std::set<Dof>::const_iterator itd = nomassDOf.begin(); itd != nomassDOf.end(); itd++){
            noMasPosInGlobalVec[idex] = pAssembler->getDofNumber(*itd);
            idex ++;
          }
          // transfer dof to non-linear system
          nlsys->setNoMassDofKeys(noMasPosInGlobalVec);
        }
      }
    
      if (_pathFollowing){
        this->computeLoadVector();
      }
    
      /* time initialization */
      // save solution of previous (initially last step == initial step BEAWARE FOR RESTART)
      pAssembler->nextStep();
    };
    
    bool nonLinearMechSolver::localPathFollowingSwitching() const{
    	// for test
    	double pfCr = _ipf->computePathFollowingLocalValue(IPStateBase::current);
      double Defor = _ipf->computeDeformationEnergy(IPStateBase::current);
    	#if defined(HAVE_MPI)
    	if (Msg::GetCommSize()> 1){
        double Cr[2];
        Cr[0] = pfCr;
        Cr[1] = Defor;
    		double totalCr[2];
    		MPI_Allreduce(&Cr[0],&totalCr[0],2,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD);
        if (totalCr[1] > 0.)
          Msg::Info("pf = %e, deforEnerg = %e cr = %e impose value %e ",totalCr[0],totalCr[1],totalCr[0]/totalCr[1],_pfManager._pathFollowingSwitchCriterion);
    		if (totalCr[0] > _pfManager._pathFollowingSwitchCriterion*totalCr[1]) return true;
    		else return false;
    	}
    	else
    	#endif //HAVE_MPI
    	{
        if (fabs(Defor) > 0.)
          printf("rank %d pf = %e, deforEnerg = %e cr = %e impose value %e \n",Msg::GetCommRank(),pfCr,fabs(Defor),pfCr/fabs(Defor),_pfManager._pathFollowingSwitchCriterion);
    		if (pfCr > _pfManager._pathFollowingSwitchCriterion*fabs(Defor)) return true;
    		else return false;
    	}
    };
    
    double nonLinearMechSolver::computeLocalPathFollowingStep() const{
    	// for adaptive purpose
    	double localStep = _ipf->computePathFollowingLocalValue(IPStateBase::current) - _ipf->computePathFollowingLocalValue(IPStateBase::previous);
    	// accumulate from all procs
    	#if defined(HAVE_MPI)
    	if (Msg::GetCommSize() > 1){
    		double totalStep = 0;
    		MPI_Allreduce(&localStep,&totalStep,1,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD);
    		return totalStep;
    	}
    	else
    	#endif //HAVE_MPI
    	{
    		return localStep;
    	}
    
    };
    
    int nonLinearMechSolver::oneStaticStep(const double curtime,const double dt, const int numstep)
    {
      double tsystresol = Cpu();
      int niteNR = 0;
      if (_pathFollowing){
        this->oneStepPreSolvePathFollowing(curtime,dt,numstep);
        niteNR = NewtonRaphsonPathFollowing(numstep);
      }
      else{
        this->oneStepPreSolve(curtime,dt,numstep);
        niteNR = NewtonRaphson(numstep);
      }
    	tsystresol = Cpu() - tsystresol;
      Msg::Info("Time of Newton-Raphson resolution %f",tsystresol);
      return niteNR;
    }
    
    double nonLinearMechSolver::finalizeStaticScheme(const double curtime, const int step)
    {
      this->endOfScheme(curtime,step);
      Msg::Info("NonLinearStatic OK");
      return curtime;
    }
    
    void nonLinearMechSolver::createOnelabViewOption(const std::string &fname,const int displayType) const
    {
      std::string optfname(fname);
      optfname += ".opt";
      FILE * fopt = fopen(optfname.c_str(),"w");
    
      fprintf(fopt,"n = %d;\n",_GmshOneLabViewNum);
      fprintf(fopt,"View[n].VectorType = %d;\n",displayType);
    
      fclose(fopt);
    
      _GmshOneLabViewNum++;
      return;
    }
    
    void nonLinearMechSolver::createOnelabDisplacementView(const std::string &fname, const double time) const
    {
      static bool initialized = false;
      // ask the unknown field to create the view
      _ufield->onelabView(_meshFileName,fname,this,std::string("displacement"),0,time,_timeManager->getLastIterationIndex());
    
      if(!initialized)
      {
        this->createOnelabViewOption(fname,5); // 5 for displacement view type
        initialized = true;
      }
    }
    
    void nonLinearMechSolver::createOnelabVelocityView(const std::string &fname, const double time) const
    {
      static bool initialized = false;
      // ask the unknown field to create the view
      _ufield->onelabView(_meshFileName,fname,this,std::string("velocity"),1,time,_timeManager->getLastIterationIndex());
    
      if(!initialized)
      {
        this->createOnelabViewOption(fname,4); // 4 for 3D arrow view type
        initialized = true;
      }
    }
    
    void nonLinearMechSolver::getOnelabArchiveNodalUnknowns(fullVector<double> &nodalValues) const
    {
      _ufield->valuesForOnelab(nodalValues);
      return;
    }
    
    void nonLinearMechSolver::setTimeManager(const TimeManager& tm)
    {
      //
      //double endt = _timeManager->getEndTime();
      //double numStep = _timeManager->getNumSteps();
      //
      if (_timeManager) delete _timeManager;
      _timeManager = tm.clone();
      // need to transfer the time and other values
      //_timeManager->setEndTime(endt);
      //_timeManager->setNumSteps(numStep);
    };
    
    
    bool nonLinearMechSolver::checkVertexBelongToDomains(const int phyVer) const{
      elementGroup gV(0,phyVer);
      MVertex* v = NULL;
      if (gV.vsize() > 0)
      {
        v = (gV.vbegin()->second);
      }
      if (v == NULL) {
        Msg::Error("vertex does not exist");
        return true;
      }
      for (int i=0; i< domainVector.size(); i++){
        if (domainVector[i]->elementGroupSize() == 0){
          Msg::Error("domain vector must be initialized firts");
        }
        for (elementGroup::vertexContainer::const_iterator it = domainVector[i]->vertex_begin(); it!= domainVector[i]->vertex_end(); it++){
          MVertex* vv = it->second;
          if (vv->getNum() == v->getNum()) return true;
        }
      }
    
      return false;
    };
    
    double nonLinearMechSolver::getArchivedNodalValue(const int numphys,const int comp,nonLinearBoundaryCondition::whichCondition wc) const
    {
      bool found = false;
      double dofvalue = 0;
      for(std::vector<unknownField::archiveNode>::const_iterator itn = _ufield->getArchiveNodeContainer().begin(); itn!=_ufield->getArchiveNodeContainer().end();++itn)
      {
        if (itn->physnum == numphys && itn->_comp == comp &&itn->wc == wc)
        {
          found = true;
          if (whatScheme == StaticLinear)
          {
            pAssembler->getDofValue(itn->D,dofvalue);
          }
          else
          {
            pAssembler->getDofValue(itn->D,dofvalue,itn->wc);
          }
          break;
        }
      }
    
      #if defined(HAVE_MPI)
      if (getNumRanks() > 1)
      {
        double sumValue;
        MPI_Allreduce(&dofvalue,&sumValue, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
        return sumValue;
      }
      else
      #endif // HAVE_MPI
      {
        if (!found){
          Msg::Error("Cannot get a value in getArchivedNodalValue.\nTo get a value, the disp archiving on node with physical %d must be defined with archivingNodeDisplacement",numphys);
          return 0.;
        }
        else
          return dofvalue;
      }
    };
    
    double nonLinearMechSolver::getArchivedForceOnPhysicalGroup(std::string onwhat, const int numphys,const int comp) const
    {
      static std::string node("Node");
      static std::string edge("Edge");
      static std::string face("Face");
      static std::string volu("Volume");
      int dim=0;
      if(onwhat == node )
        dim = 0;
      else if(onwhat == edge)
        dim = 1;
      else if(onwhat == face)
        dim = 2;
      else if(onwhat == volu)
        dim = 3;
    
      double force =0.;
      bool found = false;
      for(std::vector<archiveForce>::const_iterator itf = vaf.begin(); itf != vaf.end();++itf)
      {
        if(itf->numphys != numphys) continue;
        if(itf->comp != comp) continue;
        if(itf->dim != dim) continue;
        force = itf->fval; found = true; break;
      }
    
      #if defined(HAVE_MPI)
      if (getNumRanks() > 1){
        double sumValue;
        MPI_Allreduce(&force,&sumValue, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
        return sumValue;
      }
      else
      #endif // HAVE_MPI
      {
        if (!found){
          Msg::Error("Cannot found a force value in getarchivedForceOnPhysicalGroup");
          return 0.;
        }
        else
          return force;
      }
    }
    
    double nonLinearMechSolver::getEnergy(int we) const
    {
      double eneryPart =_energField->get(energeticField::whichEnergy(we));
      #if defined(HAVE_MPI)
      if(Msg::GetCommSize() > 1){
        double totalEnerg= 0.;;
        MPI_Allreduce(&eneryPart,&totalEnerg,1,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD);
        return totalEnerg;
      }
      #endif // HAVE_MPI
      return eneryPart;
    }
    void nonLinearMechSolver::forceArchiving(const double curtime,const int numstep, const bool forceSave)
    {
      pAssembler->getForces(vaf); // get force value from system
      for(int i=0;i<vaf.size();i++){
        if (vaf[i].vdof.size()!=0 && (vaf[i].contact_root == -1 || vaf[i].contact_root == Msg::GetCommRank()))
        {
          if (numstep > vaf[i].lastSaveStep){
            if((numstep%(vaf[i].nstep)==0 or forceSave))
            {
              vaf[i].lastSaveStep = numstep;
              fprintf(vaf[i].FP,"%.16g;%.16g\n",curtime,vaf[i].fval);
              fflush(vaf[i].FP);
            }
    
          }
        }
      }
    }
    
    double nonLinearMechSolver::getHomogenizedStress(const int i, const int j) const{
    	return _currentState->getHomogenizedStress()(i,j);
    };
    double nonLinearMechSolver::getHomogenizedTangent(const int i, const int j, const int k, const int l) const{
    	return _currentState->getHomogenizedTangentOperator_F_F()(i,j,k,l);
    };
    
    std::string nonLinearMechSolver::getHomogenizedTangentCompNameInMatrixForm(int row, int col) const
    {
      int i, j, k, l;
      Tensor23::getIntsFromIndex(row,i,j);
      Tensor23::getIntsFromIndex(col,k,l);
      return "L"+std::to_string(i)+std::to_string(j)+std::to_string(k)+std::to_string(l);
    };
    
    void nonLinearMechSolver::getHomogenizedTangent(fullMatrix<double>& Cel) const
    {
      const STensor43& L = _currentState->getHomogenizedTangentOperator_F_F();
      if (Cel.size1() != 9 || Cel.size2() !=9)
      {
        Cel.resize(9,9,true);
      }
    
      for (int row = 0; row <9; row ++)
      {
        int i, j;
        Tensor23::getIntsFromIndex(row,i,j);
        for (int col = 0; col < 9; col ++)
        {
          int k,l;
          Tensor23::getIntsFromIndex(col,k,l);
          Cel(row,col) = L(i,j,k,l);
        }
      }
    };
    
    void nonLinearMechSolver::getAcousticTensor(double n0, double n1, double n2, fullMatrix<double>& A) const
    {
      if (A.size1() != 3 || A.size2() != 3)
      {
        A.resize(3,3,true);
      }
      else
      {
        A.setAll(0.);
      };
      SVector3 n;
      n(0) = n0;
      n(1) = n1;
      n(2) = n2;
      const STensor43& L = _currentState->getHomogenizedTangentOperator_F_F();
      for (int i=0; i< 3; i++)
      {
        for (int j=0; j<3; j++)
        {
          for (int k=0; k<3; k++)
          {
            for (int l=0; l<3; l++)
            {
              A(i,k) += n(j)*L(i,j,k,l)*n(l);
            }
          }
        }
      }
    }
    
    double nonLinearMechSolver::getHomogenizedSecondTangent(const int i, const int j, const int k, const int p, const int q, const int r) const{
      return _currentState->getHomogenizedTangentOperator_G_G()(i,j,k,p,q,r);
    };
    
    double nonLinearMechSolver::getHomogenizedSecondStress(const int i, const int j, const int k) const{
      return _currentState->getHomogenizedSecondOrderStress()(i,j,k);
    };
    
    double nonLinearMechSolver::getHomogenizedFirstSecondTangent(const int i, const int j, const int p, const int q, const int r) const{
      return _currentState->getHomogenizedTangentOperator_F_G()(i,j,p,q,r);
    };
    
    double nonLinearMechSolver::getHomogenizedSecondFirstTangent(const int i, const int j, const int k, const int p, const int q) const{
      return _currentState->getHomogenizedTangentOperator_G_F()(i,j,k,p,q);
    };
    
    double nonLinearMechSolver::getHomogenizedCohesiveJump(const int i) const{
      return _currentState->getHomogenizedCohesiveJump()(i);
    };
    
    double nonLinearMechSolver::getNodeIP(const int physical, const int ipVal) const{
      return _ipf->getIPValueOnPhysical(0,physical,ipVal);
    };
    
    void nonLinearMechSolver::setSelectiveUpdate(const selectiveUpdateBase& selectiveObj){
      _failureBasedOnPreviousState = false;
      if (_selectiveObject) delete _selectiveObject;
      _selectiveObject = selectiveObj.clone();
    };
    
    void nonLinearMechSolver::endSchemeMonitoring(const EndSchemeMonitoringBase& endObj){
      if (_endSchemeMonitoringObject != NULL) delete _endSchemeMonitoringObject;
      _endSchemeMonitoringObject = endObj.clone();
    };
    
    bool nonLinearMechSolver::solverEndedByMornitoring() const
    {
      if (_endSchemeMonitoringObject != NULL)
      {
        return _endSchemeMonitoringObject->solverEndedByMornitoring();
      }
      return false;
    };
    
    void nonLinearMechSolver::setNoMassPredictorWithoutVelocityAndAcceleration(const bool fl){
      _implicitOpts.noMassPredictorWithoutVelocityAndAcceleration = fl;
      if (fl)
      {
        Msg::Info("no velocity and acceleration in predictor of nomass dof with Implicit CH scheme");
      }
    
    };
    
    // for crack insertion control
    void nonLinearMechSolver::setMaxAllowedCrackInsertionAtOneStep(const int maxNum)
    {
      if(maxNum < 0) Msg::Error("broken number must be positive nonLinearMechSolver::setMaxAllowedCrackInsertionAtOneStep");
      Msg::Info("Only %d interface elements are allowed to be cracked during one step", maxNum);
    
      ReduceTimeStepWithMaximalNumberOfBrokenElements  endObj(maxNum);
      this->endSchemeMonitoring(endObj);
    }
    
    
    // for element erosion
    void nonLinearMechSolver::setElementErosion(const bool bulkErosion, const bool interfaceErosion, const int method){
    	_bulkElementErosionFlag  = bulkErosion;
    	_interfaceElementErosionFlag = interfaceErosion;
    	if (_bulkElementErosionFlag or _interfaceElementErosionFlag){
    		_erosionType = (elementErosionType)method;
    		if (_erosionType == nonLinearMechSolver::FIRST_IP_FAILED){
    			Msg::Info("An element is removed when first IP reaches failure state");
    		}
    		else if (_erosionType == nonLinearMechSolver::ALL_IP_FAILED){
    			Msg::Info("An element is removed when all IPs reach failure state");
    		}
    		else{
    			Msg::Error("_erosionType is not defined nonLinearMechSolver::setElementErosion");
    		}
    	}
    };
    
    void nonLinearMechSolver::setGlobalErosionCheck(const bool flag, const FailureCriterionBase* fcr){
    	if (flag){
    		Msg::Info("erosion is checked globally based on a global criterion");
        if (_erosionGlobalCriterion!=NULL) delete _erosionGlobalCriterion;
    		_erosionGlobalCriterion = fcr->clone();
    	}
    };
    
    //
    // C++ Interface: terms Quasi Static
    //
    // Description: Files with definition of function : nonLinearMechSolver::solveSNL()
    //
    //
    // Author:  <Gauthier BECKER>, (C) 2010
    //
    // Copyright: See COPYING file that comes with this distribution
    //
    //
    
    // compute Fext-Fint
    double nonLinearMechSolver::computeRightHandSide()
    {
      pAssembler->clearRHSfixed();
      this->computeInternalForces();
    
      // compute ext forces
      this->computeExternalForces();
    
      // compute contact forces
      this->computeContactForces();
    
      // for periodic BC
      if (_microFlag){
        if (_systemType == nonLinearMechSolver::MULT_ELIM){
          _pAl->assembleConstraintResidualToSystem();
          this->computeStiffMatrix();
          if(computedUdF() and _pathFollowing and useWhichModuliForBF()==partDomain::Present){
             this->computeBodyForceVector();
          }
        }
        else if (_systemType == nonLinearMechSolver::DISP_MULT) {
          _pAl->computeLinearConstraints();
        }
      }
    
    	if (_pathFollowing and _pfManager._pathFollowingMethod == pathFollowingManager::LOCAL_BASED){
    		this->computePathFollowingConstraint();
    	}
    
      // save Fint component to archive ( write in file when Fint = Fext)
    //  return lsys->normInfRightHandSide();
      return pAssembler->normInfRightHandSide();
    }
    
    void nonLinearMechSolver::computeInternalForces()
    {
      for(std::vector<partDomain*>::iterator itdom=domainVector.begin(); itdom!=domainVector.end();++itdom)
      {
        partDomain *dom = *itdom;
        Assemble(dom->getLinearBulkTerm(),*(dom->getFunctionSpace()),dom->element_begin(),dom->element_end(),
                 *(dom->getBulkGaussIntegrationRule()),_ufield, *pAssembler,
                 nonLinearSystemBase::Fint,_elementErosionFilter);
    
        if(dom->IsInterfaceTerms()){
          dgPartDomain *dgdom = static_cast<dgPartDomain*>(dom);
          Assemble(dgdom->getLinearInterfaceTerm(),*(dgdom->getFunctionSpace()),dgdom->gi->begin(),dgdom->gi->end(),*(dgdom->getInterfaceGaussIntegrationRule()),
                     _ufield,*pAssembler,nonLinearSystemBase::Fint,_elementErosionFilter); // Use the same GaussQuadrature rule than on the boundary
          
          // Assembling loop on elementary boundary interface terms
          Assemble(dgdom->getLinearVirtualInterfaceTerm(),*(dgdom->getFunctionSpace()),dgdom->gib->begin(),dgdom->gib->end(),
                   *(dgdom->getInterfaceGaussIntegrationRule()),_ufield,*pAssembler,nonLinearSystemBase::Fint,_elementErosionFilter); // Use the same GaussQuadrature rule than on the boundary
    
        }
      }
    }
    
    void nonLinearMechSolver::computeContactForces()
    {
      for(contactContainer::iterator it = _allContact.begin(); it!=_allContact.end(); ++it){
        contactDomain *cdom = *it;
        contactLinearTermBase<double> *cterm = static_cast<contactLinearTermBase<double>*>(cdom->getForceTerm());
        cterm->clearContactNodes();
        Assemble(*(cterm),*(cdom->getSpace()),cdom->gSlave->begin(),
                cdom->gSlave->end(),*(cdom->getGaussIntegration()),*pAssembler,nonLinearSystemBase::Fext,_elementErosionFilter);
      }
      for(defoDefoContactContainer::iterator it = _allDefoDefoContact.begin(); it!=_allDefoDefoContact.end(); ++it){
        defoDefoContactDomain *cdom = *it;
        std::vector< LinearTermBase<double> *> &term = cdom->getRefToForceTerm(); //we might need to change the class
        for(int i=0;i<term.size(); i++)
        {
          contactLinearTermBase<double>* cterm=static_cast< contactLinearTermBase<double>* > (term[i]);
          cterm->clearContactNodes();
          AssembleDefoDefoContact(*(cterm),*((cdom->getRefToVSpace())[i]),((cdom->getRefToGroup())[i]).begin(),
                ((cdom->getRefToGroup())[i]).end(),
                *((cdom->getRefToVQuadratureMaster())[i]),*pAssembler,nonLinearSystemBase::Fint,_elementErosionFilter); // put in internal forces for path following
        }
      }
    }
    
    void  nonLinearMechSolver::computeMassMatrix(){
       static std::string Bname("B");
       pAssembler->getLinearSystem(Bname)->zeroMatrix();
    
       pAssembler->setCurrentMatrix(Bname);
       for(std::vector<partDomain*>::iterator itdom=domainVector.begin(); itdom!=domainVector.end(); ++itdom)
       {
         partDomain* dom = *itdom;
         // Assembling loop on Elementary terms
         AssembleItMap(*(dom->getBilinearMassTerm()),*(dom->getFunctionSpace()),dom->element_begin(),dom->element_end(),
                   *(dom->getBulkGaussIntegrationRule()),*pAssembler,_elementErosionFilter);
        };
       static std::string Aname("A");
       pAssembler->setCurrentMatrix(Aname);;
    };
    
    void nonLinearMechSolver::computeStiffMatrix()
    {
      if (_stiffEstimation)
      {
        if (!_stiffnessModification)
        {
          _stiffEstimation = false;
          if (_stiffnessModificationMonitoring->tangentType() == IPStateBase::previous)
          {
            _ipf->copy(IPStateBase::current,IPStateBase::temp);
            _ipf->copy(IPStateBase::previous,IPStateBase::current);
          }
        }
        //Msg::Info("call nonLinearMechSolver::computeStiffMatrix");
        pAssembler->zeroMatrix();
        
        for(std::vector<partDomain*>::iterator itdom=domainVector.begin(); itdom!=domainVector.end(); ++itdom)
        {
          partDomain* dom = *itdom;
          // Assembling loop on Elementary terms
          AssembleItMap(*(dom->getBilinearBulkTerm()),*(dom->getFunctionSpace()),dom->element_begin(),dom->element_end(),
                   *(dom->getBulkGaussIntegrationRule()),*pAssembler,_elementErosionFilter);
    
          if(dom->IsInterfaceTerms())
          {
            dgPartDomain *dgdom = static_cast<dgPartDomain*>(dom);
            // Assembling loop on elementary interface terms
            AssembleItMap(*(dgdom->getBilinearInterfaceTerm()),*(dom->getFunctionSpace()),dgdom->gi->begin(),dgdom->gi->end(),
                         *(dgdom->getInterfaceGaussIntegrationRule()),*pAssembler,_elementErosionFilter);
            
            // Assembling loop on elementary boundary interface terms
            AssembleItMap(*(dgdom->getBilinearVirtualInterfaceTerm()),*(dom->getFunctionSpace()),dgdom->gib->begin(),dgdom->gib->end(),
                     *(dgdom->getInterfaceGaussIntegrationRule()),*pAssembler,_elementErosionFilter);
          }
        }
        
        // NeumannBC (non linear flux)
        if(this->getScheme() != StaticLinear)
        {
          for(std::list<nonLinearNeumannBC>::iterator itbc = allNeumann.begin(); itbc != allNeumann.end();++itbc){
            nonLinearNeumannBC &neu = *itbc; 
            for(int j=0;j<neu._vspace.size();j++)
            {
              FunctionSpaceBase *spn = neu._vspace[j];
              const elementGroup *gneu = neu._vgroup[j];
              QuadratureBase *qneu = neu._vquadrature[j];
              AssembleSkipEmpty(*(neu._vmatrix_term[j]),*spn,gneu->begin(),gneu->end(),*(qneu),*pAssembler,_elementErosionFilter);
            }
          }
        }
        else{
          for(std::list<nonLinearNeumannBC>::iterator itbc = allNeumann.begin(); itbc != allNeumann.end();++itbc){
            nonLinearNeumannBC &neu = *itbc;
            for(int j=0;j<neu._vspace.size();j++)
            {
              FunctionSpaceBase *spn = neu._vspace[j];
              const elementGroup *gneu = neu._vgroup[j];
              QuadratureBase *qneu = neu._vquadrature[j];
              AssembleSkipEmpty(*(neu._vmatrix_term[j]),*spn,gneu->begin(),gneu->end(),*(qneu),*pAssembler,_elementErosionFilter);
            }
          }
        }
    
        // Contact
        // little particular because it the term itself which know (via its linked linear term) the element
        // where there is contact and so a bilinearterm != 0 as to be computed
        for(contactContainer::iterator it = _allContact.begin(); it!=_allContact.end(); ++it){
          contactDomain *cdom = *it;
          contactBilinearTermBase<double> *sterm = static_cast<contactBilinearTermBase<double>*>(cdom->getStiffnessTerm());
          AssembleItMap(*sterm,*(cdom->getSpace()),sterm->getContactNodes()->elemBegin(),
                          sterm->getContactNodes()->elemEnd(),*(cdom->getGaussIntegration()),*pAssembler,_elementErosionFilter);
      //    sterm->clearContactNodes();
        }
        for(defoDefoContactContainer::iterator it = _allDefoDefoContact.begin(); it!=_allDefoDefoContact.end(); ++it){
          defoDefoContactDomain *cdom = *it;
          const std::vector<BilinearTermBase*> &sterm = static_cast< const std::vector<BilinearTermBase* > >(cdom->getConstRefToStiffnessTerm()); //we might need to change the class
          for(int i=0; i<sterm.size(); i++)
          {
            AssembleSkipEmptyDefoDefoContact(*(sterm[i]),*(cdom->getRefToVSpace()[i]),((cdom->getRefToGroup())[i]).begin(),
                ((cdom->getRefToGroup())[i]).end(),
                *(cdom->getRefToVQuadratureMaster()[i]),*pAssembler,_elementErosionFilter);
          }
        }
    
        // for periodic BC
        if (_microFlag)
          if (_systemType == nonLinearMechSolver::DISP_MULT)
            _pAl->assembleConstraintMatrix();
            
            
        if (!_stiffnessModification)
        {
          if (_stiffnessModificationMonitoring->tangentType() == IPStateBase::previous)
          {
            // copy back to current state
            _ipf->copy(IPStateBase::temp,IPStateBase::current);
          }
        }
      }
      
    
    	if (_pathFollowing and _pfManager._pathFollowingMethod == pathFollowingManager::LOCAL_BASED){
    		this->computeDPathFollowingConstraintDUnknowns();
    	}
    }
    
    void nonLinearMechSolver::computeElasticStiffMatrix(){
      if (_stiffEstimation)
      {
        if (!_stiffnessModification)
        {
          _stiffEstimation = false;
        }
        
        pAssembler->zeroMatrix();
        
        for(std::vector<partDomain*>::iterator itdom=domainVector.begin(); itdom!=domainVector.end(); ++itdom)
        {
          partDomain* dom = *itdom;
          // Assembling loop on Elementary terms
          AssembleItMap(*(dom->getBilinearElasticBulkTerm()),*(dom->getFunctionSpace()),dom->element_begin(),dom->element_end(),
                   *(dom->getBulkGaussIntegrationRule()),*pAssembler,_elementErosionFilter);
        }
    
        // Contact
        // little particular because it the term itself which know (via its linked linear term) the element
        // where there is contact and so a bilinearterm != 0 as to be computed
        for(contactContainer::iterator it = _allContact.begin(); it!=_allContact.end(); ++it){
          contactDomain *cdom = *it;
          contactBilinearTermBase<double> *sterm = static_cast<contactBilinearTermBase<double>*>(cdom->getStiffnessTerm());
          AssembleItMap(*sterm,*(cdom->getSpace()),sterm->getContactNodes()->elemBegin(),
                          sterm->getContactNodes()->elemEnd(),*(cdom->getGaussIntegration()),*pAssembler,_elementErosionFilter);
      //    sterm->clearContactNodes();
        }
        for(defoDefoContactContainer::iterator it = _allDefoDefoContact.begin(); it!=_allDefoDefoContact.end(); ++it){
          defoDefoContactDomain *cdom = *it;
          const std::vector<BilinearTermBase*> &sterm = static_cast< const std::vector<BilinearTermBase* > >(cdom->getConstRefToStiffnessTerm()); //we might need to change the class
          for(int i=0; i<sterm.size(); i++)
          {
            AssembleSkipEmptyDefoDefoContact(*(sterm[i]),*(cdom->getRefToVSpace()[i]),((cdom->getRefToGroup())[i]).begin(),
                ((cdom->getRefToGroup())[i]).end(),
                *(cdom->getRefToVQuadratureMaster()[i]),*pAssembler,_elementErosionFilter);
          }
        }
        // for periodic BC
        if (_microFlag)
          if (_systemType == nonLinearMechSolver::DISP_MULT)
            _pAl->assembleConstraintMatrix();
        }
    
        if (_pathFollowing and _pfManager._pathFollowingMethod == pathFollowingManager::LOCAL_BASED){
          this->computeDPathFollowingConstraintDUnknowns();
        }
    }
    
    // Newton Raphson scheme to solve one step
    int nonLinearMechSolver::NewtonRaphson(const int numstep){
      if (whatScheme == Implicit){
        // dynamic predictor
        pAssembler->systemSolveIntReturn();
      }
    
      // compute ipvariable
      if(computedUdF()){
           _ipf->setDeformationGradientGradient(this->getMicroBC()->getSecondOrderKinematicalVariable(), IPStateBase::current);
      }
      _ipf->compute1state(IPStateBase::current, true);
      // Compute Right Hand Side (Fext-Fint)
      double normFinf = this->computeRightHandSide();
      // norm0 = norm(Fext) + norm(Fint) to have a relative convergence
      double norm0 = pAssembler->norm0Inf();
    
      // loop until convergence
      int iter=0;
      if(norm0 == 0){ // no force (can append if contact )
        Msg::Info("no force go next step");
        if (_microFlag) this->computeStiffMatrix();
        return 0;
      }
      double relnorm = normFinf/norm0;
      Msg::Info("iteration n %d : residu : %e, absolute residu : %e",iter,relnorm,normFinf);
      while ((relnorm>_tol and normFinf>_absTol) or iter == 0){ // ensure 1 iteration min
    
        iter++;
        // Compute Stiffness Matrix
        this->computeStiffMatrix();
    
        // Solve KDu = Fext-Fint
        double t = Cpu();
        int succeed = pAssembler->systemSolveIntReturn();
        t = Cpu() - t;
        if(!succeed)
        {
          iter = _timeManager->getMaxNbIterations();
          break;
        }
        // update ipvariable
        _ipf->compute1state(IPStateBase::current,true);
    
        if (!_iterativeNR) break;
        // new forces
        normFinf = this->computeRightHandSide();
    
        // Check of convergence
        relnorm = normFinf/norm0;
        Msg::Info("iteration n %d : residu : %e, absolute residu : %e, solving time: %f",iter,relnorm, normFinf,t);
    
        // perform line search if activate (default)
        while((relnorm>_tol and normFinf>_absTol) and (iter > 1))    // avoid line search for linear case
        {
          int ls = pAssembler->lineSearch();
          if(ls == 1) break; // processus is converged or failed
          // update ipvariable
          _ipf->compute1state(IPStateBase::current,true);
          // new RightHandSide
          this->computeRightHandSide();
        }
        if((iter == _timeManager->getMaxNbIterations()) or std::isnan(relnorm))
        {
          // reset system value
          iter = _timeManager->getMaxNbIterations(); // for isnan case
          break;
        }
      }
      return iter;
    }
    
    
    linearSystem<double>* nonLinearMechSolver::createSNLSystem()
    {
      // Select the solver for linear system Ax=b (KDeltau = Fext-Fint)
      linearSystem<double> *lsys = NULL;
      if(nonLinearMechSolver::whatSolver == Taucs){
        #if defined(HAVE_TAUCS)
        lsys = new nonLinearSystemCSRTaucs<double>(_lineSearch);
        Msg::Info("Taucs is chosen to solve");
        #else
        lsys = new nonLinearSystemGmm<double>(_lineSearch);
        dynamic_cast<nonLinearSystemGmm<double>*>(lsys)->setNoisy(2);
        Msg::Error("Taucs is not installed\n Gmm is chosen to solve");
        #endif
      }
      else if(nonLinearMechSolver::whatSolver == Petsc){
        #if defined(HAVE_PETSC)
        if (_pathFollowing){
          if ((!_isPartitioned) and (Msg::GetCommSize()>1)){
    				if (_pfManager._pathFollowingMethod == pathFollowingManager::GLOBAL_ARC_LENGTH_BASED){
    					lsys = new pathFollowingSystemPETSC<double>(PETSC_COMM_SELF);
    				}
    				else if (_pfManager._pathFollowingMethod == pathFollowingManager::LOCAL_BASED){
    					lsys = new generalLocalControlBasedPathFollowingSystemPETSc<double>(PETSC_COMM_SELF);
    				}
            else if (_pfManager._pathFollowingMethod == pathFollowingManager::HYPERELLIPTIC_BASED){
              lsys = new generalHyperellipticPathFollowingSystemPETSC<double>(PETSC_COMM_SELF);
            }
    				else
    					Msg::Error("path following method %d is not defined",_pfManager._pathFollowingMethod);
          }
          else{
    				if (_pfManager._pathFollowingMethod == pathFollowingManager::GLOBAL_ARC_LENGTH_BASED){
    					lsys = new pathFollowingSystemPETSC<double>(PETSC_COMM_WORLD);
    				}
    				else if (_pfManager._pathFollowingMethod == pathFollowingManager::LOCAL_BASED){
    					lsys = new generalLocalControlBasedPathFollowingSystemPETSc<double>(PETSC_COMM_WORLD);
    				}
            else if (_pfManager._pathFollowingMethod == pathFollowingManager::HYPERELLIPTIC_BASED){
              lsys = new generalHyperellipticPathFollowingSystemPETSC<double>(PETSC_COMM_WORLD);
            }
    				else
    					Msg::Error("path following method %d is not defined", _pfManager._pathFollowingMethod);
          }
    			// set path following options
    			// paramater remains default if it is negative
    			pathFollowingSystemBase* pfsysBase = dynamic_cast<pathFollowingSystemBase*>(lsys);
    			if (_pfManager._controlTypePathFollowing >=0){
    				pfsysBase->setControlType(_pfManager._controlTypePathFollowing);
    			}
    			if (_pfManager._correctionMethodPathFollowing>=0){
    				pfsysBase->setCorrectionMethod(_pfManager._correctionMethodPathFollowing);
    			}
    			if (_pfManager._tranversalCriterionPathFollowing>=0){
    				pfsysBase->setTranversalCriterion(_pfManager._tranversalCriterionPathFollowing);
    			}
    			if (_pfManager._solverTypePathFollowing >=0){
    				pfsysBase->setSolverType(_pfManager._solverTypePathFollowing,_pfManager._pathFollowingEqRatio);
    			}
    
        }
        else{
          if ((!_isPartitioned) and (Msg::GetCommSize()>1)){
            lsys = new nonLinearSystemPETSc<double>(PETSC_COMM_SELF,_lineSearch);
          }
          else
          {
            lsys = new nonLinearSystemPETSc<double>(PETSC_COMM_WORLD,_lineSearch);
          }
    
        }
        if(_solver_options.size())
          lsys->setParameter(std::string("petsc_solver_options"),_solver_options);
        Msg::Info("PETSc is chosen to solve");
        #else
        Msg::Error("PETSc is not installed\n Gmm is chosen to solve");
        if (_pathFollowing){
          Msg::Error("path following has not been implemented with Gmm, load control is considered");
        }
    
        lsys = new nonLinearSystemGmm<double>(_lineSearch);
        dynamic_cast<nonLinearSystemGmm<double>*>(lsys)->setNoisy(2);
    
        #endif
      }
      else if(nonLinearMechSolver::whatSolver == GMRESk){
        lsys = new nonLinearSystemGMRESk<double>(_lineSearch);
        Msg::Info("GMRESk is chosen to solve");
      }
      else{
        lsys = new nonLinearSystemGmm<double>(_lineSearch);
        static_cast<nonLinearSystemGmm<double>*>(lsys)->setNoisy(2);
        Msg::Info("Gmm is chosen to solve");
      }
    
      nonLinearSystem<double>* nlsys = dynamic_cast<nonLinearSystem<double>*>(lsys);
      if (nlsys != NULL){
        // initialization data
        #if defined(HAVE_MPI)
        int rootRank = 0;
        std::set<int> OtherRanks;
        if ((!_isPartitioned) and (Msg::GetCommSize() >1) ){
          // if sequential mesh in mpi run
          if (!_microFlag){
            // if macro problem
            rootRank = 0;
            for (int i=1; i< Msg::GetCommSize(); i++){
              OtherRanks.insert(i);
            }
          }
          else{
            // if micro problem
            rootRank = Msg::GetCommRank();
          }
        }
        else{
          rootRank = Msg::GetCommRank();
        }
        nlsys->setWorkingRanks(rootRank,OtherRanks);
        #endif // HAVE_MPI
      }
    
      return lsys;
    }
    
    linearSystem<double>* nonLinearMechSolver::createExplicitSystem()
    {
      linearSystem<double> *esys;
      if(nonLinearMechSolver::whatSolver == Petsc){
       #if defined(HAVE_PETSC)
         Msg::Info("Explicit scheme based on PETSc");
         esys = new explicitHulbertChungPetsc<double>(_explicitOpts.alpham,_explicitOpts.beta,_explicitOpts.gamma,_explicitOpts.timeStepByBenson,_explicitOpts.dynamicRelaxation);
       #else
         Msg::Warning("petsc is not install on this computer. Use blas explicit system");
        #if !defined(HAVE_BLAS)
         Msg::Warning("blas is not installed. Use not optimized default implementation");
        #endif // HAVE_BLAS
         esys = new explicitHulbertChungBlas<double>(_explicitOpts.alpham,_explicitOpts.beta,_explicitOpts.gamma,_explicitOpts.timeStepByBenson,_explicitOpts.dynamicRelaxation);
       #endif // HAVE_PETSC
      }
      else{
         #if !defined(HAVE_BLAS)
          Msg::Warning("blas is not installed. Use not optimized default implementation. Maybe you can use PETSc ?");
         #else
          Msg::Info("Explicit scheme based on BLAS");
         #endif // HAVE_BLAS
          esys = new explicitHulbertChungBlas<double>(_explicitOpts.alpham,_explicitOpts.beta,_explicitOpts.gamma,_explicitOpts.timeStepByBenson,_explicitOpts.dynamicRelaxation);
      }
       return esys;
    }
    
    linearSystem<double>* nonLinearMechSolver::createImplicitSystem(){
      linearSystem<double> *esys;
      if(nonLinearMechSolver::whatSolver == Petsc){
       #if defined(HAVE_PETSC)
         Msg::Info("Implicit scheme based on PETSc");
         esys = new implicitHulbertChungPetsc<double>(_implicitOpts.alpham,_implicitOpts.alphaf,_implicitOpts.beta,_implicitOpts.gamma);
       #else
         Msg::Warning("petsc is not install on this computer. Use blas explicit system");
       #endif // HAVE_PETSC
      }
      else{
        Msg::Error("Petsc is not installed");
      }
    
       return esys;
    
    }
    
    linearSystem<double>* nonLinearMechSolver::createEigenSystem(){
      linearSystem<double> *sys = NULL;
      #if defined(HAVE_PETSC)
      sys = new linearSystemPETSc<double>();
      if(_solver_options.size())
        sys->setParameter(std::string("petsc_solver_options"),_solver_options);
      #else
      Msg::Error("Petsc is not intalled");
      #endif // HAVE_PETSC
      return sys;
    }
    
    void nonLinearMechSolver::createSystem()
    {
      if (_previousInit and _resetRestart)
      {
        // collect if previous dof exists
        if (_collection==NULL)
        {
          _collection = new DofCollection();
        }
        _collection->collect(pAssembler);
        // clear data
        delete pAssembler;
        pAssembler = NULL;
      }
      
      if(_vcompBySys.size()==0)
      {
        // create mono system and manager
        linearSystem<double> *lsys=NULL;
        linearSystem<double> *lsys2 = NULL;
        
        if(whatScheme==Explicit)
        {
          lsys = this->createExplicitSystem();
          pAssembler = new staticDofManager(lsys,whatScheme,false,true);
        }
        else if (whatScheme == Implicit)
        {
          lsys = this->createImplicitSystem();
          pAssembler = new staticDofManager(lsys,whatScheme,true,true);
        }
        else if (whatScheme == StaticLinear or whatScheme == StaticNonLinear)
        {
          lsys = this->createSNLSystem();
          if (whatScheme == StaticLinear)
          {
            pAssembler = new staticDofManager(lsys,whatScheme,true,true);
          }
          else
          {
            pAssembler = new staticDofManager(lsys,whatScheme,true,true);
          }
        }
        else if (whatScheme == Eigen)
        {
          lsys = this->createEigenSystem();
          if (_eigOpts.type == eigenSolverOptions::Dynamic)
          {
            lsys2 = this->createEigenSystem();
          }
          if (lsys2 != NULL and lsys != NULL)
          {
            pAssembler = new staticDofManager(lsys,lsys2,true);
          }
          else
          {
            pAssembler = new staticDofManager(lsys,whatScheme,true,true);
          }
        }
        else
        {
          Msg::Error("scheme %d has not been implemented",whatScheme);
        }
      }
      else 
      {
        // create multisystem and manager
        pAssembler = new dofManagerMultiSystems();
        dofManagerMultiSystems* allManager = static_cast<dofManagerMultiSystems*>(pAssembler);
        for(int i=0;i<_vcompBySys.size();i++)
        {
          linearSystem<double> *lsys;
          bool globalPos = true;
          if(_vschemeBySys[i] == Explicit)
          {
            lsys = this->createExplicitSystem();
            globalPos = false;
          }
          else
            lsys = this->createSNLSystem();
              
          nlsDofManager* currentAssembler = new staticDofManager(lsys,_vschemeBySys[i],globalPos,true);
          allManager->add(currentAssembler,_vcompBySys[i]);
        }
      }
    
    	// initial time step
    	if(whatScheme==Explicit or whatScheme==Multi)
      {
      
        double timeStep = _explicitOpts.gammas * this->initialCriticalExplicitTimeStep();
        #if defined(HAVE_MPI)
        if(Msg::GetCommSize()>1){
          // in case of mpi the time step is choosen as the minimal value compute on each partition
          double mpiTimeStep;
          MPI_Allreduce(&timeStep,&mpiTimeStep,1,MPI_DOUBLE,MPI_MIN,MPI_COMM_WORLD);
          timeStep = mpiTimeStep;
        }
        #endif // HAVE_MPI
        bool oneExplicitScheme=true;
        if (whatScheme==Multi)
        {
          oneExplicitScheme = false;
          for(int i=0;i<_vschemeBySys.size();i++)
          {
            if(_vschemeBySys[i]==Explicit)
            {
              oneExplicitScheme = true;
              break;
            }
          }
        }
        if (oneExplicitScheme)
        {
          Msg::Info("initial time step: %e",timeStep);
          pAssembler->setTimeStep(timeStep);
          _timeManager->setTimeStep(timeStep);
        }
    	}
    }
    
    int nonLinearMechSolver::NewtonRaphsonPathFollowing(const int numstep)
    {
       std::string name = "A";
      linearSystem<double>* lsys =pAssembler->getLinearSystem(name);
      nonLinearSystem<double>* nonsys = dynamic_cast<nonLinearSystem<double>*>(lsys);
    	pathFollowingSystem<double>* pathsys = dynamic_cast<pathFollowingSystem<double>*>(lsys);
    	
      if(computedUdF()){
          _ipf->setDeformationGradientGradient(this->getMicroBC()->getSecondOrderKinematicalVariable(), IPStateBase::current);
          _ipf->setValuesForBodyForce(IPStateBase::current);
      }
          
      // copy state from previous
    	//_ipf->copy(IPStateBase::previous,IPStateBase::current);
    	double normFinf= this->computeRightHandSide();
    
    	// loop until convergence
      int iter=0;
      double relnorm = 0.;
    
    	double norm0 = pAssembler->norm0Inf();
    	if (norm0 > 0) relnorm = normFinf/norm0;
    
    	double pfRef = pathsys->getPathFollowingConstraintRelativeRedidual();
    
      Msg::Info("iteration n %d : residu : %e, absolute residu : %e norm0: %e pf residual: %e",iter,relnorm,normFinf,norm0,pfRef);
    
    	while (((relnorm>_tol) and (normFinf>_absTol)) or ((pfRef> _tol) and (pfRef> _absTol)) or (iter == 0)){
        iter++;
        // Compute Stiffness Matrix
        this->computeStiffMatrix();
    
        // Solve KDu = Fext-Fint
        double t = Cpu();
        int succeed = pAssembler->systemSolveIntReturn();
        t = Cpu()-t;
        if(!succeed)
        {
          iter = _timeManager->getMaxNbIterations();
          break;
        }
        // update ipvariable
        _ipf->compute1state(IPStateBase::current,true);
    		// update load
    		double loadParam = pathsys->getControlParameter();
    		double dloadParam = fabs(pathsys->getControlParameterStep());
    		this->oneStepPreSolve(loadParam,dloadParam,numstep);
    
        if (!_iterativeNR) break;
        // new forces
        normFinf = this->computeRightHandSide();
    
        if (norm0 == 0.) {
          norm0 = pAssembler->norm0Inf();
        }
    
        // Check of convergence
        relnorm = normFinf/norm0;
    		pfRef = pathsys->getPathFollowingConstraintRelativeRedidual();
        Msg::Info("iteration n %d : residu : %e, absolute residu : %e pf residual: %e  solving time: %f ",  iter,relnorm,normFinf,pfRef,t);
    
        if((iter == _timeManager->getMaxNbIterations()) or std::isnan(relnorm))
        {
          iter = _timeManager->getMaxNbIterations(); // for isnan case
          break;
        }
      }
    
      return iter;
    };
    
    
    double nonLinearMechSolver::solveSNL()
    {
      _timeManager->initializeTimeSteppingPlan(); // start time stepping plan
      Msg::Info("Data : nstep =%d endtime=%f",_timeManager->getNumSteps(),_timeManager->getEndTime());
      this->initializeStaticScheme(); // init everything
      static std::string name = "A";
    	linearSystem<double>* lsys =pAssembler->getLinearSystem(name);
    	pathFollowingSystem<double>* pathsys = dynamic_cast<pathFollowingSystem<double>*>(lsys);
      
      // if restart
      if(restartManager::available() and !_resetRestart)
      {
        if (getNumRanks() > 1)
          Msg::Barrier(); // To wait rank0 before end
        restartManager::openRestartFile("nonLinearMechSolver");
        restartManager::restart(this);
        // add restart other vars here if necessary
        //
        //
        restartManager::closeRestartFile();
    
        restartManager::InitFromRestartFile();
    
        if (getNumRanks() > 1)
          Msg::Barrier(); // To wait rank0 before end
        struct stat st;
        if(stat("beforeRestart",&st) == 0)
        {
          std::ostringstream oss;
          oss << _timeManager->getLastIterationIndex();
          std::string cpDir="mv beforeRestart restart"+oss.str();
          int oks = system(cpDir.c_str());
        }
        #if defined(HAVE_MPI)
        if(Msg::GetCommSize()>1)
        { //we need to reinitializae after the restart of the nonLinearSolver
          Msg::Info("MPI restart initialization begins");
          pAssembler->systemMPIComm(); // To initiate the MPI Communication
          Msg::Info("MPI restart initialization OK");
        }
        #endif // HAVE_MPI
      }
      if(restartManager::internalStateRestartAvailable())
      {
          loadInternalState(this->getRestartDomainTag());
    
          // to debug the loading of internal state
          // write the stress*.msh and disp*.msh files to check
          // loaded values
          /*
          double DT = _timeManager->getTimeStep();
          double CURtime = DT+_timeManager->getLastTime();
          int NStep = _timeManager->getLastIterationIndex();
          _ufield->archive(CURtime,NStep+1,false);
          _ipf->archive(CURtime,NStep+1,false);
           */
      }
      _resetRestart=false;
      if (getNumRanks() > 1)
        Msg::Barrier(); // To wait rank0 before end
        
      while (!_timeManager->reachEndTime())
      {
        double endtime = _timeManager->getEndTime(); // endtime
        double dt = _timeManager->getTimeStep();  // current timestep
        double curtime = dt+_timeManager->getLastTime(); // current time = lastime + timeStep
        int ii = _timeManager->getLastIterationIndex(); // last num step index
        
        // set dofmananger and ipfield
        pAssembler->setTimeStep(dt);
        _ipf->setTime(curtime,dt,ii+1);
        
        // start solving
        Msg::Info("t= %e on %e (step = %d)",curtime,endtime,ii);
        int niteNR  = this->oneStaticStep(curtime,dt,ii+1);
        // after solving
        
        bool redoStepWithSelectiveUpdate = false;
        bool willEnd = false;
        if (niteNR < _timeManager->getMaxNbIterations())
        {
          // check for monitoring or selective update
          if (!_ipf->isAllChecked() or (_endSchemeMonitoringObject!=NULL) or (_selectiveObject != NULL)){
            _ipf->checkAll();
          }
    
          if (_selectiveObject != NULL){
            _selectiveObject->checkSelectiveUpdate(this);
            if (_selectiveObject->solverIterateBySelectiveUpdate()){
              redoStepWithSelectiveUpdate = true;
            }
          }
    
          if (_endSchemeMonitoringObject!=NULL ){
            // check of end cr is satisfy
            _endSchemeMonitoringObject->checkEnd(this);
            willEnd = _endSchemeMonitoringObject->solverEndedByMornitoring();
            if (!willEnd){
              if (_endSchemeMonitoringObject->reduceTimeStepByMornitoring()){
                Msg::Warning("reduce by monitoring");
                niteNR = _timeManager->getMaxNbIterations();
              }
            }
          }
        }
        
        if(niteNR >= _timeManager->getMaxNbIterations()) // time step reduction
        {
          // reduce time step for next solve
          _timeManager->reduceTimeStep(); // solver fails counter in _timeManager is increased by 1
          if (_pathFollowing)
          {
            _pfManager.reducePathFollowingStep(_timeManager);
          }
          
          // reset everything to previous step
          pAssembler->resetUnknownsToPreviousTimeStep();
          _ipf->copy(IPStateBase::previous,IPStateBase::current);
          if (_selectiveObject != NULL)
          {
           _selectiveObject->resetToPreviousStep();
          }
          if (_endSchemeMonitoringObject!=NULL){
           _endSchemeMonitoringObject->resetToPreviousStep();
          }
          
          //
          if(_timeManager->getNbFails() > _timeManager->getMaxNbFails()) // end of simulation
          {
            Msg::Error("Simulation end due to convergence problem! Values are set to the last converged time step in case you want to switch to another scheme");
            break;
          }
          else
          {
            Msg::Warning("Convergence of Newton-Raphson failed (%d fails on %d allowable fails) --> reduced time step",_timeManager->getNbFails(),_timeManager->getMaxNbFails());
          }
          
          
        }
        else if (redoStepWithSelectiveUpdate)
        {
          // redo simulation without time change
          Msg::Warning("redo solving with selective update");
          _selectiveObject->selectiveUpdate(this);
        }
        else
        {
          if (_pathFollowing){
            _pfManager.saveIncrementHistory(this);
          }
          // as simulation go wells
          _timeManager->saveTimeHistory();
          // for arching
          this->oneStepPostSolve(curtime,ii+1);
          
          // estimate time step for next solve
          _timeManager->computeTimeStepForNextSolving(niteNR);
          
          if (_pathFollowing)
          {
            _pfManager.computePathFollowingIncrementForNextSolving(this,niteNR,_timeManager);
          }
          
          if (willEnd)
          {
            Msg::Info("Computation is finished following _endSchemeMonitoring");
            _endSchemeMonitoringObject->endSolverByMonitoring(true);
            break;
          }
        }
      }
      saveInternalState(this->getRestartDomainTag());
    
      /* end of scheme */
      return this->finalizeStaticScheme(_timeManager->getLastTime(),_timeManager->getLastIterationIndex());
    };
    
    
    double nonLinearMechSolver::solveMulti()
    {
      _timeManager->initializeTimeSteppingPlan();
      Msg::Info("Mixed static and dynamic solving: endtime=%e",_timeManager->getEndTime());
      Msg::Info("Static Data: tolerance: %lf",_tol);
      Msg::Info("Dynamic Data: beta=%f gamma=%f alpha_m=%f",_explicitOpts.beta, _explicitOpts.gamma, _explicitOpts.alpham);
      /* init data */
      this->init();
      this->init2();
      // iterative scheme (loop on timestep)
    
      /* mass matrix computation outside the loop */
      for(std::vector<partDomain*>::iterator it = domainVector.begin(); it!=domainVector.end(); ++it){
        partDomain *dom = *it;
        AssembleMass(dom->getBilinearMassTerm(),*(dom->getFunctionSpace()),dom->element_begin(),dom->element_end(),
                   *(dom->getBulkGaussIntegrationRule()),*pAssembler);
      }
      if(_notResetedContact){
        for(nonLinearMechSolver::contactContainer::iterator itc = _allContact.begin(); itc!= _allContact.end(); ++itc){
          contactDomain *cdom = *itc;
          if(cdom->isRigid()){
            rigidContactSpaceBase *rcsp = static_cast<rigidContactSpaceBase*>(cdom->getSpace());
            AssembleMass(cdom->getMassTerm(),rcsp,pAssembler);
          }
        }
      }
      pAssembler->nextStep();   // save solution of previous (initially last step == initial step BEAWARE FOR RESTART)
      //
      bool forceTimeStepEvaluation=false;
      if(restartManager::available() and !_resetRestart)
      {
        if (getNumRanks() > 1)
          Msg::Barrier(); // To wait rank0 before end
        restartManager::openRestartFile("nonLinearMechSolver");
        restartManager::restart(this);
        //
        //
        restartManager::closeRestartFile();
        restartManager::InitFromRestartFile();
        if (getNumRanks() > 1)
          Msg::Barrier(); // To wait rank0 before end
    
        struct stat st;
        if(stat("beforeRestart",&st) == 0)
        {
          std::ostringstream oss;
          oss << _timeManager->getLastIterationIndex();
          std::string cpDir="mv beforeRestart restart"+oss.str();
          int oks = system(cpDir.c_str());
        }
        #if defined(HAVE_MPI)
        if(Msg::GetCommSize()>1)
        { //we need to reinitializae after the restart of the nonLinearSolver
          Msg::Info("MPI restart initialization begins");
          pAssembler->systemMPIComm(); // To initiate the MPI Communication
          Msg::Info("MPI restart initialization OK");
        }
        #endif // HAVE_MPI
        forceTimeStepEvaluation=true;
      };
      _resetRestart=false;
      if (getNumRanks() > 1)
        Msg::Barrier(); // To wait rank0 before end
    
      //
      bool oneExplicitScheme=false;
      for(int i=0;i<_vschemeBySys.size();i++)
      {
        if(_vschemeBySys[i]==Explicit)
        {
          oneExplicitScheme = true;
          break;
        }
      }
      
      double dt =0.;  // shear during simulation
      /* time loop */
      while(!_timeManager->reachEndTime())
      {
        double endtime = _timeManager->getEndTime();
        double lasttime = _timeManager->getLastTime();
        int step = _timeManager->getLastIterationIndex();
        // compute the time step (explicit limitation)
        if(((step% _explicitOpts.numstepExpl ==0) || forceTimeStepEvaluation) && oneExplicitScheme)
        {
          dt = _explicitOpts.gammas * this->criticalExplicitTimeStep();
          #if defined(HAVE_MPI)
           if(Msg::GetCommSize()>1){
              // in case of mpi the time step is choosen as the minimal value compute on each partition
              double mpiTimeStep;
              MPI_Allreduce(&dt,&mpiTimeStep,1,MPI_DOUBLE,MPI_MIN,MPI_COMM_WORLD);
              dt = mpiTimeStep;
           }
          #endif // HAVE_MPI
          Msg::Info("explicit time step value: %e at time %e",dt,lasttime);
          pAssembler->setTimeStep(dt);
          _timeManager->setTimeStep(dt);
          forceTimeStepEvaluation = false;
        }
        else if(!oneExplicitScheme)
        {
          dt = _timeManager->getTimeStep();
          pAssembler->setTimeStep(dt);
          Msg::Info("implicit time step value: %e at time %e",dt,lasttime);
        }
        //
        double curtime = lasttime+dt;
        if(curtime > endtime)
        {
          curtime = endtime;
          dt = curtime-lasttime;
          pAssembler->setTimeStep(dt);
          _timeManager->setTimeStep(dt);
        };
    
        this->oneStepPreSolve(curtime,dt,step+1);
        // Solve one step by NR scheme
        int niteNR=0;
        if((step>0 && (step%_explicitOpts.numstepImpl==0)) or !oneExplicitScheme)
        {
          // not solve if step = 0, to make sure if explicit solver present, it starts first
          Msg::Info("Solve implicit system(s): step = %d, t = %e, dt = %e",step,curtime,dt);
          double tsystresol = Cpu();
          niteNR = NewtonRaphson(step+1);
          tsystresol = Cpu() - tsystresol;
          Msg::Info("Time of Newton-Raphson resolution: %f: ",tsystresol);
          
          if(niteNR >= _timeManager->getMaxNbIterations()) // time step reduction
          {
            if (!oneExplicitScheme) // if no explicit, time step reduce as normal
            {
              // reduce time step for next solve
              _timeManager->reduceTimeStep(); // solver fails counter in _timeManager is increased by 1
              // reset everything to previous step
              pAssembler->resetUnknownsToPreviousTimeStep();
              _ipf->copy(IPStateBase::previous,IPStateBase::current);
              
              if(_timeManager->getNbFails() > _timeManager->getMaxNbFails()) // end of simulation
              {
                Msg::Error("Simulation end due to convergence problem! Values are set to the last converged time step in case you want to switch to another scheme");
                break;
              }
              else
              {
                Msg::Warning("Convergence of Newton-Raphson failed (%d fails on %d allowable fails) --> reduced time step",_timeManager->getNbFails(),_timeManager->getMaxNbFails());
              }          
            }
            else
            {
              Msg::Error("Simulation end due to convergence problem! Values are set to the last converged time step in case you want to switch to another scheme");
              break;
            }
          }
          else if(niteNR!=0)
          {
            _timeManager->saveTimeHistory();
            this->oneStepPostSolve(curtime,step+1);
            if (!oneExplicitScheme)
            {
              // time step is adapted if no explict
              _timeManager->computeTimeStepForNextSolving(niteNR);
            }
          }
        }
        if(niteNR==0) // no Implicit computation or no force on the implicit systems
        {
          // solve only the Explicit scheme
          int globalsuc = 0;
          for(int i=0;i<_vschemeBySys.size();i++)
          {
            nlsDofManager *nlass = pAssembler->getManager(i);
            if(nlass->getScheme()==Explicit)
              globalsuc = nlass->systemSolveIntReturn();
          }
          if(globalsuc){ // otherwise no resolution (QS scheme only with no forces) !!
            // compute the new forces
            _ipf->compute1state(IPStateBase::current,false);
            double normRHS = this->computeRightHandSide();
            if(std::isnan(normRHS))
            {
              Msg::Error("Nan force value --> end");
              break;
            }
            if(_explicitOpts.dynamicRelaxation)
            {
              double norm0 = pAssembler->norm0Inf();
              if(step%_explicitOpts.numstepExpl ==0 ) Msg::Info("Rel norm for dynamic relaxation %e tol = %e",normRHS/norm0,_tol);
              if(normRHS/norm0 < _tol){
                Msg::Info("Dynamic relaxation procedure is achieved");
                break;
              }
              if(curtime == _timeManager->getEndTime())
                Msg::Info("Dynamic relaxation ended as the final time is reached. But convergence is not achieved!");
            }
          }
          _timeManager->saveTimeHistory();
          this->oneStepPostSolve(curtime,step+1);
        }
      }
      /* end of scheme */
      this->endOfScheme(_timeManager->getLastTime(),_timeManager->getLastIterationIndex());
      Msg::Info("Multi OK");
      return _timeManager->getLastTime();
    }
    void nonLinearMechSolver::unknownBuildView(const int comp,const int nbstep)
    {
      int nstep_;
      nsba==0 ? nstep_ = nbstep : nstep_=nsba;
    
      switch(comp){
       case 1:
        unknownView.emplace_back("velocity",1,nlsField::crude,0,nlsField::val, nstep_);
        break;
       case 2:
        unknownView.emplace_back("acceleration",2,nlsField::crude,0,nlsField::val,nstep_);
        break;
       default:
        unknownView.emplace_back("displacement",0,nlsField::crude,0,nlsField::val,nstep_);
      }
    }
    
    void nonLinearMechSolver::OneUnknownBuildView(const std::string vname, const int comp,const int nbstep)
    {
      int nstep_;
      nsba==0 ? nstep_ = nbstep : nstep_=nsba;
    
      int unknownComp = -1 - comp;
      unknownView.emplace_back(vname,unknownComp,nlsField::crude,0,nlsField::val,nstep_);
    };
    
    void nonLinearMechSolver::internalPointBuildView(const std::string vname,const int comp,
                                                     const int nbstep,const int ev, const int gp)
    {
      nlsField::ElemValue ev_;
      switch(ev){
       case 0:
        ev_ = nlsField::crude;
        break;
       case 1:
        ev_ = nlsField::mean;
        break;
       case 2 :
        ev_ = nlsField::min;
        break;
       case 3 :
        ev_ = nlsField::max;
        break;
       default:
        ev_ = nlsField::mean;
      }
      int nstep_;
      nsba==0 ? nstep_ = nbstep : nstep_=nsba;
      ipView.emplace_back(vname,comp,ev_,gp,nlsField::val,nstep_);
    }
    void nonLinearMechSolver::internalPointBuildViewInterface(const std::string vname,const int comp,
                                                     const int nbstep,const int ev, const int gp)
    {
      nlsField::ElemValue ev_;
      switch(ev){
       case 0:
        ev_ = nlsField::crude;
        break;
       case 1:
        ev_ = nlsField::mean;
        break;
       case 2 :
        ev_ = nlsField::min;
        break;
       case 3 :
        ev_ = nlsField::max;
        break;
       default:
        ev_ = nlsField::mean;
      }
      int nstep_;
      nsba==0 ? nstep_ = nbstep : nstep_=nsba;
      ipViewInterface.emplace_back(vname,comp,ev_,gp,nlsField::val,nstep_);
    }
    void nonLinearMechSolver::internalPointBuildViewIncrement(const std::string vname,const int comp,
                                                     const int nbstep,const int ev,const int gp)
    {
      nlsField::ElemValue ev_;
      switch(ev){
       case 0:
        ev_ = nlsField::crude;
        break;
       case 1:
        ev_ = nlsField::mean;
        break;
       case 2 :
        ev_ = nlsField::min;
        break;
       case 3 :
        ev_ = nlsField::max;
        break;
       default:
        ev_ = nlsField::mean;
      }
      int nstep_;
      nsba==0 ? nstep_ = nbstep : nstep_=nsba;
      ipView.emplace_back("Increment_"+vname,comp,ev_,gp,nlsField::increment,nstep_);
    }
    void nonLinearMechSolver::internalPointBuildViewRate(const std::string vname,const int comp,
                                                     const int nbstep,const int ev, const int gp)
    {
      nlsField::ElemValue ev_;
      switch(ev){
       case 0:
        ev_ = nlsField::crude;
        break;
       case 1:
        ev_ = nlsField::mean;
        break;
       case 2 :
        ev_ = nlsField::min;
        break;
       case 3 :
        ev_ = nlsField::max;
        break;
       default:
        ev_ = nlsField::mean;
      }
      int nstep_;
      nsba==0 ? nstep_ = nbstep : nstep_=nsba;
      ipView.emplace_back("Rate_"+vname,comp,ev_,gp,nlsField::rate,nstep_);
    }
    void nonLinearMechSolver::energyBuildView(const std::string vname,const int comp,const int nbstep)
    {
      int nstep_;
      nsba==0 ? nstep_ = nbstep : nstep_=nsba;
      switch(comp){
       case 1:
        energyView.emplace_back("kinetic",1,nlsField::crude,0,nlsField::val,nstep_);
        break;
       case 2:
        energyView.emplace_back("deformation",2,nlsField::crude,0,nlsField::val,nstep_);
        break;
       case 3:
        energyView.emplace_back("external work",3,nlsField::crude,0,nlsField::val,nstep_);
        break;
       default:
        energyView.emplace_back("total",4,nlsField::crude,0,nlsField::val,nstep_);
      }
    }
    
    void nonLinearMechSolver::internalPointBuildViewShell(int loc, const std::string vname,const int comp,
                                                     const int nbstep,const int ev, const int gp)
    {
      nlsField::ElemValue ev_;
      switch(ev){
       case 0:
        ev_ = nlsField::crude;
        break;
       case 1:
        ev_ = nlsField::mean;
        break;
       case 2 :
        ev_ = nlsField::min;
        break;
       case 3 :
        ev_ = nlsField::max;
        break;
       default:
        ev_ = nlsField::mean;
      }
      int nstep_;
      nsba==0 ? nstep_ = nbstep : nstep_=nsba;
      ipView.emplace_back(vname,comp,ev_,gp,nlsField::val,nstep_,loc);
    }
    void nonLinearMechSolver::internalPointBuildViewIncrementShell(int loc, const std::string vname,const int comp,
                                                     const int nbstep,const int ev,const int gp)
    {
      nlsField::ElemValue ev_;
      switch(ev){
       case 0:
        ev_ = nlsField::crude;
        break;
       case 1:
        ev_ = nlsField::mean;
        break;
       case 2 :
        ev_ = nlsField::min;
        break;
       case 3 :
        ev_ = nlsField::max;
        break;
       default:
        ev_ = nlsField::mean;
      }
      int nstep_;
      nsba==0 ? nstep_ = nbstep : nstep_=nsba;
      ipView.emplace_back("Increment_"+vname,comp,ev_,gp,nlsField::increment,nstep_,loc);
    }
    void nonLinearMechSolver::internalPointBuildViewRateShell(int loc, const std::string vname,const int comp,
                                                     const int nbstep,const int ev, const int gp)
    {
      nlsField::ElemValue ev_;
      switch(ev){
       case 0:
        ev_ = nlsField::crude;
        break;
       case 1:
        ev_ = nlsField::mean;
        break;
       case 2 :
        ev_ = nlsField::min;
        break;
       case 3 :
        ev_ = nlsField::max;
        break;
       default:
        ev_ = nlsField::mean;
      }
      int nstep_;
      nsba==0 ? nstep_ = nbstep : nstep_=nsba;
      ipView.emplace_back("Rate_"+vname,comp,ev_,gp,nlsField::rate,nstep_,loc);
    }
    
    void nonLinearMechSolver::energyComputation(const int val)
    {
      _energyComputation=val;
    }
    
    void nonLinearMechSolver::fractureEnergy(const int val)
    {
      _fractureEnergyComputation=val;
    }
    
    void nonLinearMechSolver::fillConstraintDof()
    {
    
      //for(nonLinearConstraintBC& cbc : allConstraint)
      for(std::list<nonLinearConstraintBC>::iterator cbc=allConstraint.begin(); cbc!=allConstraint.end(); cbc++)
      {
        cbc->constraintDofs.clear();
        std::vector<MVertex*> vv;
        int dim=0;
        switch(cbc->onWhat){
         case nonLinearBoundaryCondition::ON_VERTEX:
          dim = 0;
          break;
         case nonLinearBoundaryCondition::ON_EDGE:
          dim = 1;
          break;
         case nonLinearBoundaryCondition::ON_FACE:
          dim = 2;
          break;
         case nonLinearBoundaryCondition::ON_VOLUME:
          dim = 3;
          break;
        }
        pModel->getMeshVerticesForPhysicalGroup(dim,cbc->_tag,vv);
    
        // loop on domain (to find the domain where the force is applied)
        for(std::vector<partDomain*>::iterator itdom=domainVector.begin(); itdom!=domainVector.end(); ++itdom){
          partDomain *dom = *itdom;
          FunctionSpaceBase *sp = dom->getFunctionSpace();
          std::vector<Dof> R;
          if(!(dom->getFormulation())){  // Cg/Dg
            FilterDof* filter = dom->createFilterDof(cbc->_comp);
            for(elementGroup::vertexContainer::const_iterator itv=dom->vertex_begin(); itv!=dom->vertex_end(); ++itv){
              MVertex *ver = itv->second;
              for(std::vector<MVertex*>::iterator it=vv.begin(); it!=vv.end(); ++it){
                MVertex *vver = *it;
                if(ver == vver){
                  MPoint ele(ver);
                  // build the dof
                  R.clear();
                  sp->getKeys(&ele,R);
                  for(int j=0;j<R.size(); j++)
                    if(filter->operator()(R[j]))
                      cbc->constraintDofs.push_back(R[j]);
                  break;
                }
              }
            }
            delete filter;
          }
          else{ // full Dg
            for(elementGroup::elementContainer::const_iterator ite = dom->element_begin(); ite!=dom->element_end(); ++ite){
              MElement *ele = ite->second;
              int nbvertex = ele->getNumVertices();
              sp->getKeys(ele,R);
              for(std::vector<MVertex*>::iterator it=vv.begin(); it!=vv.end(); ++it){
                for(int j=0;j<nbvertex; j++){
                  if(ele->getVertex(j) == *it){
                    cbc->constraintDofs.push_back(R[j+nbvertex*cbc->_comp]);
                    break;
                  }
                }
              }
              R.clear();
            }
          }
        }
        cbc->constraintDofs.unique();
      }
    };
    
    void nonLinearMechSolver::applyConstraintBC()
    {
      //for(const nonLinearConstraintBC& cbc: allConstraint){
      for(std::list<nonLinearConstraintBC>::const_iterator cbc=allConstraint.begin(); cbc!=allConstraint.end(); cbc++){
        for (std::list<Dof>::const_iterator it =cbc->constraintDofs.begin(); it != cbc->constraintDofs.end(); it++)
        {
          if (it!=cbc->constraintDofs.begin())
          {
            DofAffineConstraint<double> cons;
            cons.linear.push_back(std::pair<Dof,double>(*(cbc->constraintDofs.begin()),1));
            cons.shift=0;
            pAssembler->setLinearConstraint(*it,cons);
            cons.linear.clear();
          }
        };
      }
    };
    
    void nonLinearMechSolver::initCornerBC(){
      if (!_testFlag) {
        Msg::Warning("fix corner Dof is only used for microscopic test only");
        return;
      }
      if (_pbcGroup != NULL){
        for(std::list<nonLinearDirichletBCAtCorner>::iterator cbc=allCornerConstraint.begin(); cbc!=allCornerConstraint.end(); cbc++){
          std::vector<Dof>& allDof = cbc->constraintDofs;
          std::vector<Dof> R;
          MVertex* v = _pbcGroup->getCornerVertex(cbc->_cornerNumber);
          _pbcGroup->getDofsOfCornerVertex(cbc->_cornerNumber,R);
          allDof.push_back(R[cbc->_comp]);
          double val = cbc->_f->operator()(v->x(),v->y(),v->z());
          pAssembler->fixDof(R[cbc->_comp],val);
        }
    
        for(std::list<nonLinearNeumannBCAtCorner>::iterator cbc=allCornerForce.begin(); cbc!=allCornerForce.end(); cbc++){
          std::vector<Dof>& allDof = cbc->constraintDofs;
          std::vector<Dof> R;
          MVertex* v = _pbcGroup->getCornerVertex(cbc->_cornerNumber);
          _pbcGroup->getDofsOfCornerVertex(cbc->_cornerNumber,R);
          allDof.push_back(R[cbc->_comp]);
        }
      }
    };
    
    void nonLinearMechSolver::fixDofCorner(){
      if (!_testFlag) {
        Msg::Warning("fix corner Dof is only used for microscopic test only");
        return;
      }
      if (_pbcGroup != NULL){
        for(std::list<nonLinearDirichletBCAtCorner>::const_iterator cbc=allCornerConstraint.begin(); cbc!=allCornerConstraint.end(); cbc++){
          const std::vector<Dof>& allDof = cbc->constraintDofs;
          MVertex* v = _pbcGroup->getCornerVertex(cbc->_cornerNumber);
          for (int i=0; i< allDof.size(); i++){
            double val = cbc->_f->operator()(v->x(),v->y(),v->z());
            pAssembler->fixDof(allDof[i],val);
          }
        }
      }
    };
    
    void nonLinearMechSolver::applyLinearConstraintBetweenTwoGroups()
    {
      if (isDgDomain())
      {
        if (allLinearConstraintBC.size() > 0)
        {
          Msg::Error("linear constraint BC between two groups is not implemented");
          Msg::Exit(0);
          
        }
      }
      else
      {
        FunctionSpaceBase* sp = domainVector[0]->getFunctionSpace();
        for (std::list<nonLinearLinearConstraintBCBetweenTwoGroups>::iterator itbc=allLinearConstraintBC.begin(); itbc!=allLinearConstraintBC.end(); itbc++)
        {
          nonLinearLinearConstraintBCBetweenTwoGroups& bc = *itbc;
          const linearCombinationOfVertices* rh = bc.rightHandSide;
          const setInt& excludeVertices = bc.excludedVertices;
          double factNeg = bc.slaveFactor;
          // get right hand side term
          std::vector<int> comp(1,bc.comp);
          std::vector<std::pair<Dof,double> > Rroot;
          for (std::map<MVertex*,std::pair<int,double> >::const_iterator itvroot = rh->combinationV.begin(); itvroot != rh->combinationV.end(); itvroot++)
          {
            MVertex* vr = itvroot->first;
            const std::pair<int,double>& compFact = itvroot->second;
            //
            std::vector<Dof> R;
            std::vector<int> compR(1,compFact.first);
            getKeysFromVertex(sp,vr,compR,R);
            Rroot.push_back(std::pair<Dof,double>(R[0],compFact.second));
          }
          
          for (elementGroup::vertexContainer::const_iterator itvmaster = bc.gMaster->vbegin(); itvmaster != bc.gMaster->vend(); itvmaster++)
          {
            MVertex* vmaster = itvmaster->second;
            if (rh->combinationV.find(vmaster) == rh->combinationV.end() and !(excludeVertices.included(vmaster->getNum())))
            {
              SPoint3 pp;
              bc.vgOperation->apply(vmaster,pp);
              MElement* eleSlave = NULL;
              for (elementGroup::elementContainer::const_iterator ite = bc.gSlave->begin(); ite != bc.gSlave->end(); ite++)
              {
                MElement* es = ite->second;
                if (es->getDim() == 0)
                {
                  eleSlave = es;
                }
                else if (isInside(pp,es)){
                  eleSlave = es;
                  Msg::Info("found slave element %d for vertex %d",eleSlave->getNum(),vmaster->getNum());
                  break;
                }
              }
              
              std::vector<MVertex*> vv;
              int size = 0;
              std::vector<double> fVal;
              if (eleSlave != NULL)
              {
                SPoint3 point;
                size = eleSlave->getNumVertices();
                eleSlave->getVertices(vv);
    
                if (eleSlave->getDim() == 0)
                {
                  point = vv[0]->point();
                }
                else if (eleSlave->getDim() == 1)
                {
                  point = project(pp,vv[0]->point(),vv[1]->point());
                }
                else if (eleSlave->getDim() == 2)
                {
                  point = project(pp,vv[0]->point(),vv[1]->point(),vv[2]->point());
                };
    
                double xyz[3]={point[0],point[1],point[2]};
                double uvw[3];
                eleSlave->xyz2uvw(xyz,uvw);
    
                fVal.resize(size);
                eleSlave->getShapeFunctions(uvw[0],uvw[1],uvw[2],&fVal[0]);
              }
    
              std::vector<Dof> RMaster, RSlave;
              getKeysFromVertex(sp,vmaster,comp,RMaster);
              for (int iv=0; iv < size; iv++)
              {
                getKeysFromVertex(sp,vv[iv],comp,RSlave);
              }
    
              if (!pAssembler->isConstrained(RMaster[0]) and !pAssembler->isFixed(RMaster[0]))
              {
                DofAffineConstraint<double> cons;
                cons.shift = 0.;
                for (int ivf=0; ivf< size; ivf++)
                {
                  cons.linear.push_back(std::pair<Dof,double>(RSlave[ivf],factNeg*fVal[ivf]));
                }
                
                for (int ivf = 0; ivf < Rroot.size(); ivf++)
                {
                  cons.linear.push_back(Rroot[ivf]);
                }
                pAssembler->setLinearConstraint(RMaster[0],cons);
              }
            }
            
          }
        }
      }
    };
    
    void nonLinearMechSolver::applyPBCBetweenTwoGroups()
    {
    	std::set<MVertex*> edgeVertices;
      std::set<MVertex*> cornerVertices;
      if (allPeriodic.size() > 0)
      {
        for (int i=0; i< _edgePeriodicPhysicals.size(); i++)
        {
          elementGroup gr(1,_edgePeriodicPhysicals[i]);
          for (elementGroup::vertexContainer::const_iterator it = gr.vbegin(); it != gr.vend(); it++)
          {
            edgeVertices.insert(it->second);
          }
        }
        for (int i=0; i< _nodePeriodicPhysicals.size(); i++)
        {
          elementGroup gr(0,_nodePeriodicPhysicals[i]);
          for (elementGroup::vertexContainer::const_iterator it = gr.vbegin(); it != gr.vend(); it++)
          {
            cornerVertices.insert(it->second);
          }
        }
      }
      // create pbcmap
    	if (isDgDomain())
      {
        // apply constraint
        if (allPeriodic.size() > 0)
        {
          Msg::Info("begin accounting PBC rank %d",Msg::GetCommRank());
        }
        
    		for (std::list<nonLinearPeriodicBCBetweenTwoGroups>::iterator pbctg=allPeriodic.begin(); pbctg!=allPeriodic.end(); pbctg++)
        {
          nonLinearPeriodicBCBetweenTwoGroups& bc = *pbctg;
          elementGroup* gMaster = bc.g2;
          elementGroup* gSlave = bc.g1;
          MVertex* vRootMaster = bc.v2;
          MVertex* vRootSlave = bc.v1;
          
          auto checkConsider = [&bc, &vRootMaster, &vRootSlave, &edgeVertices, &cornerVertices](MVertex* vMaster)
          {
            if ((vRootMaster != NULL) && (vRootSlave !=NULL))
            {
              if (vMaster->getNum() == vRootMaster->getNum())
              {
                return false;
              }
            }
            //
            if (bc.onWhat == nonLinearBoundaryCondition::ON_FACE)
            {
              if (edgeVertices.find(vMaster) != edgeVertices.end())
              {
                return false;
              }
            }
            else if (bc.onWhat == nonLinearBoundaryCondition::ON_EDGE)
            {
              if (cornerVertices.find(vMaster) != cornerVertices.end())
              {
                return false;
              }
            }
            return true;
          };
          
          std::set<pbcElement*> allSlaveElements;
          pbcElement::createPBCElements(*gSlave,domainVector,allSlaveElements);
          
          for (elementGroup::elementContainer::const_iterator itg = gMaster->begin(); itg != gMaster->end(); itg++)
          {
            MElement* eleMaster = itg->second;
            SPoint3 masterBaryCenter = eleMaster->barycenter();
            //
            // projection element is first found if periodic meshes are used
            std::set<pbcElement*>::iterator foundSlaveIt = allSlaveElements.begin();
            pbcElement* closestSlave = *(foundSlaveIt);
            double length = masterBaryCenter.distance(closestSlave->getSubElement()->barycenter());
            // find closest bary center as slave element
            std::set<pbcElement*>::iterator slaveIt = foundSlaveIt;
            slaveIt++;
            for (slaveIt; slaveIt != allSlaveElements.end(); slaveIt++)
            {
              pbcElement* es = *slaveIt;
              double lengthtemp = masterBaryCenter.distance(es->getSubElement()->barycenter());
              if (lengthtemp < length){
                closestSlave = es;
                length = lengthtemp;
                foundSlaveIt = slaveIt;
              };
            }
            // check if two elements are fitted
            auto checkFittingElements = [&bc, &length](MElement* eleMaster, MElement* eleclosest)
            {
              if (bc.onWhat == nonLinearBoundaryCondition::ON_VERTEX)
              {
                return true;
              }
              if (eleMaster->getNumPrimaryVertices() != eleclosest->getNumPrimaryVertices())
              {
                return false;
              }
              int N = eleMaster->getNumPrimaryVertices();
              std::vector<MVertex*> allVerticesMaster(N,NULL);
              std::vector<MVertex*> allVerticesClosest(N,NULL);
              
              for (int iv=0; iv< N; iv++)
              {
                allVerticesMaster[iv] = eleMaster->getVertex(iv);
                allVerticesClosest[iv] = eleclosest->getVertex(iv);            
              }
              
              fullVector<double> distanceTwoVer(N);
              for (int iv=0; iv< N; iv++)
              {
                auto findNearestVertex = [&allVerticesClosest](MVertex* vp)
                {
                  MVertex* vinit = allVerticesClosest[0];
                  double dist=vinit->distance(vp);
                  for (int j=1; j< allVerticesClosest.size(); j++)
                  {
                    MVertex* v=allVerticesClosest[j];
                    double tempDist=v->distance(vp);
                    if (tempDist<dist) 
                    {
                      dist=tempDist;
                      vinit=v;
                    };
                  };
                  return vinit;
                };
                MVertex* temp = findNearestVertex(allVerticesMaster[iv]);
                distanceTwoVer(iv) = temp->distance(allVerticesMaster[iv]) - length;
              };
              
              if (distanceTwoVer.norm() < 1e-4*length)
              {
                //Msg::Info("found two pbc elements master %d slave %d",eleMaster->getNum(),eleclosest->getNum());
                return true;
              }
              else
              {
                return false;
              }
              return false;
            };
            
            pbcElement* pbcElementSlave = NULL;
            if (checkFittingElements(eleMaster,closestSlave->getSubElement()))
            {
              pbcElementSlave = closestSlave;
            }
             //
            for (int iv =0; iv < eleMaster->getNumVertices(); iv ++)
            {
              MVertex* vMaster = eleMaster->getVertex(iv);
              if (checkConsider(vMaster))
              {
                std::vector<pbcVertex*> allMasterVertices;
                pbcVertex::createPBCVertices(vMaster,domainVector,allMasterVertices);
                for (int ic=0; ic< allMasterVertices.size(); ic++)
                {
                  // find negative in allSlaveElements
                  pbcVertex* pbcVertexMaster = allMasterVertices[ic];
                  
                  if (pbcElementSlave == NULL)
                  {  
                    if (bc.onWhat == nonLinearBoundaryCondition::ON_VERTEX)
                    {
                      pbcElementSlave  = *(allSlaveElements.begin());
                    }
                    else
                    {
                      for (std::set<pbcElement*>::const_iterator ite = allSlaveElements.begin(); ite != allSlaveElements.end(); ite++)
                      {
                        pbcElement* es = *ite;
                        if (InterpolationOperations::isInside(pbcVertexMaster->getVertex(),es->getSubElement()))
                        {
                          pbcElementSlave = es;
                          break;
                        }
                      }
                    }
                  }
                  //
                  if (pbcElementSlave == NULL)
                  {
                    Msg::Error("slave element is not found nonLinearMechSolver::nonLinearPeriodicBCBetweenTwoGroups");
                  }
                  else
                  {
                    MElement* eleSlave = pbcElementSlave->getSubElement();
                    SPoint3 point;
                    std::vector<MVertex*> vv;
                    int size = eleSlave->getNumVertices();
                    eleSlave->getVertices(vv);
    
                    SPoint3 pp = vMaster->point();
                    if (eleSlave->getDim() == 0){
                      point = vv[0]->point();
                    }
                    else if (eleSlave->getDim() == 1){
                      point = project(pp,vv[0]->point(),vv[1]->point());
                    }
                    else if (eleSlave->getDim() == 2){
                      point = project(pp,vv[0]->point(),vv[1]->point(),vv[2]->point());
                    };
    
                    double xyz[3]={point[0],point[1],point[2]};
                    double uvw[3];
                    eleSlave->xyz2uvw(xyz,uvw);
    
                    double fVal[size];
                    eleSlave->getShapeFunctions(uvw[0],uvw[1],uvw[2],fVal);
    
                    //
                    std::vector<Dof> keyRootMaster, keyRootSlave;
                    if ((vRootMaster != NULL) && (vRootSlave != NULL))
                    {
                       // find space for master and slave domain
                      std::vector<pbcVertex*> vRootMasterPBC;
                      std::vector<pbcVertex*> vRootSlavePBC;
                      pbcVertex::createPBCVertices(vRootMaster,domainVector,vRootMasterPBC);
                      pbcVertex::createPBCVertices(vRootSlave,domainVector,vRootSlavePBC);
                      
                      vRootMasterPBC[0]->getKeys(bc.comp,keyRootMaster);
                      vRootSlavePBC[0]->getKeys(bc.comp,keyRootSlave);
                    }
                    //
                    std::vector<Dof> RMaster;
                    pbcVertexMaster->getKeys(bc.comp,RMaster);
                    
                    for (int icomp =0; icomp < bc.comp.size(); icomp++)
                    {
                      std::vector<int> comp(1, bc.comp[icomp]);
                      std::vector<Dof> RSlave;
                      pbcElementSlave->getKeys(comp,RSlave);
                      if (!pAssembler->isConstrained(RMaster[icomp]) and !pAssembler->isFixed(RMaster[icomp]))
                      {
                        DofAffineConstraint<double> cons;
                        cons.shift = 0.;
                        for (int ivf=0; ivf< size; ivf++){
                          cons.linear.push_back(std::pair<Dof,double>(RSlave[ivf],fVal[ivf]));
                        }
                        if ((vRootMaster != NULL) && (vRootSlave !=NULL))
                        {
                          cons.linear.push_back(std::pair<Dof,double>(keyRootMaster[icomp],1.));
                          cons.linear.push_back(std::pair<Dof,double>(keyRootSlave[icomp],-1.));
                        }
                        pAssembler->setLinearConstraint(RMaster[icomp],cons);
                      }
                    }
                  }
                  //
                }
              }
            }
          }
        }
        if (allPeriodic.size() > 0)
          Msg::Info("end accounting PBC");
        
        for (std::list<nonLinearAveragePeriodicBCBetweenTwoGroups>::iterator itpbc = allAveragePeriodic.begin(); itpbc != allAveragePeriodic.end(); itpbc++)
        {
          nonLinearAveragePeriodicBCBetweenTwoGroups& pbc = *itpbc;
          Msg::Error("nonLinearAveragePeriodicBCBetweenTwoGroups has not been implemented for fullDG case");
          break;
        }
    	}
    	else
      {
        // apply constraint
        if (allPeriodic.size() > 0)
          Msg::Info("begin accounting PBC rank %d",Msg::GetCommRank());
        
        std::map<MVertex*,partDomain*> vertexDomainMap;
        std::map<MElement*, partDomain*> boundElementDomainMap;
        auto getVertexDomain =[&vertexDomainMap] (std::vector<partDomain*>& allDom, MVertex* v)
        {
          std::map<MVertex*,partDomain*>::iterator itF = vertexDomainMap.find(v);
          if (itF != vertexDomainMap.end()) return itF->second;
          partDomain* foundDom = NULL;
          for (int idom =0; idom < allDom.size(); idom++)
          {
            partDomain* dom = allDom[idom];
            if (dom->g_vfind(v))
            {
              foundDom = dom;
              vertexDomainMap[v] = dom;
              return foundDom;
            }
          }
          return foundDom;
        };
        auto getBoundElementDomain = [&boundElementDomainMap](std::vector<partDomain*>& allDom, MElement* ele)
        {
          std::map<MElement*, partDomain*>::iterator itF = boundElementDomainMap.find(ele);
          if (itF != boundElementDomainMap.end()) return itF->second;
          partDomain* foundDom = NULL;
          for (int idom =0; idom < allDom.size(); idom++)
          {
            partDomain* dom = allDom[idom];
            for (elementGroup::elementContainer::const_iterator itedom = dom->element_begin(); itedom != dom->element_end(); itedom++)
            {
              if (checkInterfaceElementBelongToBulkElement(ele,itedom->second))
              {
                foundDom = dom;
                boundElementDomainMap[ele] = dom;
                return foundDom;
              }
            }
          }
          return foundDom;
        };
      
        for (std::list<nonLinearPeriodicBCBetweenTwoGroups>::iterator pbctg=allPeriodic.begin(); pbctg!=allPeriodic.end(); pbctg++)
        {
          nonLinearPeriodicBCBetweenTwoGroups& bc = *pbctg;
          elementGroup* gMaster = bc.g2;
          elementGroup* gSlave = bc.g1;
          MVertex* vRootMaster = bc.v2;
          MVertex* vRootSlave = bc.v1;
          //
          partDomain* vRootMasterDom = NULL;
          partDomain* vRootSlaveDom = NULL;
          if ((vRootMaster != NULL) && (vRootSlave !=NULL))
          {
            vRootMasterDom = getVertexDomain(domainVector,vRootMaster);
            vRootSlaveDom = getVertexDomain(domainVector,vRootSlave);
          }
          for (elementGroup::vertexContainer::const_iterator itvm = gMaster->vbegin(); itvm != gMaster->vend(); itvm++)
          {
            MVertex* vMaster = itvm->second;
            bool willConsider = true;
            if ((vRootMaster != NULL) && (vRootSlave !=NULL))
            {
              if (vMaster->getNum() == vRootMaster->getNum())
              {
                willConsider = false;
              }
            }
            //
            if (willConsider)
            {
              if (bc.onWhat == nonLinearBoundaryCondition::ON_FACE)
              {
                if (edgeVertices.find(vMaster) != edgeVertices.end())
                {
                  willConsider = false;
                }
              }
              else if (bc.onWhat == nonLinearBoundaryCondition::ON_EDGE)
              {
                if (cornerVertices.find(vMaster) != cornerVertices.end())
                {
                  willConsider = false;
                }
              }
            }
            
            if (willConsider)
            {
              MElement* eleSlave = NULL;
              if (bc.onWhat == nonLinearBoundaryCondition::ON_VERTEX)
              {
                eleSlave = NULL;
                if (gSlave->size() > 0)
                {
                  eleSlave = gSlave->begin()->second;
                }
              }
              else
              {
                for (elementGroup::elementContainer::const_iterator ite = gSlave->begin(); ite != gSlave->end(); ite++)
                {
                  MElement* es = ite->second;
                  if (InterpolationOperations::isInside(vMaster,es))
                  {
                    eleSlave = es;
                    break;
                  }
                }
              }
    
              if (eleSlave == NULL)
              {
                Msg::Error("slave element is not found nonLinearMechSolver::nonLinearPeriodicBCBetweenTwoGroups between %d %d",bc.phys1,bc.phys2);
              }
              else
              {
                SPoint3 point;
                std::vector<MVertex*> vv;
                int size = eleSlave->getNumVertices();
                eleSlave->getVertices(vv);
    
                SPoint3 pp = vMaster->point();
                if (eleSlave->getDim() == 0){
                  point = vv[0]->point();
                }
                else if (eleSlave->getDim() == 1){
                  point = project(pp,vv[0]->point(),vv[1]->point());
                }
                else if (eleSlave->getDim() == 2){
                  point = project(pp,vv[0]->point(),vv[1]->point(),vv[2]->point());
                };
    
                double xyz[3]={point[0],point[1],point[2]};
                double uvw[3];
                eleSlave->xyz2uvw(xyz,uvw);
    
                double fVal[size];
                eleSlave->getShapeFunctions(uvw[0],uvw[1],uvw[2],fVal);
    
                // find space for master and slave domain
                partDomain* masterDom = getVertexDomain(domainVector,vMaster);
                partDomain* slaveDom = getBoundElementDomain(domainVector,eleSlave);
                
                std::vector<Dof> keyRootMaster, keyRootSlave;
                if ((vRootMaster != NULL) && (vRootSlave != NULL))
                {
                  getKeysFromVertex(vRootMasterDom->getFunctionSpace(),vRootMaster,bc.comp,keyRootMaster);
                  getKeysFromVertex(vRootSlaveDom->getFunctionSpace(),vRootSlave,bc.comp,keyRootSlave);
                }
                //
                std::vector<Dof> RMaster;
                getKeysFromVertex(masterDom->getFunctionSpace(),vMaster,bc.comp,RMaster);
                
                for (int icomp =0; icomp < bc.comp.size(); icomp++)
                {
                  std::vector<int> comp(1, bc.comp[icomp]);
                  std::vector<Dof> RSlave;
                  for (int iv=0; iv < size; iv++)
                  {
                    getKeysFromVertex(slaveDom->getFunctionSpace(),vv[iv],comp,RSlave);
                  }
    
                  if (!pAssembler->isConstrained(RMaster[icomp]) and !pAssembler->isFixed(RMaster[icomp]))
                  {
                    DofAffineConstraint<double> cons;
                    cons.shift = 0.;
                    for (int ivf=0; ivf< size; ivf++){
                      cons.linear.push_back(std::pair<Dof,double>(RSlave[ivf],fVal[ivf]));
                    }
                    if ((vRootMaster != NULL) && (vRootSlave !=NULL))
                    {
                      cons.linear.push_back(std::pair<Dof,double>(keyRootMaster[icomp],1.));
                      cons.linear.push_back(std::pair<Dof,double>(keyRootSlave[icomp],-1.));
                    }
                    pAssembler->setLinearConstraint(RMaster[icomp],cons);
                  }
                }
              }
    				}
    			};
    		};
        if (allPeriodic.size() > 0)
          Msg::Info("end accounting PBC");
        
        // for all average PBC
        if (allAveragePeriodic.size() > 0)
          Msg::Info("begin accounting average PBC rank %d",Msg::GetCommRank());
        //
        for (std::list<nonLinearAveragePeriodicBCBetweenTwoGroups>::iterator itpbc = allAveragePeriodic.begin(); itpbc != allAveragePeriodic.end(); itpbc++)
        {
          nonLinearAveragePeriodicBCBetweenTwoGroups& pbc = *itpbc;
          elementGroup* gMaster = pbc.g2;
          elementGroup* gSlave = pbc.g1;
          MVertex* vRootMaster = pbc.v2;
          MVertex* vRootSlave = pbc.v1;
          
          std::vector<MVertex*> vecMaster, vecSlave;
          fullVector<double> weightMaster, weightSlave;
          supplementConstraint::computeWeight(gMaster,vecMaster,weightMaster);
          supplementConstraint::computeWeight(gSlave,vecSlave,weightSlave);
          int sizeDofMaster = vecMaster.size();
          int sizeDofSlave = vecSlave.size();
          
          fullVector<double> weight;
          if ((vRootMaster != NULL) && (vRootSlave != NULL))
          {
            weight.resize(sizeDofMaster+sizeDofSlave+2,true);
            weight(sizeDofMaster+sizeDofSlave) = -1.;
            weight(sizeDofMaster+sizeDofSlave+1) = 1.;
          }
          else
          {
            weight.resize(sizeDofMaster+sizeDofSlave,true);
          }
          for (int i=0; i< sizeDofMaster; i++){
            weight(i) = weightMaster(i);
          }
          for (int i=0; i< sizeDofSlave; i++){
            weight(i+sizeDofMaster) = -weightSlave(i);
          }
          
          for (int icomp =0; icomp < pbc.comp.size(); icomp ++)
          {
            std::vector<int> comp(1,pbc.comp[icomp]);
            
            std::vector<Dof> allDof;
            for (int i=0; i< sizeDofMaster; i++)
            {
              partDomain* dom = getVertexDomain(domainVector,vecMaster[i]);
              getKeysFromVertex(dom->getFunctionSpace(),vecMaster[i],comp,allDof);
            }
            for (int i=0; i< sizeDofSlave; i++)
            {
              partDomain* dom = getVertexDomain(domainVector,vecSlave[i]);
              getKeysFromVertex(dom->getFunctionSpace(),vecSlave[i],comp,allDof);
            }
            
            
            if ((vRootMaster != NULL) && (vRootSlave != NULL))
            {
              partDomain* vRootMasterDom = getVertexDomain(domainVector,vRootMaster);
              partDomain* vRootSlaveDom = getVertexDomain(domainVector,vRootSlave);
              getKeysFromVertex(vRootMasterDom->getFunctionSpace(),vRootMaster,comp,allDof);
              getKeysFromVertex(vRootSlaveDom->getFunctionSpace(),vRootSlave,comp,allDof);
            }
    
            // find positive dof
            int positive = -1;
            double maxWeight = 0.;
            for (int i=0; i< sizeDofMaster+sizeDofSlave; i++)
            {
              bool checkRoot = true;
              if ((vRootMaster != NULL) && (vRootSlave != NULL))
              {
                if ((allDof[i] == allDof[sizeDofMaster+sizeDofSlave]) || (allDof[i] == allDof[sizeDofMaster+sizeDofSlave+1]))
                {
                  checkRoot = false;
                }
              }
              
              if ((!pAssembler->isConstrained(allDof[i])) and (!pAssembler->isFixed(allDof[i])) and checkRoot)
              {
                if (fabs(weight(i)) > fabs(maxWeight))
                {
                  positive = i;
                  maxWeight = weight(i);
                }
              }
            }
    
            Msg::Info("positive = %d weigth = %e v1 = %d v2 = %d comp = %d",positive,maxWeight,pbc.v1->getNum(),pbc.v2->getNum(),pbc.comp[icomp]);
    
            if (positive == -1)
            {
              Msg::Error("nonLinearAveragePeriodicBCBetweenTwoGroups can not be used since all Dofs are constrained or fixed in other BCs");
            }
            else
            {
              double fact = 1./maxWeight;
              weight.scale(fact);
              DofAffineConstraint<double> cons;
              cons.shift = 0.;
              for (int i=0; i< sizeDofMaster+sizeDofSlave+2; i++){
                if (i != positive){
                  cons.linear.push_back(std::pair<Dof,double>(allDof[i],-weight(i)));
                }
              }
              pAssembler->setLinearConstraint(allDof[positive],cons);
            }
          }
          if (allAveragePeriodic.size() > 0)
            Msg::Info("end accounting average PBC");
        };
      };
    };
    
    void nonLinearMechSolver::crackTracking(std::string fname)
    {
      _crackTrackingName = fname;
     #if defined(HAVE_MPI)
      if(Msg::GetCommSize() > 1){
        std::ostringstream oss;
        oss << Msg::GetCommRank();
        fname += oss.str();
      }
     #endif // HAVE_MPI
      fname = getFileSavingPrefix()+ fname;
      _crackTrackingFile = fopen(fname.c_str(),"w");
      fprintf(_crackTrackingFile,"time0,time1;inter num;num minus;num plus;x0;y0;z0;x1;y1;z1\n");
    }
    
    // In MPI only 1 final file on rank 0
    void nonLinearMechSolver::postproFragment(std::string fname)
    {
      _fragmentationName = fname;
     #if defined(HAVE_MPI)
      if(Msg::GetCommSize()>1)
      {
         // change file name (include the part number in it
        std::ostringstream oss;
        oss << Msg::GetCommRank();
        std::string partnum = oss.str();
        // retrieve file extension
        size_t ext_pos;
        ext_pos = fname.find_last_of('.');
        std::string ext(fname,ext_pos+1,fname.size());
        // set file name
        std::string newname(fname,0,ext_pos);
        std::string fileName = newname + "_part" + partnum +"." + ext;
        // open file
        fileName = getFileSavingPrefix()+fileName;
        _fragmentationFile = fopen(fileName.c_str(),"w");
      }
      else
     #endif //HAVE_MPI
      {
        fname = getFileSavingPrefix()+fname;
        _fragmentationFile = fopen(fname.c_str(),"w");
      }
    }
    
    void nonLinearMechSolver::crackTracking(const double curtime)
    {
      if(_crackTrackingFile != NULL) // do nothing otherwise
      {
         // loop on domain
         for(std::vector<partDomain*>::const_iterator itdom = domainVector.begin(); itdom != domainVector.end(); ++itdom)
         {
            partDomain *dom = *itdom;
            if(dom->IsInterfaceTerms())
            {
              dgPartDomain *dgdom = static_cast<dgPartDomain*>(dom);
              if((dgdom->getMaterialLawMinus()->getType() == materialLaw::fracture)
                 and (dgdom->getMaterialLawPlus()->getType() == materialLaw::fracture)
                 and (dgdom->getDim() == 2))
              {
                // loop on interface element
                QuadratureBase *integBound = dgdom->getInterfaceGaussIntegrationRule();
                IntPt *GP;
                const materialLaw2LawsInitializer *mfminus = dynamic_cast<const materialLaw2LawsInitializer*>(dgdom->getMaterialLawMinus());
                const materialLaw2LawsInitializer *mfplus = dynamic_cast<const materialLaw2LawsInitializer*>(dgdom->getMaterialLawPlus());
                for(elementGroup::elementContainer::const_iterator itele = dgdom->gi->begin(); itele != dgdom->gi->end(); ++itele)
                {
                  MElement *ele = itele->second;
                  AllIPState::ipstateElementContainer *vips = _ipf->getAips()->getIPstate(ele->getNum());
                  int npts_inter = integBound->getIntPoints(ele,&GP);
                  int jj;
                  /* Assume same value on minus and plus element !!*/
                  bool prevOther = false; // other previous Gauss point already broken
                  for(jj=0;jj<npts_inter;jj++)
                  {
                    IPStateBase *ipsm = (*vips)[jj];
                    IPVariable *ipvmcur = ipsm->getState(IPStateBase::current);
                    IPVariable *ipvmprev= ipsm->getState(IPStateBase::previous);
                    bool fbmp = mfminus->fullBroken(ipvmprev);
                    bool fbmc = mfminus->fullBroken(ipvmcur);
                    if((fbmc==false))
                      break;
                    else if(fbmp==false){
                      if(prevOther==false)
                        prevOther = true;
                      else
                        break;
                    }
                  }
                  if( (jj==npts_inter) and (prevOther==true))// element is broken when all Gauss points are broken
                  {
                    /* get the time of crack initialization at extremities !! */
                    IPStateBase *ipsm = (*vips)[0];
                    IPVariable *ipvmcur = ipsm->getState(IPStateBase::current);
                    double time00 = mfminus->timeInitBroken(ipvmcur);
                    ipsm = (*vips)[npts_inter-1];
                    ipvmcur = ipsm->getState(IPStateBase::current);
                    double time01 = mfminus->timeInitBroken(ipvmcur);
                    double time0;
                    int index1,index0; // 0 or 1
                    if(time00 < time01){
                      time0 = time00;
                      index0 = 0;
                      index1 = 1;
                    }
                    else{
                      time0 = time01;
                      index0 = 1;
                      index1 = 0;
                    }
                    /* find current position of element */
                    FunctionSpaceBase *sp = dgdom->getFunctionSpace();
                    std::vector<Dof> R;
                    MInterfaceLine *iele = dynamic_cast<MInterfaceLine*>(ele);  // track a 2D crack--> interface == line
                    MElement *minusele = iele->getElem(0);
                    MElement *plusele = iele->getElem(1);
                    int totalnodes = minusele->getNumVertices() + plusele->getNumVertices();
                    // number of dimension
                    sp->getKeys(iele,R); // can't only get keys of elem minus on interDomain !!
                    int dimMinus = R.size()/totalnodes;
                    int numdofsMinus = dimMinus*minusele->getNumVertices();
                    int numdofsPlus = dimMinus*plusele->getNumVertices();
                    // limit R to the dofs of minus elem (can't resize as Dof() is not defined)
                    for(int kk=0;kk<numdofsPlus;kk++)
                    {
                      R.pop_back();
                    }
                    fullVector<double> disp(R.size());
                    _ufield->get(R,disp);
                    std::vector<double> elempos;
                    currentConfig::elementPositionXYZ(minusele,disp,elempos);
                    std::vector<int> vn(2);
                    iele->getLocalVertexNum(0,vn);
                    int numvertex = minusele->getNumVertices();
                    fprintf(_crackTrackingFile,"%.16g;%.16g;%ld;%ld;%ld;%.16g;%.16g;%.16g;%.16g;%.16g;%.16g\n",time0,curtime,ele->getNum(),iele->getElem(index0)->getNum(),iele->getElem(index1)->getNum(),elempos[vn[index0]],elempos[vn[index0]+numvertex],elempos[vn[index0]+2*numvertex],elempos[vn[index1]],elempos[vn[index1]+numvertex],elempos[vn[index1]+2*numvertex]);
                }
              }
            }
          }
        }
        fflush(_crackTrackingFile);
      }
    }
    
    void nonLinearMechSolver::postproFragment()
    {
      if(_fragmentationFile!=NULL)
      {
        std::set<interfaceFragment> sfrag; // contains interface element not broken
        std::set<pairMassElem> isolatedfrag; // contains elements of broken interface
        // loop on domain
        for(std::vector<partDomain*>::const_iterator itdom = domainVector.begin(); itdom != domainVector.end(); ++itdom)
        {
          partDomain *dom = *itdom;
          if(dom->IsInterfaceTerms())
          {
            dgPartDomain *dgdom = static_cast<dgPartDomain*>(dom);
            if((dgdom->getMaterialLawMinus()->getType() == materialLaw::fracture)
               and (dgdom->getMaterialLawPlus()->getType() == materialLaw::fracture)) // same for dim 3 normally. But not tested
            {
              // loop on interface element
              QuadratureBase *integBound = dgdom->getInterfaceGaussIntegrationRule();
              IntPt *GP;
              const materialLaw2LawsInitializer *mfminus = dynamic_cast<const materialLaw2LawsInitializer*>(dgdom->getMaterialLawMinus());
              const materialLaw2LawsInitializer *mfplus = dynamic_cast<const materialLaw2LawsInitializer*>(dgdom->getMaterialLawPlus());
              for(elementGroup::elementContainer::const_iterator itele = dgdom->gi->begin(); itele != dgdom->gi->end(); ++itele)
              {
                MElement *ele = itele->second;
                AllIPState::ipstateElementContainer *vips = _ipf->getAips()->getIPstate(ele->getNum());
                int npts_inter = integBound->getIntPoints(ele,&GP);
                int jj;
                /* Assume same value on minus and plus element !!*/
                for(jj=0;jj<npts_inter;jj++)
                {
                  IPStateBase *ipsm = (*vips)[jj];
                  IPVariable *ipvmcur = ipsm->getState(IPStateBase::current);
                  if(!(mfminus->fullBroken(ipvmcur)))
                    break;
                }
                MInterfaceElement *miel = dynamic_cast<MInterfaceElement*>(ele);
                if(!(jj==npts_inter))// element is not broken
                {
                  sfrag.insert(interfaceFragment(miel,dgdom->getMaterialLawMinus()->density(),dgdom->getMaterialLawPlus()->density()));
                }
                else
                {
                 #if defined(HAVE_MPI)
                  if((miel->getElem(0)->getPartition() == Msg::GetCommRank()+1) or (Msg::GetCommSize()==1)) // insert only the element that is on the partition
                 #endif // HAVE_MPI
                  {
                    isolatedfrag.insert(pairMassElem(miel->getElem(0),dgdom->getMaterialLawMinus()->density()));
                  }
                 #if defined(HAVE_MPI)
                  if((miel->getElem(1)->getPartition() == Msg::GetCommRank()+1) or (Msg::GetCommSize()==1)) // insert only the element that is on the partition
                 #endif // HAVE_MPI
                  {
                    isolatedfrag.insert(pairMassElem(miel->getElem(1),dgdom->getMaterialLawPlus()->density()));
                  }
                }
              }
            }
          }
        }
        // loop on interelement
        std::set<fragment> fragmentlist;
        std::set<fragment>::iterator it1=fragmentlist.end();
        std::set<fragment>::iterator it2=fragmentlist.end();
        for(std::set<interfaceFragment>::iterator it=sfrag.begin(); it!=sfrag.end();++it)
        {
           it1=fragmentlist.end();
           it2=fragmentlist.end();
           for(std::set<fragment>::iterator itf=fragmentlist.begin(); itf!=fragmentlist.end(); ++itf)
           {
              if((*itf).match(*it))
              {
                if(it1==fragmentlist.end())
                {
                  it1 = itf;
                }
                else
                {
                  it2 = itf;
                  break; // can only found 2 elements;
                }
              }
           }
           // insert fragment
           if((it1!=fragmentlist.end()) and(it2==fragmentlist.end())) // match with one --> insert interfacefragment to the fragment
           {
              (*it1).add(*it);
           }
           else if(it2!=fragmentlist.end()) // match twice --> regroup both fragment
           {
              (*it1).add(*it2);
              fragmentlist.erase(it2);
           }
           else // no match --> create a new one
           {
              fragmentlist.insert(fragment(*it));
           }
        }
        // Loop on isolated fragment. If not founded the fragment contains only 1 element which has to be inserted in fragmentlist
        for(std::set<pairMassElem>::iterator it=isolatedfrag.begin(); it!=isolatedfrag.end();++it)
        {
          std::set<fragment>::iterator itfound=fragmentlist.end();
          for(std::set<fragment>::iterator itf=fragmentlist.begin(); itf!=fragmentlist.end();++itf)
          {
            if((*itf).match(*it))
            {
              itfound = itf;
              break;
            }
          }
          if(itfound==fragmentlist.end())
          {
            fragmentlist.insert(fragment(*it));
          }
        }
    
        // at this point fragmentlist contains all fragment of the partition
        // the file can be written except for fragment defined on more than 1
        // partition which have to be regrouped.
        double massallfrag=0.;
        int fragnum=1;
        fprintf(_fragmentationFile,"number;number of element;mass\n");
        #if defined(HAVE_MPI)
        std::set<fragmentMPI> fragmentlistMPI;
        int mysizeMPI = 0;  // compute the size of data to transfert
        #endif // HAVE_MPI
        std::vector<std::set<fragment>::iterator> fragmypart;
        for(std::set<fragment>::iterator it=fragmentlist.begin(); it!=fragmentlist.end();++it)
        {
          #if defined(HAVE_MPI)
          if((Msg::GetCommSize()>1) and ((*it).definedOnMoreThanOneRank())){
            fragmentlistMPI.insert(fragmentMPI(*it));
            mysizeMPI +=(*it).numberOfValueToCommunicateMPI();
          }
          else // fragment is totally included on this rank so can be written into the files
          #endif // HAVE_MPI
          {
            fprintf(_fragmentationFile,"%d;%d;%e\n",fragnum,(*it).numberOfElements(),(*it).mass());
            fragnum++;
            massallfrag+= (*it).mass();
            fragmypart.push_back(it);
          }
        }
        #if defined(HAVE_MPI)
        // regroup inter rank fragment (the data is send to rank 0 which do the job (change this ??))
        // size to transfert
        int *alldatasize;
        double* allfragMPI;
        double** allfragMPIrank0;
        MPI_Status mpistatus;
        if(Msg::GetCommRank()==0){
          // get fragment of the other ranks
          alldatasize = new int[Msg::GetCommSize()];
          allfragMPIrank0 = new double*[Msg::GetCommSize()];
          alldatasize[0] = mysizeMPI;
          for(int i=1;i<Msg::GetCommSize();i++)
          {
            MPI_Recv(&alldatasize[i],1,MPI_INT,i,i,MPI_COMM_WORLD,&mpistatus);
            allfragMPIrank0[i] = new double[alldatasize[i]];
            MPI_Recv(allfragMPIrank0[i],alldatasize[i],MPI_DOUBLE,i,i,MPI_COMM_WORLD,&mpistatus);
          }
          // double loop to avoid wait of other rank for MPI_comm ??
          // insert fragment of each rank
          std::set<fragmentMPI>::iterator it1;
          std::set<fragmentMPI>::iterator it2;
          for(int i=1;i<Msg::GetCommSize();i++)
          {
            int curpos=0;
            while(curpos<alldatasize[i])
            {
              fragmentMPI newfrag = fragmentMPI(allfragMPIrank0[i],curpos);
              curpos += newfrag.memorySize();
              // add fragment in the list
              std::vector<std::set<fragmentMPI>::iterator> vit;
              for(std::set<fragmentMPI>::iterator itf=fragmentlistMPI.begin(); itf!=fragmentlistMPI.end(); ++itf)
              {
                if((*itf).match(newfrag))
                {
                  newfrag.add(*itf);
                  vit.push_back(itf);
                }
              }
              // delete matched fragment
              for(int i=vit.size()-1; i>=0;i--)
              {
                fragmentlistMPI.erase(vit[i]);
              }
              // insert the new one
              fragmentlistMPI.insert(newfrag);
            }
          }
          // write to files
          for(std::set<fragmentMPI>::iterator it=fragmentlistMPI.begin(); it!=fragmentlistMPI.end();++it)
          {
            fprintf(_fragmentationFile,"%d;%d;%e\n",fragnum,(*it).numberOfElements(),(*it).mass());
            fragnum++;
            massallfrag+= (*it).mass();
          }
    
    
          // free memory
          for(int i=1;i<Msg::GetCommSize();i++)
            delete[] allfragMPIrank0[i];
          delete[] allfragMPIrank0;
        }
        else
        {
          // build a double vector to transfert all data to node 0
          allfragMPI = new double[mysizeMPI];
          int vecpos = 0;
          for(std::set<fragmentMPI>::iterator it=fragmentlistMPI.begin(); it!=fragmentlistMPI.end(); ++it)
          {
            vecpos +=(*it).fillMPIvector(&allfragMPI[vecpos]);
          }
          MPI_Send(&mysizeMPI,1,MPI_INT,0,Msg::GetCommRank(),MPI_COMM_WORLD);
          // send data
          MPI_Send(allfragMPI,mysizeMPI,MPI_DOUBLE,0,Msg::GetCommRank(),MPI_COMM_WORLD);
    
          delete[] allfragMPI;
        }
        if (getNumRanks() > 1)
          Msg::Barrier(); // To wait rank0 before end
        #endif // HAVE_MPI
        // total mass at the end
        fprintf(_fragmentationFile,"total mass; %e\n number of fragment %d\n",massallfrag,fragnum-1);
        fflush(_fragmentationFile);
        /* create a view of fragment ( ie a mesh with a different physical number for each fragment) */
        //filename
        std::string fragmshname;
       #if defined(HAVE_MPI)
        if(Msg::GetCommSize()>1)
        {
          std::ostringstream oss;
          oss << Msg::GetCommRank();
          std::string partnum = oss.str();
          fragmshname = getFileSavingPrefix()+ "fragview_part"+partnum+".msh";
        }
        else
       #endif //HAVE_MPI
        {
          fragmshname = getFileSavingPrefix()+ "fragview.msh";
        }
        FILE *fpfragmsh = fopen(fragmshname.c_str(),"w");
        FILE *fpmsh = fopen(_meshFileName.c_str(),"r");
        fprintf(fpfragmsh,"$MeshFormat\n2.2 0 8\n$EndMeshFormat\n");
        // The node are copy only on rank 0
        char what[256];
    //    if(Msg::GetCommRank()==0)
        {
          while(1)
          {
            int oks = fscanf(fpmsh,"%s",what);
            if(!strcmp(what,"$Nodes"))
            {
              fprintf(fpfragmsh,"$Nodes\n");
              oks = fscanf(fpmsh,"%s",what);
              fprintf(fpfragmsh,"%s\n",what);
              break;
            }
          }
          int j=1;
          while(!feof(fpmsh))
          {
            int oks = fscanf(fpmsh,"%s",what);
            fprintf(fpfragmsh,"%s",what);
            if(!strcmp(what,"$EndNodes"))
            {
              fprintf(fpfragmsh,"\n");
              break;
            }
            if(j%4==0)
            {
              fprintf(fpfragmsh,"\n");
            }
            else
            {
              fprintf(fpfragmsh," ");
            }
            j++;
          }
        }
    /*    else{
          while(!feof(fpmsh))
          {
            fscanf(fpmsh,"%s",what);
            if(!strcmp(what,"$EndNodes"))
            {
              break;
            }
          }
        }
    */
        int oks = fscanf(fpmsh,"%s",what);
        fprintf(fpfragmsh,"%s\n",what);
        int allelem; // total number of element in the mesh file
        oks = fscanf(fpmsh,"%d",&allelem);
        // compute the total number of element included on this partition
        int totalelem=0;
        for(int i=0;i<fragmypart.size();i++)
        {
          totalelem +=fragmypart[i]->numberOfElements();
        }
       #if defined(HAVE_MPI)
        if(Msg::GetCommRank()==0)
        {
          for(std::set<fragmentMPI>::iterator it = fragmentlistMPI.begin(); it!=fragmentlistMPI.end();++it)
          {
            totalelem +=it->numberOfElements();
          }
        }
       #endif // HAVE_MPI
        fprintf(fpfragmsh,"%d\n",totalelem);
        int num1,num2;
        int physfrag;
        int typemshint;
        for(int k=0;k<allelem;k++)
        {
          int elenum;
          oks = fscanf(fpmsh,"%d",&elenum);
          // check if the element is in the fragment of this partition
          bool writeelem=false;
          physfrag=0;
          for(int i=0;i<fragmypart.size();i++)
          {
            physfrag++;
            if(fragmypart[i]->isElem(elenum))
            {
              writeelem=true;
              break;
            }
          }
         #if defined(HAVE_MPI)
          if(Msg::GetCommRank()==0 and !writeelem)
          {
            for(std::set<fragmentMPI>::iterator it = fragmentlistMPI.begin(); it!=fragmentlistMPI.end();++it)
            {
              physfrag++;
              if(it->isElem(elenum))
              {
                writeelem = true;
                break;
              }
            }
          }
         #endif // HAVE_MPI
          oks = fscanf(fpmsh,"%d %d",&typemshint,&num2);
          if(writeelem) fprintf(fpfragmsh,"%d %d %d ",elenum,typemshint,num2);
          oks = fscanf(fpmsh,"%d",&num1); // read the physical number which has to be replaced
          if(writeelem) fprintf(fpfragmsh,"%d ",physfrag+1000000*Msg::GetCommRank());
          int nver = MElement::getInfoMSH(typemshint);       // get the number of vertices of the element
          // the gentity has to be different for each fragment ie same as physical
          oks = fscanf(fpmsh,"%d",&num1);
          if(writeelem) fprintf(fpfragmsh,"%d ",physfrag+1000000*Msg::GetCommRank());
          for(int i=2;i<num2;i++){
            oks = fscanf(fpmsh,"%d",&num1);
            if(writeelem) fprintf(fpfragmsh,"%d ",num1);
          }
          for(int i=0;i<nver;i++)
          {
            oks = fscanf(fpmsh,"%d",&num1);
            if(writeelem){
              if(i!=nver-1)
                fprintf(fpfragmsh,"%d ",num1);
              else
                fprintf(fpfragmsh,"%d\n",num1);
            }
          }
          //if(writeelem) fprintf(fpfragmsh,"\n");
        }
        fprintf(fpfragmsh,"$EndElements");
        fclose(fpmsh);
        fclose(fpfragmsh);
        if (getNumRanks() > 1)
          Msg::Barrier(); // wait all otherwise archiving problem ??
      }
    }
    
    void nonLinearMechSolver::fillMapOfInterfaceElementsInOneDomain(MElement *e, std::vector<MElement*> &eleFound,
                                    const elementGroup *g) const
    {
        // take only the primary vertex (avoid unnecessery check for element with order > 1
       int nbVertex = e->getNumPrimaryVertices();
    
      std::vector<MVertex*> vv;
      for(int i=0;i<nbVertex;i++)
        vv.push_back(e->getVertex(i));
    
      // In 3D, in //, it is possible that all the nodes are in group g, but not from the same element
      // We need to identify by face.
      for(elementGroup::elementContainer::const_iterator ite=g->begin(); ite !=g->end(); ite++)
      {
        MElement *gele = ite->second;
        int nbVertex2 = gele->getNumPrimaryVertices();
        bool oneNotFound = false;
        for(std::vector<MVertex*>::const_iterator itvv = vv.begin(); itvv!= vv.end(); itvv++)
        {
          oneNotFound = true;
          for(int j=0;j<nbVertex2;j++)
          {
            if((gele->getVertex(j)) == (*itvv))
            {
              oneNotFound = false;
              break;
            }
          }
          if(oneNotFound) break;
        }
        if(!oneNotFound)
        {
          eleFound.push_back(e);
          break;
        }
      }
    }
    
    void nonLinearMechSolver::fillLocalReferenceBasisForAllInterfaceElement(){
      //printf("begin filling reference local basis\n");
    	for (int idom= 0; idom < domainVector.size(); idom++){
    		const dgPartDomain* dgdom = dynamic_cast<const dgPartDomain*>(domainVector[idom]);
    		if (dgdom != NULL){
    			IntPt* GP;
    			for (elementGroup::elementContainer::const_iterator ite = dgdom->gi->begin(); ite != dgdom->gi->end(); ite++){
    				MElement* ele = ite->second;
    				int npts  = dgdom->getInterfaceGaussIntegrationRule()->getIntPoints(ele,&GP);
    				for (int i=0; i< npts; i++){
    					int type = numericalMaterialBase::createTypeWithTwoInts(ele->getNum(), i);
    					SVector3 n, b, t;
    					computeInterfaceReferenceLocalBasis(ele,&GP[i],n,t,b);
    
    					STensor3 mat;
    					mat(0,0) = n(0); mat(1,0) = n(1);  mat(2,0) = n(2);
    					mat(0,1) = t(0); mat(1,1) = t(1);  mat(2,1) = t(2);
    					mat(0,2) = b(0); mat(1,2) = b(1);  mat(2,2) = b(2);
    
    					_allInterfaceLocalBasis[type] = mat; // negative
    					type = numericalMaterialBase::createTypeWithTwoInts(ele->getNum(), i+npts);
    					_allInterfaceLocalBasis[type] = mat; // positive
    				}
    			}
    		}
    	}
    	//printf("done filling reference local basis size = %d\n",allInterfaceLocalBasis.size());
    };
    
    void nonLinearMechSolver::getLocalBasis(const MElement* ele, const int gpt, SVector3& n, SVector3& t, SVector3& b) const {
    	int type = numericalMaterialBase::createTypeWithTwoInts(ele->getNum(), gpt);
    	std::map<int,STensor3>::const_iterator itfind = _allInterfaceLocalBasis.find(type);
    	if ( itfind == _allInterfaceLocalBasis.end()){
    		Msg::Error(" rank %d local basis at interface element %d gpt %d does not exist",Msg::GetCommRank(), ele->getNum(),gpt);
    	}
    	else{
    		const STensor3& mat = itfind->second;
    		n(0) = mat(0,0); n(1) = mat(1,0);  n(2) = mat(2,0);
    		t(0) = mat(0,1); t(1) = mat(1,1);  t(2) = mat(2,1);
    		b(0) = mat(0,2); b(1) = mat(1,2);  b(2) = mat(2,2);
    
    		//mat.print("found basis");
    	}
    };
    
    
    
    void nonLinearMechSolver::createStrainMapping(const std::string  filename, const int nbstepArch){
      if (_strainMap) delete _strainMap;
      _strainMap = new strainMapping(*this,nbstepArch);
      _strainMap->readMesh(filename);
      GModel::setCurrent(pModel);
    };
    
    void nonLinearMechSolver::blockDissipation(const IPStateBase::whichState ws, const bool fl){
      if (_ipf){
    		if (fl and !_damageIsBlocked){
          printf("rank %d damage is blocked %s  \n",Msg::GetCommRank(),getFileSavingPrefix().c_str());
        }
    		else if (fl and _damageIsBlocked){
    			printf("rank %d damage is reblocked %s  \n",Msg::GetCommRank(),getFileSavingPrefix().c_str());
    		}
        else if (!fl and _damageIsBlocked){
          printf("rank %d damage is unblocked %s \n",Msg::GetCommRank(),getFileSavingPrefix().c_str());
        }
    		_damageIsBlocked = fl;
    
      }
      else
        Msg::Error("IPField has not been initialized");
    }
    
    bool nonLinearMechSolver::dissipationIsBlocked() const{
    	return _damageIsBlocked;
    };
    
    void nonLinearMechSolver::setTimeForMicroBC(const double time){
    	if (_microBC!=NULL){
        _microBC->setTime(time);
      }
    };
    
    void nonLinearMechSolver::microNumberDof(){
      if (_pathFollowing){
        _systemType = MULT_ELIM;
        _pAl->numberDof(MULT_ELIM);
      }
      else{
        if (_controlType == LOAD_CONTROL){
        // number normally dof
    			_pAl->numberDof(_systemType);
        // allocate micro system
        }
        else if (_controlType == ARC_CONTROL_EULER){
          _systemType = DISP_MULT;
          _pAl->numberDof(DISP_MULT);
        }
        else{
          Msg::Error("control Type must be correct defined");
        }
      }
    	std::string A("A");
      if (_systemType == DISP_ELIM_UNIFIED){
        // PAL has to be set before allocating linear system
        pbcSystemConden<double>* pbc = dynamic_cast<pbcSystemConden<double>*>(pAssembler->getLinearSystem(A));
        if (pbc){
          pbc->setPBCAlgorithm(_pAl);
        }
      }
      // allocate system
      int systemSize = pAssembler->sizeOfR();
    	if (_pathFollowing){
    		// if path following and one rhs resolution
    		if (_pfManager._pathFollowingMethod == pathFollowingManager::GLOBAL_ARC_LENGTH_BASED){
    
    		}
    		else if (_pfManager._pathFollowingMethod == pathFollowingManager::LOCAL_BASED){
    			// add 1 DOF to total system as load factor unknown
    			if (_pfManager._solverTypePathFollowing == pathFollowingSystemBase::ONE_RHS){
    				systemSize++;
    			}
    		}
    		else{
    			Msg::Error("_pathFollowingMethod = %d has not been implemented",_pfManager._pathFollowingMethod);
    		}
    	}
    
      pAssembler->getLinearSystem(A)->allocate(systemSize);
    
      if (_systemType == DISP_ELIM_UNIFIED){
        // initial kinematic variable for this method
        pbcSystemConden<double>* pbc = dynamic_cast<pbcSystemConden<double>*>(pAssembler->getLinearSystem(A));
        if (pbc){
          fullVector<double> kinVec;
          _pAl->getMBC()->getKinematicalVector(kinVec);
          for (int i=0; i< kinVec.size(); i++){
            double val = kinVec(i);
            pbc->initialKinematicVector(i,val);
          }
    
        }
      }
    
      // preallocated system
      for(int i=0; i<_mpiUserDom;++i)
      {
        partDomain *dom = domainVector[i];
        if(dom->elementGroupSize() > 0){
            SparsityDofs(*(dom->getFunctionSpace()), dom->element_begin(), dom->element_end(),*pAssembler);
        }
        // account also interface
        dgPartDomain *dgdom = dynamic_cast<dgPartDomain*>(dom);
        if (dgdom){
          #if defined(HAVE_MPI)
          if(Msg::GetCommSize()>1)
              SparsityInterfaceDofsMPI(dgdom->getFunctionSpace(), dgdom->gi->begin(), dgdom->gi->end(),pAssembler);
          #endif
          SparsityDofs(*(dgdom->getFunctionSpace()), dgdom->gi->begin(), dgdom->gi->end(),*pAssembler);
        }
      }
      #if defined(HAVE_MPI)
       // MPI interface sparsity
      if(Msg::GetCommSize()>1){
        for(int i=_mpiUserDom; i<domainVector.size();++i)
        {
          dgPartDomain *dgdom = dynamic_cast<dgPartDomain*>(domainVector[i]);
          if (dgdom){
            SparsityInterfaceDofsMPI(dgdom->getFunctionSpace(), dgdom->gi->begin(), dgdom->gi->end(),pAssembler);
          }
        }
      }
      #endif // HAVE_MPI
    
      if (_systemType == nonLinearMechSolver::DISP_MULT){
        _pAl->sparsityLagMultiplierDofs();
      }
    
    	pAssembler->getLinearSystem(A)->preAllocateEntries();
    
    
      // assemble all linear constraint matrix to system in case of using mult -elim method
      int dimKin = _pAl->getNumberOfMacroToMicroKinematicVariables();
      int dimStress = _pAl->getNumberMicroToMacroHomogenizedVariables();
    
      bool insystemCondenFlag = false;
    
      if (_tangentflag and (_homogenizeTangentMethod == INSYSTEMCONDEN or computedUdF()))
      {
        insystemCondenFlag = true;
      };
    
      if (_systemType == MULT_ELIM){
        _pAl->allocateConstraintMatrixToSystem(dimKin,dimStress,insystemCondenFlag);
        // assembleLinearConstraintMatrixToSystem must be called at least once
        _pAl->assembleLinearConstraintMatrixToSystem();
        if (insystemCondenFlag){
          _pAl->assembleKinematicMatrix();
        }
      }
      else if (_systemType == DISP_ELIM_UNIFIED ){
        std::string A("A");
        pbcSystemConden<double>* pbc = dynamic_cast<pbcSystemConden<double>*>(pAssembler->getLinearSystem(A));
        if (pbc == NULL) Msg::Error("pbcSystemConden has to be used");
        // computeConstraintProjectMatrices must be called at least once
        pbc->computeConstraintProjectMatrices();
        if (insystemCondenFlag){
          // only stress matrix is necessary in this case
          pbc->allocateStressMatrix(dimStress,pAssembler->sizeOfR());
          if(computedUdF())
             pbc->allocateBodyForceMatrix(pAssembler->sizeOfR());
        }  
        if (_homogenizeTangentMethod == PERTURB and computedUdF()){
           pbc->allocateStressMatrix(dimStress,pAssembler->sizeOfR());
           pbc->allocateBodyForceMatrix(pAssembler->sizeOfR());
        }
      } 
    };
    
    void nonLinearMechSolver::setPeriodicity(const double x, const double y, const double z, const std::string direction){
      Msg::Warning(" nonLinearMechSolver::setPeriodicity is no longer necessary!");
    };
    
    void nonLinearMechSolver::addMicroBC(const nonLinearMicroBC* bc){
      _microFlag = true;
    	if (_microBCOld) delete _microBCOld;
    	_microBCOld = _microBC;
      _microBC = bc->clone();
    	if (_microBCOld != NULL){
    		_microBC->collecDataFromPrevious(*_microBCOld);
    	}
    	// add BC suppport
    	std::vector<elementGroup*>& bgroup  = _microBC->getBoundaryElementGroup();
    	const std::vector<int>& bphysical = _microBC->getBoundaryPhysicals();
    	bgroup.clear();
    	for (int i=0; i< bphysical.size(); i++){
    		elementGroup* gr = new elementGroup(_dim-1,bphysical[i]);
    		bgroup.push_back(gr);
    	}
    };
    void nonLinearMechSolver::addMicroBCForFailure(const nonLinearMicroBC* mbc){
    	// this BC sleeps util failure
    	// once failure occurs, _microBC is replaced by _microFailureBC using addMicroBC
    	if (_microFailureBC!=NULL) delete _microFailureBC;
    	_microFailureBC = mbc->clone();
    };
    
    double nonLinearMechSolver::getRVEVolume() const{
    	return _rveVolume;
    };
    
    void nonLinearMechSolver::setRVEVolume(const double val){
      _rveVolume = val;
    }
    
    const STensor3& nonLinearMechSolver::getRVEGeometricalInertia() const {
       return _rveGeoInertia;
    };
    
    void nonLinearMechSolver::setRVEGeometricalInertia(const STensor3& val){
      STensorOperation::zero(_rveGeoInertia);
      _rveGeoInertia(0,0) = val(0,0);
      _rveGeoInertia(1,1) = val(1,1);
      _rveGeoInertia(2,2) = val(2,2);
    }
    
    const SVector3& nonLinearMechSolver::getRVEGeometry() const {
       return _rveGeometry;
    };
    
    void nonLinearMechSolver::setRVEGeometry(const SVector3& val){
      STensorOperation::zero(_rveGeometry);
      _rveGeometry(0) = val(0);
      _rveGeometry(1) = val(1);
      _rveGeometry(2) = val(2);
    }
    
    double nonLinearMechSolver::getHomogenizedDensity() const{
      return _rho;
    };
    
    double nonLinearMechSolver::getConstitutiveExtraDofDiffusionEquationRatio(const int index) const
    {
      double eqRatio=1.;
      bool found=false;
    
    	for (int i=0; i<domainVector.size(); i++)
    	{
    		const partDomain* dom = domainVector[i];
    		double eqR=dom->getConstitutiveExtraDofDiffusionEqRatio(index);
    		if(eqR>0.)
    		{
    			 if(!found)
    			 {
    				 eqRatio = eqR;
    				 found=true;
    			 }
    			 else if(eqR!=eqRatio)
    				 Msg::Error("not all the domains have the same EqRatio field %d",index);
    		}
    	}
      return eqRatio;
    };
    
    double nonLinearMechSolver::getNonConstitutiveExtraDofEquationRatio(const int index) const
    {
      double eqRatio=1.;
      bool found=false;
    
    	for (int i=0; i<domainVector.size(); i++)
    	{
    		const partDomain* dom = domainVector[i];
    		double eqR=dom->getNonConstitutiveExtraDofEqRatio(index);
    		if(eqR>0.)
    		{
    			 if(!found)
    			 {
    				 eqRatio = eqR;
    				 found=true;
    			 }
    			 else if(eqR!=eqRatio)
    				 Msg::Error("not all the domains have the same EqRatio field %d",index);
    		}
    	}
      return eqRatio;
    };
    
    bool nonLinearMechSolver::getExtractIrreversibleEnergyFlag() const{return _extractIrreversibleEnergy;};
    bool nonLinearMechSolver::getDamageToCohesiveJumpFlag() const {return _damageToCohesiveJump;};
    int nonLinearMechSolver::getHomogenizationOrder() const {
      if (_microBC != NULL){
        return _microBC->getOrder();
      }
      else{
        return 0;
      }
    };
    bool nonLinearMechSolver::inMultiscaleSimulation() const{return _multiscaleFlag;};
    
    const SVector3& nonLinearMechSolver::getLostSolutionUniquenssNormal() const{
    	return _lostSolutionUniquenssNormal;
    };
    
    double& nonLinearMechSolver::getHomogenizedCrackFace(){
    	return _homogenizedCrackSurface;
    };
    const double& nonLinearMechSolver::getHomogenizedCrackFace() const{
    	return _homogenizedCrackSurface;
    };
    
    STensor3& nonLinearMechSolver::getFdamOnset() {return _FdamOnset;};
    const STensor3& nonLinearMechSolver::getFdamOnset() const {return _FdamOnset;};
    
    bool nonLinearMechSolver::withEnergyDissipation() const{
    	for (std::map<int, materialLaw*>::const_iterator it = maplaw.begin(); it!= maplaw.end(); it++){
    		if (it->second->withEnergyDissipation()){
    			return true;
    		}
    	}
    	return false;
    };
    
    bool nonLinearMechSolver::withFractureLaw() const{
      for (std::map<int, materialLaw*>::const_iterator it = maplaw.begin(); it!= maplaw.end(); it++){
    		if (it->second->getType() == materialLaw::fracture){
    			return true;
    		}
    	}
    	return false;
    };
    
    const elementErosionFilter& nonLinearMechSolver::getElementErosionFilter() const{
    	return _elementErosionFilter;
    };
    
    bool nonLinearMechSolver::solverIsBroken() const{
      return _solverIsBroken;
    }
    
    void nonLinearMechSolver::brokenSolver(const bool fl){
      _solverIsBroken = fl;
    };
    
    void nonLinearMechSolver::getTotalIntegralByVolumeIntegralOnPhysical(const int phys, const int num, double& val) const{
      val = 0.;
      for (int i=0; i< domainVector.size(); i++){
        partDomain* dom = domainVector[i];
        if (dom->getPhysical() == phys){
          double volumePart = 0;
          double valPartAverage = dom->computeMeanValueDomain(num,_ipf,volumePart);
          val += (valPartAverage*volumePart);
        }
      }
      #if defined(HAVE_MPI)
      if (_isPartitioned and (Msg::GetCommSize() > 1)){
        double valTmp = val;
        MPI_Allreduce(&valTmp,&val,1,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD);
      }
      #endif // HAVE_MPI
    };
    
    void nonLinearMechSolver::getTotalIntegralByVolumeIntegral(const int num, double& val) const{
      val = 0.;
      for (int i=0; i< domainVector.size(); i++){
        partDomain* dom = domainVector[i];
        double volumePart = 0;
        double valPartAverage = dom->computeMeanValueDomain(num,_ipf,volumePart);
        val += (valPartAverage*volumePart);
      }
      #if defined(HAVE_MPI)
      if (_isPartitioned and (Msg::GetCommSize() > 1)){
        double valTmp = val;
        MPI_Allreduce(&valTmp,&val,1,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD);
      }
      #endif // HAVE_MPI
    };
    
    void nonLinearMechSolver::getHomogenizedPropertyByVolumeIntegral(const int num, double & val) const{
      val = 0.;
      double volume = 0.;
      for (int i=0; i< domainVector.size(); i++){
        partDomain* dom = domainVector[i];
        double volumePart = 0;
        double valPart = dom->computeMeanValueDomain(num,_ipf,volumePart);
        val += (valPart*volumePart);
        volume += volumePart;
      }
    
      #if defined(HAVE_MPI)
      if (_isPartitioned and (Msg::GetCommSize() > 1)){
        double send[2] = {volume, val };
        double receive[2];
        MPI_Allreduce(send,receive,2,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD);
        volume = receive[0];
        val = receive[1];
      }
      #endif // HAVE_MPI
      double invV = (1./volume);
      val *= invV;
    };
    
    void nonLinearMechSolver::getHomogenizedPropertyByVolumeIntegralOnPhysical(const int phys, const int num, double & val) const{
      val = 0.;
      double volume = 0.;
      for (int i=0; i< domainVector.size(); i++){
        partDomain* dom = domainVector[i];
        if (dom->getPhysical() == phys){
          double volumePart = 0;
          double valPart = dom->computeMeanValueDomain(num,_ipf,volumePart);
          val += (valPart*volumePart);
          volume += volumePart;
        }
      }
    
      #if defined(HAVE_MPI)
      if (_isPartitioned and (Msg::GetCommSize() > 1)){
        double send[2] = {volume, val };
        double receive[2];
        MPI_Allreduce(send,receive,2,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD);
        volume = receive[0];
        val = receive[1];
      }
      #endif // HAVE_MPI
      double invV = (1./volume);
      val *= invV;
    };
    
    void nonLinearMechSolver::getHomogenizedPropertyByVolumeIntegralOnActiveDissipationDomain(const int num, double& val, const IPStateBase::whichState ws) const{
      val = 0.;
      double dissipationVolume = 0.;
      for (int i=0; i< domainVector.size(); i++)
      {
        partDomain* dom = domainVector[i];
        double dissipationVolumePart = 0;
        double valPart = dom->averagingOnActiveDissipationZone(num,_ipf,dissipationVolumePart,ws);
        val += (valPart*dissipationVolumePart);
        dissipationVolume += dissipationVolumePart;
      }
      if (dissipationVolume > 0)
      {
        val /= dissipationVolume;
      }
    };
    
    void nonLinearMechSolver::getHomogenizedIncrementalPropertyByVolumeIntegralOnActiveDissipationDomain(const int num, double& val) const{
      val = 0.;
      double dissipationVolume = 0.;
      for (int i=0; i< domainVector.size(); i++)
      {
        partDomain* dom = domainVector[i];
        double dissipationVolumePart = 0;
        double valPart = dom->averagingIncrementOnActiveDissipationZone(num,_ipf,dissipationVolumePart);
        val += (valPart*dissipationVolumePart);
        dissipationVolume += dissipationVolumePart;
      }
      if (dissipationVolume > 0)
      {
        val /= dissipationVolume;
      }
    };
    
    
    void nonLinearMechSolver::extractAverageStressByVolumeIntegral(homogenizedData* homoData){
    	// in case of multiscale analyses, previous macro-state is initial micro-state
    	double volumeRVEInv = 1./this->getRVEVolume();
      // basic mechanics quantities
    	if (_microBC->getTotalNumberOfMechanicalDofs()> 0){
    		double& damageVolume = homoData->getActiveDissipationVolume();
    		damageVolume = 0.;
    		double& clength = homoData->getAverageLocalizationBandWidth();
    		clength = 0.;
    		if (withEnergyDissipation()){
          // compute current damage volume
    			for (int i=0; i<domainVector.size(); i++){
    				damageVolume += domainVector[i]->computeVolumeActiveDissipationDomain(_ipf);
    			}
    			// active damage center
    			if (damageVolume > 0.){
    				// compute homogenized crack length
    				this->computeHomogenizedCrackFace();
            // true damage surface = _surfaceReductionRatio*_homogenizedCrackSurface
            clength = damageVolume/(_surfaceReductionRatio*_homogenizedCrackSurface);
    			}
    		}
    
        STensor3& P = homoData->getHomogenizedStress();
        STensorOperation::zero(P);
        for (int i=0; i<domainVector.size(); i++){
          domainVector[i]->computeAverageStress(_ipf,P);
        };
        P *= (volumeRVEInv);
        
        if (_microBC->getOrder() == 2){
    	STensor33& Q = homoData->getHomogenizedSecondOrderStress();
    	STensorOperation::zero(Q);
    	for (int i=0; i<domainVector.size(); i++){
    	    domainVector[i]->computeAverageHighOrderStress(_ipf,Q);
    	};
    	
    	if(computedUdF()){
    	   /*static STensor3 BmX;
    	   static STensor33 BmXoX;
    	   STensorOperation::zero(BmX);
    	   STensorOperation::zero(BmXoX);
    	   for (int i=0; i<domainVector.size(); i++){
    	     domainVector[i]->computeBodyForceMultiplyPosition(_ipf, BmX, BmXoX);
    	   }
    	   BmX.print("BmX");
    	   BmXoX.print("bmXoX- BMoJ");*/
                 
               const STensor63& dQdG_correction = homoData->getHomogenizedCorrectionTerm_G_G();
               static STensor33 GM;
               static STensor33 modTerm_Q;
               if(_pathFollowing){
                 GM = this->getMicroBC()->getSecondOrderKinematicalVariable();
               }
               else{
                 GM = this->getMicroBC()->getCurrentDeformationGradientGradient();     
               }
                   
               STensorOperation::STensor63ContractionSTensor33(dQdG_correction, GM, modTerm_Q); 
               if(useWhichModuliForBF()==partDomain::Present){
                 const STensor43& dPdF = homoData->getHomogenizedTangentOperator_F_F();
                 for (int i=0; i<3; i++){
                 for (int j=0; j<3; j++)
                   for (int k=0; k<3; k++)
                      for (int l=0; l<3; l++)
                         for (int p=0; p<3; p++)  
                            for (int q=0; q<3; q++){  
                            Q(i,j,q) -= 0.5*(dPdF(i,j,k,l)*(GM(k,l,p))*_rveGeoInertia(p,q)+dPdF(i,q,k,l)*(GM(k,l,p))*_rveGeoInertia(p,j));
                            }  
                 }   
               }   
               else{
                  const STensor43& dPdF = this->getHomogenizationState(IPStateBase::previous)->getHomogenizedTangentOperator_F_F();
                  for (int i=0; i<3; i++){
                  for (int j=0; j<3; j++)
                   for (int k=0; k<3; k++)
                      for (int l=0; l<3; l++)
                         for (int p=0; p<3; p++)  
                            for (int q=0; q<3; q++){  
                            Q(i,j,q) -= 0.5*(dPdF(i,j,k,l)*(GM(k,l,p))*_rveGeoInertia(p,q)+dPdF(i,q,k,l)*(GM(k,l,p))*_rveGeoInertia(p,j));
                            }  
                 }   
               }            
               Q += modTerm_Q*this->getRVEVolume(); 
    	}	
    	Q *= (volumeRVEInv);			
        }
    
        // extract cohesive law
        if (this->withEnergyDissipation() and this->getDamageToCohesiveJumpFlag()){
          // cohesive traction
          SVector3& T = homoData->getHomogenizedCohesiveTraction();
          STensorOperation::zero(T);
          // cohesive traction
          for (int i=0; i<3; i++){
            for (int j=0; j<3; j++){
              T(i) += P(i,j)*_lostSolutionUniquenssNormal(j);
            }
          }
    
          // compute cohesive jump
          STensor3& Fdam = homoData->getHomogenizedActiveDissipationDeformationGradient();
          Fdam = _homogenizedDissipationStrainLastActiveDissipation;
    
          STensor3 dFdam(0.);
          if (damageVolume > 0.){
            for (int i=0; i<domainVector.size(); i++){
              domainVector[i]->computeActiveDissipationAverageStrainIncrement(_ipf,dFdam);
            }
            dFdam *= (1./damageVolume);
            Fdam += dFdam;
          }
    
          // compute cohesive jump
          SVector3& cjump = homoData->getHomogenizedCohesiveJump();
          cjump  = _homogenizedCohesiveJumpLastActiveDissipation;
    
          if (_solverIsBroken){
            STensor3 dFinterf = this->getMicroBC()->getFirstOrderKinematicalVariable();
            dFinterf -= _homogenizedStrainLastActiveDissipation;
            double beta = damageVolume*volumeRVEInv/_surfaceReductionRatio;
    
            for (int i=0; i<3; i++){
              for (int j=0; j<3; j++){
                cjump(i) += (clength/(1.-beta))*(dFdam(i,j) - dFinterf(i,j))*_lostSolutionUniquenssNormal(j);
              }
            };
          }
        };
    
    		double& defoEnergy = homoData->getDeformationEnergy();
    		double& plasEnergy = homoData->getPlasticEnergy();
    		double& irrEnergy = homoData->getIrreversibleEnergy();
    
    		defoEnergy = 0.;
    		plasEnergy = 0.;
    		irrEnergy = 0.;
    		for (int i=0; i< domainVector.size(); i++){
    			defoEnergy += domainVector[i]->computeDeformationEnergy(_ipf);
    			plasEnergy += domainVector[i]->computePlasticEnergy(_ipf);
    			irrEnergy += domainVector[i]->computeIrreversibleEnergy(_ipf);
    		}
    		defoEnergy *= volumeRVEInv;
    		plasEnergy *= volumeRVEInv;
    		irrEnergy *= volumeRVEInv;
    	};
    
    	if (_microBC->getTotalNumberOfConDofs() > 0){
    		// for constitutive-extra Dof
    		for (int index=0; index< _microBC->getTotalNumberOfConDofs(); index++ ){
    			SVector3& fluxT = homoData->getHomogenizedConstitutiveExtraDofFlux(index);
    			double &internalEnergyExtraDof = homoData->getHomogenizedConstitutiveExtraDofInternalEnergy(index);
    			double &mecaSource = homoData->getHomogenizedConstitutiveExtraDofMechanicalSource(index);
    			double &fieldCapacityPerUnitField = homoData->getHomogenizedConstitutiveExtraDofFieldCapacityPerUnitField(index);
    
          STensorOperation::zero(fluxT);
    			internalEnergyExtraDof = 0.;
    			mecaSource = 0.;
    			fieldCapacityPerUnitField = 0.;
    			for (int i=0; i<domainVector.size(); i++){
    				domainVector[i]->computeConstitutiveExtraDofAverageFlux(index,_ipf,fluxT);
    				domainVector[i]->computeConstitutiveExtraDofAverageInternalEnergy(index,_ipf,internalEnergyExtraDof);
    				domainVector[i]->computeConstitutiveExtraDofAverageMechanicalSource(index,_ipf,mecaSource);
    				domainVector[i]->computeConstitutiveExtraDofAverageFieldCapacityPerUnitField(index,_ipf,fieldCapacityPerUnitField);
    			};
    
    			fluxT *= volumeRVEInv;
    			internalEnergyExtraDof *= volumeRVEInv;
    			mecaSource *= volumeRVEInv;
    			fieldCapacityPerUnitField *= volumeRVEInv;
    		}
    	}
    
    	// non-constitutive extraDof
    	if (_microBC->getTotalNumberOfNonConDofs()> 0){
    		for (int index=0; index<_microBC->getTotalNumberOfNonConDofs(); index++){
    			SVector3& fluxV = homoData->getHomogenizedNonConstitutiveExtraDofFlux(index);
    			STensorOperation::zero(fluxV);
    			for (int i=0; i<domainVector.size(); i++){
    				domainVector[i]->computeNonConstitutiveExtraDofAverageFlux(index,_ipf,fluxV);
    			};
    			fluxV *= volumeRVEInv;
    		}
    	}
    };
    
    void nonLinearMechSolver::homogenizedDataToFile(const double time){
      homogenizedData* data = this->getHomogenizationState(IPStateBase::current);
    
      _homogenizedFiles->dataToFile(time, this, data);
      if (_extractElasticTangentOperator){
        Tensor43::writeData(_elasticDPDFTangentFile,_elasticState->getHomogenizedTangentOperator_F_F(),time);
      }
    };
    
    void nonLinearMechSolver::extractAverageStressByCondensation(homogenizedData* homoData){
      // condensation can compute conly only stresses and extraDof flux
      // other homogenized properties can be directly computed from
      // volumetric integrals
    
      /**create object in case of NULL **/
      if (_condensation == NULL){
        _condensation = this->createStiffnessCondensation(true,false);
      }
    
      /** solve**/
      fullVector<double> vecStress;
      _condensation->stressSolve(vecStress,getRVEVolume());
    
    	int atRow = 0;
    	// mechanical parts
    	if (_microBC->getTotalNumberOfMechanicalDofs()> 0){
    		STensor3& P = homoData->getHomogenizedStress();
    		P*= (0.);
    		// mechanics stress
    		for (int row = atRow; row<atRow+9; row++){
    			int i,j;
    			Tensor23::getIntsFromIndex(row-atRow,i,j);
    			P(i,j) =vecStress(row);
    		};
    		atRow+= 9;
    
    		if (_microBC->getOrder() == 2){
    			STensor33& Q = homoData->getHomogenizedSecondOrderStress();
    			Q*= (0.);
    			// mechanics second order stress
    			for (int row = atRow; row<atRow+27; row++){
    				int i,j,k;
    				Tensor33::getIntsFromIndex(row-atRow,i,j,k);
    				Q(i,j,k) = vecStress(row);
    			};
    			atRow += 27;
    		}
    
    		// compute energy by volume integral
    		double& defoEnergy = homoData->getDeformationEnergy();
    		double& plasEnergy = homoData->getPlasticEnergy();
    		double& irrEnergy = homoData->getIrreversibleEnergy();
    
    		defoEnergy = 0.;
    		plasEnergy = 0.;
    		irrEnergy = 0.;
    		for (int i=0; i< domainVector.size(); i++){
    			defoEnergy += domainVector[i]->computeDeformationEnergy(_ipf);
    			plasEnergy += domainVector[i]->computePlasticEnergy(_ipf);
    			irrEnergy += domainVector[i]->computeIrreversibleEnergy(_ipf);
    		}
    
    		defoEnergy /= (this->getRVEVolume());
    		plasEnergy /= (this->getRVEVolume());
    		irrEnergy /= (this->getRVEVolume());
    
    	}
    
      if (_microBC->getTotalNumberOfConDofs() > 0){
    		for (int index = 0; index < _microBC->getTotalNumberOfConDofs(); index++){
    			SVector3& extraDofFlux = homoData->getHomogenizedConstitutiveExtraDofFlux(index);
    			extraDofFlux*=(0.);
    			// extradofs
    			for (int row = atRow; row<atRow+3; row++){
    				extraDofFlux(row-atRow) = vecStress(row)/getConstitutiveExtraDofDiffusionEquationRatio(index);
    			};
    			atRow+=3;
    
    			//compute by volume integrals
    			double &internalEnergyExtraDof = homoData->getHomogenizedConstitutiveExtraDofInternalEnergy(index);
    			double &mecaSource = homoData->getHomogenizedConstitutiveExtraDofMechanicalSource(index);
    			double &fieldCapacityPerUnitField = homoData->getHomogenizedConstitutiveExtraDofFieldCapacityPerUnitField(index);
    
    			internalEnergyExtraDof =0.;
    			mecaSource = 0;
    			fieldCapacityPerUnitField = 0.;
    
    			for (int i=0; i<domainVector.size(); i++){
    				domainVector[i]->computeConstitutiveExtraDofAverageInternalEnergy(index,_ipf,internalEnergyExtraDof);
    				domainVector[i]->computeConstitutiveExtraDofAverageMechanicalSource(index,_ipf,mecaSource);
    				domainVector[i]->computeConstitutiveExtraDofAverageFieldCapacityPerUnitField(index,_ipf,fieldCapacityPerUnitField);
    			}
    			double vtotal = this->getRVEVolume();
    			double vin = 1./vtotal;
    
    			internalEnergyExtraDof*= vin;
    			mecaSource *= vin;
    			fieldCapacityPerUnitField *= vin;
    		}
      }
    
    	if (_microBC->getTotalNumberOfNonConDofs()){
    		for (int index = 0; index < _microBC->getTotalNumberOfNonConDofs(); index++){
    			SVector3& extraDofFlux = homoData->getHomogenizedNonConstitutiveExtraDofFlux(index);
    			extraDofFlux*=(0.);
    			// extradofs
    			for (int row = atRow; row<atRow+3; row++){
    				extraDofFlux(row-atRow) = vecStress(row)/getNonConstitutiveExtraDofEquationRatio(index);
    			};
    			atRow+=3;
    		}
    	}
    };
    
    void nonLinearMechSolver::extractAveragePropertiesByCondensation(homogenizedData* homoData){
      // condensation can compute conly only tangent operators of stresses and extraDof flux
      // as surface integrals can be used
      // other homogenized properties cannot be computed
      // using other method to estimated tangent
      // if considering mechanical source
    
      /**create object in case of NULL **/
      if (_condensation == NULL){
        if (_homogenizeStressMethod == VOLUME)
          _condensation = this->createStiffnessCondensation(false, true);
        else
          _condensation = this->createStiffnessCondensation(true, true);
      }
    
      if (_pAl->isModifiedConstraintMatrix()){
        // recomputing all constraint matrix if necessary
        _condensation->clear();
        _pAl->setIsModifiedConstraintMatrixFlag(false);
      }
    
      
    
      /** compute splitted stiffness matrix**/
      this->computeSplittedStiffnessMatrix(false); //to get the homogenized stress derivative with respect to FM we do not want the body force effect
    
      /** solve**/
      fullMatrix<double> L;
      double t=Cpu();
      if (_multiscaleFlag == false)
        Msg::Info("Begin performing static condensation");
      _condensation->tangentCondensationSolve(L,getRVEVolume());
      
      if(computedUdF())
      {
        static STensor53 dPdG;
        static STensor53 dQdF;
        static STensor63 dQdG;
        this->HOStressTangent_modificationTerm(dPdG, dQdF, dQdG);
        this->modifyHOStressTangent(L,dPdG, dQdF, dQdG); //to get the homogenized stress derivative with respect to GM we want the body force effect
      }
      
      t = Cpu() -t;
      if (_multiscaleFlag == false)
        Msg::Info("Done performing static condensation (%f s)",t);
      fillTangent(L,homoData);
    
      if (_microBC->getTotalNumberOfConDofs() > 0) {
        // re-estimate tangent values due to use eqRatio
    		for (int index=0; index < _microBC->getTotalNumberOfConDofs(); index++){
    			double eqRatio=1./getConstitutiveExtraDofDiffusionEquationRatio(index);
    			if (_microBC->getTotalNumberOfMechanicalDofs()>0){
    				homoData->getHomogenizedTangentOperator_gradT_F(index) *= eqRatio;
    				if (_microBC->getOrder() == 2){
    					homoData->getHomogenizedTangentOperator_gradT_G(index) *= eqRatio;
    				}
    			}
    			for (int j=0; j< _microBC->getTotalNumberOfConDofs(); j++){
    				homoData->getHomogenizedTangentOperator_gradT_gradT(index,j) *= eqRatio;
    				homoData->getHomogenizedTangentOperator_gradT_T(index,j) *= eqRatio;
    			}
    
    			for (int j=0; j< _microBC->getTotalNumberOfNonConDofs(); j++){
    				homoData->getHomogenizedTangentOperator_gradT_gradV(index,j) *= eqRatio;
    			}
    
    		}
    
      }
    
    	if (_microBC->getTotalNumberOfNonConDofs() > 0) {
    		for (int index=0; index< _microBC->getTotalNumberOfNonConDofs(); index++){
    			double eqRatio=1./getNonConstitutiveExtraDofEquationRatio(index);
    			if (_microBC->getTotalNumberOfMechanicalDofs() >0){
    				homoData->getHomogenizedTangentOperator_gradV_F(index) *= eqRatio;
    				if (_microBC->getOrder() == 2){
    					homoData->getHomogenizedTangentOperator_gradV_G(index) *= eqRatio;
    				}
    			}
    
    			for (int j=0; j< _microBC->getTotalNumberOfNonConDofs(); j++){
    				homoData->getHomogenizedTangentOperator_gradV_gradV(index,j) *= eqRatio;
    			}
    
    			for (int j=0; j< _microBC->getTotalNumberOfConDofs(); j++){
    				homoData->getHomogenizedTangentOperator_gradV_T(index,j) *= eqRatio;
    				homoData->getHomogenizedTangentOperator_gradV_gradT(index,j) *= eqRatio;
    			}
    		}
    	};
    	
      if (_homogenizeStressMethod == VOLUME){
        extractAverageStressByVolumeIntegral(homoData);
      }
      else{
        extractAverageStressByCondensation(homoData);
      }
    };
    
    void nonLinearMechSolver::extractAveragePropertiesByInSystemCondensationDISP_MULT(homogenizedData* homoData)
    {
      // extract tangent  
      _pAl->zeroMultipleRHS();
      _pAl->assembmeToMultipleRHS();
      double t = Cpu();
      Msg::Info("start solving multiple RHS");
      _pAl->systemSolveMultipleRHS();
      Msg::Info("time to solve multiple RHS = %.5f",Cpu()-t);
      
      double vRVE = this->getRVEVolume();
      STensor43& L = homoData->getHomogenizedTangentOperator_F_F();
      STensorOperation::zero(L);
      //
      double wtotal = 0;
      for (int i=0; i< domainVector.size(); i++)
      {
        partDomain* dom = domainVector[i];
        FunctionSpaceBase* space = dom->getFunctionSpace();
        std::vector<Dof> R;
        IntPt* GP;
        for (elementGroup::elementContainer::const_iterator ite = dom->element_begin(); ite != dom->element_end(); ite++ )
        {
          MElement* e = ite->second;
          int npts = dom->getBulkGaussIntegrationRule()->getIntPoints(e,&GP);
          const AllIPState::ipstateElementContainer* ips =_ipf->getAips()->getIPstate(e->getNum());
          R.clear();
          space->getKeys(e,R);
          int nbFF = e->getNumShapeFunctions();
          std::vector<int> DofPos(R.size());
          for (int iR=0; iR < R.size(); iR++)
          {
            DofPos[iR] = pAssembler->getDofNumber(R[iR]);
          }
          for (int j=0; j< npts; j++)
          {
            const ipFiniteStrain* ipv = dynamic_cast<const ipFiniteStrain*>((*ips)[j]->getState(IPStateBase::current));
            const std::vector<TensorialTraits<double>::GradType> &Grads = ipv->gradf(space,e,GP[j]);
            const STensor43& Llocal = ipv->getConstRefToTangentModuli();
            wtotal += ipv->getJacobianDeterminant(e,GP[j])*GP[j].weight;
            double wJ = ipv->getJacobianDeterminant(e,GP[j])*GP[j].weight/vRVE;
            //L.axpy(wJ,Llocal);
            for (int k=0; k<3; k++)
            {
              for (int l=0; l<3; l++)
              {
                for (int p=0; p<3; p++)
                {
                  for (int q=0; q<3; q++) 
                  {
                    int indexpq = Tensor23::getIndex(p,q);
                    for (int r=0; r< nbFF; r++)
                    {
                      for (int a=0; a<3; a++)
                      {
                        for (int b=0; b<3; b++)
                        {
                          L(k,l,p,q)  += wJ*Llocal(k,l,a,b)*Grads[r+a*nbFF](b)*_pAl->getFromMultipleRHSSolution(DofPos[r+a*nbFF],indexpq);
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
      
      Msg::Info("wtotal= %e vRVE = %e",wtotal,vRVE);
      L.print("L");
      // extract stress
      this->extractAverageStress(homoData);
    };
    
    void nonLinearMechSolver::extractAveragePropertiesByInSystemCondensation(homogenizedData* homoData){
      // extract tangent
      bool needdUdF = false;
      bool needdUdG = false;
    
      if(computedUdF()){ 
        needdUdF=true; 
        needdUdG=true; 
      }
      double vRVE = this->getRVEVolume();
      // estimation of stress matrix
      _pAl->zeroStressMatrix();
      _pAl->zeroBodyForceMatrix();
    
      for (int idom=0; idom<domainVector.size(); idom++){
        partDomain* dom = domainVector[idom];
    	if (dom->elementGroupSize()>0){
    	   dom->setRVEGeometricalInertia(_rveGeoInertia);
    	   dom->setRVEVolume(_rveVolume);
    	   AssembleStressMatrix(*dom->getBilinearTangentTerm(),*dom->getFunctionSpace(),dom->element_begin(),
               dom->element_end(),*dom->getBulkGaussIntegrationRule(),*pAssembler,_elementErosionFilter);
    	}
      }
    
      if(needdUdF){ 
          for (int idom=0; idom<domainVector.size(); idom++){
            partDomain* dom = domainVector[idom];
    	if (dom->elementGroupSize()>0){
    	   AssembleBodyForceMatrix(*dom->getBilinearBodyForceTangentTerm(),*dom->getFunctionSpace(),dom->element_begin(),
               dom->element_end(),*dom->getBulkGaussIntegrationRule(),*pAssembler,_elementErosionFilter);
    	}
          }
      }
    
    
      fullMatrix<double> L;
      double t= Cpu();
      if (_multiscaleFlag == false)
        Msg::Info("Begin performing in-system condensation");
      int goodSolve = _pAl->condensationSolve(L,vRVE,needdUdF, needdUdG, pAssembler,&homoData->getdUnknowndF(),&homoData->getdUnknowndG());  
      t = Cpu() -t;
      if (_multiscaleFlag == false)
        Msg::Info("Done performing in-system condensation (%f s)",t);
        
      if(needdUdF)
      {
        _ipf->setdFmdFM(homoData->getdUnknowndF(), IPStateBase::current);
        if(needdUdG)
          _ipf->setdFmdGM(homoData->getdUnknowndG(), IPStateBase::current);
    
        static STensor53 dPdG;
        static STensor53 dQdF;
        static STensor63 dQdG;
        this->HOStressTangent_modificationTerm(dPdG, dQdF, dQdG);
        const STensor63& dQdG_correction = homoData->getHomogenizedCorrectionTerm_G_G();
        dQdG += dQdG_correction;
        this->modifyHOStressTangent(L,dPdG, dQdF, dQdG); //to get the homogenized stress derivative with respect to GM we want the body force effect
    
      }  
      fillTangent(L,homoData);
      this->setHomogeneousTangentToDomain(homoData);
      
      // extract stress
      this->extractAverageStress(homoData);
        
      // extract cohesive law
      if (withEnergyDissipation() and getDamageToCohesiveJumpFlag()){ 
    		STensor33& DcjumpDF = homoData->getHomogenizedTangentOperator_CohesiveJump_F();
        STensorOperation::zero(DcjumpDF);
    
    		const double& clength = homoData->getAverageLocalizationBandWidth();
        double beta = homoData->getActiveDissipationVolume()/(_surfaceReductionRatio*vRVE);
        double fact = (clength/(1.-beta));
    
        static STensor43 Ifourth(1.,1.);
    
    		if (_solverIsBroken){
    			const STensor43&  dFdamDF = homoData->getHomogenizedTangentOperator_ActiveDissipationDeformationGradient_F();
    			for (int i=0; i<3; i++){
    				for (int j=0; j<3; j++){
    					for (int k=0; k<3; k++){
    						for (int l=0; l<3; l++){
    							DcjumpDF(i,j,k) += fact*(dFdamDF(i,l,j,k) - Ifourth(i,l,j,k))*_lostSolutionUniquenssNormal(l);
    						}
    					}
    				}
    			}
    		}
    
    		if (_microBC->getOrder() == 2){
    			STensor43& dcjumpdG = homoData->getHomogenizedTangentOperator_CohesiveJump_G();
          STensorOperation::zero(dcjumpdG);
    			if (_solverIsBroken){
    				const STensor53& dFdamDG = homoData->getHomogenizedTangentOperator_ActiveDissipationDeformationGradient_G();
    				for (int i=0; i<3; i++){
    					for (int j=0; j<3; j++){
    						for (int k=0; k<3; k++){
    							for (int l=0; l<3; l++){
    								for (int r=0; r<3; r++){
    									dcjumpdG(i,j,k,r) += fact*dFdamDG(i,l,j,k,r)*_lostSolutionUniquenssNormal(l);
    								}
    							}
    						}
    					}
    				}
    			}
    		}
    
    		if (_microBC->getTotalNumberOfConDofs()> 0){
    			for (int index=0; index < _microBC->getTotalNumberOfConDofs(); index++){
    				STensor3& dcjumpdgradT = homoData->getHomogenizedTangentOperator_CohesiveJump_gradT(index);
            STensorOperation::zero(dcjumpdgradT);
    
    				SVector3& dcjumpdT = homoData->getHomogenizedTangentOperator_CohesiveJump_T(index);
            STensorOperation::zero(dcjumpdT);
    
    				if (_solverIsBroken){
    					const STensor3& dFdamDT = homoData->getHomogenizedTangentOperator_ActiveDissipationDeformationGradient_T(index);
    					const STensor33& dFdamDgradT = homoData->getHomogenizedTangentOperator_ActiveDissipationDeformationGradient_gradT(index);
    					for (int i=0; i<3; i++){
    						for (int j=0; j<3; j++){
    							dcjumpdT(i) += fact*dFdamDT(i,j)*_lostSolutionUniquenssNormal(j);
    							for (int k=0; k<3; k++){
    								dcjumpdgradT(i,k) += fact*dFdamDgradT(i,j,k)*_lostSolutionUniquenssNormal(j);
    							};
    						}
    					}
    				};
    			}
    		}
    
    		if (_microBC->getTotalNumberOfNonConDofs()> 0){
    			for (int index=0; index < _microBC->getTotalNumberOfNonConDofs(); index++){
    				STensor3& dcjumpdgradV = homoData->getHomogenizedTangentOperator_CohesiveJump_gradV(index);
            STensorOperation::zero(dcjumpdgradV);
    
    				if (_solverIsBroken){
    					const STensor33& dFdamDgradV = homoData->getHomogenizedTangentOperator_ActiveDissipationDeformationGradient_gradV(index);
    					for (int i=0; i<3; i++){
    						for (int j=0; j<3; j++){
    							for (int k=0; k<3; k++){
    								dcjumpdgradV(i,k) += fact*dFdamDgradV(i,j,k)*_lostSolutionUniquenssNormal(j);
    							};
    						}
    					}
    				};
    			}
    		}
      }
    
      if (_extractElasticTangentOperator)
      {
        if (_multiscaleFlag == false)
          Msg::Info("extract elastic tengent operator");
        //estimating elastic stiffness matrix
        this->computeElasticStiffMatrix();
    
        // estimation of stress matrix
        _pAl->zeroStressMatrix();
        _pAl->zeroBodyForceMatrix();
        for (int idom=0; idom<domainVector.size(); idom++){
          partDomain* dom = domainVector[idom];
          if (dom->elementGroupSize()>0){
            dom->setRVEGeometricalInertia(_rveGeoInertia);
            AssembleStressMatrix(*dom->getBilinearElasticTangentTerm(),*dom->getFunctionSpace(),dom->element_begin(),
                              dom->element_end(),*dom->getBulkGaussIntegrationRule(),*pAssembler,_elementErosionFilter);
          }
    
        } 
        
        if(needdUdF){
          for (int idom=0; idom<domainVector.size(); idom++){
            partDomain* dom = domainVector[idom];
    	if (dom->elementGroupSize()>0){
    	   AssembleBodyForceMatrix(*dom->getBilinearBodyForceTangentTerm(),*dom->getFunctionSpace(),dom->element_begin(),
               dom->element_end(),*dom->getBulkGaussIntegrationRule(),*pAssembler,_elementErosionFilter);
    	}
          } 
        }   
    
        //
        t= Cpu();
        if (_multiscaleFlag == false)
          Msg::Info("Begin performing in-system condensation");
        int goodSolve = _pAl->condensationSolve(L,vRVE,needdUdF, needdUdG, pAssembler,&homoData->getdUnknowndF(),&homoData->getdUnknowndG()); 
        t = Cpu() -t;
        if (_multiscaleFlag == false)
          Msg::Info("Done performing in-system condensation (%f s)",t);
    
        fillTangent(L,_elasticState);
    
        if (_multiscaleFlag == false){
          _elasticState->getHomogenizedTangentOperator_F_F().print("Elastic tangent operator");
          Msg::Info("done extracting elastic tengent operator");
        }
      };
    
    };
    
    void nonLinearMechSolver::extractdUdFByInSystemCondensation(homogenizedData* homoData){
      // extract tangent
      _pAl->zeroBodyForceMatrix();
      
      for (int idom=0; idom<domainVector.size(); idom++){
         partDomain* dom = domainVector[idom];
         if (dom->elementGroupSize()>0){
    	AssembleBodyForceMatrix(*dom->getBilinearBodyForceTangentTerm(),*dom->getFunctionSpace(),dom->element_begin(),
               dom->element_end(),*dom->getBulkGaussIntegrationRule(),*pAssembler,_elementErosionFilter);
         }
      }
      
      double t= Cpu();
      if (_multiscaleFlag == false)
        Msg::Info("Begin performing in-system condensationfor dUdF");
      int goodSolve = _pAl->condensationdUdF(pAssembler, &homoData->getdUnknowndF());  
      t = Cpu() -t;
      if (_multiscaleFlag == false)
        Msg::Info("Done performing in-system condensation (%f s)",t);
    };
    
    void nonLinearMechSolver::extractAverageStress(homogenizedData* homoData){
      if (_homogenizeStressMethod == VOLUME)
        this->extractAverageStressByVolumeIntegral(homoData);
      else if (_homogenizeStressMethod == SURFACE)
        this->extractAverageStressByCondensation(homoData);
      else {
        Msg::Error("This method is not implemented");
      }
    };
    
    void nonLinearMechSolver::extractAverageStressAndTangent(homogenizedData* homoData){
      if (_homogenizeTangentMethod == CONDEN or _homogenizeTangentMethod==UNIFIED_CONDEN){
        this->extractAveragePropertiesByCondensation(homoData);
      }
      else if (_homogenizeTangentMethod == PERTURB){
        this->extractAveragePropertiesPerturbation(homoData);
      }
      else if (_homogenizeTangentMethod == INSYSTEMCONDEN){
        if (_systemType == DISP_MULT)
        {
          this->extractAveragePropertiesByInSystemCondensationDISP_MULT(homoData);
        }
        else
          this->extractAveragePropertiesByInSystemCondensation(homoData);
      }
      else{
        Msg::Error("Homogenization tangent method must be correctly defined");
      }
    
      // estimate loss of ellipticity
      if (_checkFailureOnset){
        const STensor43& L = homoData->getHomogenizedTangentOperator_F_F();
        double& cr = homoData->getLostOfSolutionUniquenessCriterion();
        cr = _lostSolutionUniquenssTolerance - 1.;
    
        double detNLN = 0;
        if (_checkWithNormal){
          // check lost of solution uniqueness
          if (_lostSolutionUniquenssNormal.norm() <=0.){
            Msg::Error("can not check failure with zero normal");
          }
          //
          FailureDetection::getLostSolutionUniquenessCriterionFollowingDirection(_dim,L,_lostSolutionUniquenssNormal,detNLN);
        }
        else{
          // the material softening is not allowed in bulk IP
          FailureDetection::getLostSolutionUniquenessCriterion(_dim,L,_lostSolutionUniquenssNormal,detNLN);
        }
    
        // maximal Cr
    		if (_maximalLostEllipticityCriterion < detNLN and detNLN > 0.){
    			_maximalLostEllipticityCriterion = detNLN;
    		}
    		if (_maximalLostEllipticityCriterion > 0){
          cr = _lostSolutionUniquenssTolerance -  detNLN/_maximalLostEllipticityCriterion; 
    		}
      }
    };
    
    void nonLinearMechSolver::extractAveragePropertiesPerturbation(homogenizedData* homoData){
      // extract stress
      this->extractAverageStress(homoData);
      bool Extract_Qstress = this->computedUdF();
    	// mesh  chracteristic size for perturbation
    	double elength = 0.;
    	for (int i=0; i< domainVector.size(); i++){
    		if (domainVector[i]->elementGroupSize() > 0){
    			const MElement* ele = (domainVector[0]->element_begin()->second);
    			double volume = const_cast<MElement*>(ele)->getVolume();
    
    			if (_dim == 2){
    				elength = sqrt(volume);
    			}
    			else if (_dim ==3){
    				elength = pow(volume,1.0/3.0);
    			}
    			break;
    		}
    	}
    
    	//
    	/** function for compute tangent by perturbation on current state**/
        /*
        save all computed current data to tmp step
      */
    
      _ipf->copy(IPStateBase::current,IPStateBase::temp);
      std::string Aname ="A";
      linearSystem<double>* lsys = pAssembler->getLinearSystem(Aname);
      nonLinearSystem<double>* nonlsys = dynamic_cast<nonLinearSystem<double>*>(lsys);
      nonlsys->copy(IPStateBase::current,IPStateBase::temp);
    
           homogenizedData homoDataPlus(*homoData);
           homogenizedData homoDatadUdF(*homoData);
    	double t= Cpu();
      if (_multiscaleFlag == false)
        Msg::Info("Begin performing tangent by perturbation");
    
    	if (_microBC->getTotalNumberOfMechanicalDofs()> 0){
    		STensor3 F = _microBC->getFirstOrderKinematicalVariable();		
    		const STensor3& P = homoData->getHomogenizedStress();        
    		for (int i=0; i<_dim; i++){
    			for (int j=0; j<_dim; j++){
    				nonlsys->copy(IPStateBase::previous,IPStateBase::current);
    				/** perturbation in deformation gradient **/
    				STensor3 Fplus(F);
    				Fplus(i,j) += _tangentPerturbation;
    				_microBC->setFirstOrderKinematicalVariable(Fplus);
    
    				/** solve perturbated system**/
    				this->OneStep(0);
    
    				/** get homogenized stress in perturbed system **/
    				this->extractAverageStress(&homoDataPlus);
    			
    				const STensor3& Pplus = homoDataPlus.getHomogenizedStress();
    				for (int k=0; k<3; k++){
    					for (int l=0; l<3; l++){
    						homoData->getHomogenizedTangentOperator_F_F()(k,l,i,j) = (Pplus(k,l) - P(k,l))/(_tangentPerturbation);
    						homoDataPlus.getHomogenizedTangentOperator_F_F()(k,l,i,j) = (Pplus(k,l) - P(k,l))/(_tangentPerturbation);
    					}
    				}
    
    				if (_microBC->getOrder() == 2 and !Extract_Qstress){
    				        // extract stress
    					const STensor33& Q = homoData->getHomogenizedSecondOrderStress();
    					const STensor33& Qplus = homoDataPlus.getHomogenizedSecondOrderStress();
    					for (int k=0; k<3; k++)
    						for (int l=0; l<3; l++)
    							for (int m=0; m<3; m++)
    								homoData->getHomogenizedTangentOperator_G_F()(k,l,m,i,j) = (Qplus(k,l,m) - Q(k,l,m))/(_tangentPerturbation);
    				}
    
    				if (_damageToCohesiveJump){
    					const SVector3& cjump = homoData->getHomogenizedCohesiveJump();
    					const SVector3& cjumpplus = homoDataPlus.getHomogenizedCohesiveJump();
    					for (int k=0; k<3; k++){
    						homoData->getHomogenizedTangentOperator_CohesiveJump_F()(k,i,j) = (cjumpplus(k) - cjump(k))/(_tangentPerturbation);
    					}
    				}
    
    				if (_extractIrreversibleEnergy){
    					const double& irrEnerg = homoData->getIrreversibleEnergy();
    					const double& irrEnergPlus = homoDataPlus.getIrreversibleEnergy();
    					homoData->getHomogenizedTangentOperator_IrreversibleEnergy_F()(i,j) = (irrEnergPlus - irrEnerg)/_tangentPerturbation;
    				}
    
    
    				if (_microBC->getTotalNumberOfConDofs() > 0){
    					for (int index=0; index < _microBC->getTotalNumberOfConDofs(); index++){
    						const SVector3& flux = homoData->getHomogenizedConstitutiveExtraDofFlux(index);
    						const SVector3& fluxPlus = homoDataPlus.getHomogenizedConstitutiveExtraDofFlux(index);
    						for (int k=0; k<3; k++){
    							homoData->getHomogenizedTangentOperator_gradT_F(index)(k,i,j) = (fluxPlus(k)-flux(k))/(_tangentPerturbation);
    
    						}
    						const double& mecasrc = homoData->getHomogenizedConstitutiveExtraDofMechanicalSource(index);
    						const double& mecasrcPlus = homoDataPlus.getHomogenizedConstitutiveExtraDofMechanicalSource(index);
    						homoData->getHomogenizedTangentOperator_ConstitutiveExtraDofMechanicalSource_F(index)(i,j) = (mecasrcPlus-mecasrc)/(_tangentPerturbation);
    					}
    				}
    
    				if (_microBC->getTotalNumberOfNonConDofs() > 0){
    					for (int index=0; index < _microBC->getTotalNumberOfNonConDofs(); index++){
    						const SVector3& flux = homoData->getHomogenizedNonConstitutiveExtraDofFlux(index);
    						const SVector3& fluxPlus = homoDataPlus.getHomogenizedNonConstitutiveExtraDofFlux(index);
    						for (int k=0; k<3; k++){
    							homoData->getHomogenizedTangentOperator_gradV_F(index)(k,i,j) = (fluxPlus(k)-flux(k))/(_tangentPerturbation);
    
    						}
    					}
    				}
    			}
    		}	
    		
    		/** back micro BC as before perturbation**/
    		_microBC->setFirstOrderKinematicalVariable(F);		
    
    		if (_microBC->getOrder() == 2){
    		    if(Extract_Qstress){
    		      nonlsys->copy(IPStateBase::previous,IPStateBase::current);
    		      this->OneStep(0);
    		      this->extractAveragePropertiesByInSystemCondensation(&homoDatadUdF);
    		      
    		      nonlsys->copy(IPStateBase::previous,IPStateBase::current);
    		      _ipf->setdFmdFM(homoDatadUdF.getdUnknowndF(), IPStateBase::current);
    		      this->OneStep(0);		      
    		      this->extractAverageStress(homoData);
    		      
    		      for (int i=0; i<_dim; i++){
    		        for (int j=0; j<_dim; j++){
    			    nonlsys->copy(IPStateBase::previous,IPStateBase::current);
    			    _ipf->setdFmdFM(homoDatadUdF.getdUnknowndF(), IPStateBase::current);
    			     /** perturbation in deformation gradient **/
    			    STensor3 Fplus(F);
    			    Fplus(i,j) += _tangentPerturbation;
    			    _microBC->setFirstOrderKinematicalVariable(Fplus);
    
    			     /** solve perturbated system**/
    			    this->OneStep(0);
    	                    /** get homogenized stress in perturbed system **/
    			    this->extractAverageStress(&homoDataPlus);
    	                   // extract stress
    			    const STensor33& Q = homoData->getHomogenizedSecondOrderStress();
    			    const STensor33& Qplus = homoDataPlus.getHomogenizedSecondOrderStress();
    			    for (int k=0; k<3; k++)
    				for (int l=0; l<3; l++)
    				    for (int m=0; m<3; m++)
    					homoData->getHomogenizedTangentOperator_G_F()(k,l,m,i,j) = (Qplus(k,l,m) - Q(k,l,m))/(_tangentPerturbation);
    			 }
    		      }
    		    }
    			/**perturbation in gradient of deformation gradient **/
    			STensor33 G = _microBC->getSecondOrderKinematicalVariable();
    			double secondpert = _tangentPerturbation/elength;
    
    			for (int i=0; i<_dim; i++){
    				for (int j=0; j<_dim; j++){
    					for (int s=0; s<=j; s++){
    						nonlsys->copy(IPStateBase::previous,IPStateBase::current);
    						_ipf->setdFmdFM(homoDatadUdF.getdUnknowndF(), IPStateBase::current);
    						STensor33 Gplus(G);
    						Gplus(i,j,s) += secondpert;
    						Gplus(i,s,j) += secondpert;
    
    						_microBC->setSecondOrderKinematicalVariable(Gplus);
    						
    						
    						/** solve perturbed system**/
    						this->OneStep(0);
    
    						/** get homogenized stress in perturbed system **/
                                                  this->extractAverageStress(&homoDataPlus);
                                                                                                 
    						//
    						const STensor3& P = homoData->getHomogenizedStress();
    						const STensor3& Pplus = homoDataPlus.getHomogenizedStress();
    						for (int k=0; k<3; k++){
    						    for (int l=0; l<3; l++){
    							homoData->getHomogenizedTangentOperator_F_G()(k,l,i,j,s) = (Pplus(k,l) - P(k,l))/(2.*secondpert);
    							homoData->getHomogenizedTangentOperator_F_G()(k,l,i,s,j) = homoData->getHomogenizedTangentOperator_F_G()(k,l,i,j,s);
    						    }
    						}
     
    						const STensor33& Q = homoData->getHomogenizedSecondOrderStress();
    						const STensor33& Qplus = homoDataPlus.getHomogenizedSecondOrderStress();						
    						for (int k=0; k<3; k++){
    							for (int l=0; l<3; l++){
    								for (int m=0; m<3; m++){
    									homoData->getHomogenizedTangentOperator_G_G()(k,l,m,i,j,s) = (Qplus(k,l,m) - Q(k,l,m))/(2.*secondpert);
    									homoData->getHomogenizedTangentOperator_G_G()(k,l,m,i,s,j)  = homoData->getHomogenizedTangentOperator_G_G()(k,l,m,i,j,s);
    								}
    							}
    						}
    
    						if (_damageToCohesiveJump){
    							const SVector3& cjump = homoData->getHomogenizedCohesiveJump();
    							const SVector3& cjumpplus = homoDataPlus.getHomogenizedCohesiveJump();
    							for (int k=0; k<3; k++){
    								homoData->getHomogenizedTangentOperator_CohesiveJump_G()(k,i,j,s) = (cjumpplus(k) - cjump(k))/(2.*secondpert);
    								homoData->getHomogenizedTangentOperator_CohesiveJump_G()(k,i,s,j) = homoData->getHomogenizedTangentOperator_CohesiveJump_G()(k,i,j,s);
    							}
    						}
    
    						if (_extractIrreversibleEnergy){
    							const double& irrEnerg = homoData->getIrreversibleEnergy();
    							const double& irrEnergPlus = homoDataPlus.getIrreversibleEnergy();
    							homoData->getHomogenizedTangentOperator_IrreversibleEnergy_G()(i,j,s) = (irrEnergPlus - irrEnerg)/(2.*secondpert);
    							homoData->getHomogenizedTangentOperator_IrreversibleEnergy_G()(i,s,j) = homoData->getHomogenizedTangentOperator_IrreversibleEnergy_G()(i,j,s);
    						}
    
    						if (_microBC->getTotalNumberOfConDofs() > 0){
    							for (int index=0; index < _microBC->getTotalNumberOfConDofs(); index++){
    								const SVector3& flux = homoData->getHomogenizedConstitutiveExtraDofFlux(index);
    								const SVector3& fluxPlus = homoDataPlus.getHomogenizedConstitutiveExtraDofFlux(index);
    								for (int k=0; k<3; k++){
    									homoData->getHomogenizedTangentOperator_gradT_G(index)(k,i,j,s) = (fluxPlus(k)-flux(k))/(2.*secondpert);
    									homoData->getHomogenizedTangentOperator_gradT_G(index)(k,i,s,j) = homoData->getHomogenizedTangentOperator_gradT_G(index)(k,i,j,s);
    								}
    								const double& mecasrc = homoData->getHomogenizedConstitutiveExtraDofMechanicalSource(index);
    								const double& mecasrcPlus = homoDataPlus.getHomogenizedConstitutiveExtraDofMechanicalSource(index);
    								homoData->getHomogenizedTangentOperator_ConstitutiveExtraDofMechanicalSource_G(index)(i,j,s) = (mecasrcPlus-mecasrc)/(2.*secondpert);
    								homoData->getHomogenizedTangentOperator_ConstitutiveExtraDofMechanicalSource_G(index)(i,s,j) = homoData->getHomogenizedTangentOperator_ConstitutiveExtraDofMechanicalSource_G(index)(i,j,s);
    							}
    						}
    
    						if (_microBC->getTotalNumberOfNonConDofs() > 0){
    							for (int index=0; index < _microBC->getTotalNumberOfNonConDofs(); index++){
    								const SVector3& flux = homoData->getHomogenizedNonConstitutiveExtraDofFlux(index);
    								const SVector3& fluxPlus = homoDataPlus.getHomogenizedNonConstitutiveExtraDofFlux(index);
    								for (int k=0; k<3; k++){
    									homoData->getHomogenizedTangentOperator_gradV_G(index)(k,i,j,s) = (fluxPlus(k)-flux(k))/(2.*secondpert);
    									homoData->getHomogenizedTangentOperator_gradV_G(index)(k,i,s,j) = homoData->getHomogenizedTangentOperator_gradV_G(index)(k,i,j,s);
    								}
    							}
    						}
    
    					}
    				}
    			}
    			_microBC->setSecondOrderKinematicalVariable(G);
    		}
    	}
    
    	if (_microBC->getTotalNumberOfConDofs() > 0){
    		for (int index = 0; index < _microBC->getTotalNumberOfConDofs(); index ++){
    			SVector3 gradT = _microBC->getConstitutiveExtraDofDiffusionKinematicalVariable(index);
    			double gradTpert = _tangentPerturbation*(_microBC->getInitialConstitutiveExtraDofDiffusionValue(index))/elength;
    
    			for (int i=0; i<_dim; i++){
    				nonlsys->copy(IPStateBase::previous,IPStateBase::current);
    				SVector3 gradTplus(gradT);
    
    				gradTplus(i) += gradTpert;
    				_microBC->setConstitutiveExtraDofDiffusionKinematicalVariable(index,gradTplus);
    				/** solve perturbed system**/
    				this->OneStep(0);
    
    				/** get homogenized stress in perturbed system **/
    				this->extractAverageStress(&homoDataPlus);
    
    				if (_microBC->getTotalNumberOfMechanicalDofs() > 0){
    					const STensor3& P = homoData->getHomogenizedStress();
    					const STensor3& Pplus = homoDataPlus.getHomogenizedStress();
    					for (int p=0; p<3; p++){
    						for (int q=0; q<3; q++){
    							homoData->getHomogenizedTangentOperator_F_gradT(index)(p,q,i) = (Pplus(p,q)- P(p,q))/(gradTpert);
    						}
    					}
    
    					if (_microBC->getOrder() == 2){
    						const STensor33& Q = homoData->getHomogenizedSecondOrderStress();
    						const STensor33& Qplus = homoDataPlus.getHomogenizedSecondOrderStress();
    						for (int p=0; p<3; p++)
    							for (int q=0; q<3; q++)
    								for (int r=0; r<3; r++)
    									homoData->getHomogenizedTangentOperator_G_gradT(index)(p,q,r,i) = (Qplus(p,q,r) - Q(p,q,r))/(gradTpert);
    					}
    
    					if (_damageToCohesiveJump){
    						const SVector3& cjump = homoData->getHomogenizedCohesiveJump();
    						const SVector3& cjumpplus = homoDataPlus.getHomogenizedCohesiveJump();
    						for (int p=0; p<3; p++){
    							homoData->getHomogenizedTangentOperator_CohesiveJump_gradT(index)(p,i) = (cjumpplus(p) - cjump(p))/(gradTpert);
    						}
    					}
    
    					if (_extractIrreversibleEnergy){
    						const double& irrEnerg = homoData->getIrreversibleEnergy();
    						const double& irrEnergPlus = homoDataPlus.getIrreversibleEnergy();
    						homoData->getHomogenizedTangentOperator_IrreversibleEnergy_gradT(index)(i) = (irrEnergPlus-irrEnerg)/gradTpert;
    					}
    				}
    
    				for (int fluxIndex = 0; fluxIndex < _microBC->getTotalNumberOfConDofs(); fluxIndex ++){
    					const SVector3& flux = homoData->getHomogenizedConstitutiveExtraDofFlux(fluxIndex);
    					const SVector3& fluxPlus = homoDataPlus.getHomogenizedConstitutiveExtraDofFlux(fluxIndex);
    					for (int p=0; p<3; p++){
    						homoData->getHomogenizedTangentOperator_gradT_gradT(fluxIndex,index)(p,i) = (fluxPlus(p) - flux(p))/(gradTpert);
    					}
    
    					const double& source = homoData->getHomogenizedConstitutiveExtraDofMechanicalSource(fluxIndex);
    					const double& sourcePlus = homoDataPlus.getHomogenizedConstitutiveExtraDofMechanicalSource(fluxIndex);
    					homoData->getHomogenizedTangentOperator_ConstitutiveExtraDofMechanicalSource_gradT(fluxIndex,index)(i) = (sourcePlus-source)/gradTpert;
    				};
    
    				for (int fluxIndex = 0; fluxIndex < _microBC->getTotalNumberOfNonConDofs(); fluxIndex ++){
    					const SVector3& flux = homoData->getHomogenizedNonConstitutiveExtraDofFlux(fluxIndex);
    					const SVector3& fluxPlus = homoDataPlus.getHomogenizedNonConstitutiveExtraDofFlux(fluxIndex);
    					for (int p=0; p<3; p++){
    						homoData->getHomogenizedTangentOperator_gradV_gradT(fluxIndex,index)(p,i) = (fluxPlus(p) - flux(p))/(gradTpert);
    					}
    				}
    				_microBC->setConstitutiveExtraDofDiffusionKinematicalVariable(index,gradT);
    			}
    
    			// perturtabtion on T
    			const double T = _microBC->getConstitutiveExtraDofDiffusionConstantVariable(index);
    			double Tpert = _tangentPerturbation*(_microBC->getInitialConstitutiveExtraDofDiffusionValue(index));
    
    			nonlsys->copy(IPStateBase::previous,IPStateBase::current);
    			double Tplus = T+Tpert;
    
    			_microBC->setConstitutiveExtraDofDiffusionConstantVariable(index,Tplus);
    			/** solve perturbed system**/
    			this->OneStep(0);
    
    			/** get homogenized stress in perturbed system **/
    			this->extractAverageStress(&homoDataPlus);
    
    			if (_microBC->getTotalNumberOfMechanicalDofs() > 0){
    				const STensor3& P = homoData->getHomogenizedStress();
    				const STensor3& Pplus = homoDataPlus.getHomogenizedStress();
    				for (int p=0; p<3; p++){
    					for (int q=0; q<3; q++){
    						homoData->getHomogenizedTangentOperator_F_T(index)(p,q) = (Pplus(p,q)- P(p,q))/(Tpert);
    					}
    				}
    
    				if (_microBC->getOrder() == 2){
    					const STensor33& Q = homoData->getHomogenizedSecondOrderStress();
    					const STensor33& Qplus = homoDataPlus.getHomogenizedSecondOrderStress();
    					for (int p=0; p<3; p++)
    						for (int q=0; q<3; q++)
    							for (int r=0; r<3; r++)
    								homoData->getHomogenizedTangentOperator_G_T(index)(p,q,r) = (Qplus(p,q,r) - Q(p,q,r))/(Tpert);
    				}
    
    				if (_damageToCohesiveJump){
    					const SVector3& cjump = homoData->getHomogenizedCohesiveJump();
    					const SVector3& cjumpplus = homoDataPlus.getHomogenizedCohesiveJump();
    					for (int p=0; p<3; p++){
    						homoData->getHomogenizedTangentOperator_CohesiveJump_T(index)(p) = (cjumpplus(p) - cjump(p))/(Tpert);
    					}
    				}
    				if (_extractIrreversibleEnergy){
    					const double& irrEnerg = homoData->getIrreversibleEnergy();
    					const double& irrEnergPlus = homoDataPlus.getIrreversibleEnergy();
    					homoData->getHomogenizedTangentOperator_IrreversibleEnergy_T(index) = (irrEnergPlus-irrEnerg)/Tpert;
    				}
    			}
    
    			for (int fluxIndex = 0; fluxIndex < _microBC->getTotalNumberOfConDofs(); fluxIndex ++){
    				const SVector3& flux = homoData->getHomogenizedConstitutiveExtraDofFlux(fluxIndex);
    				const SVector3& fluxPlus = homoDataPlus.getHomogenizedConstitutiveExtraDofFlux(fluxIndex);
    				for (int p=0; p<3; p++){
    					homoData->getHomogenizedTangentOperator_gradT_T(fluxIndex,index)(p) = (fluxPlus(p) - flux(p))/(Tpert);
    				}
    
    				const double& source = homoData->getHomogenizedConstitutiveExtraDofMechanicalSource(fluxIndex);
    				const double& sourcePlus = homoDataPlus.getHomogenizedConstitutiveExtraDofMechanicalSource(fluxIndex);
    				homoData->getHomogenizedTangentOperator_ConstitutiveExtraDofMechanicalSource_T(fluxIndex,index) = (sourcePlus-source)/Tpert;
    			};
    
    			for (int fluxIndex = 0; fluxIndex < _microBC->getTotalNumberOfNonConDofs(); fluxIndex ++){
    				const SVector3& flux = homoData->getHomogenizedNonConstitutiveExtraDofFlux(fluxIndex);
    				const SVector3& fluxPlus = homoDataPlus.getHomogenizedNonConstitutiveExtraDofFlux(fluxIndex);
    				for (int p=0; p<3; p++){
    					homoData->getHomogenizedTangentOperator_gradV_T(fluxIndex,index)(p) = (fluxPlus(p) - flux(p))/(Tpert);
    				}
    			}
    			_microBC->setConstitutiveExtraDofDiffusionConstantVariable(index,T);
    		}
    	}
    
    	if (_microBC->getTotalNumberOfNonConDofs() > 0){
    		for (int index = 0; index < _microBC->getTotalNumberOfNonConDofs(); index ++){
    			SVector3 gradV = _microBC->getNonConstitutiveExtraDofDiffusionKinematicalVariable(index);
    			double gradVpert = (1+gradV.norm())*_tangentPerturbation;
    
    			for (int i=0; i<_dim; i++){
    				nonlsys->copy(IPStateBase::previous,IPStateBase::current);
    				SVector3 gradVplus(gradV);
    				gradVplus(i) += gradVpert;
    				_microBC->setNonConstitutiveExtraDofDiffusionKinematicalVariable(index,gradVplus);
    				/** solve perturbed system**/
    				this->OneStep(0);
    
    				/** get homogenized stress in perturbed system **/
    				this->extractAverageStress(&homoDataPlus);
    
    				if (_microBC->getTotalNumberOfMechanicalDofs() > 0){
    					const STensor3& P = homoData->getHomogenizedStress();
    					const STensor3& Pplus = homoDataPlus.getHomogenizedStress();
    					for (int p=0; p<3; p++){
    						for (int q=0; q<3; q++){
    							homoData->getHomogenizedTangentOperator_F_gradV(index)(p,q,i) = (Pplus(p,q)- P(p,q))/(gradVpert);
    						}
    					}
    
    					if (_microBC->getOrder() == 2){
    						const STensor33& Q = homoData->getHomogenizedSecondOrderStress();
    						const STensor33& Qplus = homoDataPlus.getHomogenizedSecondOrderStress();
    						for (int p=0; p<3; p++)
    							for (int q=0; q<3; q++)
    								for (int r=0; r<3; r++)
    									homoData->getHomogenizedTangentOperator_G_gradV(index)(p,q,r,i) = (Qplus(p,q,r) - Q(p,q,r))/(gradVpert);
    					}
    
    					if (_damageToCohesiveJump){
    						const SVector3& cjump = homoData->getHomogenizedCohesiveJump();
    						const SVector3& cjumpplus = homoDataPlus.getHomogenizedCohesiveJump();
    						for (int p=0; p<3; p++){
    							homoData->getHomogenizedTangentOperator_CohesiveJump_gradV(index)(p,i) = (cjumpplus(p) - cjump(p))/(gradVpert);
    						}
    					}
    					if (_extractIrreversibleEnergy){
    						const double& irrEnerg = homoData->getIrreversibleEnergy();
    						const double& irrEnergPlus = homoDataPlus.getIrreversibleEnergy();
    						homoData->getHomogenizedTangentOperator_IrreversibleEnergy_gradV(index)(i) = (irrEnergPlus-irrEnerg)/gradVpert;
    					}
    				}
    
    				for (int fluxIndex = 0; fluxIndex < _microBC->getTotalNumberOfConDofs(); fluxIndex ++){
    					const SVector3& flux = homoData->getHomogenizedConstitutiveExtraDofFlux(fluxIndex);
    					const SVector3& fluxPlus = homoDataPlus.getHomogenizedConstitutiveExtraDofFlux(fluxIndex);
    					for (int p=0; p<3; p++){
    						homoData->getHomogenizedTangentOperator_gradT_gradV(fluxIndex,index)(p,i) = (fluxPlus(p) - flux(p))/(gradVpert);
    					}
    
    					const double& source = homoData->getHomogenizedConstitutiveExtraDofMechanicalSource(fluxIndex);
    					const double& sourcePlus = homoDataPlus.getHomogenizedConstitutiveExtraDofMechanicalSource(fluxIndex);
    					homoData->getHomogenizedTangentOperator_ConstitutiveExtraDofMechanicalSource_gradV(fluxIndex,index)(i) = (sourcePlus-source)/gradVpert;
    				};
    
    				for (int fluxIndex = 0; fluxIndex < _microBC->getTotalNumberOfNonConDofs(); fluxIndex ++){
    					const SVector3& flux = homoData->getHomogenizedNonConstitutiveExtraDofFlux(fluxIndex);
    					const SVector3& fluxPlus = homoDataPlus.getHomogenizedNonConstitutiveExtraDofFlux(fluxIndex);
    					for (int p=0; p<3; p++){
    						homoData->getHomogenizedTangentOperator_gradV_gradV(fluxIndex,index)(p,i) = (fluxPlus(p) - flux(p))/(gradVpert);
    					}
    				}
    				_microBC->setNonConstitutiveExtraDofDiffusionKinematicalVariable(index,gradV);
    			}
    		}
    	}
    
      t = Cpu() -t;
      if (_multiscaleFlag == false)
        Msg::Info("Done performing tangent by perturbation (%f s)",t);;
    
      /**back to current step from temp state **/
      _ipf->copy(IPStateBase::temp,IPStateBase::current);
      nonlsys->copy(IPStateBase::temp,IPStateBase::current);
    }
    
    void nonLinearMechSolver::computeDensity(){
      _rho = 0;
      for (int i=0; i<domainVector.size(); i++){
        partDomain* dom = domainVector[i];
    		// only on volumic domain
    		if (dom->elementGroupSize()>0){
    			double rhodom = dom->getMaterialLaw()->density();
    			double voldom =dom->computeVolumeDomain(_ipf);
    			_rho += (voldom*rhodom);
    		}
      };
      double vtotal = this->getRVEVolume();
      _rho /= vtotal;
    };
    
    stiffnessCondensation* nonLinearMechSolver::createStiffnessCondensation(const bool sflag, const bool tflag){
      stiffnessCondensation* stiffCon = NULL;
    
      /**condensation require **/
      if (_pAl == NULL){
        _pAl = new pbcAlgorithm(this);
      };
      if (!_pAl->isSplittedDof()) _pAl->splitDofs();
    
      if (_homogenizeTangentMethod == UNIFIED_CONDEN){
        #if defined(HAVE_PETSC)
        Msg::Info("stiffnessCondensationPETSc is used for nonLinearMixedBC");
        stiffCon = new stiffnessCondensationPETSc(pAssembler,_pAl,sflag,tflag);
        #else
        Msg::Error("Petsc must be used");
        #endif // HAVE_PETSC
      }
      else if (_homogenizeTangentMethod == CONDEN){
        if (dynamic_cast<nonLinearMinimalKinematicBC*>(_microBC)){
          #if defined(HAVE_PETSC)
          Msg::Info("stiffnessCondensationPETScWithoutDirectConstraints is used for nonLinearMinimalKinematicBC");
          stiffCon = new stiffnessCondensationPETScWithoutDirectConstraints(pAssembler,_pAl,sflag,tflag);
          #else
          Msg::Error("Petsc must be used");
          #endif // HAVE_PETSC
        }
        else if (dynamic_cast<nonLinearDisplacementBC*>(_microBC)){
          #if defined(HAVE_PETSC)
          Msg::Info("stiffnessCondensationLDBCPETSc is used for nonLinearDisplacementBC");
          stiffCon = new stiffnessCondensationLDBCPETSc(pAssembler,_pAl,sflag,tflag);
          #else
          Msg::Error("Petsc must be used");
          #endif // HAVE_PETSC
        }
        else if (dynamic_cast<nonLinearPeriodicBC*>(_microBC)){
          nonLinearPeriodicBC* pbc = dynamic_cast<nonLinearPeriodicBC*>(_microBC);
          if (_pAl->getSplittedDof()->sizeOfVirtualDof() >0 and pbc->getOrder()==1){
            #if defined(HAVE_PETSC)
            Msg::Info("interpolationStiffnessCondensationPETSC is used for nonLinearPeriodicBC");
            stiffCon = new interpolationStiffnessCondensationPETSC(pAssembler,_pAl,sflag,tflag);
            #else
            Msg::Error("Petsc must be used");
            #endif
          }
          else if ((pbc->getPBCMethod() == nonLinearPeriodicBC::LIM) and (pbc->getMaxDegree() == 1)){
            #if defined(HAVE_PETSC)
            Msg::Info("stiffnessCondensationLDBCPETSc is used for nonLinearPeriodicBC as order = 1");
            stiffCon = new stiffnessCondensationLDBCPETSc(pAssembler,_pAl,sflag,tflag);
            #else
            Msg::Error("Petsc must be used");
            #endif
          }
          else{
            #if defined(HAVE_PETSC)
            if (_pAl->getNumberDirectConstraints()>0){
              Msg::Info("stiffnessCondensationPETScWithDirectConstraints is used for nonLinearPeriodicBC");
              stiffCon = new stiffnessCondensationPETScWithDirectConstraints(pAssembler,_pAl,sflag,tflag);
            }
            else{
              Msg::Info("stiffnessCondensationPETScWithoutDirectConstraints is used for nonLinearPeriodicBC");
              stiffCon = new stiffnessCondensationPETScWithoutDirectConstraints(pAssembler,_pAl,sflag,tflag);
            }
            #else
            if (_pAl->getNumberDirectConstraints()>0){
              Msg::Info("stiffnessCondensationFullMatrixWithDirectConstraints is used for nonLinearPeriodicBC");
              stiffCon = new stiffnessCondensationFullMatrixWithDirectConstraints(pAssembler,_pAl,sflag,tflag);
            }
            else{
              Msg::Error("Petsc must be used");
            }
            #endif // HAVE_PETSC
          }
    
        }
        else if (dynamic_cast<nonLinearMixedBC*>(_microBC)){
          #if defined(HAVE_PETSC)
          if (_pAl->getNumberDirectConstraints()>0){
            Msg::Info("stiffnessCondensationPETScWithDirectConstraints is used for nonLinearMixedBC");
            stiffCon = new stiffnessCondensationPETScWithDirectConstraints(pAssembler,_pAl,sflag,tflag);
          }
          else{
            Msg::Info("stiffnessCondensationPETScWithoutDirectConstraints is used for nonLinearMixedBC");
            stiffCon = new stiffnessCondensationPETScWithoutDirectConstraints(pAssembler,_pAl,sflag,tflag);
          }
          #else
          if (_pAl->getNumberDirectConstraints()>0){
            Msg::Info("stiffnessCondensationFullMatrixWithDirectConstraints is used for nonLinearMixedBC");
            stiffCon = new stiffnessCondensationFullMatrixWithDirectConstraints(pAssembler,_pAl,sflag,tflag);
          }
          else{
            Msg::Error("Petsc must be used");
          }
          #endif // HAVE_PETSC
        }
        else{
          Msg::Error("Micro BC type must be defined");
        }
    
      }
    
      //
      if (stiffCon == NULL){
        Msg::Warning("only homogenized stress is computed by surface integral");
        #if defined(HAVE_PETSC)
        Msg::Info("stiffnessCondensationPETSc is used");
        stiffCon = new stiffnessCondensationPETSc(pAssembler,_pAl,sflag,tflag);
        #else
        Msg::Error("Petsc must be used");
        #endif // HAVE_PETSC
      }
    
      stiffCon->setSplittedDofSystem(_pAl);
    
      if (tflag) {
        _pAl->getSplittedDof()->allocateSplittedMatrix();
        // preallocate
        for(std::vector<partDomain*>::iterator itdom=domainVector.begin(); itdom!=domainVector.end(); ++itdom){
          partDomain* dom = *itdom;
          SparsityDofsSplittedStiffness(*(dom->getFunctionSpace()), dom->element_begin(), dom->element_end(),*_pAl->getSplittedDof());
    			dgPartDomain* dgdom = dynamic_cast<dgPartDomain*>(dom);
    			if (dgdom!=NULL){
    				SparsityDofsSplittedStiffness(*(dgdom->getFunctionSpace()), dgdom->gi->begin(), dgdom->gi->end(),*_pAl->getSplittedDof());
    			}
        }
      };
    
      return stiffCon;
    };
    
    void nonLinearMechSolver::fillTangent(const fullMatrix<double>& L, homogenizedData* homoData){
    	// stress vector is arranged by this order
    	// S = [P Q FD fluxT1 mecaSrc1 -- fluxTn mecaSrcn fluxV1 --- fluxVm]
    	// K = [F G gradT1 T1 --- gradTn Tn gradV1 --- gradVm] see nonLinearMicroBC::getKinematicVector
    	// from dS/dK --> all tangents are explicitly given
    
      // mechanics part
      int atRow = 0;
    	if (_microBC->getTotalNumberOfMechanicalDofs() > 0){
    		for (int row = atRow; row<atRow+9; row++){
    			int i,j;
    			Tensor23::getIntsFromIndex(row-atRow,i,j);
    			// tangent P-F
    			int atCol = 0;
    
    			for (int col = atCol; col< atCol+9; col++){
    				int k,l;
    				Tensor23::getIntsFromIndex(col-atCol,k,l);
    				homoData->getHomogenizedTangentOperator_F_F()(i,j,k,l) = L(row,col);
    			}
    			atCol+= 9;
    
    			// tangent P-secondorder
    			if (_microBC->getOrder() == 2){
    				for (int col =atCol; col< atCol+27; col++){
    					int p,q,r;
    					Tensor33::getIntsFromIndex(col-atCol,p,q,r);
    					homoData->getHomogenizedTangentOperator_F_G()(i,j,p,q,r) = L(row,col);
    				};
    				atCol += 27;
    			}
    
    			if (_microBC->getTotalNumberOfConDofs()>0){
    				for (int index=0; index < _microBC->getTotalNumberOfConDofs(); index++){
    					// tangent P-GradextraDof
    					for (int col = atCol; col< atCol+3; col++){
    						int p;
    						Tensor13::getIntsFromIndex(col-atCol,p);
    						homoData->getHomogenizedTangentOperator_F_gradT(index)(i,j,p) = L(row,col);
    					}
    					atCol += 3;
    					// tangent P-ExtraDof
    					homoData->getHomogenizedTangentOperator_F_T(index)(i,j) =L(row,atCol);
    					atCol += 1;
    
    				}
    			};
    
    			if (_microBC->getTotalNumberOfNonConDofs() > 0){
    				for (int index=0; index < _microBC->getTotalNumberOfNonConDofs(); index++){
    					// tangent P-GradextraDof
    					for (int col = atCol; col< atCol+3; col++){
    						int p;
    						Tensor13::getIntsFromIndex(col-atCol,p);
    						homoData->getHomogenizedTangentOperator_F_gradV(index)(i,j,p) = L(row,col);
    					}
    					atCol += 3;
    				}
    			}
    		};
    		atRow += 9;
    
    		if (_microBC->getOrder() == 2){
    			// second order stress
    			for (int row = atRow; row<atRow+27; row++){
    				int i,j,k;
    				Tensor33::getIntsFromIndex(row-atRow,i,j,k);
    
    				// tangent Q-F
    				int atCol = 0;
    				for (int col = atCol; col<atCol+9; col++){
    					int p,q;
    					Tensor23::getIntsFromIndex(col-atCol,p,q);
    					homoData->getHomogenizedTangentOperator_G_F()(i,j,k,p,q) = L(row,col);
    					
    				}
    				atCol+= 9;
    
    				// tangent Q-Q;
    				for (int col=atCol; col < atCol+27; col++){
    					int p,q,r;
    					Tensor33::getIntsFromIndex(col-atCol,p,q,r);
    					homoData->getHomogenizedTangentOperator_G_G()(i,j,k,p,q,r) = L(row,col);
    					
    				}
    				atCol += 27;
    
    				if (_microBC->getTotalNumberOfConDofs()>0){
    					for (int index=0; index < _microBC->getTotalNumberOfConDofs(); index++){
    						// tangent P-GradextraDof
    						for (int col = atCol; col< atCol+3; col++){
    							int p;
    							Tensor13::getIntsFromIndex(col-atCol,p);
    							homoData->getHomogenizedTangentOperator_G_gradT(index)(i,j,k,p) = L(row,col);
    						}
    						atCol += 3;
    						// tangent P-ExtraDof
    						homoData->getHomogenizedTangentOperator_G_T(index)(i,j,k) =L(row,atCol);
    						atCol += 1;
    
    					}
    				};
    
    				if (_microBC->getTotalNumberOfNonConDofs() > 0){
    					for (int index=0; index < _microBC->getTotalNumberOfNonConDofs(); index++){
    						// tangent P-GradextraDof
    						for (int col = atCol; col< atCol+3; col++){
    							int p;
    							Tensor13::getIntsFromIndex(col-atCol,p);
    							homoData->getHomogenizedTangentOperator_G_gradV(index)(i,j,k,p) = L(row,col);
    						}
    						atCol += 3;
    					}
    				}
    
    			};
    			atRow += 27;
    		}
    
    		if (_extractIrreversibleEnergy and (_homogenizeTangentMethod == INSYSTEMCONDEN)){
    			for (int row = atRow; row<atRow+1; row++){
    				// tangent FD-F
    				int atCol = 0;
    				for (int col = atCol; col< atCol+9; col++){
    					int k,l;
    					Tensor23::getIntsFromIndex(col-atCol,k,l);
    					homoData->getHomogenizedTangentOperator_IrreversibleEnergy_F()(k,l) = L(row,col);
    				}
    				atCol+= 9;
    
    				// tangent FD-G
    				if (_microBC->getOrder() == 2){
    					for (int col = atCol; col< atCol+27; col++){
    						int p,q,r;
    						Tensor33::getIntsFromIndex(col-atCol,p,q,r);
    						homoData->getHomogenizedTangentOperator_IrreversibleEnergy_G()(p,q,r) = L(row,col);
    					}
    					atCol+= 27;
    				}
    
    				if (_microBC->getTotalNumberOfConDofs()>0){
    					for (int index=0; index < _microBC->getTotalNumberOfConDofs(); index++){
    						// tangent P-GradextraDof
    						for (int col = atCol; col< atCol+3; col++){
    							int p;
    							Tensor13::getIntsFromIndex(col-atCol,p);
    							homoData->getHomogenizedTangentOperator_IrreversibleEnergy_gradT(index)(p) = L(row,col);
    						}
    						atCol += 3;
    						// tangent P-ExtraDof
    						homoData->getHomogenizedTangentOperator_IrreversibleEnergy_T(index) =L(row,atCol);
    						atCol += 1;
    
    					}
    				};
    
    				if (_microBC->getTotalNumberOfNonConDofs() > 0){
    					for (int index=0; index < _microBC->getTotalNumberOfNonConDofs(); index++){
    						// tangent P-GradextraDof
    						for (int col = atCol; col< atCol+3; col++){
    							int p;
    							Tensor13::getIntsFromIndex(col-atCol,p);
    							homoData->getHomogenizedTangentOperator_IrreversibleEnergy_gradV(index)(p) = L(row,col);
    						}
    						atCol += 3;
    					}
    				}
    			}
    			atRow += 1;
    		}
    
    		// this tangent is only computed by using option INSYSTEMCONDEN
    		if (_damageToCohesiveJump and (_homogenizeTangentMethod == INSYSTEMCONDEN)){
    			// first order damage only
    			double Vd = homoData->getActiveDissipationVolume();
    			double vRve = this->getRVEVolume();
    			double fact = vRve/Vd;
    
    			for (int row = atRow; row<atRow+9; row++){
    				int i,j;
    				Tensor23::getIntsFromIndex(row-atRow,i,j);
    
    				// tangent FD-F
    				int atCol = 0;
    				for (int col = atCol; col< atCol+9; col++){
    					int k,l;
    					Tensor23::getIntsFromIndex(col-atCol,k,l);
    					homoData->getHomogenizedTangentOperator_ActiveDissipationDeformationGradient_F()(i,j,k,l) = fact*L(row,col);
    				}
    				atCol+= 9;
    
    				// tangent FD-G
    				if (_microBC->getOrder() == 2){
    					for (int col = atCol; col< atCol+27; col++){
    						int p,q,r;
    						Tensor33::getIntsFromIndex(col-atCol,p,q,r);
    						homoData->getHomogenizedTangentOperator_ActiveDissipationDeformationGradient_G()(i,j,p,q,r) = fact*L(row,col);
    					}
    					atCol+= 27;
    				}
    
    				if (_microBC->getTotalNumberOfConDofs()>0){
    					for (int index=0; index < _microBC->getTotalNumberOfConDofs(); index++){
    						// tangent P-GradextraDof
    						for (int col = atCol; col< atCol+3; col++){
    							int p;
    							Tensor13::getIntsFromIndex(col-atCol,p);
    							homoData->getHomogenizedTangentOperator_ActiveDissipationDeformationGradient_gradT(index)(i,j,p) = fact*L(row,col);
    						}
    						atCol += 3;
    						// tangent P-ExtraDof
    						homoData->getHomogenizedTangentOperator_ActiveDissipationDeformationGradient_T(index)(i,j) = fact*L(row,atCol);
    						atCol += 1;
    
    					}
    				};
    
    				if (_microBC->getTotalNumberOfNonConDofs() > 0){
    					for (int index=0; index < _microBC->getTotalNumberOfNonConDofs(); index++){
    						// tangent P-GradextraDof
    						for (int col = atCol; col< atCol+3; col++){
    							int p;
    							Tensor13::getIntsFromIndex(col-atCol,p);
    							homoData->getHomogenizedTangentOperator_ActiveDissipationDeformationGradient_gradV(index)(i,j,p) = fact*L(row,col);
    						}
    						atCol += 3;
    					}
    				}
    			}
    			atRow += 9;
    		}
    	}
    
      if (_microBC->getTotalNumberOfConDofs() >0){
    		for (int index=0; index < _microBC->getTotalNumberOfConDofs(); index++){
    			for (int row = atRow; row<atRow+3; row++){
    				int i;
    				Tensor13::getIntsFromIndex(row-atRow, i);
    				int atCol = 0;
    				if (_microBC->getTotalNumberOfMechanicalDofs() > 0){
    					// // gradExtraDof - P
    					for (int col = atCol; col<atCol+9; col++){
    						int p,q;
    						Tensor23::getIntsFromIndex(col-atCol,p,q);
    						homoData->getHomogenizedTangentOperator_gradT_F(index)(i,p,q) = L(row,col);
    					}
    					atCol += 9;
    					// grad ExtraDof- Q --> need to complete
    					if (_microBC->getOrder() == 2){
    						for (int col = atCol; col<atCol+27; col++){
    							int p,q,r;
    							Tensor33::getIntsFromIndex(col-atCol,p,q,r);
    							homoData->getHomogenizedTangentOperator_gradT_G(index)(i,p,q,r) = L(row,col);
    						}
    						atCol += 27;
    					}
    
    				}
    
    				for (int fieldInx = 0; fieldInx < _microBC->getTotalNumberOfConDofs(); fieldInx++){
    				// gradExtraDOf- grad ExtraDof
    					for (int col = atCol; col < atCol+3; col++){
    						int p;
    						Tensor13::getIntsFromIndex(col-atCol,p);
    						homoData->getHomogenizedTangentOperator_gradT_gradT(index,fieldInx)(i,p) = L(row,col);
    					}
    					atCol += 3;
    					// gradExtraDof- ExtraDof
    					homoData->getHomogenizedTangentOperator_gradT_T(index,fieldInx)(i) = L(row,atCol);
    					atCol += 1;
    
    				}
    				if (_microBC->getTotalNumberOfNonConDofs() > 0){
    					for (int fieldInx = 0; fieldInx < _microBC->getTotalNumberOfNonConDofs(); fieldInx++){
    						for (int col = atCol; col < atCol+3; col++){
    							int p;
    							Tensor13::getIntsFromIndex(col-atCol,p);
    							homoData->getHomogenizedTangentOperator_gradT_gradV(index,fieldInx)(i,p) = L(row,col);
    						}
    						atCol += 3;
    					}
    				}
    			};
    			atRow+=3;
    
    			if (_homogenizeTangentMethod == INSYSTEMCONDEN){
    				// for mechanical source
    				for (int row = atRow; row<atRow+1; row++){
    					// mecasource - P
    					int atCol = 0;
    					if (_microBC->getTotalNumberOfMechanicalDofs() > 0){
    						for (int col = atCol; col<atCol+9; col++){
    							int p,q;
    							Tensor23::getIntsFromIndex(col-atCol,p,q);
    							homoData->getHomogenizedTangentOperator_ConstitutiveExtraDofMechanicalSource_F(index)(p,q) = L(row,col);
    						}
    						atCol += 9;
    						// grad ExtraDof- Q --> need to complete
    						if (_microBC->getOrder() == 2){
    							for (int col = atCol; col<atCol+27; col++){
    								int p,q,r;
    								Tensor33::getIntsFromIndex(col-atCol,p,q,r);
    								homoData->getHomogenizedTangentOperator_ConstitutiveExtraDofMechanicalSource_G(index)(p,q,r) = L(row,col);
    							}
    							atCol += 9;
    						}
    
    					}
    					// mecasource- grad ExtraDof
    					for (int fieldInx = 0; fieldInx < _microBC->getTotalNumberOfConDofs(); fieldInx++){
    					// grad ExtraDof
    						for (int col = atCol; col < atCol+3; col++){
    							int p;
    							Tensor13::getIntsFromIndex(col-atCol,p);
    							homoData->getHomogenizedTangentOperator_ConstitutiveExtraDofMechanicalSource_gradT(index,fieldInx)(p) = L(row,col);
    						}
    						atCol += 3;
    
    						// - ExtraDof
    						homoData->getHomogenizedTangentOperator_ConstitutiveExtraDofMechanicalSource_T(index,fieldInx) = L(row,atCol);
    						atCol += 1;
    
    					}
    					if (_microBC->getTotalNumberOfNonConDofs() > 0){
    						for (int fieldInx = 0; fieldInx < _microBC->getTotalNumberOfNonConDofs(); fieldInx++){
    						//  grad nonConExtraDof
    							for (int col = atCol; col < atCol+3; col++){
    								int p;
    								Tensor13::getIntsFromIndex(col-atCol,p);
    								homoData->getHomogenizedTangentOperator_ConstitutiveExtraDofMechanicalSource_gradV(index,fieldInx)(p) = L(row,col);
    							}
    							atCol += 3;
    						}
    					}
    				};
    				atRow+=1;
    			};
    		}
      }
    
    	if (_microBC->getTotalNumberOfNonConDofs() > 0){
    		for (int index=0; index < _microBC->getTotalNumberOfNonConDofs(); index++){
    			for (int row = atRow; row<atRow+3; row++){
    				int i;
    				Tensor13::getIntsFromIndex(row-atRow, i);
    				int atCol = 0;
    				if (_microBC->getTotalNumberOfMechanicalDofs() > 0){
    					// // gradExtraDof - P
    					for (int col = atCol; col<atCol+9; col++){
    						int p,q;
    						Tensor23::getIntsFromIndex(col-atCol,p,q);
    						homoData->getHomogenizedTangentOperator_gradV_F(index)(i,p,q) = L(row,col);
    					}
    					atCol += 9;
    					// grad ExtraDof- Q --> need to complete
    					if (_microBC->getOrder() == 2){
    						for (int col = atCol; col<atCol+27; col++){
    							int p,q,r;
    							Tensor33::getIntsFromIndex(col-atCol,p,q,r);
    							homoData->getHomogenizedTangentOperator_gradV_G(index)(i,p,q,r) = L(row,col);
    						}
    						atCol += 27;
    					}
    
    				}
    				if (_microBC->getTotalNumberOfConDofs() > 0){
    					for (int fieldInx = 0; fieldInx < _microBC->getTotalNumberOfConDofs(); fieldInx++){
    					// gradExtraDOf- grad ExtraDof
    						for (int col = atCol; col < atCol+3; col++){
    							int p;
    							Tensor13::getIntsFromIndex(col-atCol,p);
    							homoData->getHomogenizedTangentOperator_gradV_gradT(index,fieldInx)(i,p) = L(row,col);
    						}
    						atCol += 3;
    
    						// gradExtraDof- ExtraDof
    						homoData->getHomogenizedTangentOperator_gradV_T(index,fieldInx)(i) = L(row,atCol);
    						atCol += 1;
    
    					}
    				}
    
    				for (int fieldInx = 0; fieldInx < _microBC->getTotalNumberOfNonConDofs(); fieldInx++){
    				// gradExtraDOf- grad ExtraDof
    					for (int col = atCol; col < atCol+3; col++){
    						int p;
    						Tensor13::getIntsFromIndex(col-atCol,p);
    						homoData->getHomogenizedTangentOperator_gradV_gradV(index,fieldInx)(i,p) = L(row,col);
    					}
    					atCol += 3;
    				}
    			};
    			atRow+=3;
    		}
    	}
    
    };
    
    
    void  nonLinearMechSolver::set2ndOrderHomogenizedTangentOperatorWithCorrection(){
       STensor63& dQdG = this->getHomogenizationState(IPStateBase::current)->getHomogenizedTangentOperator_G_G(); 
       STensor63& dQdG_P = this->getHomogenizationState(IPStateBase::previous)->getHomogenizedTangentOperator_G_G();
       STensor63& dQdG_I = this->getHomogenizationState(IPStateBase::initial)->getHomogenizedTangentOperator_G_G();  
       STensor63& dQdG_correction = this->getHomogenizationState(IPStateBase::current)->getHomogenizedCorrectionTerm_G_G();
       STensor63& dQdG_correction_P = this->getHomogenizationState(IPStateBase::previous)->getHomogenizedCorrectionTerm_G_G();
       STensor63& dQdG_correction_I = this->getHomogenizationState(IPStateBase::initial)->getHomogenizedCorrectionTerm_G_G();
       STensorOperation::zero(dQdG_correction);
       double delta = 1.0e-4;
        /* for(int i=0; i<3; i++)
             for(int j=0; j<3; j++) 
                for(int k=0; k<3; k++){ 
                   if(dQdG(i,j,k,i,j,k) < 0.0){
                      dQdG_correction(i,j,k,i,j,k) = -dQdG(i,j,k,i,j,k) + delta;
                      dQdG(i,j,k,i,j,k) += dQdG_correction(i,j,k,i,j,k);                  
                   }
                }*/
         dQdG_P = dQdG;        
         dQdG_I = dQdG;
         dQdG_correction_P = dQdG_correction;
         dQdG_correction_I = dQdG_correction; 
    }       
    
    
    void nonLinearMechSolver::HOStressTangent_modificationTerm(STensor53& dPdG, STensor53& dQdF, STensor63& dQdG){
      double volumeRVEInv = 1./this->getRVEVolume();
      STensorOperation::zero(dQdG);
      STensorOperation::zero(dQdF);
      STensorOperation::zero(dPdG);
      for (int i=0; i<domainVector.size(); i++){
          domainVector[i]->computeAverageHighOrderStressTangentModification(_ipf,this->getRVEVolume(),dPdG,dQdF, dQdG);
      };
        dPdG *= (volumeRVEInv);    
        dQdF *= (volumeRVEInv);
        dQdG *= (volumeRVEInv);
    }
    
    
    
    void nonLinearMechSolver::modifyHOStressTangent(fullMatrix<double>& L, const STensor53& dPdG, const STensor53& dQdF, const STensor63& dQdG){
    	// stress vector is arranged by this order
    	// S = [P Q FD fluxT1 mecaSrc1 -- fluxTn mecaSrcn fluxV1 --- fluxVm]
    	// K = [F G gradT1 T1 --- gradTn Tn gradV1 --- gradVm] see nonLinearMicroBC::getKinematicVector
    	// from dS/dK --> all tangents are explicitly given	
    	
      int atRow = 0;
      int atCol = 9;
      for (int row = atRow; row<atRow+9; row++){
        int i,j;
        Tensor23::getIntsFromIndex(row-atRow,i,j);
        // tangent P-G
        for (int col =atCol; col< atCol+27; col++){
    	int p,q,r;
    	Tensor33::getIntsFromIndex(col-atCol,p,q,r);
    	L(row,col) += dPdG(i,j,p,q,r);
        };
      }
      // tangent Q-F	
      atRow = 9;
      atCol = 0;
      for (int row = atRow; row<atRow+27; row++){
        int i,j,k;
        Tensor33::getIntsFromIndex(row-atRow,i,j,k);
        for (int col=atCol; col < atCol+9; col++){
          int p,q;
          Tensor23::getIntsFromIndex(col-atCol,p,q);
          L(row,col) += dQdF(i,j,k,p,q);
        }
      }  
      
      // tangent Q-G
      atCol = 9;	
      for (int row = atRow; row<atRow+27; row++){
        int i,j,k;
        Tensor33::getIntsFromIndex(row-atRow,i,j,k);
        for (int col=atCol; col < atCol+27; col++){
          int p,q,r;
          Tensor33::getIntsFromIndex(col-atCol,p,q,r);
          L(row,col) += dQdG(i,j,k,p,q,r);
        }
      }  
    };
    
    
    
    void nonLinearMechSolver::checkFailureOnset(){
      if (_checkFailureOnset){
        const homogenizedData* homoDataPrev = this->getHomogenizationState(IPStateBase::previous);
        homogenizedData* homoData = this->getHomogenizationState(IPStateBase::current);
    
        if (!_solverIsBroken){
          const double cr = homoData->getLostOfSolutionUniquenessCriterion();
          if (cr > 0.){
            _solverIsBroken = true;
            _FdamOnset = homoData->getHomogenizedActiveDissipationDeformationGradient();
            if (_checkWithNormal){
              printf("solver is broken %s at rank %d  real tolearance = %e, following prescribed normal direction [%e %e %e] \n",
                    getFileSavingPrefix().c_str(),Msg::GetCommRank(),cr,_lostSolutionUniquenssNormal[0],_lostSolutionUniquenssNormal[1],_lostSolutionUniquenssNormal[2]);
            }
            else{
              printf("solver is broken %s at rank %d  real tolearance = %e, with minimal normal direction [%e %e %e] \n",
                    getFileSavingPrefix().c_str(),Msg::GetCommRank(),cr, _lostSolutionUniquenssNormal[0],_lostSolutionUniquenssNormal[1],_lostSolutionUniquenssNormal[2]);
            }
          }
          else {
            _solverIsBroken = false;
          }
        }
    	}
    };
    
    void nonLinearMechSolver::computeHomogenizedCrackFace(){
    	if (_pAl == NULL){
    		Msg::Error("pbcAlgorithm object must be created");
    	}
    
      SVector3 Lx, Ly, Lz;
      _pAl->getPBCConstraintGroup()->getPeriodicity(Lx, Ly, Lz);
    
    	if (_lostSolutionUniquenssNormal.norm() < 1e-6){
    		// normally, _lostSolutionUniquenssNormal.norm() == 1
    		_homogenizedCrackSurface = std::max(Lx.norm(),Ly.norm()); // nothing to do
    		return;
    	}
    
    	SPoint3 v1 = _pAl->getPBCConstraintGroup()->getRootPoint();
    	Msg::Info("rootPoint %f %f %f ",v1[0],v1[1],v1[2]);
    
      SPoint3 damageVolumeCenter(0.,0.,0.);
      double damageVolume = 0.;
      for (int i=0; i<domainVector.size(); i++){
        damageVolume += domainVector[i]->computeVolumeActiveDissipationDomain(_ipf);
      }
      if (damageVolume > 0.){
        for (int i=0; i<domainVector.size(); i++){
          domainVector[i]->computeActiveDissipationCenter(_ipf,damageVolumeCenter);
        }
        damageVolumeCenter *= (1./damageVolume);
        Msg::Info("center of active damage zone : %f %f %f \n",damageVolumeCenter[0],damageVolumeCenter[1],damageVolumeCenter[2]);
      }
    
    
    	_homogenizedCrackSurface = 0.;
    
    	if (getDim() == 2){
    		SPoint3 v2(v1);
    		v2 += (Lx.point());
    		SPoint3 v4(v1);
    		v4 += (Ly.point());
    		SPoint3 v3(v1);
    		v3 += (Lx.point());
    		v3 += (Ly.point());
    
    		if (dot(_lostSolutionUniquenssNormal,Lx)/(Lx.norm()) < 1e-8){
    			_homogenizedCrackSurface = Lx.norm();
    			Msg::Info("_homogenizedCrackSurface following Lx = %f",_homogenizedCrackSurface);
    		}
    		else if (dot(_lostSolutionUniquenssNormal,Ly)/(Ly.norm()) < 1e-8){
    			_homogenizedCrackSurface = Ly.norm();
    			Msg::Info("_homogenizedCrackSurface following Ly = %f",_homogenizedCrackSurface);
    		}
    		else{
    			std::vector<SPoint3> twoPoint;
    			SPoint3 prCenterTo12 = planeDirProject(damageVolumeCenter,v1,v2,_lostSolutionUniquenssNormal);
    			if (inside(prCenterTo12,v1,v2)){
    				twoPoint.push_back(prCenterTo12);
    			}
    			SPoint3 prCenterTo41 = planeDirProject(damageVolumeCenter,v1,v4,_lostSolutionUniquenssNormal);
    			if (inside(prCenterTo41,v1,v4)){
    				twoPoint.push_back(prCenterTo41);
    			}
    			if (twoPoint.size() < 2){
    			SPoint3 prCenterTo23 = planeDirProject(damageVolumeCenter,v2,v3,_lostSolutionUniquenssNormal);
    				if (inside(prCenterTo23,v2,v3)){
    					twoPoint.push_back(prCenterTo23);
    				}
    			}
    			if (twoPoint.size() < 2){
    				SPoint3 prCenterTo34 = planeDirProject(damageVolumeCenter,v3,v4,_lostSolutionUniquenssNormal);
    				if (inside(prCenterTo34,v3,v4)){
    					twoPoint.push_back(prCenterTo34);
    				}
    			}
    
    			if (twoPoint.size() == 2){
    				_homogenizedCrackSurface = twoPoint[0].distance(twoPoint[1]);
    				Msg::Info("_homogenizedCrackSurface = %f ",_homogenizedCrackSurface);
    			}
    			else{
    				Msg::Error("computeHomogenizedCrackLength is wrong %s _lostSolutionUniquenssNormal = [%f %f %f]",getFileSavingPrefix().c_str(),
    								_lostSolutionUniquenssNormal[0],_lostSolutionUniquenssNormal[1],_lostSolutionUniquenssNormal[2]);
    			}
    
    		}
    		_homogenizedCrackSurface *= (Lz.norm()); // generally, _LzNorm = 1
    
    		#ifdef _DEBUG
    		printf("%s _homogenizedCrackSurface = %e \n",getFileSavingPrefix().c_str(),_homogenizedCrackSurface);
    		#endif //_DEBUG
    
    	}
    	else if (getDim() == 3){
    		Msg::Info("nonLinearMechSolver::computeHomogenizedCrackFace for 3D has not been implemented");
    	}
    	else{
    		Msg::Info("computeHomogenizedCrackLength has not been implemented for %d D problems");
    
    	}
    };
    
    
    void nonLinearMechSolver::setMessageView(const bool view){
      _messageView = view;
    }
    
    void nonLinearMechSolver::extractAverageProperties(bool stiff){
      homogenizedData* homoData = this->getHomogenizationState(IPStateBase::current);
      /* get homogenization value */
      if (stiff){
        this->extractAverageStressAndTangent(homoData);
      }
      else if (_stressflag){
        this->extractAverageStress(homoData);
      }
    
    };
    
    eigenSolver* nonLinearMechSolver::eigenSolve(const int numstep)
    {
      #if defined(HAVE_SLEPC)
      // get system
     // stiffness
      std::string strA = "A";
      linearSystem<double>* lsysA = pAssembler->getLinearSystem(strA);
      linearSystemPETSc<double>* lpetA = dynamic_cast<linearSystemPETSc<double>*>(lsysA);
    
      // mass
      linearSystemPETSc<double>* lpetB = NULL;
      if (_eigOpts.type == eigenSolverOptions::Dynamic)
      {
        std::string strB = "B";
        linearSystem<double>* lsysB = pAssembler->getLinearSystem(strB);
        lpetB = dynamic_cast<linearSystemPETSc<double>*>(lsysB);
      }
      if (lpetA==NULL)
      {
        Msg::Error("stiffness matrix is null");
        return NULL;
      }
      eigenSolver* eigsol = new eigenSolver(lpetA,lpetB, _eigOpts.hermitian);
      bool ok = eigsol->solve(_eigOpts.numeigenvalue,"smallestReal", _eigOpts.method,_eigOpts.convergenCriterion,_eigOpts.maxNumIteration);
      // save eigenvalue to file
      // first column is real part and second column is the imag part
      std::string eigValueFileName = getFileSavingPrefix()+"eigenValues_step"+int2str(numstep)+".csv";
      FILE* eigValueFile = fopen(eigValueFileName.c_str(),"w");
      for (int i=0; i<_eigOpts.numeigenvalue; i++)
      {
        std::complex<double> val = eigsol->getEigenValue(i);
        fprintf(eigValueFile,"%d\t%.16g  \t %.16g \n",i,val.real(), val.imag());
      }
      fclose(eigValueFile);
    
      // normalized mode with infinite norm
      std::vector<int> modeView(_eigview.size());
      for (int i=0; i< _eigview.size(); i++)
      {
        modeView[i] = _eigview[i].comp;
      }
      eigsol->normalize_mode(modeView,1.);
      return eigsol;
      #else
      Msg::Error("SLEPs must be used");
      return NULL;
      #endif //HAVE_SLEPC
    };
    
    void nonLinearMechSolver::nextStep(const double curtime, const int step)
    {
      /* field next step */
      if (_selectiveObject != NULL){
        _selectiveObject->nextStep();
      }
      if (_endSchemeMonitoringObject!=NULL)
      {
        _endSchemeMonitoringObject->nextStep();
      }
      _ipf->nextStep(curtime); //ipvariable
      pAssembler->nextStep();
      // update for bodyforce 
      if(computedUdF()){
         this->extractdUdFByInSystemCondensation(this->getHomogenizationState(IPStateBase::current));
      }
      
      // contact
      for(contactContainer::iterator it = _allContact.begin(); it!=_allContact.end(); ++it){
        contactDomain *cdom = *it;
        cdom->nextStep();
      }
      for(defoDefoContactContainer::iterator it = _allDefoDefoContact.begin(); it!=_allDefoDefoContact.end(); ++it){
        defoDefoContactDomain *cdom = *it;
        cdom->nextStep();
      }
    };
    
    void nonLinearMechSolver::computeSplittedStiffnessMatrix(bool accountBodyForceForHO){
        _pAl->getSplitStiffness()->zeroSubMatrix();
        for(std::vector<partDomain*>::iterator itdom=domainVector.begin(); itdom!=domainVector.end(); ++itdom){
          partDomain* dom = *itdom;
          BiNonLinearTermBase *term = static_cast<BiNonLinearTermBase *> (dom->getBilinearBulkTerm());
          bool oldFlag=term->getAccountBodyForceForHO();
          term->setAccountBodyForceForHO(accountBodyForceForHO);
          AssembleSplittedStiffness(*term,*(dom->getFunctionSpace()),dom->element_begin(),dom->element_end(),
                 *(dom->getBulkGaussIntegrationRule()),*_pAl->getSplittedDof());
          term->setAccountBodyForceForHO(oldFlag);
    
          if(dom->IsInterfaceTerms()){
            dgPartDomain *dgdom = static_cast<dgPartDomain*>(dom);
            // Assembling loop on elementary interface terms
            BiNonLinearTermBase *dgterm = static_cast<BiNonLinearTermBase *> (dgdom->getBilinearInterfaceTerm());
            bool oldFlag=dgterm->getAccountBodyForceForHO();
            dgterm->setAccountBodyForceForHO(accountBodyForceForHO);
            AssembleSplittedStiffness(*(dgterm),*(dom->getFunctionSpace()),dgdom->gi->begin(),dgdom->gi->end(),
                     *(dgdom->getInterfaceGaussIntegrationRule()),*_pAl->getSplittedDof());
            dgterm->setAccountBodyForceForHO(oldFlag);
    
            // Assembling loop on elementary boundary interface terms        
            BiNonLinearTermBase *virtualdgterm = static_cast<BiNonLinearTermBase *> (dgdom->getBilinearVirtualInterfaceTerm());
            oldFlag=virtualdgterm->getAccountBodyForceForHO();
            virtualdgterm->setAccountBodyForceForHO(accountBodyForceForHO);
            AssembleSplittedStiffness(*(virtualdgterm),*(dom->getFunctionSpace()),dgdom->gib->begin(),dgdom->gib->end(),
                   *(dgdom->getInterfaceGaussIntegrationRule()),*_pAl->getSplittedDof());
            virtualdgterm->setAccountBodyForceForHO(oldFlag);
    
          }
      }
    
      for(contactContainer::iterator it = _allContact.begin(); it!=_allContact.end(); ++it){
        contactDomain *cdom = *it;
        contactBilinearTermBase<double> *sterm = static_cast<contactBilinearTermBase<double>*>(cdom->getStiffnessTerm());
        AssembleSplittedStiffness(*sterm,*(cdom->getSpace()),sterm->getContactNodes()->elemBegin(),
                        sterm->getContactNodes()->elemEnd(),*(cdom->getGaussIntegration()),*_pAl->getSplittedDof());
      }
      for(defoDefoContactContainer::iterator it = _allDefoDefoContact.begin(); it!=_allDefoDefoContact.end(); ++it){
          defoDefoContactDomain *cdom = *it;
          const std::vector<BilinearTermBase*> &sterm = static_cast< const std::vector<BilinearTermBase* > >(cdom->getConstRefToStiffnessTerm()); //we might need to change the class
          for(int i=0; i<sterm.size(); i++)
          {
            AssembleSplittedStiffnessDefoDefoContact(*(sterm[i]),*(cdom->getRefToVSpace()[i]),((cdom->getRefToGroup())[i]).begin(),
                ((cdom->getRefToGroup())[i]).end(),
                *(cdom->getRefToVQuadratureMaster()[i]),*_pAl->getSplittedDof());
          }
      }
    };
    
    homogenizedData* nonLinearMechSolver::getHomogenizationState(const IPStateBase::whichState state){
      if (state == IPStateBase::initial or state == IPStateBase::previous){
        return _initialState;
      }
      else if (state == IPStateBase::current){
        return _currentState;
      }
      else{
        Msg::Error("This state does not exist");
        return NULL;
      }
    }
    
    const homogenizedData* nonLinearMechSolver::getHomogenizationState(const IPStateBase::whichState state) const{
      if (state == IPStateBase::initial or state == IPStateBase::previous){
        return _initialState;
      }
      else if (state == IPStateBase::current){
        return _currentState;
      }
      else{
        Msg::Error("This state does not exist");
        return NULL;
      }
    }
    
    
    void nonLinearMechSolver::resetSolverToInitialStep(){
      // system
      std::string name = "A";
      linearSystem<double>* lsys = pAssembler->getLinearSystem(name);
      nonLinearSystem<double>* nonlsys = dynamic_cast<nonLinearSystem<double>*>(lsys);
      if (nonlsys){
        nonlsys->copy(IPStateBase::initial,IPStateBase::current);
        nonlsys->copy(IPStateBase::initial,IPStateBase::previous);
      }
    
    
      pathFollowingSystem<double>* pathSys = dynamic_cast<pathFollowingSystem<double>*>(nonlsys);
      if (pathSys){
        pathSys->resetControlParameter();
      }
    
      // ipf field
    
      _ipf->copy(IPStateBase::initial,IPStateBase::current);
      _ipf->copy(IPStateBase::initial,IPStateBase::previous);
    
      // homogenized data
      (*_currentState) = (*_initialState);
    
      if (_damageIsBlocked){
        _ipf->blockDissipation(IPStateBase::current,true);
      }
      else{
        _ipf->blockDissipation(IPStateBase::current,false);
      }
    
    };
    
    void nonLinearMechSolver::prepareForNextMicroSolve(){
      // in microBC
    	bool BCIsChanged = false;
    	if (_failureBCIsSwitched){
    		if (_microFailureBC == NULL) {
    			Msg::Error("_microFailureBC is null in nonLinearMechSolver::nextStep");
    		}
    		else{
    			const homogenizedData* homodataPrev = this->getHomogenizationState(IPStateBase::initial);
    			const homogenizedData* homodata = this->getHomogenizationState(IPStateBase::current);
    			if (_solverIsBroken){
    				this->switchMicroBC(_microFailureBC);
    				printf("ranks %d microBC has just been changed \n",Msg::GetCommRank());
    				BCIsChanged = true;
            _failureBCIsSwitched = false; // pass one time
    			}
    		}
    	}
    
    	if (!BCIsChanged){
    		_microBC->nextStep();
    	}
    
      // next step for solver
      std::string name = "A";
      linearSystem<double>* lsys = pAssembler->getLinearSystem(name);
      nonLinearSystem<double>* nonlsys = dynamic_cast<nonLinearSystem<double>*>(lsys);
      pathFollowingSystem<double>* pathSys = dynamic_cast<pathFollowingSystem<double>*>(nonlsys);
      if (nonlsys){
        nonlsys->copy(IPStateBase::current,IPStateBase::initial);
      }
    
      if (pathSys){
        pathSys->resetControlParameter();
      }
    
      if (_damageIsBlocked){
        _ipf->blockDissipation(IPStateBase::current,true);
      }
      else{
        _ipf->blockDissipation(IPStateBase::current,false);
      }
    
    
    	if (withEnergyDissipation()){
        _ipf->checkActiveDissipation();
    		if (_ipf->getNumOfActiveDissipationIPsCurrent() > 0){
    			// store historical value
    			_homogenizedStressLastActiveDissipation = this->getHomogenizationState(IPStateBase::current)->getHomogenizedStress();
    			_homogenizedStrainLastActiveDissipation = this->getMicroBC()->getFirstOrderKinematicalVariable();
          _homogenizedDissipationStrainLastActiveDissipation = this->getHomogenizationState(IPStateBase::current)->getHomogenizedActiveDissipationDeformationGradient();
    			_homogenizedCohesiveJumpLastActiveDissipation = this->getHomogenizationState(IPStateBase::current)->getHomogenizedCohesiveJump();
    		}
    	}
    
    	 // save data for nex macroscopic step
      _ipf->copy(IPStateBase::current,IPStateBase::initial);
    
      // for homogenized data
      (*_initialState)=(*_currentState);
    };
    
    
    linearSystem<double>* nonLinearMechSolver::createMicroSNLSystem(){
      linearSystem<double>* lsys = NULL;
      if (_pathFollowing){
        _controlType = nonLinearMechSolver::LOAD_CONTROL; // this means compute until time = 1.
        _systemType == nonLinearMechSolver::MULT_ELIM; // always multiplier elimination
        #if defined(HAVE_PETSC)
        Msg::Info("Path following is used");
        lsys = new pbcPathFollowingSystemPETSC<double>(PETSC_COMM_SELF);
    
    		// set path following options
    		// paramater remains default if it is negative
    		pathFollowingSystemBase* pfsysBase = dynamic_cast<pathFollowingSystemBase*>(lsys);
    		if (_pfManager._controlTypePathFollowing >=0){
    			pfsysBase->setControlType(_pfManager._controlTypePathFollowing);
    		}
    		if (_pfManager._correctionMethodPathFollowing>=0){
    			pfsysBase->setCorrectionMethod(_pfManager._correctionMethodPathFollowing);
    		}
    		if (_pfManager._tranversalCriterionPathFollowing>=0){
    			pfsysBase->setTranversalCriterion(_pfManager._tranversalCriterionPathFollowing);
    		}
    		if (_pfManager._solverTypePathFollowing >=0){
    			pfsysBase->setSolverType(_pfManager._solverTypePathFollowing,_pfManager._pathFollowingEqRatio);
    		}
    
        #else
        Msg::Error("This option used with petsc only");
        #endif
      }
      else{
        if (_controlType == nonLinearMechSolver::LOAD_CONTROL){
          if (_systemType == nonLinearMechSolver::MULT_ELIM){
            if(nonLinearMechSolver::whatSolver == Petsc){
              #if defined(HAVE_PETSC)
              lsys = new pbcNonLinearSystemPETSc<double>(PETSC_COMM_SELF);
              if(_solver_options.size())
                lsys->setParameter(std::string("petsc_solver_options"),_solver_options);
              if (_outputFile)
                fprintf(_outputFile,"PETSc is chosen to solve \n");
              #endif
            }
            else{
              Msg::Error("Petsc is required");
            }
          }
          else if (_systemType == nonLinearMechSolver::DISP_ELIM_UNIFIED){
            if(nonLinearMechSolver::whatSolver == Petsc){
              #if defined(HAVE_PETSC)
              lsys = new pbcConstraintEliminationNonLinearSystemPETSc<double>(PETSC_COMM_SELF);
              if(_solver_options.size())
                lsys->setParameter(std::string("petsc_solver_options"),_solver_options);
              if (_outputFile)
                fprintf(_outputFile,"PETSc is chosen to solve \n");
              #endif
            }
            else{
              Msg::Error("Petsc is required");
            }
    
          }
          else{
            if(nonLinearMechSolver::whatSolver == Petsc){
              #if defined(HAVE_PETSC)
              lsys = new nonLinearSystemPETSc<double>(PETSC_COMM_SELF,_lineSearch);
              if(_solver_options.size())
                lsys->setParameter(std::string("petsc_solver_options"),_solver_options);
              if (_outputFile)
                fprintf(_outputFile,"PETSc is chosen to solve \n");
              #else
              lsys = new nonLinearSystemGmm<double>(_lineSearch);
              dynamic_cast<nonLinearSystemGmm<double>*>(lsys)->setNoisy(2);
              if (_outputFile)
                fprintf(_outputFile,"PETSc is not installed\n Gmm is chosen to solve \n");
              #endif
            }
            else
              lsys= nonLinearMechSolver::createSNLSystem();
          }
    
        }
        else if (_controlType == nonLinearMechSolver::ARC_CONTROL_EULER){
          if (nonLinearMechSolver::whatSolver == Petsc){
            #if defined(HAVE_PETSC)
            lsys = new forwardEulerLinearSystemPETSc<double>();
            if(_solver_options.size())
              lsys->setParameter(std::string("petsc_solver_options"),_solver_options);
            if (_outputFile)
              fprintf(_outputFile,"PETSc is chosen to solve \n");
            #else
            Msg::Error("Petsc is not available");
            #endif
          }
          else{
            Msg::Error("This is not implemented without PETSc");
          };
        };
      }
    
      if (_outputFile)
        fflush(_outputFile);
    
      return lsys;
    };
    
    void nonLinearMechSolver::createMicroSystem(){
      
      if (_previousInit and _resetRestart)
      {
        // collect if previous dof exists
        if (_collection==NULL)
        {
          _collection = new DofCollection();
        }
        _collection->collect(pAssembler);
        // clear data
        delete pAssembler;
        pAssembler = NULL;
      }
      
    	linearSystem<double>* lsys = createMicroSNLSystem();
    	// create a new one
      pAssembler = new staticDofManager(lsys,whatScheme,false,false);
    };
    
    void nonLinearMechSolver::init2Micro(){
    	// get quantities for micro BC
    	if (_microBC == NULL){
    		Msg::Error("Microscopic BC must be specified nonLinearMechSolver::init2Micro");
    	}
    
      // create system, dofManager
      this->createMicroSystem();
    
    	// init all other BC
      this->initAllBCsOnDofs();
    
      // create periodic algorithm
      if (_pAl != NULL){
    		delete _pAl; Msg::Info("pbcAlgorithm is deleted");
    	}
    	_pAl = new pbcAlgorithm(this); // create all constraints, etc
    
      // number Dof and allocate the system
      this->microNumberDof();
      
      // make data as previous  if exists
      this->updateDataFromPreviousSolver();
    
      /* initialization of Dof value */
      this->setInitialCondition();
    
    	if (!_archive){
    		unknownView.clear();
    		ipView.clear();
        ipViewInterface.clear();
    		energyView.clear();
    	}
    
    	if (_ufield) delete _ufield;
    	_ufield = new unknownField(this,3,anoded,unknownView); // 3 components by nodes User choice ??
    
    	if (!_previousInit){
    		if (_ipf) delete _ipf;
    		_ipf = new IPField(this,vaip,ipView,ipViewInterface); // Field for GaussPoint
    
    		_ipf->compute1state(IPStateBase::current,true);
                    _ipf->copy(IPStateBase::current,IPStateBase::previous);
    		_ipf->copy(IPStateBase::current,IPStateBase::initial);
    		
        if (initbrokeninter.size() > 0 || initbrokeninterInDomains.size() > 0)
        {
          _ipf->initialBroken(pModel, initbrokeninter);
          _ipf->initialBrokenDomain(pModel,initbrokeninterInDomains);
          _ipf->compute1state(IPStateBase::current,true);
          _ipf->copy(IPStateBase::current,IPStateBase::previous);
          _ipf->copy(IPStateBase::current,IPStateBase::initial);
        }
        
    	}
    	else{
        _ipf->copy(IPStateBase::current,IPStateBase::initial);
        _ipf->copy(IPStateBase::current,IPStateBase::previous);
    	}
    
    	if (_energField) delete _energField;
    	_energyComputation = 0;
    	_fractureEnergyComputation = 0;
    	_energField = new energeticField(this,energyView,_energyComputation,_fractureEnergyComputation,getScheme());
    
      // init terms
      this->initTerms();
    
      if (_tangentflag and _homogenizeTangentMethod == INSYSTEMCONDEN){
        this->computeStiffMatrix();
      }
    
      // rve_volume from periodicity
      if (!_previousInit){
         if (_rveVolume <=0.){
           _rveVolume = _pAl->getRVEVolume();
           this->setRVEGeometricalInertia(_pAl->getRVEGeometricalInertia());
           this->setRVEGeometry(_pAl->getRVEGeometry());
         }
         Msg::Info("RVE volume used = %e",_rveVolume);
         // compute density
         this->computeDensity();
         if (_isHommProSaveToFile){
           std::string denfile = getFileSavingPrefix()+"density.csv";
           FILE* fileden = fopen(denfile.c_str(),"w");
           fprintf(fileden,"%e",this->_rho);
           fclose(fileden);
         }
         _initialState = new homogenizedData(this,_damageToCohesiveJump,_extractIrreversibleEnergy);
         _currentState = new homogenizedData(this,_damageToCohesiveJump,_extractIrreversibleEnergy);
    
         if (_extractElasticTangentOperator){
             _elasticState = new homogenizedData(this,_damageToCohesiveJump,_extractIrreversibleEnergy);
             std::string elasticFileName = getFileSavingPrefix()+"elasticTangent.csv";
             _elasticDPDFTangentFile = Tensor43::createFile(elasticFileName);
         }
    
         /** initial tangent**/
    
         this->extractAverageProperties(_tangentflag);
         // body force is applied, initial tangent need to be recomputed with correct dUdF
         if(_homogenizeTangentMethod != PERTURB and computedUdF()){
           _ipf->setdFmdFM(this->getHomogenizationState(IPStateBase::current)->getdUnknowndF(), IPStateBase::current);
           _ipf->setValuesForBodyForce(IPStateBase::current);
           _ipf->copy(IPStateBase::current,IPStateBase::previous);
           _ipf->copy(IPStateBase::current,IPStateBase::initial);
           this->extractAverageProperties(_tangentflag);              
         }     
        //
           this->IPVolumeIntegralArchiving(0.,0,true);
           this->IPDataOnPhysicalArchiving(0.,0,true);
           this->averageClusterArchiving(0.,0,true);
    
    	(*_initialState) = (*_currentState);
    
    	if (_multiscaleFlag == false){
    			if (_microBC->getTotalNumberOfMechanicalDofs() > 0) {
            if (_stressflag)
            {
              _currentState->getHomogenizedStress().print("stress");
            }
    	if (_tangentflag){
    	     _currentState->getHomogenizedTangentOperator_F_F().print("tangent");
    	}
    	if (_microBC->getOrder() == 2 && _stressflag)
    	     _currentState->getHomogenizedSecondOrderStress().print("ho stress");
    	}
    
    	for (int index=0; index< _microBC->getTotalNumberOfConDofs(); index++){
    	    _currentState->getHomogenizedConstitutiveExtraDofFlux(index).print("ConExtraDof flux");
    	    printf("_currentState constitutive extra dof %d internal energy: %f \n", index, _currentState->getHomogenizedConstitutiveExtraDofInternalEnergy(index));
    	}
    
    	for (int index=0; index< _microBC->getTotalNumberOfNonConDofs(); index++){
    		_currentState->getHomogenizedNonConstitutiveExtraDofFlux(index).print("nonConExtraDof flux");
    	}
    	}
    
    	_homogenizedFiles = new homogenizedDataFiles(this);
    	bool saveCohesiveLawFile = (_isHommProSaveToFile and _damageToCohesiveJump);
    	_homogenizedFiles->openFiles(this,_isHommStrainSaveToFile,_isHommProSaveToFile,saveCohesiveLawFile);
    	this->homogenizedDataToFile(0.);
         }
         else{
    		// extract with new BC
    	    this->extractAverageProperties(_tangentflag);
              // initial data
               this->IPVolumeIntegralArchiving(0.,0,true);
               this->IPDataOnPhysicalArchiving(0.,0,true);
               this->averageClusterArchiving(0.,0,true);
        
    	   if (_multiscaleFlag == false){
    	     if (_microBC->getTotalNumberOfMechanicalDofs() > 0) {
                 if (_stressflag)
                    _currentState->getHomogenizedStress().print("stress");
    	     if (_tangentflag){
    		_currentState->getHomogenizedTangentOperator_F_F().print("tangent");
    	     }
    	     if (_microBC->getOrder() == 2 && _stressflag)
    	       _currentState->getHomogenizedSecondOrderStress().print("ho stress");
    	     }
                for (int index=0; index< _microBC->getTotalNumberOfConDofs(); index++){
    	      _currentState->getHomogenizedConstitutiveExtraDofFlux(index).print("ConExtraDof flux");
                   printf("_currentState constitutive extra dof %d internal energy: %f \n", index, _currentState->getHomogenizedConstitutiveExtraDofInternalEnergy(index));
    	    }
                for (int index=0; index< _microBC->getTotalNumberOfNonConDofs(); index++){
    	      _currentState->getHomogenizedNonConstitutiveExtraDofFlux(index).print("nonConExtraDof flux");
    	    }
    	  }
    	    bool saveCohesiveLawFile = (_isHommProSaveToFile and _damageToCohesiveJump);
    	    _homogenizedFiles->openFiles(this,_isHommStrainSaveToFile,_isHommProSaveToFile,saveCohesiveLawFile);
    	    this->homogenizedDataToFile(0.);
         }
    
         _previousInit = true;
    
    };
      
           
           
    void nonLinearMechSolver::initMicroSolver(){
      if (_messageView){
        std::string filename = getFileSavingPrefix()+"out.txt";
    		if (_outputFile != NULL) fclose(_outputFile);
        _outputFile = fopen(filename.c_str(),"w");
    
        fprintf(_outputFile,"Initializing on processor %d of %d \nMicro-problem: %s \nMicro SNL Data : nstep =%d  endtime = %f\n",
            Msg::GetCommRank(),Msg::GetCommSize(),getFileSavingPrefix().c_str(),
            _timeManager->getNumSteps(),_timeManager->getEndTime());
      }
      // read mesh file
      this->init();
    	this->init2Micro();
    
    	if (_outputFile) {
    		fprintf(_outputFile,"---------End of initialization-------- \n");
    		fflush(_outputFile);
    	}
    };
    
    bool nonLinearMechSolver::microSolve(bool forced){
      double time = 0;
      this->resetSolverToInitialStep();
      if (_microBC->notSameState(_sameStateCriterion,_absTol) or forced)
      {
        _sucessMicroSolve = false;
        if (_outputFile)
          fprintf(_outputFile,"Solving in procs %d \n",Msg::GetCommRank());
        // solve system
        if (whatScheme ==StaticLinear){
          time = solveMicroSolverStaticLinear();
        }
        else if (whatScheme == StaticNonLinear){
          time = solveMicroSolverSNL();
        }
        else
          Msg::Error("This scheme is not implemented");
    
        if (_sucessMicroSolve)
        {
          this->extractAverageProperties(_tangentflag);
        }
    
        if (_outputFile)
          fflush(_outputFile);
      }
      else
      {
        _sucessMicroSolve = true;
      }
      return _sucessMicroSolve;
    };
    
    void nonLinearMechSolver::OneStep(const int numstep){
      int niteNR = 0;
    
      if (_pathFollowing){  
        niteNR = pathFollowingPerturbation();
      }
      else{
        if (_systemType == MULT_ELIM)
          niteNR = microNewtonRaphson(numstep);
        else {
          if (_systemType == DISP_ELIM)
            _pAl->applyPBCByConstraintElimination();
           else if (_systemType == DISP_ELIM_UNIFIED){
             _pAl->updateSystemUnknown();
           }
          niteNR = NewtonRaphson(numstep);
        }
        if(niteNR == _timeManager->getMaxNbIterations()) // time step reduction
        {
          Msg::Error("convergent fails in tangent perturbation %s",getFileSavingPrefix().c_str());
        }
      }
    
    };
    
    double nonLinearMechSolver::solveMicroSolverSNL(){
      std::string name = "A";
      linearSystem<double>* lsys =pAssembler->getLinearSystem(name);
      nonLinearSystem<double>* nonsys = dynamic_cast<nonLinearSystem<double>*>(lsys);
      pathFollowingSystem<double>* pathSys = dynamic_cast<pathFollowingSystem<double>*>(lsys);
      //
      if (_multiscaleFlag)
      {
        _timeManager->setEndTime(1.);
      }
      _timeManager->initializeTimeSteppingPlan();
      //
      if (_pathFollowing){
        _pAl->computeLoadVector();
      }
      
      // if restart
      if(restartManager::available() and !_resetRestart)
      {
        if (getNumRanks() > 1)
          Msg::Barrier(); // To wait rank0 before end
        restartManager::openRestartFile("nonLinearMechSolver");
        restartManager::restart(this);
        // add restart other vars here if necessary
        //
        //
        restartManager::closeRestartFile();
    
        restartManager::InitFromRestartFile();
        if (getNumRanks() > 1)
          Msg::Barrier(); // To wait rank0 before end
        struct stat st;
        if(stat("beforeRestart",&st) == 0)
        {
          std::ostringstream oss;
          oss << _timeManager->getLastIterationIndex();
          std::string cpDir="mv beforeRestart restart"+oss.str();
          int oks = system(cpDir.c_str());
        }
        #if defined(HAVE_MPI)
        if(Msg::GetCommSize()>1)
        { //we need to reinitializae after the restart of the nonLinearSolver
          Msg::Info("MPI restart initialization begins");
          pAssembler->systemMPIComm(); // To initiate the MPI Communication
          Msg::Info("MPI restart initialization OK");
        }
        #endif // HAVE_MPI
      }
      _resetRestart=false;
      
      //
      _sucessMicroSolve = false;
      bool willFinish = false;
      
      while(!_timeManager->reachEndTime())
      {
        // read-only values to make sure all this paramete cannot be change during step
        const double endtime = _timeManager->getEndTime();
        const double dt = _timeManager->getTimeStep();
        const double curtime = dt +_timeManager->getLastTime();
        const int ii = _timeManager->getLastIterationIndex();
        const double tol  = 1e-6;
        double tsystresol = Cpu();     
        if (_outputFile)
          fprintf(_outputFile,"MICRO t= %e on %e \n",curtime,endtime);
        
        if (!_multiscaleFlag)
          Msg::Info("MICRO t= %e on %e, step =%e",curtime,endtime,dt);
    
        pAssembler->setTimeStep(dt);
        _ipf->setTime(curtime,dt,ii+1);
        if(computedUdF()){
          _ipf->setdFmdFM(this->getHomogenizationState(IPStateBase::current)->getdUnknowndF(), IPStateBase::current);
    
        }
        
        // Solve one step by NR scheme
        int niteNR = 0;
        if (_pathFollowing)
        {
          pathSys->setPathFollowingIncrement(dt);
          _pAl->updateConstraint(_ipf);
          
          if(computedUdF() and useWhichModuliForBF() != partDomain::Present){
             this->computeBodyForceVector();
          }
    
          niteNR = microNewtonRaphsonPathFollowing(ii+1);
    
            
          if (niteNR < _timeManager->getMaxNbIterations())
          {
            double control = pathSys->getControlParameter();
            if (_outputFile)
              fprintf(_outputFile,"control variable %e \n",control);
            if (control>1. and fabs(control-1) >tol)
            {
              if (_outputFile)
                fprintf(_outputFile,"Load correction step\n");
              willFinish = true;
    
              nonsys->resetUnknownsToPreviousTimeStep();
              _ipf->copy(IPStateBase::previous,IPStateBase::current);
    
              // switch control to obtain the control parameter equal to 1
              pathSys->setControlType(pathFollowingSystemBase::LOAD_CONTROL);
              _timeManager->setTimeStep(1.-pathSys->getControlParameter());
              continue;
            }
            else if (fabs(control-1) <=tol) 
            {
              willFinish = true;
            }
            else if (control<0)
            {
              Msg::Error("path following is not converge %s !",getFileSavingPrefix().c_str());
              _sucessMicroSolve = false;
              break;
            }
            
          }
        }
        else
        {
          this->oneStepPreSolve(curtime,dt,ii+1);
          _pAl->updateConstraint(_ipf);
          if (_systemType == MULT_ELIM)
            niteNR = microNewtonRaphson(ii+1);
          else {
            if (_systemType == DISP_ELIM){
              _pAl->applyPBCByConstraintElimination();
            }
            else if (_systemType == DISP_ELIM_UNIFIED){
              _pAl->updateSystemUnknown();
            }
            niteNR = NewtonRaphson(ii+1);
          }
        }
    
        if(niteNR >= _timeManager->getMaxNbIterations()) // time step reduction
        {
          _sucessMicroSolve = false;
          // reduce time step for next solve
          _timeManager->reduceTimeStep(); // solver fails counter in _timeManager is increased by 1
           // copy
          nonsys->resetUnknownsToPreviousTimeStep();
          _ipf->copy(IPStateBase::previous,IPStateBase::current);
    
    
          this->getHomogenizationState(IPStateBase::current)->operator=(*this->getHomogenizationState(IPStateBase::previous));
           
          //
          if(_timeManager->getNbFails() > _timeManager->getMaxNbFails()) // end of simulation
          {
            Msg::Error("Simulation end due to convergence problem  %s!!!", getFileSavingPrefix().c_str());
            if (_outputFile)
              fprintf(_outputFile,"Simulation end due to convergence problem %s !!!\n", getFileSavingPrefix().c_str());
           break;
          }
          else
          {
            if (_outputFile)
              fprintf(_outputFile,"Convergence of Newton-Raphson failed %s --> reduced time step\n",
                        getFileSavingPrefix().c_str());
            printf("Convergence of Newton-Raphson failed %s --> reduced time step\n",
                        getFileSavingPrefix().c_str());
          }
        }
        else
        {
          _sucessMicroSolve = true;
          
          _timeManager->saveTimeHistory();
          
          //
          if (!_multiscaleFlag)
          {
            // if not multiscale analysis,
            // check saving data all step
    	if (_outputFile)
    	   fflush(_outputFile);
            //
            this->oneStepPostSolve(curtime,ii+1);
          }
          else
          {
            // get next step without saving
            this->nextStep(curtime,ii+1);
          }
          if (_pathFollowing)
          {
            if (fabs(endtime-curtime) < tol*endtime and (pathSys->getControlParameter()<1.))
            {
              // extend endtime of the control parameter not yet reach 1
              _timeManager->setEndTime(2.*endtime);
            }
          }
          // move to next step in time manager
          _timeManager->computeTimeStepForNextSolving(niteNR);
          
          tsystresol = Cpu() - tsystresol;
          if (!_multiscaleFlag)
            Msg::Info("Time resolution for step %d  equal to %f",ii+1,tsystresol);
          
        }
        if(_pathFollowing)
        {
          if (willFinish)
          {
            // last solve
            pathSys->setControlType(_pfManager._controlTypePathFollowing);
            _sucessMicroSolve = true;
            break;
          }
        }
      }
      return _timeManager->getLastTime();
    };
    
    void nonLinearMechSolver::archiveData(const double curtime, const int numstep, const bool forceView){
      // Archiving
      if (_archive)
      {
        //printf("arching solver ele %d gpt %d\n",getElementSolverNumber(),getGaussPointSolverNumber());
        /*writing deformed mesh */
        if (_isWriteDeformedMeshToFile){
          this->writeDeformedMesh(numstep);
        }
        /* Archiving */
        _ufield->archive(curtime,numstep,forceView);
        _ipf->archive(curtime,numstep,forceView);
        _energField->archive(curtime,numstep,forceView);
    
        this->crackTracking(curtime);
        this->forceArchiving(curtime,numstep,forceView);   // Edge force value;
        this->pathFollowingArchiving(curtime,numstep);
    
        this->IPVolumeIntegralArchiving(curtime,numstep,forceView);
        this->IPDataOnPhysicalArchiving(curtime,numstep,forceView);
        this->averageClusterArchiving(curtime,numstep,forceView);
    
        if (_strainMap !=NULL){
          _strainMap->buildDisplacementViewAndStrainView(numstep,forceView);
        }
      }
      this->homogenizedDataToFile(curtime);
    };
    
    
    double nonLinearMechSolver::solveMicroSolverStaticLinear(){
      /* time initialization */
      double curtime = 1.;
      double timestep = 1.;
      int step = 1;
      /* solving */
      this->setTimeForBC(curtime);
      this->setTimeForLaw(curtime,timestep,step);
      if(computedUdF()){
        _ipf->setDeformationGradientGradient(this->getMicroBC()->getSecondOrderKinematicalVariable(), IPStateBase::current);
      }
    
      if (_systemType == DISP_ELIM)
        _pAl->applyPBCByConstraintElimination();
      else if (_systemType == DISP_MULT)
        _pAl->assembleRightHandSide();
      else if (_systemType == MULT_ELIM)
        _pAl->assembleConstraintResidualToSystem();
    
      this->computeExternalForces();
      this->computeStiffMatrix();
      if (_outputFile)
        fprintf(_outputFile,"-- done assembling!\n");
      pAssembler->systemSolve();
      if (_outputFile)
        fprintf(_outputFile,"-- done solving!\n");
      _ipf->compute1state(IPStateBase::current,true);
    
       /* end of scheme */
      _sucessMicroSolve = true; 
      return curtime;
    };
    
    double nonLinearMechSolver::solveMicroSolverForwardEuler(){
      // external force vector // not change in function of time
      _timeManager->initializeTimeSteppingPlan();
      //
      this->setTimeForBC(1.);
      if(computedUdF()){
        _ipf->setDeformationGradientGradient(this->getMicroBC()->getSecondOrderKinematicalVariable(), IPStateBase::current);
      }
      _pAl->assembleRightHandSide();
      //
      std::string Aname = "A";
      linearSystem<double>* lsys = pAssembler->getLinearSystem(Aname);
      forwardEulerArcLengthBase<double>* elsys = dynamic_cast<forwardEulerArcLengthBase<double>*>(lsys);
      double endTime = _timeManager->getEndTime();
      while (!_timeManager->reachEndTime())
      {
        double lastTime = _timeManager->getLastTime();
        int lastIter = _timeManager->getLastIterationIndex();
        double timeStep = _timeManager->getTimeStep();
        if (lastTime+ timeStep > endTime)
        {
          timeStep = endTime-lastTime;
        }
        
        if (_outputFile)
          fprintf(_outputFile,"iter = %d, time = %e, entime = %e\n",lastIter+1,lastTime+timeStep,endTime);
          
        Msg::Info("iter = %d, time = %e, entime = %e",lastIter+1,lastTime+timeStep,endTime);
        this->computeStiffMatrix();
        elsys->setArcLengthControlIncrement(timeStep);
        //pAssembler->systemSolve();
        int ss = pAssembler->systemSolveIntReturn();
        double control;
        elsys->getControlParameter(control);
        this->setTimeForBC(control);
        // update ipvariable
    
        if(computedUdF()){
          _ipf->setDeformationGradientGradient(this->getMicroBC()->getSecondOrderKinematicalVariable(), IPStateBase::current);
        }
        _ipf->compute1state(IPStateBase::current,true);
    
        _timeManager->saveTimeHistory();
        if (!_multiscaleFlag)
        {
          this->oneStepPostSolve(control,lastIter+1);
        }
        else
        {
          this->nextStep(control,lastIter+1);
        }
        lsys->zeroMatrix();
        elsys->nextStep();
        _timeManager->computeTimeStepForNextSolving(1);
      }
      
      return _timeManager->getLastTime();
    };
    
    void nonLinearMechSolver::setTime(const double ctime,const double dtime, const int curstep){
      _macroTimeStep = dtime;
      _macroTime = ctime;
      _macroStep = curstep;
    }
    
    double nonLinearMechSolver::microSolveStaticLinear(){
      double t= Cpu();
      initMicroSolver();
      double time = 0;
      if (_controlType == LOAD_CONTROL){
        time= solveMicroSolverStaticLinear();
    
        extractAverageProperties(_tangentflag);
    
    		if (_microBC->getTotalNumberOfMechanicalDofs() > 0){
    			if (_stressflag)
    				_currentState->getHomogenizedStress().print("stress");
    			if (_tangentflag)
    				_currentState->getHomogenizedTangentOperator_F_F().print("tangent");
    
    		}
    
    		for (int index =0; index < _microBC->getTotalNumberOfConDofs(); index++){
          _currentState->getHomogenizedConstitutiveExtraDofFlux(index).print("con extra dof flux");
          printf("double con extra dof %d internal energy %f\n",index,_currentState->getHomogenizedConstitutiveExtraDofInternalEnergy(index));
    		}
        this->endOfScheme(time,_timeManager->getLastIterationIndex());
      }
      else if (_controlType == ARC_CONTROL_EULER)
        Msg::Error("This is not exist");
    
    
      Msg::Info("StaticLinear OK, total time = %e seconds",t);
      return time;
    };
    
    double nonLinearMechSolver::microSolveSNL(){
      double t= Cpu();
      initMicroSolver();
      double time = 0;
      if (_controlType == LOAD_CONTROL or _pathFollowing)
        time = solveMicroSolverSNL();
      else if (_controlType == ARC_CONTROL_EULER)
        time = solveMicroSolverForwardEuler();
    
      this->endOfScheme(time,_timeManager->getLastIterationIndex());
      t = Cpu() -t;
      Msg::Info("StaticNonLinear OK, total time = %e seconds",t);
    
      return time;
    };
    
    int nonLinearMechSolver::pathFollowingPerturbation(){
      std::string name = "A";
      linearSystem<double>* lsys = pAssembler->getLinearSystem(name);
      pbcSystem<double> * pbcSys = dynamic_cast<pbcSystem<double>*>(lsys);
      nonLinearSystem<double>* nlsys = dynamic_cast<nonLinearSystem<double>*>(lsys);
    	pathFollowingSystem<double>* pathsys = dynamic_cast<pathFollowingSystem<double>*>(lsys);
    
      // compute ipvariable
    
      if(computedUdF()){
          _ipf->setDeformationGradientGradient(this->getMicroBC()->getSecondOrderKinematicalVariable(), IPStateBase::current);
      }
      _ipf->compute1state(IPStateBase::current, true);
      double normFinfInit = this->computeRightHandSide();
    
      // loop until convergence
      int iter=0;
      if(normFinfInit <_tol){ // no force (can append if contact )
        if (_outputFile)
          fprintf(_outputFile,"NO FORCE GO TO NEXT STEP\n");
          Msg::Error("perturbation is too small!");
        return 0;
      }
      double relnorm = 1;
    
      if (_outputFile)
        fprintf(_outputFile,"MICROITERATION n %d : RESIDU : %e \n",iter,relnorm);
    
      while (relnorm>_tol){
        iter++;
        // Solve KDu = Fext-Fint
        int succeed = pathsys->solvePerturbedSystem();
        if(!succeed)
        {
          Msg::Error("solution is not converged in perturbed system");
          iter = _timeManager->getMaxNbIterations();
          break;
        }
    
        // update ipvariable
        _ipf->compute1state(IPStateBase::current,true);
    
        // break in case of non-iterative procedure
        if (!_iterativeNR) break;
    
        // check convergence criterion
        double normFinf = this->computeRightHandSide();
        relnorm = normFinf/normFinfInit;
        if (_outputFile)
          fprintf(_outputFile,"MICROITERATION n %d : RESIDU : %e \n",iter,relnorm);
        if((iter == _timeManager->getMaxNbIterations()) or std::isnan(relnorm))
        {
          // reset system value
          Msg::Error("solution is not converged in perturbed system");
          iter = _timeManager->getMaxNbIterations(); // for isnan case
          break;
        }
      }
      return iter;
    };
    
    int nonLinearMechSolver::microNewtonRaphson(const int numstep){
      std::string name = "A";
      linearSystem<double>* lsys = pAssembler->getLinearSystem(name);
      pbcSystem<double> * pbcSys = dynamic_cast<pbcSystem<double>*>(lsys);
      nonLinearSystem<double>* nlsys = dynamic_cast<nonLinearSystem<double>*>(lsys);
    
      // compute ipvariable
    
      if(computedUdF()){
          _ipf->setDeformationGradientGradient(this->getMicroBC()->getSecondOrderKinematicalVariable(), IPStateBase::current);
      }
      _ipf->compute1state(IPStateBase::current, true);
      double normFinfInit = this->computeRightHandSide();
    
      // loop until convergence
      int iter=0;
      if(normFinfInit <_tol){ // no force (can append if contact )
        if (_outputFile)
          fprintf(_outputFile,"NO FORCE GO TO NEXT STEP\n");
        return 0;
      }
      double relnorm = 1;
      double normFinf =1;
    
      if (_outputFile)
        fprintf(_outputFile,"MICROITERATION n %d : RESIDU : %e \n",iter,relnorm);
    
      while ((relnorm>_tol and normFinf>_absTol)){
        iter++;
        // Solve KDu = Fext-Fint
        double t = Cpu();
        int succeed = pAssembler->systemSolveIntReturn();
        t = Cpu() -t;
    
        if(!succeed)
        {
          iter = _timeManager->getMaxNbIterations();
          break;
        }
    
        // update ipvariable
        _ipf->compute1state(IPStateBase::current,true);
    
        // break in case of non-iterative procedure
        if (!_iterativeNR) break;
    
        // check convergence criterion
        normFinf = this->computeRightHandSide();
        relnorm = normFinf/normFinfInit;
        if (_outputFile)
          fprintf(_outputFile,"MICROITERATION n %d : RESIDU : %e COMPUTING TIME: %f\n",iter,relnorm,t);
        if((iter == _timeManager->getMaxNbIterations()) or std::isnan(relnorm))
        {
          // reset system value
          iter = _timeManager->getMaxNbIterations(); // for isnan case
          break;
        }
      }
      return iter;
    }
    
    int nonLinearMechSolver::microNewtonRaphsonPathFollowing(const int numstep){
      std::string name = "A";
      linearSystem<double>* lsys =pAssembler->getLinearSystem(name);
      nonLinearSystem<double>* nonsys = dynamic_cast<nonLinearSystem<double>*>(lsys);
      pathFollowingSystem<double>* pathSys = dynamic_cast<pathFollowingSystem<double>*>(lsys);
    
      // copy state from previous
      this->setTimeForBC(pathSys->getControlParameter());
      if(computedUdF()){
          _ipf->setDeformationGradientGradient(this->getMicroBC()->getSecondOrderKinematicalVariable(), IPStateBase::current);
      }
      _ipf->compute1state(IPStateBase::current,true);
      double normFint = this->computeRightHandSide();
      double norm0 = 0.;
    
      // loop until convergence
      int iter=0;
      double relnorm = 1;
      if (_outputFile)
        fprintf(_outputFile,"iteration n %d : residu : %e, absolute residu : %e \n",iter,relnorm,normFint);
      else if (!_multiscaleFlag)
        Msg::Info("iteration n %d : residu : %e, absolute residu : %e ",iter,relnorm,normFint);
      while ((relnorm>_tol and normFint>_absTol) or iter == 0){
    
        iter++;
        int succeed = pAssembler->systemSolveIntReturn();
        if(!succeed)
        {
          iter = _timeManager->getMaxNbIterations();
          break;
        }
        // update ipvariable
        this->setTimeForBC(pathSys->getControlParameter());
    
        if(computedUdF()){
          _ipf->setDeformationGradientGradient(this->getMicroBC()->getSecondOrderKinematicalVariable(), IPStateBase::current);
        }
        
        _ipf->compute1state(IPStateBase::current,true);
    
        if (!_iterativeNR) break;
        normFint = this->computeRightHandSide();
        if (norm0 == 0.) norm0 = nonsys->norm0Inf();
        relnorm = normFint/norm0;
    
        if (_outputFile)
          fprintf(_outputFile,"iteration n %d : residu : %e, absolute residu : %e \n",iter,relnorm,normFint);
        else if (!_multiscaleFlag)
          Msg::Info("iteration n %d : residu : %e, absolute residu : %e",iter,relnorm,normFint);
    
        if((iter == _timeManager->getMaxNbIterations()) or std::isnan(relnorm))
        {
          // reset system value
          iter = _timeManager->getMaxNbIterations(); // for isnan case
          break;
        }
      }
    
      return iter;
    };
    
    
    void nonLinearMechSolver::setModeView(const int view, const int nbstepArch){
    	std::string str = "ModeShape" + int2str(view);
      _eigview.emplace_back(str,view,nlsField::crude,0,nlsField::val,nbstepArch);
    };
    
    
    void nonLinearMechSolver::tangentAveragingFlag(const bool fl)
    {
    	_tangentflag = fl;
    	#ifdef _DEBUG
    	if (_tangentflag) Msg::Info("Tangent averaging is activated");
    	else Msg::Info("Tangent averaging is desactivated");
    	#endif
    };
    
    void nonLinearMechSolver::stressAveragingFlag(const bool fl){
    	_stressflag = fl;
    	#ifdef _DEBUG
    	if (_stressflag) Msg::Info("Stress averaging is activated");
    	else Msg::Info("Stress averaging is desactivated");
    	#endif
    };
    
    void nonLinearMechSolver::setStressAveragingMethod(const int method){
      _homogenizeStressMethod = STRESS_HOMO_TYPE(method);
    };
    void nonLinearMechSolver::setTangentAveragingMethod(const int method, const double prec){
      _homogenizeTangentMethod = (TANGENT_HOMO_TYPE)method;
      _tangentPerturbation = prec;
    };
    
    void nonLinearMechSolver::eigenValueSolver(const int num){
    	_eigOpts.numeigenvalue = num;
    };
    
    void nonLinearMechSolver::setEigenSolverParamerters(const int type, const int numiter, const std::string method, const double tol, const bool mktofile, const bool hem){
      if (type == 0){
        _eigOpts.type  = eigenSolverOptions::Static;
        Msg::Info("static eigen solver is used");
      }
      else if (type ==1){
        _eigOpts.type  = eigenSolverOptions::Dynamic;
        Msg::Info("dynamic eigen solver is used");
      }
      else
        Msg::Error("eigensolver type %d is not correctly defined",type);
    
      _eigOpts.maxNumIteration  = numiter;
      _eigOpts.method = method;
      _eigOpts.convergenCriterion = tol;
      _eigOpts.MKToFile = mktofile;
      _eigOpts.hermitian = hem;
    };
    
    
    void nonLinearMechSolver::setMicroProblemIndentification(int ele, int gpt){
    	_enumMinus = ele;
      _enumPlus = ele;
    	_gnum = gpt;
    };
    
    void nonLinearMechSolver::setMicroProblemIndentification(int eleMinus, int elePlus, int gpt){
    	_enumMinus = eleMinus;
      _enumPlus = elePlus;
    	_gnum = gpt;
    };
    
    void nonLinearMechSolver::setHomogenizationPropertyArchiveFlag(const bool flg){
      _isHommProSaveToFile = flg;
    };
    void nonLinearMechSolver::setDisplacementAndIPArchiveFlag(const bool flg){
      _archive = flg;
    }
    void nonLinearMechSolver::setStrainArchiveFlag(const bool flg){
      _isHommStrainSaveToFile = flg;
    };
    
    
    void nonLinearMechSolver::setExtractPerturbationToFileFlag(const bool flag){
      _extractPerturbationToFile = flag;
    };
    
    void nonLinearMechSolver::setSystemType(const int i){
      _systemType = (SYSTEM_TYPE)i;
    };
    void nonLinearMechSolver::setControlType(const int i){
      _controlType = (CONTROL_TYPE)i;
    };
    void nonLinearMechSolver::setMicroSolverFlag(const bool flag){
      _microFlag = flag;
    };
    
    void nonLinearMechSolver::setMultiscaleFlag( const bool flag){
      _multiscaleFlag = true;
    };
    
    void nonLinearMechSolver::setSameStateCriterion(const double cr){
     Msg::Info("microscopic computation does not occurs if the strain diffrence is less than %e",cr);
     _sameStateCriterion = cr;
    };
    
    void nonLinearMechSolver::saveStiffnessMatrixToFile(const int iter){
      linearSystem<double>* lsys=NULL;
      std::string name="A";
      lsys=pAssembler->getLinearSystem(name);
    #if defined(HAVE_PETSC)
      linearSystemPETSc<double>* lpet=dynamic_cast<linearSystemPETSc<double>*>(lsys);
      if(lpet==NULL)return;
      else{
        functionPETSc::MatToFile(lpet->getMatrix(),"data/Mat_Stiffness.txt");
      }
      implicitHulbertChungPetsc<double>* hcsys=dynamic_cast<implicitHulbertChungPetsc<double>*>(lsys);
      if(hcsys==NULL)return;
      else{
        functionPETSc::MatToFile(hcsys->getMassMatrix(),"data/Mat_Mass.txt");
      }
    #endif
    };
    
    
    void nonLinearMechSolver::setDisturbedEigenMode(int num, double val, bool flag){
    	_eigOpts.isPerturbedEigenMode = flag;
    	_eigOpts.numberPerturbedMode = num;
    	_eigOpts.valPerturbedMode = val;
    	if (flag)
    		Msg::Info("Set disturbing by eigenmodes: num = %d, val = %f ",num,val);
    };
    
    void nonLinearMechSolver::writeDisturbedMeshByEigenVector(eigenSolver& eigS, int numberMode, double fact){
    	Msg::Info("Begin writing deformed mesh");
      GModel* dispgmodel = new GModel();
      dispgmodel->readMSH(_meshFileName.c_str());
      std::set<MVertex*> computedVertex;
    
      int numMode = eigS.getNumberEigenvectors();
      if (numberMode >numMode)
        numberMode = numMode;
    
      eigenVectorData eigData(&eigS,pAssembler,numberMode,fact);
    
      for (int i=0; i<domainVector.size(); i++){
      	partDomain* dom = domainVector[i];
        FunctionSpaceBase* sp = dom->getFunctionSpace();
    
        for (elementGroup::elementContainer::const_iterator it = dom->element_begin(); it!= dom->element_end(); it++){
          MElement* e = it->second;
          std::vector<Dof> keys;
          sp->getKeys(e,keys);
          // get displacement results
    			std::vector<double> vals;
    			eigData.get(keys,vals);
    
    			for (int iver =0; iver< e->getNumVertices(); iver++){
            MVertex* v = e->getVertex(iver);
    				MVertex* vdisp = dispgmodel->getMeshVertexByTag(v->getNum());
    				// modify directly vertex coordinates
            if (computedVertex.find(vdisp) == computedVertex.end()){
              computedVertex.insert(vdisp);
              double x = vals[iver];
              double y = vals[iver+1*e->getNumVertices()];
              double z = 0.;
              if (_dim == 3)
                z = vals[iver+2*e->getNumVertices()];
              vdisp->x() += x;
              vdisp->y() += y;
              vdisp->z() += z;
        		}
        	}
     	 	}
      }
    
      // write deformed mesh to file
      std::string filename = "Disturb_mesh.msh";
      dispgmodel->writeMSH(filename, CTX::instance()->mesh.mshFileVersion,CTX::instance()->mesh.binary, CTX::instance()->mesh.saveAll,CTX::instance()->mesh.saveParametric, CTX::instance()->mesh.scalingFactor);
      computedVertex.clear();
      delete dispgmodel;
    	Msg::Info("End writing deformed mesh");
    };
    
    void nonLinearMechSolver::setWriteDeformedMeshToFile(bool flag){
      _isWriteDeformedMeshToFile = flag;
    };
    
    void nonLinearMechSolver::writeDeformedMesh(int step){
      Msg::Info("Begin writing deformed mesh");
      GModel* dispgmodel = new GModel();
      dispgmodel->readMSH(_meshFileName.c_str());
      std::set<MVertex*> computedVertex;
    
      for (int i=0; i<domainVector.size(); i++){
      	partDomain* dom = domainVector[i];
        FunctionSpaceBase* sp = dom->getFunctionSpace();
        for (elementGroup::elementContainer::const_iterator it = dom->element_begin(); it!= dom->element_end(); it++){
          MElement* e = it->second;
          std::vector<Dof> keys;
          sp->getKeys(e,keys);
          // get displacement results
          std::vector<double> vals;
          pAssembler->getDofValue(keys,vals);
          for (int iver =0; iver< e->getNumVertices(); iver++){
            MVertex* v = e->getVertex(iver);
    				MVertex* vdisp = dispgmodel->getMeshVertexByTag(v->getNum());
    				// modify directly vertex coordinates
            if (computedVertex.find(vdisp) == computedVertex.end()){
              computedVertex.insert(vdisp);
              double x = vals[iver];
              double y = vals[iver+1*e->getNumVertices()];
              double z = 0.;
              if (_dim == 3)
                z = vals[iver+2*e->getNumVertices()];
              vdisp->x() += x;
              vdisp->y() += y;
              vdisp->z() += z;
        		}
        	}
     	 	}
      }
      // write deformed mesh to file
      std::string filename = "deformed_mesh_"+int2str(step)+ ".msh";
      dispgmodel->writeMSH(filename, CTX::instance()->mesh.mshFileVersion,CTX::instance()->mesh.binary, CTX::instance()->mesh.saveAll,CTX::instance()->mesh.saveParametric, CTX::instance()->mesh.scalingFactor);
      computedVertex.clear();
      delete dispgmodel;
    	Msg::Info("End writing deformed mesh");
    };
    
    
    /** perform a particular test**/
    void nonLinearMechSolver::activateTest(const bool fl){
      _testFlag = fl;
      if (_testFlag)
      {
        _microFlag = false; // desactive the microFlag
        Msg::Info("activation test");
      }
    };
    
    void nonLinearMechSolver::initialize_test(){
      if (_microBC == NULL) {
        Msg::Error("test with micro BC must be coupled with one micro BC");
      };
      if (_pbcGroup != NULL) delete _pbcGroup;
    
      std::vector<elementGroup*>& bgroup  = _microBC->getBoundaryElementGroup();
      const std::vector<int>& bphysical = _microBC->getBoundaryPhysicals();
      bgroup.clear();
      for (int i=0; i< bphysical.size(); i++){
        elementGroup* gr = new elementGroup(_dim-1,bphysical[i]);
        bgroup.push_back(gr);
      }
    
      FunctionSpaceBase* lagspace = domainVector[0]->getFunctionSpace();
      _pbcGroup = new pbcConstraintElementGroup(this,lagspace,NULL);
      /*create all linear constraints*/
      _pbcGroup->createConstraintForTest();
      /*enforce constraints to system*/
      _pbcGroup->applyLinearConstraintsToSystem(pAssembler);
      /* init homogenized stress file*/
      _rveVolume = _pbcGroup->getRVEVolume();
      //this->setRVEGeometricalInertia(_pAl->getRVEGeometricalInertia());
      //this->setRVEGeometry(_pAl->getRVEGeometry());
      this->setRVEGeometricalInertia(_pbcGroup->getRVEGeometricalInertia());
      this->setRVEGeometry(_pbcGroup->getRVEGeometry());
    
    };
    
    void nonLinearMechSolver::displacementBCOnControlNode(const int pos, const int comp, const double value){
      allCornerConstraint.emplace_back();
      nonLinearDirichletBCAtCorner& cornerConstraint = allCornerConstraint.back();
      cornerConstraint._tag = pos;
      cornerConstraint._comp = comp;
      cornerConstraint._cornerNumber = pos;
      cornerConstraint._f = new simpleFunctionTime<double>(value);
    };
    
    void nonLinearMechSolver::displacementBCOnControlNode(const int pos, const int comp, simpleFunctionTime<double> *fct)
    {
      allCornerConstraint.emplace_back();
      nonLinearDirichletBCAtCorner& cornerConstraint = allCornerConstraint.back();
      cornerConstraint._tag = pos;
      cornerConstraint._comp = comp;
      cornerConstraint._cornerNumber = pos;
      cornerConstraint._f = fct;
    }
    
    void nonLinearMechSolver::forceBCOnControlNode(const int pos, const int comp, const double value){
      allCornerForce.emplace_back();
      nonLinearNeumannBCAtCorner& cornerConstraint = allCornerForce.back();
      cornerConstraint._tag = pos;
      cornerConstraint._comp = comp;
      cornerConstraint._cornerNumber = pos;
      cornerConstraint._f = new simpleFunctionTime<double>(value);
    };
    
    void nonLinearMechSolver::getDofOnControlNode(const int pos, std::vector<Dof>& R){
      if (_testFlag){
        if (_pbcGroup != NULL){
          _pbcGroup->getDofsOfCornerVertex(pos,R);
        }
      }
    };
    
    void nonLinearMechSolver::setExtractCohesiveLawFromMicroDamage(const bool check){
      _damageToCohesiveJump = check;
      if (check){
        _tangentflag = true;
        Msg::Info("extract cohesive jump from microdamage, tangent computation has to be activated");
        _checkFailureOnset = true;
      }
    };
    
    void nonLinearMechSolver::setLostSolutionUniquenssTolerance(const double tol){
    	_lostSolutionUniquenssTolerance = tol;
    }
    
    void nonLinearMechSolver::setExtractIrreversibleEnergyFlag(const bool ex){
    	_extractIrreversibleEnergy =ex;
    }
    
    void nonLinearMechSolver::setLocalizationNormal(const SVector3& normal){
      _lostSolutionUniquenssNormal = normal;
    
      if (_lostSolutionUniquenssNormal.norm() == 0.){
        Msg::Info("zero normal is set");
      }
      else{
        _lostSolutionUniquenssNormal.normalize();
      }
    };
    void nonLinearMechSolver::setLocalizationNormal(const double n1, const double n2, const double n3){
      _lostSolutionUniquenssNormal(0) = n1;
      _lostSolutionUniquenssNormal(1) = n2;
      _lostSolutionUniquenssNormal(2) = n3;
    
      if (_lostSolutionUniquenssNormal.norm() == 0.){
        Msg::Info("zero normal is set");
      }
      else{
        _lostSolutionUniquenssNormal.normalize();
      }
    };
    
    void nonLinearMechSolver::setRVELengthInNormalDirection(const double l, const double sfR){
      _RVELengthInCohesiveNormal = l;
    	_surfaceReductionRatio = sfR;
    };
    
    void nonLinearMechSolver::setVoidPartInLocalizationBand(const double vp){
    	_voidPartInLocalizationBand = vp;
    };
    
    void nonLinearMechSolver::setCheckFailureOnset(const bool fl, const bool withNormal){
    	_checkFailureOnset = fl;
    	_checkWithNormal = withNormal;
    	if (_checkFailureOnset){
        if (_checkWithNormal){
          printf("failure will check with prescribed normal\n");
        }
        else{
          printf("failure will check without normal\n");
        }
    	}
    };
    
    void nonLinearMechSolver::switchMicroBC(const nonLinearMicroBC* mbc){
    	this->addMicroBC(mbc);
    
    	// update data with solver
    	nonLinearMicroBC*  microBC = this->getMicroBC();
    	microBC->updateWithSolver(this);
    
    	// recreate system,
      this->createMicroSystem();
    
    	// init all other BC, fixed DOf, etc
      this->initAllBCsOnDofs();
    
      // create new pbcAlgorithm
      if (_pAl != NULL){
    		delete _pAl;
    	}
    	_pAl = new pbcAlgorithm(this); // create all constraints, etc
    
      // renumber Dof and reallocate the system
      this->microNumberDof();
    
    	// make data as previous  if exists
      this->updateDataFromPreviousSolver();
      
    	_ipf->copy(IPStateBase::current,IPStateBase::initial);
    	_ipf->copy(IPStateBase::current,IPStateBase::previous);
    };
    
    void nonLinearMechSolver::setSwitchMicroBCFlag(const bool fl){
    	_failureBCIsSwitched = fl;
    };
    
    void nonLinearMechSolver::setExtractElasticTangentOperator(const bool fl){
      _extractElasticTangentOperator = fl;
      if (_extractElasticTangentOperator){
        Msg::Info("extract elastic tangent works if setTangentAveragingMethod(2) is used ");
      }
    };
    
    void nonLinearMechSolver::setBatchComputationsFlag(const bool fl){
      _batchComputations = fl;
    };