Example - Linear Static Analysis

In this section, the example of the implementation of linear static analysis will be shown. The meta steps are not used, so the default one is created. The class definition includes the declaration of characteristic components of the problem - the stiffness matrix and load and displacement vectors. Two additional variables are used to store the solver type and the sparse matrix type, which can be selected by the user. The services for solving the solution step (solveYourselfAt), requesting unknowns (giveUnknownComponent), context i/o (saveContext and restoreContext), problem initialization (initializeFrom), and consistency checking (checkConsistency) are overloaded.

class LinearStatic : public StructuralEngngModel
{ 
/*
   This class implements LinearStatic Engineering problem.
 Multiple loading works only if linear elastic material (such as isoLE)  is used.
 (Other non-linear materials kepp load history, so such multiple loading
 will cause that next step will be assumed as new load increment, 
 not the total new load). Because they always copute real stresses acording
 to reached strain state, they are not able to respond to linear analysis.
  
DESCRIPTION:
   Solution of this problem is series of loading cases, maintained as sequence of
   time-steps. This solution is in form of linear equation system Ax=b
TASK:
   Creating Numerical method for solving Ax=b
   Interfacing Numerical method to Elements
   Managing time  steps
*/

 protected:
  SparseMtrx* stiffnessMatrix;
  FloatArray loadVector;
  FloatArray displacementVector;
  
  LinSystSolverType solverType;
  SparseMtrxType sparseMtrxType;
  /// Numerical method used to solve the problem
  SparseLinearSystemNM *nMethod;

  int initFlag;

#ifdef __PETSC_MODULE
  Vec _loadVec, _dispVec;
#endif

 public:
  LinearStatic (int i, EngngModel* _master = NULL) ;
  ~LinearStatic () 
    {delete  stiffnessMatrix; if (nMethod) delete nMethod;}
// solving
  void solveYourself ();
  void solveYourselfAt (TimeStep *);
  //int requiresNewLhs () {return 0;}
 /**
  Updates nodal values
  (calls also this->updateDofUnknownsDictionary for updating dofs unknowns dictionaries
  if model supports changes of static system). The element internal state update is also forced using
  updateInternalState service.
  */
  virtual void               updateYourself (TimeStep*) ;
  double   giveUnknownComponent ( EquationID, ValueModeType, TimeStep*, Domain*, Dof*);
  contextIOResultType saveContext (FILE* stream, void *obj = NULL) ;
  contextIOResultType restoreContext (FILE* stream, void *obj = NULL);

 void   updateDomainLinks();

  TimeStep* giveNextStep ();
  NumericalMethod* giveNumericalMethod (TimeStep*);
  //void printReactionForces (TimeStep *);
  void               terminate (TimeStep*);

 IRResultType initializeFrom (InputRecord* ir);

 // consistency check
 virtual int checkConsistency (); // returns nonzero if o.k.

  /** DOF printing routine. Called by DofManagers to print Dof specific part.
  Dof class provides component printing routines, but emodel is responsible
  for what will be printed at DOF level.
  @param stream output stream
  @param iDof dof to be processed
  @param atTime solution step
  */
 virtual void printDofOutputAt (FILE* stream, Dof* iDof, TimeStep* atTime);

// identification
  const char* giveClassName () const { return "LinearStatic";}
  classType giveClassID ()      const { return LinearStaticClass;}
  fMode giveFormulation () { return TL; }
#ifdef __PARALLEL_MODE
  /**
     Determines the space necessary for send/receive buffer.
     It uses related communication map pattern to determine the maximum size needed.
     @param commMap communication map used to send/receive messages
     @param buff communication buffer
     @return upper bound of space needed
  */
  int estimateMaxPackSize (IntArray& commMap, CommunicationBuffer& buff, int packUnpackType) ;
#endif
 
} ;

The listing of the LinearStatic class implementation follows. First, the giveNumericalMethod returns the numerical method, which will be used. The new instance is created if not defined according to nMethod attribute, which is initialized from input (see instanciateYourself). Note, that thanks to the general interface, defined by the NumericalMethod class and the component mapping approach, the further communication with the numerical method uses only the interface declared by the base NumerialMethod class and therefore there is no need to worry about particular numerical method details.

NumericalMethod* LinearStatic :: giveNumericalMethod (TimeStep* atTime)
// only one has reason for LinearStatic 
//     - SolutionOfLinearEquations

{
  if (nMethod) return nMethod ;
	
	SparseLinearSystemNM* nm;
	if (solverType == ST_Direct) {
		nm = (SparseLinearSystemNM*) new LDLTFactorization (1,this->giveDomain(1),this);
		nMethod = nm;
		return nm;
	} else {
		nm = (SparseLinearSystemNM*) new IMLSolver (1,this->giveDomain(1),this);
		nMethod = nm;
		return nm;
	}
}

