/*!
        @file    action_G_Rectangle.cpp

        @brief

        @author  Hideo Matsufuru (matsufuru)
                 $LastChangedBy: matufuru $

        @date    $LastChangedDate:: 2025-09-02 15:10:15 #$

        @version $LastChangedRevision: 2654 $
*/

#include "lib/Action/Gauge/action_G_Rectangle.h"
#include "lib/Tools/timer.h"

#ifdef USE_FACTORY_AUTOREGISTER
namespace {
  bool init = Action_G_Rectangle::register_factory();
}
#endif

const std::string Action_G_Rectangle::class_name = "Action_G_Rectangle";

//====================================================================
void Action_G_Rectangle::set_parameters(const Parameters& params)
{
  std::string vlevel;
  if (!params.fetch_string("verbose_level", vlevel)) {
    m_vl = vout.set_verbose_level(vlevel);
  }

  //- fetch and check input parameters
  double beta, c_plaq, c_rect;

  int err = 0;
  err += params.fetch_double("beta", beta);
  err += params.fetch_double("c_plaq", c_plaq);
  err += params.fetch_double("c_rect", c_rect);

  if (err) {
    vout.crucial(m_vl, "Error at %s: input parameter not found.\n", class_name.c_str());
    exit(EXIT_FAILURE);
  }


  set_parameters(beta, c_plaq, c_rect);

  //- post-process
  m_force_G->set_parameters(params);
}


//====================================================================
void Action_G_Rectangle::get_parameters(Parameters& params) const
{
  params.set_double("beta", m_beta);
  params.set_double("c_plaq", m_c_plaq);
  params.set_double("c_rect", m_c_rect);

  params.set_string("verbose_level", vout.get_verbose_level(m_vl));
}


//====================================================================
void Action_G_Rectangle::set_parameters(const double beta,
                                        const double c_plaq, const double c_rect)
{
  //- print input parameters
  vout.general(m_vl, "%s:\n", class_name.c_str());
  vout.general(m_vl, "  beta   = %12.6f\n", beta);
  vout.general(m_vl, "  c_plaq = %12.6f\n", c_plaq);
  vout.general(m_vl, "  c_rect = %12.6f\n", c_rect);

  //- range check
  // NB. beta,c_plaq,c_rect == 0 is allowed.

  //- store values
  m_beta   = beta;
  m_c_plaq = c_plaq;
  m_c_rect = c_rect;

  //- post-process
}


//====================================================================
double Action_G_Rectangle::langevin(RandomNumbers *rand)
{
  const double H_U = calcH();

  return H_U;
}


//====================================================================
double Action_G_Rectangle::calcH()
{
  const int Nc    = CommonParameters::Nc();
  const int Ndim  = CommonParameters::Ndim();
  const int Ndim2 = Ndim * (Ndim - 1) / 2;

  const int Nvol = CommonParameters::Nvol();
  const int NPE  = CommonParameters::NPE();

  const double eps = CommonParameters::epsilon_criterion();

  vout.general(m_vl, "%s %s: Hamiltonian\n",
               class_name.c_str(), m_label.c_str());
  vout.increase_indent();

  Timer timer;
  timer.start();

  double plaqF = 0.0;
  double rectF = 0.0;

  for (int mu = 0; mu < Ndim; ++mu) {
    for (int nu = mu + 1; nu < Ndim; ++nu) {
      Field_G Cup1;
      m_staple.upper(Cup1, *m_U, mu, nu);

      //- plaquette term
      for (int site = 0; site < Nvol; ++site) {
        plaqF += ReTr(m_U->mat(site, mu) * Cup1.mat_dag(site));
      }

      //- rectangular terms
      // NB. skip this part, if m_c_rect = 0.0
      if (fabs(m_c_rect) > eps) {
        Field_G Cup2;
        m_staple.upper(Cup2, *m_U, nu, mu);

        //      +---+---+
        //      |       |   term
        //      x   <---+

        Field_G Umu;
        copy(Umu, 0, *m_U, mu);

        Field_G Unu;
        copy(Unu, 0, *m_U, nu);

        Field_G v;
        m_shift.backward(v, Cup2, mu);

        Field_G c;
        m_shift.backward(c, Umu, nu);

        Field_G w;
        mult_Field_Gnd(w, 0, c, 0, v, 0);

        mult_Field_Gnn(c, 0, Unu, 0, w, 0);

        for (int site = 0; site < Nvol; ++site) {
          rectF += ReTr(m_U->mat(site, mu) * c.mat_dag(site));
        }

        //      +---+
        //      |   |
        //      +   +   term
        //      |   |
        //      x   v

        m_shift.backward(v, Unu, mu);
        m_shift.backward(c, Cup1, nu);

        mult_Field_Gnd(w, 0, c, 0, v, 0);
        mult_Field_Gnn(c, 0, Unu, 0, w, 0);
        for (int site = 0; site < Nvol; ++site) {
          rectF += ReTr(m_U->mat(site, mu) * c.mat_dag(site));
        }
      }
    }
  }

  plaqF = Communicator::reduce_sum(plaqF);
  rectF = Communicator::reduce_sum(rectF);

  const double plaq = plaqF / Nc;
  vout.general(m_vl, "Plaquette    = %18.8f\n", plaq / Nvol / NPE / Ndim2);

  double H_U = m_c_plaq * Ndim2 * Nvol * NPE - m_c_plaq * plaqF / Nc
               + m_c_rect * Ndim2 * Nvol * NPE * 2 - m_c_rect * rectF / Nc;

  H_U = m_beta * H_U;

  vout.general(m_vl, "H_Grectangle = %18.8f\n", H_U);
  vout.general(m_vl, "H_G/dof      = %18.8f\n", H_U / Nvol / NPE / Ndim2);

  timer.stop();
  double elapsed_time = timer.elapsed_sec();
  vout.general(m_vl, "Elapsed time = %14.6f sec\n", elapsed_time);

  vout.decrease_indent();

  return H_U;
}


//====================================================================
void Action_G_Rectangle::force(Field& force)
{
  //- check of argument type
  assert(force.nin() == m_U->nin());
  assert(force.nvol() == m_U->nvol());
  assert(force.nex() == m_U->nex());

  vout.general(m_vl, "%s %s: force\n",
               class_name.c_str(), m_label.c_str());
  vout.increase_indent();

  Timer timer;
  timer.start();

  force.set(0.0);

  m_force_G->force_core(force, m_U);

  //double Fave, Fmax, Fdev;
  //force.stat(Fave, Fmax, Fdev);
  //vout.general(m_vl, "Fave = %12.6f  Fmax = %12.6f  Fdev = %12.6f\n",
  //             Fave, Fmax, Fdev);

  timer.stop();
  double elapsed_time = timer.elapsed_sec();
  vout.general(m_vl, "Elapsed time = %14.6f sec\n", elapsed_time);

  vout.decrease_indent();

}

//============================================================END=====
