// $Id: GibbsMinimization.h 1364 2024-06-13 20:31:00Z ge $
/// \file GibbsMinimization.h
/// \brief contains the GibbsMinimization function class
///
/// This is a plugin class for the ParameterSearch function
/// $Revision: 1364 $
/// \author Gerald Weber <gweberbh@gmail.com>
#ifndef GBC_GIBBSMINIMIZATION_H
#define GBC_GIBBSMINIMIZATION_H "$Id: GibbsMinimization.h 1364 2024-06-13 20:31:00Z ge $"
#include "ErrorCodes.h"
#include "ModelDataSet.h"

namespace gbc
{
template<class _NNModel>
/// \brief This class requires an xml file of data to work
class GibbsMinimization
  {
  public:
    typedef _NNModel                                  model_type;
    typedef typename model_type::value_type           value_type;
    typedef std::valarray<value_type>                 vector_type;
    typedef ModelDataSet<model_type>               modeldataset_type;
    enum merit_enum {Tm, DeltaHS, TmDeltaHS, RTmDeltaHS, DeltaG_X_Tm};

    modeldataset_type Regression;//Same name used in ParameterSearch and var-par
    model_type      Calculation;
    value_type      chi2,dgtm_chi2;
    value_type      DeltaG_X_Tm_chi2_threshold; //minimal threshold after which we add this to chi2, default=0
    merit_enum Merit;

    GibbsMinimization(void): Regression(), Calculation(), DeltaG_X_Tm_chi2_threshold(), Merit(Tm)
      {
      Regression.model=&Calculation;
      };
      
    inline  void merit_tm(void) {Merit=Tm;}
    inline  void merit_deltahs(void) {Merit=DeltaHS;}
    inline  void merit_tmdeltahs(void) {Merit=TmDeltaHS;}
    inline  void merit_rtmdeltahs(void) {Merit=RTmDeltaHS;}
    inline  void merit_deltag_x_tm(void) {Merit=DeltaG_X_Tm;}
    
    inline void print_merit_text(std::ostream& out, std::string prefix=std::string())
      {
      out << prefix;
      switch (Merit)
        {
        case Tm       : out << "Merit function is (T-T')^2"; break;
        case DeltaHS  : out << "Merit function is (DeltaH-DeltaH')^2 + (DeltaS-DeltaS')^2"; break;
        case TmDeltaHS: out << "Merit function is (T-T')^2 + (DeltaH-DeltaH')^2 + (DeltaS-DeltaS')^2"; break;
        case DeltaG_X_Tm: out << "Merit function is (T-T')^2 + sqr2sum of DeltaG x Tm linear regression"; break;
        }
      out << std::endl;
      }

   inline std::string identification(void) {return std::string("Gibbs Free energy");}

   inline value_type evaluate_from_variable_map(void)
     {
     Regression.predict_all();
     Regression.dataset.calculate_tm_difference_entire_set();
     chi2=value_type();
     dgtm_chi2=value_type();
     switch (Merit)
       {
       case DeltaG_X_Tm : dgtm_chi2 = Regression.dataset.calculate_deltag_tm_regression();
                          if (dgtm_chi2 > DeltaG_X_Tm_chi2_threshold ) chi2 = dgtm_chi2;
                          
       case Tm       : chi2 += Regression.dataset.Temperature.sqr_diff; break;
       
       case DeltaHS  : chi2 = Regression.dataset.Enthalpy.sqr_diff+Regression.dataset.Entropy.sqr_diff; break;
       
       case TmDeltaHS: chi2 = Regression.dataset.Temperature.sqr_diff+Regression.dataset.Enthalpy.sqr_diff+Regression.dataset.Entropy.sqr_diff; break;
       
       case RTmDeltaHS: chi2 = Regression.dataset.Temperature.relative_sqr_diff+Regression.dataset.Enthalpy.relative_sqr_diff+Regression.dataset.Entropy.relative_sqr_diff; break;
       }
     CERR_DEBUG(DGBM_CHI2) << " chi2 = " << chi2 << " " << std::endl;
     return chi2;
     }

   inline void print_verification(std::ostream& out)
     {
     if (Merit == DeltaG_X_Tm)
       {
       out << "dgtm_chi2=" << dgtm_chi2 << std::endl;
       }
     Regression.verify(out);
     Regression.dataset.print_temperatures(out);
     print_merit_text(out);
     }

   inline void print_regression(std::ostream& out)
     {}

   inline void print_tex(std::ostream& out)
     {
     Regression.dataset.difference();
     Regression.print_tex(out);
     }

   inline void randomise_data(void)
     {
     Regression.dataset.randomise_dataset();
     }


   };
};
#endif
