// Copyright (c) 2010-2023, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.

#include "mortarintegrator.hpp"

namespace mfem
{

void L2MortarIntegrator::AssembleElementMatrix(
   const FiniteElement &trial, const IntegrationRule &trial_ir,
   ElementTransformation &trial_Trans, const FiniteElement &test,
   const IntegrationRule &test_ir, ElementTransformation &test_Trans,
   DenseMatrix &elmat)
{
   int tr_nd = trial.GetDof();
   int te_nd = test.GetDof();
   double w;

   Vector shape, te_shape;

   elmat.SetSize(te_nd, tr_nd);
   shape.SetSize(tr_nd);
   te_shape.SetSize(te_nd);

   elmat = 0.0;
   for (int i = 0; i < test_ir.GetNPoints(); i++)
   {
      const IntegrationPoint &trial_ip = trial_ir.IntPoint(i);
      const IntegrationPoint &test_ip = test_ir.IntPoint(i);
      test_Trans.SetIntPoint(&test_ip);

      trial.CalcShape(trial_ip, shape);
      test.CalcShape(test_ip, te_shape);

      w = test_Trans.Weight() * test_ip.weight;

      te_shape *= w;
      AddMultVWt(te_shape, shape, elmat);
   }
}

void VectorL2MortarIntegrator::AssembleElementMatrix(
   const FiniteElement &trial, const IntegrationRule &trial_ir,
   ElementTransformation &trial_Trans, const FiniteElement &test,
   const IntegrationRule &test_ir, ElementTransformation &test_Trans,
   DenseMatrix &elmat)
{
   if (test.GetRangeType() == FiniteElement::SCALAR && VQ)
   {
      // assume test is scalar FE and trial is vector FE
      int dim = test.GetDim();
      int trial_dof = trial.GetDof();
      int test_dof = test.GetDof();
      double w;

      if (MQ)
         mfem_error("VectorFEMassIntegrator::AssembleElementMatrix2(...)\n"
                    "   is not implemented for tensor materials");

#ifdef MFEM_THREAD_SAFE
      DenseMatrix trial_vshape(trial_dof, dim);
      Vector shape(test_dof);
      Vector D(dim);
#else
      trial_vshape.SetSize(trial_dof, dim);
      shape.SetSize(test_dof);
      D.SetSize(dim);
#endif

      elmat.SetSize(test_dof, trial_dof);

      elmat = 0.0;
      for (int i = 0; i < test_ir.GetNPoints(); i++)
      {
         const IntegrationPoint &trial_ip = trial_ir.IntPoint(i);
         const IntegrationPoint &test_ip = test_ir.IntPoint(i);

         trial_Trans.SetIntPoint(&trial_ip);
         test_Trans.SetIntPoint(&test_ip);

         trial.CalcVShape(trial_Trans, trial_vshape);
         test.CalcShape(test_ip, shape);

         w = test_ip.weight * test_Trans.Weight();
         VQ->Eval(D, test_Trans, test_ip);
         D *= w;

         for (int d = 0; d < dim; d++)
         {
            for (int j = 0; j < test_dof; j++)
            {
               for (int k = 0; k < trial_dof; k++)
               {
                  elmat(j, k) += D[d] * shape(j) * trial_vshape(k, d);
               }
            }
         }
      }
   }
   else if (test.GetRangeType() == FiniteElement::SCALAR)
   {
      // assume test is scalar FE and trial is vector FE
      int dim = test.GetDim();
      int trial_dof = trial.GetDof();
      int test_dof = test.GetDof();
      double w;

      if (VQ || MQ)
         mfem_error("VectorFEMassIntegrator::AssembleElementMatrix2(...)\n"
                    "   is not implemented for vector/tensor permeability");

#ifdef MFEM_THREAD_SAFE
      DenseMatrix trial_vshape(trial_dof, dim);
      Vector shape(test_dof);
#else
      trial_vshape.SetSize(trial_dof, dim);
      shape.SetSize(test_dof);
#endif

      elmat.SetSize(dim * test_dof, trial_dof);

      elmat = 0.0;
      for (int i = 0; i < test_ir.GetNPoints(); i++)
      {
         const IntegrationPoint &trial_ip = trial_ir.IntPoint(i);
         const IntegrationPoint &test_ip = test_ir.IntPoint(i);

         trial_Trans.SetIntPoint(&trial_ip);
         test_Trans.SetIntPoint(&test_ip);

         trial.CalcVShape(trial_Trans, trial_vshape);
         test.CalcShape(test_ip, shape);

         w = test_ip.weight * test_Trans.Weight();

         if (Q)
         {
            w *= Q->Eval(test_Trans, test_ip);
         }

         for (int d = 0; d < dim; d++)
         {
            for (int j = 0; j < test_dof; j++)
            {
               for (int k = 0; k < trial_dof; k++)
               {
                  elmat(d * test_dof + j, k) += w * shape(j) * trial_vshape(k, d);
               }
            }
         }
      }
   }
   else
   {
      // assume both test and trial are vector FE
      int dim = test.GetDim();
      int trial_dof = trial.GetDof();
      int test_dof = test.GetDof();
      double w;

      if (VQ || MQ)
         mfem_error("VectorFEMassIntegrator::AssembleElementMatrix2(...)\n"
                    "   is not implemented for vector/tensor permeability");

#ifdef MFEM_THREAD_SAFE
      DenseMatrix trial_vshape(trial_dof, dim);
      DenseMatrix test_vshape(test_dof, dim);
#else
      trial_vshape.SetSize(trial_dof, dim);
      test_vshape.SetSize(test_dof, dim);
#endif

      elmat.SetSize(test_dof, trial_dof);

      elmat = 0.0;
      for (int i = 0; i < test_ir.GetNPoints(); i++)
      {
         const IntegrationPoint &trial_ip = trial_ir.IntPoint(i);
         const IntegrationPoint &test_ip = test_ir.IntPoint(i);

         trial_Trans.SetIntPoint(&trial_ip);
         test_Trans.SetIntPoint(&test_ip);

         trial.CalcVShape(trial_Trans, trial_vshape);
         test.CalcVShape(test_Trans, test_vshape);

         w = test_ip.weight * test_Trans.Weight();
         if (Q)
         {
            w *= Q->Eval(test_Trans, test_ip);
         }

         for (int d = 0; d < dim; d++)
         {
            for (int j = 0; j < test_dof; j++)
            {
               for (int k = 0; k < trial_dof; k++)
               {
                  elmat(j, k) += w * test_vshape(j, d) * trial_vshape(k, d);
               }
            }
         }
      }
   }
}

} // namespace mfem
