// Copyright (c) 2010-2022, 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.

#ifndef MFEM_VOLTA_SOLVER
#define MFEM_VOLTA_SOLVER

#include "../common/pfem_extras.hpp"
#include "../common/mesh_extras.hpp"
#include "electromagnetics.hpp"

#ifdef MFEM_USE_MPI

#include <string>
#include <map>

namespace mfem
{

using common::H1_ParFESpace;
using common::ND_ParFESpace;
using common::RT_ParFESpace;
using common::L2_ParFESpace;
using common::ParDiscreteGradOperator;
using common::ParDiscreteDivOperator;

namespace electromagnetics
{

class VoltaSolver
{
public:
   VoltaSolver(ParMesh & pmesh, int order,
               Array<int> & dbcs, Vector & dbcv,
               Array<int> & nbcs, Vector & nbcv,
               Coefficient & epsCoef,
               double (*phi_bc )(const Vector&),
               double (*rho_src)(const Vector&),
               void   (*p_src  )(const Vector&, Vector&),
               Vector & point_charges);
   ~VoltaSolver();

   HYPRE_BigInt GetProblemSize();

   void PrintSizes();

   void Assemble();

   void Update();

   void Solve();

   void GetErrorEstimates(Vector & errors);

   void RegisterVisItFields(VisItDataCollection & visit_dc);

   void WriteVisItFields(int it = 0);

   void InitializeGLVis();

   void DisplayToGLVis();

   const ParGridFunction & GetVectorPotential() { return *phi_; }

private:

   int myid_;      // Local processor rank
   int num_procs_; // Number of processors
   int order_;     // Basis function order

   ParMesh * pmesh_;

   Array<int> * dbcs_; // Dirichlet BC Surface Attribute IDs
   Vector     * dbcv_; // Corresponding Dirichlet Values
   Array<int> * nbcs_; // Neumann BC Surface Attribute IDs
   Vector     * nbcv_; // Corresponding Neumann Values

   VisItDataCollection * visit_dc_; // To prepare fields for VisIt viewing

   H1_ParFESpace * H1FESpace_;    // Continuous space for phi
   ND_ParFESpace * HCurlFESpace_; // Tangentially continuous space for E
   RT_ParFESpace * HDivFESpace_;  // Normally continuous space for D
   L2_ParFESpace * L2FESpace_;    // Discontinuous space for rho

   ParBilinearForm * divEpsGrad_; // Laplacian operator
   ParBilinearForm * h1Mass_;     // For Volumetric Charge Density Source
   ParBilinearForm * h1SurfMass_; // For Surface Charge Density Source
   ParBilinearForm * hDivMass_;   // For Computing D from E

   ParMixedBilinearForm * hCurlHDivEps_; // For computing D from E
   ParMixedBilinearForm * hCurlHDiv_;    // For computing D from E and P
   ParMixedBilinearForm * weakDiv_;      // For computing the source term from P

   ParLinearForm * rhod_; // Dual of Volumetric Charge Density Source

   ParLinearForm * l2_vol_int_;  // Integral of L2 field
   ParLinearForm * rt_surf_int_; // Integral of H(Div) field over boundary

   ParDiscreteGradOperator * grad_; // For Computing E from phi
   ParDiscreteDivOperator  * div_;  // For Computing rho from D

   ParGridFunction * phi_;       // Electric Scalar Potential
   ParGridFunction * rho_src_;   // Volumetric Charge Density Source
   ParGridFunction * rho_;       // Volumetric Charge Density (Div(D))
   ParGridFunction * sigma_src_; // Surface Charge Density Source
   ParGridFunction * e_;         // Electric Field
   ParGridFunction * d_;         // Electric Flux Density (aka Dielectric Flux)
   ParGridFunction * p_src_;     // Polarization Field Source

   ConstantCoefficient oneCoef_;   // Coefficient equal to 1
   Coefficient       * epsCoef_;   // Dielectric Permittivity Coefficient
   Coefficient       * phiBCCoef_; // Scalar Potential Boundary Condition
   Coefficient       * rhoCoef_;   // Charge Density Coefficient
   VectorCoefficient * pCoef_;     // Polarization Vector Field Coefficient

   // Source functions
   double (*phi_bc_func_ )(const Vector&);          // Scalar Potential BC
   double (*rho_src_func_)(const Vector&);          // Volumetric Charge Density
   void   (*p_src_func_  )(const Vector&, Vector&); // Polarization Field

   const Vector & point_charge_params_;

   std::vector<DeltaCoefficient*> point_charges_;

   std::map<std::string,socketstream*> socks_; // Visualization sockets

   Array<int> ess_bdr_, ess_bdr_tdofs_; // Essential Boundary Condition DoFs
};

} // namespace electromagnetics

} // namespace mfem

#endif // MFEM_USE_MPI

#endif // MFEM_VOLTA_SOLVER
