module m_MEF90_HookesLawIsotropic2D
#include "petsc/finclude/petsc.h"
   use m_MEF90_Parameters
   use m_MEF90_Utils
   use m_MEF90_LinAlg
   use m_MEF90_HookesLaw_Class
   use iso_c_binding

   implicit none(type)
   private
   public :: MEF90HookesLawIsotropic2D

   type, extends(MEF90HookesLaw) :: MEF90HookesLawIsotropic2D
      PetscReal :: YoungsModulus = 1.0_Kr
      PetscReal :: PoissonRatio = 0.3_Kr
      PetscReal :: lambda = 0.0_Kr
      PetscReal :: mu = 0.0_Kr
      PetscReal :: BulkModulus = 0.0_Kr
      PetscBool :: isPlaneStress = PETSC_FALSE
   contains
         procedure :: setFromOptions => MEF90HookesLawIsotropic2D_setFromOptions
         procedure :: view_internal => MEF90HookesLawIsotropic2D_view
         procedure :: mult => MEF90HookesLawIsotropic2D_mult
         procedure :: multmult => MEF90HookesLawIsotropic2D_multmult
   end type MEF90HookesLawIsotropic2D

contains
#undef __FUNCT__
#define __FUNCT__ "MEF90HookesLawIsotropic2D_setFromOptions"
!!! author: Blaise Bourdin (2025, bourdin@mcmaster.ca)
!!!
!!!  MEF90HookesLawIsotropic2D_setFromOptions: initializes a MEF90HookesLawIsotropic2D from options
!!!

   subroutine MEF90HookesLawIsotropic2D_setFromOptions(self, ierr)
      class(MEF90HookesLawIsotropic2D), intent(inout) :: self
      PetscErrorCode,intent(inout)                    :: ierr

      PetscInt                                        :: verbose = 0
      PetscViewer                                     :: stdoutViewer

      self%name = trim(self%prefix) // "HookesLaw_Isotropic"
      PetscCall(PetscOptionsBegin(self%comm, trim(self%name) // "_", "Options for MEF90HookesLawIsotropic2D_Type", "mef90HookesLaw", ierr))
         PetscCall(PetscOptionsReal('-YoungsModulus', 'Young''s modulus (E)', '[Pa]', self%YoungsModulus, self%YoungsModulus, PETSC_NULL_BOOL, ierr))
         PetscCall(PetscOptionsReal('-PoissonRatio', 'Poisson ratio (\nu))', '[]', self%PoissonRatio, self%PoissonRatio, PETSC_NULL_BOOL, ierr))
         PetscCall(PetscOptionsBool('-planeStress', '2D plane stress', '', PETSC_FALSE, self%isPlaneStress, PETSC_NULL_BOOL, ierr))
      PetscCall(PetscOptionsEnd(ierr))
      if (self%isPlaneStress) then
         self%lambda = self%YoungsModulus * self%PoissonRatio / (1.0_kr - self%PoissonRatio**2)
         self%mu = self%YoungsModulus / (1.0_kr + self%PoissonRatio)*.5_kr
         self%BulkModulus = self%lambda + 2.0_kr * self%mu / 3.0_kr
      else
         self%lambda = self%YoungsModulus * self%PoissonRatio / (1.0_kr + self%PoissonRatio) / (1.0_kr - 2.0_kr * self%PoissonRatio)
         self%mu = self%YoungsModulus / (1.0_kr + self%PoissonRatio)*.5_kr
         self%BulkModulus = self%lambda + self%mu
      end if

      PetscCall(PetscOptionsGetInt(PETSC_NULL_OPTIONS, PETSC_NULL_CHARACTER, "-verbose", verbose, PETSC_NULL_BOOL, ierr))
      if (verbose > 0) then
         PetscCall(PetscViewerASCIIGetStdout(self%comm, stdoutViewer, ierr))
         call self%view(stdoutViewer, ierr)
      end if
   end subroutine MEF90HookesLawIsotropic2D_setFromOptions

#undef __FUNCT__
#define __FUNCT__ "MEF90HookesLawIsotropic2D_view"
!!! author: Blaise Bourdin (2025, bourdin@mcmaster.ca)
!!!
!!!  MEF90HookesLawIsotropic2D_view: the default viewer for a MEF90_DefMechAT_Type
!!!

subroutine MEF90HookesLawIsotropic2D_View(self, viewer, ierr)
      class(MEF90HookesLawIsotropic2D), intent(in) :: self
      type(tPetscViewer), intent(in)               :: viewer
      PetscErrorCode, intent(inout)                :: ierr

      character(len=MEF90MXSTRLEN, kind=c_char)    :: IOBuffer
      character(len=MEF90MXSTRLEN, kind=c_char)    :: viewerType

      PetscCall(PetscViewerGetType(viewer, viewerType, ierr))
      if (viewerType == 'ascii') then
         write(IOBuffer, "(A,': Options for MEF90HookesLaw\n')") trim(self%prefix)//"HookesLaw"
         PetscCall(PetscViewerASCIIPrintf(viewer, IOBuffer, ierr))
         write(IOBuffer, "('         Type: Isotropic\n')")
         PetscCall(PetscViewerASCIIPrintf(viewer, IOBuffer, ierr))
         write(IOBuffer, "('         Youngs modulus (E): ',ES12.5,' [Pa]\n')") self%YoungsModulus
         PetscCall(PetscViewerASCIIPrintf(viewer, IOBuffer, ierr))
         write(IOBuffer, "('         Poisson ratio (nu): ',ES12.5,' []\n')") self%PoissonRatio
         PetscCall(PetscViewerASCIIPrintf(viewer, IOBuffer, ierr))
         write(IOBuffer, "('         Bulk modulus (\kappa): ',ES12.5,' [Pa]\n')") self%BulkModulus
         PetscCall(PetscViewerASCIIPrintf(viewer, IOBuffer, ierr))
         write(IOBuffer, "('         Lame coefficient (\lambda): ',ES12.5,' [Pa]\n')") self%lambda
         PetscCall(PetscViewerASCIIPrintf(viewer, IOBuffer, ierr))
         write(IOBuffer, "('         Shear modulus (\mu): ',ES12.5,' [Pa]\n')") self%mu
         PetscCall(PetscViewerASCIIPrintf(viewer, IOBuffer, ierr))
         write(IOBuffer, "('         plane stress (): ',L1,' [bool]\n')") self%isPlaneStress
         PetscCall(PetscViewerASCIIPrintf(viewer, IOBuffer, ierr))
      end if
   end subroutine MEF90HookesLawIsotropic2D_View

   subroutine MEF90HookesLawIsotropic2D_mult(A, phi, Aphi, ierr)
      class(MEF90HookesLawIsotropic2D), intent(in) :: A
      class(mef90Mat), intent(in)                  :: phi
      class(mef90Mat), allocatable, intent(out)    :: Aphi
      character(len=MEF90MXSTRLEN, kind=c_char)    :: IOBuffer
      PetscErrorCode, intent(inout)                :: ierr

      select type (phi2D => phi)
         type is (MatS2D)
            Aphi = A%lambda * trace(phi2D) * MEF90MatS2DIdentity + 2.0_Kr * A%mu * phi2D
         class default
            write (IOBuffer, *) "Incompatible arguments in "//__FUNCT__//'\n'
            PetscCall(PetscPrintf(PETSC_COMM_SELF, IOBuffer, ierr))
            SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_SUP, IOBuffer)
      end select ! phi
   end subroutine MEF90HookesLawIsotropic2D_mult

   subroutine MEF90HookesLawIsotropic2D_multmult(A, phi, psi, Aphipsi, ierr)
      class(MEF90HookesLawIsotropic2D), intent(in) :: A
      class(mef90Mat), intent(in)                  :: phi, psi
      PetscReal, intent(out)                       :: Aphipsi
      character(len=MEF90MXSTRLEN, kind=c_char)    :: IOBuffer
      PetscErrorCode, intent(inout)                :: ierr

      !! This is really absurd
      select type (phi2D => phi)
         type is (MatS2D)
         select type (psi2D => psi)
            type is (MatS2D)
               Aphipsi = A%lambda * trace(phi2D) * trace(psi2D) &
                         + 2.0_Kr * A%mu * (phi2D .dotP. psi2D)
            class default
               write (IOBuffer, *) "Incompatible arguments in "//__FUNCT__//'\n'
               PetscCall(PetscPrintf(PETSC_COMM_SELF, IOBuffer, ierr))
               SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_SUP, IOBuffer)
            end select ! psi
         class default
            write (IOBuffer, *) "Incompatible arguments in "//__FUNCT__//'\n'
            PetscCall(PetscPrintf(PETSC_COMM_SELF, IOBuffer, ierr))
            SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_SUP, IOBuffer)
      end select ! phi
   end subroutine MEF90HookesLawIsotropic2D_multmult