IRResultType
LinearStatic :: initializeFrom (InputRecord* ir)
{
  // Required by IR_GIVE_FIELD macro
  const char *__keyword, *__proc = "initializeFrom"; 
  IRResultType result;                              

  StructuralEngngModel::initializeFrom (ir);
  int val = 0;
  IR_GIVE_OPTIONAL_FIELD (ir, val, "lstype"); // Macro
  solverType = (LinSystSolverType) val;

  val = 0;
  IR_GIVE_OPTIONAL_FIELD (ir, val, "smtype"); // Macro
  sparseMtrxType = (SparseMtrxType) val;

  return IRRT_OK;
}
 


double LinearStatic ::  giveUnknownComponent (EquationID chc, ValueModeType mode, 
                TimeStep* tStep, Domain* d, Dof* dof)
// returns unknown quantity like displaacement, velocity of equation eq
// This function translates this request to numerical method language
{
 
 int eq = dof->giveEquationNumber();
 if (eq == 0) _error ("giveUnknownComponent: invalid equation number");

  if (tStep != this->giveCurrentStep ()) {
    _error ("giveUnknownComponent: unknown time step encountered");
  return 0.;
  }

  if (chc != EID_MomentumBalance) {
  _error ("giveUnknownComponent: Unknown is of undefined CharType for this problem");
  return 0.;
  }

  switch (mode)
   {
   case VM_Total:
   case VM_Incremental:
    if (displacementVector.isNotEmpty()) return displacementVector.at(eq);
    else return 0.;
    // return nMethod-> giveUnknownComponent (LinearEquationSolution, eq);

   default:
    _error ("giveUnknownComponent: Unknown is of undefined type for this problem");
   }
  return 0.;
}  


TimeStep* LinearStatic :: giveNextStep ()
{
  int istep = this->giveNumberOfFirstStep();
	//int mstep = 1;
  StateCounterType counter = 1;
	delete previousStep;

  if (currentStep != NULL) {
    istep =  currentStep->giveNumber() + 1   ;
		counter = currentStep->giveSolutionStateCounter() + 1;
	}
  previousStep = currentStep;
  currentStep = new TimeStep (istep,this, 1, (double) istep, 0., counter);
  // time and dt variables are set eq to 0 for staics - has no meaning
  return currentStep;
}

void  LinearStatic :: solveYourselfAt (TimeStep* tStep) {
//
// creates system of governing eq's and solves them at given time step
//
// first assemble problem at current time step

  if (initFlag) {

#ifdef VERBOSE
    OOFEM_LOG_INFO("Assembling stiffness matrix\n");
#endif
  
  //
  // first step  assemble stiffness Matrix
  //
  /*
    IntArray* mht = this -> GiveBanWidthVector ();
    stiffnessMatrix = new Skyline ();
    stiffnessMatrix ->  checkSizeTowardsBanWidth (mht) ;
    delete mht;
    */
  stiffnessMatrix = ::CreateUsrDefSparseMtrx(sparseMtrxType); // new Skyline ();
  if (stiffnessMatrix==NULL) _error ("solveYourselfAt: sparse matrix creation failed");
  //stiffnessMatrix = new DynCompCol ();
  //stiffnessMatrix = new CompCol ();
  
  stiffnessMatrix->buildInternalStructure (this, 1, EID_MomentumBalance);
  
  this -> assemble (stiffnessMatrix, tStep, EID_MomentumBalance, StiffnessMatrix, this->giveDomain(1));
  
  //
  // alocate space for displacementVector
  // 
  //displacementVector = new FloatArray (this->giveNumberOfEquations());
  displacementVector.resize (this->giveNumberOfEquations(EID_MomentumBalance));
  displacementVector.zero();

#ifdef __PETSC_MODULE
  if (solverType == ST_Petsc) {
    this->givePetscContext(1, EID_MomentumBalance)->createVecGlobal (&_loadVec);
    this->givePetscContext(1, EID_MomentumBalance)->createVecGlobal (&_dispVec);
  }
#endif
  initFlag = 0;
 }
#ifdef VERBOSE
  OOFEM_LOG_INFO("Assembling load\n");
#endif

#ifdef __PETSC_MODULE
  // direct interface to PETSC
  if (solverType == ST_Petsc) {
    this->petsc_assembleVectorFromElements(_loadVec, tStep, EID_MomentumBalance, 
                                           ElementForceLoadVector, VM_Total, 
                                           this->giveDomain(1));
    this->petsc_assembleVectorFromElements(_loadVec, tStep, EID_MomentumBalance, 
                                           ElementNonForceLoadVector, VM_Total, 
                                           this->giveDomain(1));
    this->petsc_assembleVectorFromDofManagers(_loadVec, tStep, EID_MomentumBalance, 
                                              NodalLoadVector, VM_Total, 
                                              this->giveDomain(1)) ;
    VecAssemblyBegin(_loadVec);
    VecAssemblyEnd (_loadVec);
   this->giveNumericalMethod(tStep);
#ifdef VERBOSE
    OOFEM_LOG_INFO("Solving ...\n");
#endif
    
    //nMethod -> solveYourselfAt(tStep);
    PetscSolver *ps = dynamic_cast<PetscSolver*>(nMethod);
    PetscSparseMtrx* psm = dynamic_cast<PetscSparseMtrx*>(stiffnessMatrix);
    ps -> petsc_solve (psm, _loadVec, _dispVec);
    
    this->givePetscContext(1, EID_MomentumBalance)->scatterG2N (_dispVec, &displacementVector, INSERT_VALUES); 
  } else 
#endif
  {
    // 
    // assembling the element part of load vector
    //
    //loadVector = new FloatArray (this->giveNumberOfEquations());
    loadVector.resize (this->giveNumberOfEquations(EID_MomentumBalance)); 
    loadVector.zero();
    
    this->assembleVectorFromElements(loadVector, tStep, EID_MomentumBalance, 
                                     ElementForceLoadVector, VM_Total, this->giveDomain(1));
    this->assembleVectorFromElements(loadVector, tStep, EID_MomentumBalance, 
                                     ElementNonForceLoadVector, VM_Total, this->giveDomain(1));
    
    // 
    // assembling the nodal part of load vector
    //
    this->assembleVectorFromDofManagers(loadVector, tStep, EID_MomentumBalance, 
                                        NodalLoadVector, VM_Total, this->giveDomain(1)) ;
    
    //
    // set-up numerical model
    //
    this->giveNumericalMethod(tStep);
    
    /*
      nMethod -> setSparseMtrxAsComponent ( LinearEquationLhs , stiffnessMatrix) ; 
      nMethod -> setFloatArrayAsComponent ( LinearEquationRhs , &loadVector) ; 
      nMethod -> setFloatArrayAsComponent ( LinearEquationSolution, &displacementVector) ;
    */
    // 
    // call numerical model to solve arised problem
    //
#ifdef VERBOSE
    OOFEM_LOG_INFO("Solving ...\n");
#endif
    
    //nMethod -> solveYourselfAt(tStep);
    nMethod -> solve (stiffnessMatrix, &loadVector, &displacementVector);
  }
  tStep->incrementStateCounter();              // update solution state counter
  //
  // update nodes, elements, etc.
  this->updateYourself(this->giveCurrentStep());
  
} 


