/* Fesslix - Stochastic Analysis
 * Copyright (C) 2010-2016 Wolfgang Betz
 *
 * Fesslix is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Fesslix is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Fesslix.  If not, see <http://www.gnu.org/licenses/>. 
 */

#include "flxfem_element_ptruss.h"
#include "flxfem.h"
#include "flxfunction_ope_calc.h"

flxElement_1D_truss::flxElement_1D_truss(flxMaterial* material, flxCrossSec* crossSec, flxEdge& edge, const bool allowFastIntegration)
: flxElement_1D_base(material, crossSec, edge),
allowFastIntegration(allowFastIntegration),degreeEA(crossSec->get_deg_A() + material->get_deg_E()),DOFvec(edge.find_dof(FlxDof::dof_wx,true))
{
  // check if enough Gauss-points are loaded
    GI->check_GA_polynomialDegree(DOFvec->get_DOFpOrder()*2-2+degreeEA);
}

const tdouble flxElement_1D_truss::get_el_info_help(const FlxElementInfo::elInfo eI) const
{
  switch (eI) {
    case FlxElementInfo::NormalF:
      return edge.get_displacement(FlxDof::dof_wx,*LX,true)*(material->get_E())*(crossSec->get_A());
    case FlxElementInfo::Moment_x:
    case FlxElementInfo::Moment_y:
    case FlxElementInfo::Moment_z:
      return ZERO;
    case FlxElementInfo::sigma_max:
    case FlxElementInfo::sigma_min:
      return edge.get_displacement(FlxDof::dof_wx,*LX,true)*(material->get_E());
    default:
      return flxElement_1D_base::get_el_info_help(eI);
  }
}

void flxElement_1D_truss::assembleStf(const tdouble* meshInterval, const tuint meshInterval_size)
{
  const tuint PORDER = DOFvec->get_DOFpOrder();
  const tdouble L = edge.calc_length();
  edge.get_vertex_1().get_point().set_global(edge.get_vertex_2().get_point());
  if (allowFastIntegration && degreeEA==0) {	// assumes degreeEA = 0; (i.e. does not depend on global consts)
    for (tuint i=0;i<meshInterval_size;++i) {
      // deal with nodal DOFs
	const tdouble EAL = (material->get_E())*(crossSec->get_A())/(L*(meshInterval[i+1]-meshInterval[i])/2.0);
	edge.update_local_StfMtx(i,DOFvec,0,DOFvec,0,EAL);
	edge.update_local_StfMtx(i,DOFvec,1,DOFvec,1,EAL);
	edge.update_local_StfMtx(i,DOFvec,1,DOFvec,0,-1.0*EAL);
      // deal with internal DOFs
	for (tuint j=1;j<PORDER;++j) {
	  edge.update_local_StfMtx(i,DOFvec,1+j,DOFvec,1+j,2.0*EAL);
	}
    }
  } else {
    const tuint Ni = PORDER+1;
    tVec d(ZERO,(pow2(Ni)+Ni)/2);
    tdouble* dp = &d[0];
    #ifndef FLX_CV_1
      tdouble s[Ni];
    #else
      tdouble *s = new tdouble[Ni];
    #endif
    const tuint N = GI->pDegree2GPs(PORDER*2-2+degreeEA);
    const tdouble* GP = GI->get_GP(N);
    const tdouble* GW = GI->get_GW(N);
    tdouble EAW,f,dx2,lxgm;
    tuint c;
    for (tuint i=0;i<meshInterval_size;++i) {	// run over all sub-elements
      dx2 = (meshInterval[i+1]-meshInterval[i])/2.0;
      lxgm = (meshInterval[i]+meshInterval[i+1])/2.0;
      // assemble stiffness matrix of the sub-element
	d = ZERO;
	for (tuint p=0;p<N;++p) {
	  edge.update_globalConst(lxgm + dx2*GP[p]);
	  EAW = material->get_E() * crossSec->get_A() * GW[p];
	  for (tuint j=0;j<Ni;++j) {	// calculate shape functions
	    s[j] = dhLP[j](GP[p]);
	  }
	  c=0;
	  for (tuint j=0;j<Ni;++j) {
	    f = EAW*s[j];
	    for (tuint k=0;k<=j;++k) {
	      dp[c++] += f*s[k];
	    }
	  }
	}
	d*= 2.0/(L*dx2);
      // map to edge stiffness matrix
	edge.update_local_StfMtx(i,DOFvec,0,DOFvec,0,dp[0]);
	edge.update_local_StfMtx(i,DOFvec,1,DOFvec,0,dp[1]);
	edge.update_local_StfMtx(i,DOFvec,1,DOFvec,1,dp[2]);
	for (tuint j=1;j<PORDER;++j) {
	  edge.update_local_StfMtx(i,DOFvec,1+j,DOFvec,0,dp[((j+1)*(j+2))/2]);
	  edge.update_local_StfMtx(i,DOFvec,1+j,DOFvec,1,dp[((j+1)*(j+2))/2+1]);
	  edge.update_local_StfMtx(i,DOFvec,1+j,DOFvec,2,&(dp[((j+1)*(j+2))/2+2]),j);
	}
    }
    #ifdef FLX_CV_1
      delete [] s;
    #endif
  }
  // assemble the next element in the list
    if (next) next->assembleStf(meshInterval,meshInterval_size);
}