end module m_MEF90_HookesLawIsotropic2D

module m_MEF90_HookesLawIsotropic3D
#include "petsc/finclude/petsc.h"
   use m_MEF90_Parameters
   use m_MEF90_Utils
   use m_MEF90_LinAlg
   use m_MEF90_HookesLaw_Class
   use iso_c_binding

   implicit none(type)
   private

   public :: MEF90HookesLawIsotropic3D

   type, extends(MEF90HookesLaw) :: MEF90HookesLawIsotropic3D
      PetscReal :: YoungsModulus = 1.0_Kr
      PetscReal :: PoissonRatio = 0.3_Kr
      PetscReal :: lambda = 0.0_Kr
      PetscReal :: mu = 0.0_Kr
      PetscReal :: BulkModulus = 0.0_Kr
   contains
         procedure, pass(self) :: setFromOptions => MEF90HookesLawIsotropic3D_setFromOptions
         procedure, pass(self) :: view_internal => MEF90HookesLawIsotropic3D_view
         procedure             :: mult => MEF90HookesLawIsotropic3D_mult
         procedure             :: multmult => MEF90HookesLawIsotropic3D_multmult
   end type MEF90HookesLawIsotropic3D

contains
#undef __FUNCT__
#define __FUNCT__ "MEF90HookesLawIsotropic3D_setFromOptions"
!!! author: Blaise Bourdin (2025, bourdin@mcmaster.ca)
!!!
!!!  MEF90HookesLawIsotropic3D_setFromOptions: initializes a MEF90HookesLawIsotropic3D_type from options
!!!

   subroutine MEF90HookesLawIsotropic3D_setFromOptions(self, ierr)
      class(MEF90HookesLawIsotropic3D), intent(inout) :: self
      PetscErrorCode,intent(inout)                    :: ierr

      PetscInt                                        :: verbose = 0
      PetscViewer                                     :: stdoutViewer

      self%name = trim(self%prefix) // "HookesLaw_Isotropic"
      PetscCall(PetscOptionsBegin(self%comm, trim(self%name) // "_", "Options for MEF90HookesLawIsotropic2D_Type", "mef90HookesLaw", ierr))
         PetscCall(PetscOptionsReal('-YoungsModulus', 'Young''s modulus (E)', '[Pa]', self%YoungsModulus, self%YoungsModulus, PETSC_NULL_BOOL, ierr))
         PetscCall(PetscOptionsReal('-PoissonRatio', 'Poisson ratio (\nu))', '[]', self%PoissonRatio, self%PoissonRatio, PETSC_NULL_BOOL, ierr))
      PetscCall(PetscOptionsEnd(ierr))
      self%lambda = self%YoungsModulus * self%PoissonRatio / (1.0_kr + self%PoissonRatio) / (1.0_kr - 2.0_kr * self%PoissonRatio)
      self%mu = self%YoungsModulus / (1.0_kr + self%PoissonRatio)*.5_kr
      self%BulkModulus = self%lambda + 2.0_kr * self%mu / 3.0_kr

      PetscCall(PetscOptionsGetInt(PETSC_NULL_OPTIONS, PETSC_NULL_CHARACTER, "-verbose", verbose, PETSC_NULL_BOOL, ierr))
      if (verbose > 0) then
         PetscCall(PetscViewerASCIIGetStdout(self%comm, stdoutViewer, ierr))
         call self%view(stdoutViewer, ierr)
      end if
   end subroutine MEF90HookesLawIsotropic3D_setFromOptions

#undef __FUNCT__
#define __FUNCT__ "MEF90HookesLawIsotropic3D_view"
!!! author: Blaise Bourdin (2025, bourdin@mcmaster.ca)
!!!
!!!  MEF90HookesLawIsotropic3D_view: the default viewer for a MEF90_DefMechAT_Type
!!!

   subroutine MEF90HookesLawIsotropic3D_View(self, viewer, ierr)
      class(MEF90HookesLawIsotropic3D), intent(in) :: self
      type(tPetscViewer), intent(in)               :: viewer
      PetscErrorCode, intent(inout)                :: ierr

      character(len=MEF90MXSTRLEN, kind=c_char)    :: IOBuffer
      character(len=MEF90MXSTRLEN, kind=c_char)    :: viewerType

      PetscCall(PetscViewerGetType(viewer, viewerType, ierr))
      if (viewerType == 'ascii') then
         write(IOBuffer, "(A,': Options for MEF90HookesLaw\n')") trim(self%prefix)//"HookesLaw"
         PetscCall(PetscViewerASCIIPrintf(viewer, IOBuffer, ierr))
         write(IOBuffer, "('         Type: Isotropic\n')")
         PetscCall(PetscViewerASCIIPrintf(viewer, IOBuffer, ierr))
         write(IOBuffer, "('         Youngs modulus (E): ',ES12.5,' [Pa]\n')") self%YoungsModulus
         PetscCall(PetscViewerASCIIPrintf(viewer, IOBuffer, ierr))
         write(IOBuffer, "('         Poisson ratio (nu): ',ES12.5,' []\n')") self%PoissonRatio
         PetscCall(PetscViewerASCIIPrintf(viewer, IOBuffer, ierr))
         write(IOBuffer, "('         Bulk modulus (\kappa): ',ES12.5,' [Pa]\n')") self%BulkModulus
         PetscCall(PetscViewerASCIIPrintf(viewer, IOBuffer, ierr))
         write(IOBuffer, "('         Lame coefficient (\lambda): ',ES12.5,' [Pa]\n')") self%lambda
         PetscCall(PetscViewerASCIIPrintf(viewer, IOBuffer, ierr))
         write(IOBuffer, "('         Shear modulus (\mu): ',ES12.5,' [Pa]\n')") self%mu
         PetscCall(PetscViewerASCIIPrintf(viewer, IOBuffer, ierr))
      end if
   end subroutine MEF90HookesLawIsotropic3D_View

   subroutine MEF90HookesLawIsotropic3D_mult(A, phi, Aphi, ierr)
      class(MEF90HookesLawIsotropic3D), intent(in) :: A
      class(mef90Mat), intent(in)                  :: phi
      class(mef90Mat), allocatable, intent(out)    :: Aphi
      character(len=MEF90MXSTRLEN, kind=c_char)    :: IOBuffer
      PetscErrorCode, intent(inout)                :: ierr

      select type (phi3D => phi)
         type is (MatS3D)
            Aphi = A%lambda * trace(phi3D) * MEF90MatS3DIdentity + 2.0_Kr * A%mu * phi3D
         class default
            write (IOBuffer, *) "Incompatible arguments in "//__FUNCT__//'\n'
            PetscCall(PetscPrintf(PETSC_COMM_SELF, IOBuffer, ierr))
            SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_SUP, IOBuffer)
      end select ! phi
   end subroutine MEF90HookesLawIsotropic3D_mult

   subroutine MEF90HookesLawIsotropic3D_multmult(A, phi, psi, Aphipsi, ierr)
      class(MEF90HookesLawIsotropic3D), intent(in) :: A
      class(mef90Mat), intent(in)                  :: phi, psi
      PetscReal, intent(out)                       :: Aphipsi
      character(len=MEF90MXSTRLEN, kind=c_char)    :: IOBuffer
      PetscErrorCode, intent(inout)                :: ierr

      !! This is really absurd
      select type (phi3D => phi)
         type is (MatS3D)
         select type (psi3D => psi)
            type is (MatS3D)
               Aphipsi = A%lambda * trace(phi3D) * trace(psi3D) &
                         + 2.0_Kr * A%mu * (phi3D .dotP. psi3D)
            class default
               write (IOBuffer, *) "Incompatible arguments in "//__FUNCT__//'\n'
               PetscCall(PetscPrintf(PETSC_COMM_SELF, IOBuffer, ierr))
               SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_SUP, IOBuffer)
            end select ! psi
         class default
            write (IOBuffer, *) "Incompatible arguments in "//__FUNCT__//'\n'
            PetscCall(PetscPrintf(PETSC_COMM_SELF, IOBuffer, ierr))
            SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_SUP, IOBuffer)
      end select ! phi
   end subroutine MEF90HookesLawIsotropic3D_multmult
end module m_MEF90_HookesLawIsotropic3D
