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

#include "flxrandomFEM.h"


const tdouble flxMaterial::get_rho() const
{
  return rho->calc();
}

flxMaterial::flxMaterial(const flxMaterial& FlxM, const tuint mid)
: rho(new FlxFunction(*(FlxM.rho))), mid(mid)
{

}

flxMaterial::~flxMaterial()
{
  delete rho;
}

void flxIsotropicMaterial::get_ElasticityMtxPS(FlxMtxSym& Emtx,bool planeStress) const
{
  const tdouble nu = get_nu();
  if (planeStress) {
    const tdouble f = get_E()/(ONE-pow2(nu));
    Emtx.set_zeroMtx();
    Emtx(0,0) = f;
    Emtx(1,0) = f*nu; 
    Emtx(1,1) = f;
    Emtx(2,2) = f*(ONE-nu)/2;
  } else {
    const tdouble f = get_E()/((ONE+nu)*(ONE-2*nu));
    Emtx.set_zeroMtx();
    Emtx(0,0) = f*(ONE-nu);
    Emtx(1,0) = f*nu; 
    Emtx(1,1) = f*(ONE-nu);
    Emtx(2,2) = f*(ONE-2*nu)/2;
  }
}

void flxIsotropicMaterial::get_ElasticityMtxPS_SRI(FlxMtxSym& Emtx, bool planeStress) const
{
  const tdouble nu = get_nu();
  if (planeStress) {
    const tdouble f = get_E()/(ONE-pow2(nu));
    Emtx.set_zeroMtx();
    Emtx(0,0) = f;
    Emtx(1,0) = f*nu; 
    Emtx(1,1) = f;
  } else {
    const tdouble f = get_E()/((ONE+nu)*(ONE-2*nu));
    Emtx.set_zeroMtx();
    Emtx(0,0) = f*(ONE-nu);
    Emtx(1,0) = f*nu; 
    Emtx(1,1) = f*(ONE-nu);
  }
}

tdouble flxIsotropicMaterial::get_ElasticityMtxPS_SRI(bool planeStress) const
{
  const tdouble nu = get_nu();
  tdouble f;
  if (planeStress) {
    f = get_E()/(ONE-pow2(nu));
  } else {
    f = get_E()/((ONE+nu)*(ONE-2*nu));
  }
  return f*(ONE-2*nu)/2;
}

flxIsotropicMaterial::~flxIsotropicMaterial()
{
  if (v1!=NULL) {
    delete v1; 
    v1 = NULL;
  }
  if (v2!=NULL) {
    delete v2;
    v2 = NULL;
  }
}

flxIsotropicMaterial::flxIsotropicMaterial(const flxIsotropicMaterial& FlxM, const tuint mid): flxMaterial(FlxM,mid)
{
  v1c = FlxM.v1c;
  v2c = FlxM.v2c;
  v1 = new FlxFunction(*(FlxM.v1));
  v2 = new FlxFunction(*(FlxM.v2));
  deg_E = FlxM.deg_E;
}

flxIsotropicMaterial* flxIsotropicMaterial::copy(const tuint mid)
{
  return new flxIsotropicMaterial(*this,mid);
}

void flxIsotropicMaterial::log_info(std::ostream& lout)
{
  lout << "  isomat: ";
  lout << v1c << "=" << v1->write() << "; " << v2c << "=" << v2->write() << "; ";
  lout << std::endl;
}

const tdouble flxIsotropicMaterial::check_E(tdouble E) const
{
  if (E>=0) return E;
  std::ostringstream ssV;
  ssV << "Young's modulus must not be negative. (E=" << E << ")";
  throw FlxException("flxIsotropicMaterial::check_E", ssV.str() );
}

const tdouble flxIsotropicMaterial::check_G(tdouble G) const
{
  if (G>=0) return G;
  std::ostringstream ssV;
  ssV << "Shear modulus must not be negative. (G=" << G << ")";
  throw FlxException("flxIsotropicMaterial::check_G", ssV.str() );
}

const tdouble flxIsotropicMaterial::get_E() const
{
  if (v1c == 'E') return check_E(v1->calc());
  if (v2c == 'E') return check_E(v2->calc());
  if ( (v1c == 'n' && v2c == 'l') || (v1c == 'l' && v2c == 'n') ) {
    tdouble lambda; tdouble nu;
    if (v1c == 'n') { 
      lambda = v2->calc();
      nu = v1->calc();
    } else {
      nu = v2->calc();
      lambda = v1->calc();
    }
    return check_E(lambda*(1+nu)*(1-2*nu)/nu);
  }
  if ( (v1c == 'n' && v2c == 'G') || (v1c == 'G' && v2c == 'n') ) {
    tdouble G; tdouble nu;
    if (v1c == 'n') { 
      G = v2->calc();
      nu = v1->calc();
    } else {
      nu = v2->calc();
      G = v1->calc();
    }
    return check_E(2*G*(1+nu));
  }
  if ( (v1c == 'l' && v2c == 'G') || (v1c == 'G' && v2c == 'l') ) {
    tdouble G; tdouble lambda;
    if (v1c == 'l') { 
      G = v2->calc();
      lambda = v1->calc();
    } else {
      lambda = v2->calc();
      G = v1->calc();
    }
    return check_E(G*(3*lambda+2*G)/(lambda+G));
  }
  // Error: not implemented
  throw FlxException_NotImplemented("flxIsotropicMaterial::get_E");
}