void    LinearStatic :: updateYourself (TimeStep* stepN) 
{
	this->updateInternalState(stepN);
	StructuralEngngModel::updateYourself(stepN);
}


contextIOResultType LinearStatic :: saveContext (FILE* stream, void *obj)
// 
// saves state variable - displacement vector
//
{
	contextIOResultType iores;
	int closeFlag = 0;

	if (stream==NULL) {
		if (!this->giveContextFile(&stream,this->giveCurrentStep()->giveNumber(),
	                             contextMode_write)) 
			THROW_CIOERR(CIO_IOERR); // override 
		closeFlag = 1;
	}

  if ((iores = StructuralEngngModel :: saveContext (stream)) != CIO_OK) 
      THROW_CIOERR(iores);
  if ((iores = displacementVector.storeYourself(stream)) != CIO_OK) 
      THROW_CIOERR(iores);

	if (closeFlag) fclose (stream); // ensure consistent records
  return CIO_OK;
}



contextIOResultType LinearStatic :: restoreContext (FILE* stream, void *obj)
// 
// restore state variable - displacement vector
//
{
	contextIOResultType iores;
	int closeFlag = 0;
	int istep = this->resolveCorrespondingStepNumber (obj);

	if (stream == NULL) {
		if (!this->giveContextFile(&stream, istep, contextMode_read))
			THROW_CIOERR(CIO_IOERR); // override 
		closeFlag = 1;
	}

	if ((iores=StructuralEngngModel::restoreContext(stream,obj))!=CIO_OK)
     THROW_CIOERR(iores);
	if ((iores=displacementVector.restoreYourself(stream))!=CIO_OK)
      THROW_CIOERR(iores);

	
  if (closeFlag) fclose (stream); // ensure consistent records
	return CIO_OK;
}





void
LinearStatic:: terminate(TimeStep* tStep)
{
	StructuralEngngModel :: terminate (tStep);
	this->printReactionForces (tStep, 1);
}



int
LinearStatic::checkConsistency ()
{
// check internal consistency
// if success returns nonzero
	int i, nelem;
	Element* ePtr;
	StructuralElement* sePtr;
	Domain* domain = this->giveDomain(1);

	nelem = domain->giveNumberOfElements();
	// check for proper element type

	for (i=1; i<= nelem; i++) {
		ePtr = domain->giveElement(i);
		sePtr = DYNAMIC_CAST (StructuralElement, ePtr);
		if (sePtr == NULL) {
			printf ("Error: Element %d has no StructuralElement base\n",i);
			return 0;
		}
	}

	EngngModel :: checkConsistency ();

	return 1;
}


void
LinearStatic::updateDomainLinks ()
{
 EngngModel::updateDomainLinks();
	this->giveNumericalMethod(giveCurrentStep())->setDomain (this->giveDomain(1));
}

Borek Patzak 2018-01-02