const bool flxIsotropicMaterial::depend_E_on_Const(const tdouble*const thenumber)
{
  if (v1c=='E') return v1->dependOn_Const(thenumber);
  if (v2c=='E') return v2->dependOn_Const(thenumber);
  return v1->dependOn_Const(thenumber) || v2->dependOn_Const(thenumber);
}

const tdouble flxIsotropicMaterial::check_nu(tdouble nu) const
{
  if (nu>=0 && nu<=0.5) return nu;
  std::ostringstream ssV;
  ssV << "Poisson's ratio Modulus must not be negative. (nu=" << nu << ")";
  throw FlxException("flxIsotropicMaterial::check_nu", ssV.str() );
}

const tdouble flxIsotropicMaterial::get_nu() const
{
  if (v1c == 'n') return check_nu(v1->calc());
  if (v2c == 'n') return check_nu(v2->calc());
  if ( (v1c == 'E' && v2c == 'G') || (v1c == 'G' && v2c == 'E') ) {
    tdouble G; tdouble E;
    if (v1c == 'E') { 
      G = v2->calc();
      E = v1->calc();
    } else {
      E = v2->calc();
      G = v1->calc();
    }
    return check_nu(E/(2*G)-1);
  }
  if ( (v1c == 'l' && v2c == 'G') || (v1c == 'G' && v2c == 'l') ) {
    tdouble G; tdouble lambda;
    if (v1c == 'l') { 
      G = v2->calc();
      lambda = v1->calc();
    } else {
      lambda = v2->calc();
      G = v1->calc();
    }
    return check_nu(lambda/(2*(lambda+G)));
  }
  // Error: not implemented
  throw FlxException_NotImplemented("flxIsotropicMaterial::get_nu");;
}

const tdouble flxIsotropicMaterial::get_lambda() const
{
  if (v1c == 'l') return v1->calc();
  if (v2c == 'l') return v2->calc();
  if ( (v1c == 'n' && v2c == 'E') || (v1c == 'E' && v2c == 'n') ) {
    tdouble E; tdouble nu;
    if (v1c == 'n') { 
      E = v2->calc();
      nu = v1->calc();
    } else {
      nu = v2->calc();
      E = v1->calc();
    }
    return E*nu/((1+nu)*(1-2*nu));
  }
  if ( (v1c == 'n' && v2c == 'G') || (v1c == 'G' && v2c == 'n') ) {
    tdouble G; tdouble nu;
    if (v1c == 'n') { 
      G = v2->calc();
      nu = v1->calc();
    } else {
      nu = v2->calc();
      G = v1->calc();
    }
    return 2*G*nu/(1-2*nu);
  }
  if ( (v1c == 'E' && v2c == 'G') || (v1c == 'G' && v2c == 'E') ) {
    tdouble G; tdouble E;
    if (v1c == 'l') { 
      G = v2->calc();
      E = v1->calc();
    } else {
      E = v2->calc();
      G = v1->calc();
    }
    return G*(E-2*G)/(3*G-E);
  }
  // Error: not implemented
  throw FlxException_NotImplemented("flxIsotropicMaterial::get_lambda");
}

const tdouble flxIsotropicMaterial::get_G() const
{
  if (v1c == 'G') return check_G(v1->calc());
  if (v2c == 'G') return check_G(v2->calc());
  if ( (v1c == 'E' && v2c == 'n') || (v1c == 'n' && v2c == 'E') ) {
    tdouble nu; tdouble E;
    if (v1c == 'E') { 
      nu = v2->calc();
      E = v1->calc();
    } else {
      E = v2->calc();
      nu = v1->calc();
    }
    return check_G(E/(2*(1+nu)));
  }
  if ( (v1c == 'l' && v2c == 'n') || (v1c == 'n' && v2c == 'l') ) {
    tdouble nu; tdouble lambda;
    if (v1c == 'l') { 
      nu = v2->calc();
      lambda = v1->calc();
    } else {
      lambda = v2->calc();
      nu = v1->calc();
    }
    return check_G(lambda*(1-2*nu)/(2*nu));
  }
  // Error: not implemented
  throw FlxException_NotImplemented("flxIsotropicMaterial::get_G");
}

flxMaterialList::~flxMaterialList() {
  for (std::map<tuint, flxMaterial*>::iterator pos = box.begin(); pos != box.end(); ++pos) delete pos->second;
}

void flxMaterialList::insert(tuint index, flxMaterial* mat, bool errSerious) {
  std::pair<tuint, flxMaterial*>Element(index, mat);
  if ( ! box.insert(Element).second ) {;
    delete mat;
    std::ostringstream ssV;
    ssV << "Material '" << index << "' has already been defined.";
    FlxError(errSerious,"flxMaterialList::insert_1", ssV.str() );
  }
}

flxMaterial* flxMaterialList::get(const tuint index, const bool errSerious) {
  std::map<tuint, flxMaterial*>::iterator pos = box.find(index);
  if ( pos != box.end() ) {
    return pos->second;
  } else {
    std::ostringstream ssV;
    ssV << "Material '" << index << "' has not been defined.";
    FlxError(errSerious,"flxMaterialList::get", ssV.str() ); return NULL; // DUMMY-RETURN
  }
}

flxCrossSecManual::~flxCrossSecManual()
{
  if (A != NULL) delete A;
  if (Iy != NULL) delete Iy;
  if (Iz != NULL) delete Iz;
  if (It != NULL) delete It;
}

const tdouble flxCrossSecManual::check_positiv(const tdouble& value, const std::string strtype) const
{
  if (value>=0) return value;
  std::ostringstream ssV;
  ssV << strtype << " must not be negative. (" << value << ").";
  throw FlxException("flxCrossSecManual::check_positiv", ssV.str() );
}

const tdouble flxCrossSecManual::get_A() const
{
  if (A == NULL) return 0.0;
  return check_positiv(A->get_fun()->calc(), "Cross-sectional area");
}

const tdouble flxCrossSecManual::get_Iy() const
{
  if (Iy == NULL) return 0.0;
  return check_positiv(Iy->get_fun()->calc(), "Moment of Inertia (Iy)");
}

const tdouble flxCrossSecManual::get_Iz() const
{
  if (Iz == NULL) return 0.0;
  return check_positiv(Iz->get_fun()->calc(), "Moment of Inertia (Iz)");
}

const tdouble flxCrossSecManual::get_It() const
{
  if (It == NULL) return 0.0;
  return check_positiv(It->get_fun()->calc(), "Torsional moment (It)");
}

const tuint flxCrossSecManual::get_deg_Iy() const
{
  if (Iy == NULL) return 0;
  return Iy->get_deg();
}

const tuint flxCrossSecManual::get_deg_Iz() const
{
  if (Iz == NULL) return 0;
  return Iz->get_deg();
}

const tuint flxCrossSecManual::get_deg_It() const
{
  if (It == NULL) return 0;
  return It->get_deg();
}

const tuint flxCrossSecManual::get_deg_A() const
{
  if (A == NULL) return 0;
  return A->get_deg();
}

void flxCrossSecManual::log_info(std::ostream& lout) const
{
  lout << "  manual: ";
  if (A!=NULL) {
    lout << "A=" << A->write() << "; ";
  } 
  if (Iy!=NULL) {
    lout << "Iy=" << Iy->write() << "; ";
  }
  if (Iz!=NULL) {
    lout << "Iz=" << Iz->write() << "; ";
  }
  if (It!=NULL) {
    lout << "It=" << It->write() << "; ";
  }
  lout << std::endl;
}

const bool flxCrossSecManual::depend_A_on_Const(const tdouble*const thenumber)
{
  if (A==NULL) return false;
  return A->get_fun()->dependOn_Const(thenumber);
}

flxCrossSecList::~flxCrossSecList() {
  for (std::map<tuint, flxCrossSec*>::iterator pos = box.begin(); pos != box.end(); ++pos) delete pos->second;
}

void flxCrossSecList::insert(const tuint index, flxCrossSec* cS, const bool errSerious) {
  std::pair<tuint, flxCrossSec*>Element(index, cS);
  if ( ! box.insert(Element).second ) {;
    delete cS;
    std::ostringstream ssV;
    ssV << "Cross section '" << index << "' has already been defined.";
    FlxError(errSerious,"flxCrossSecList::insert_1", ssV.str() );
  }
  GlobalVar.slog(4) << "FEM: defined new crossection (index=" << index << ")" << std::endl;
  cS->log_info(GlobalVar.slog(4));
}

flxCrossSec* flxCrossSecList::get(const tuint index, const bool errSerious) {
  std::map<tuint, flxCrossSec*>::iterator pos = box.find(index);
  if ( pos != box.end() ) {
    return pos->second;
  } else {
    std::ostringstream ssV;
    ssV << "Crosssection '" << index << "' has not been defined.";
    FlxError(errSerious,"flxCrossSecList::get", ssV.str() ); return NULL; // DUMMY-RETURN
  }
}

const std::string FunFEMmaterial_E::write()
{
  throw FlxException_NotImplemented("FunFEMmaterial_E::write");
}

const bool FunFEMmaterial_E::search_circref(FlxFunction* fcr)
{
  throw FlxException_NotImplemented("FunFEMmaterial_E::search_circref");
}

const bool FunFEMmaterial_E::dependOn_Const(const tdouble* const thenumber)
{
  return mat->depend_E_on_Const(thenumber);
}

const std::string FunFEMcrosssec_A::write()
{
  throw FlxException_NotImplemented("FunFEMcrosssec_A::write");
}

const bool FunFEMcrosssec_A::dependOn_Const(const tdouble* const thenumber)
{
  return cs->depend_A_on_Const(thenumber);
}

const bool FunFEMcrosssec_A::search_circref(FlxFunction* fcr)
{
  throw FlxException_NotImplemented("FunFEMcrosssec_A::search_circref");
}





