! SPDX-License-Identifier: AGPL-3.0-or-later
! Copyright 2024-2025 Max-Planck-Institut für Nachhaltige Materialien GmbH
! Copyright 2011-2024 Max-Planck-Institut für Eisenforschung GmbH
!
! DAMASK is free software: you can redistribute it and/or modify
! it under the terms of the GNU Affero General Public License as
! published by the Free Software Foundation, either version 3 of the
! License, or (at your option) any later version.
!
! This program is distributed in the hope that it will be useful,
! but WITHOUT ANY WARRANTY; without even the implied warranty of
! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
! GNU Affero General Public License for more details.
!
! You should have received a copy of the GNU Affero General Public License
! along with this program.  If not, see <https://www.gnu.org/licenses/>.
!--------------------------------------------------------------------------------------------------
!> @author Pratheek Shanthraj, Max-Planck-Institut für Eisenforschung GmbH
!> @brief material subroutine incorporating kinematics resulting from thermal expansion
!> @details to be done
!--------------------------------------------------------------------------------------------------
submodule(phase:eigen) thermalexpansion

  integer, dimension(:), allocatable :: kinematics_thermal_expansion_instance

  type :: tParameters
    type(tPolynomial) :: &
      Alpha_11, &
      Alpha_33
  end type tParameters

  type(tParameters), dimension(:), allocatable :: param

contains


!--------------------------------------------------------------------------------------------------
!> @brief module initialization
!> @details reads in material parameters, allocates arrays, and does sanity checks
!--------------------------------------------------------------------------------------------------
module function thermalexpansion_init(kinematics_length) result(myKinematics)

  integer, intent(in)                  :: kinematics_length
  logical, dimension(:,:), allocatable :: myKinematics

  integer :: ph, k
  character(len=:), allocatable :: refs
  type(tList), pointer :: &
    kinematics
  type(tDict), pointer :: &
    phases, &
    phase, &
    mech


  myKinematics = kinematics_active('thermalexpansion',kinematics_length)
  if (count(myKinematics) == 0) return

  print'(/,1x,a)', '<<<+-  phase:mechanical:eigen:thermalexpansion init  -+>>>'
  print'(/,1x,a,1x,i0)', '# phases:',count(myKinematics); flush(IO_STDOUT)

  phases => config_material%get_dict('phase')
  allocate(param(count(myKinematics)))
  allocate(kinematics_thermal_expansion_instance(size(phases)), source=0)

  do ph = 1, size(phases)
    if (any(myKinematics(:,ph))) kinematics_thermal_expansion_instance(ph) = count(myKinematics(:,1:ph))
    if (count(myKinematics(:,ph)) == 0) cycle

    phase => phases%get_dict(ph)
    mech => phase%get_dict('mechanical')
    kinematics => mech%get_list('eigen')

    do k = 1, size(kinematics)
      if (myKinematics(k,ph)) then
        associate(prm  => param(kinematics_thermal_expansion_instance(ph)))

          print'(/,1x,a,1x,i0,a)', 'phase',ph,': '//phases%key(ph)
          refs = config_listReferences(kinematics%get_dict(k),indent=3)
          if (len(refs) > 0) print'(/,1x,a)', refs

          prm%Alpha_11 = polynomial(kinematics%get_dict(k),'Alpha_11','T')
          if (any(phase_lattice(ph) == ['hP','tI'])) &
            prm%Alpha_33 = polynomial(kinematics%get_dict(k),'Alpha_33','T')
        end associate
      end if
    end do
  end do

end function thermalexpansion_init


!--------------------------------------------------------------------------------------------------
!> @brief constitutive equation for calculating the velocity gradient
!--------------------------------------------------------------------------------------------------
module subroutine thermalexpansion_LiAndItsTangent(Li, dLi_dTstar, ph,me)

  integer, intent(in) :: ph, me
  real(pREAL),   intent(out), dimension(3,3) :: &
    Li                                                                                              !< thermal velocity gradient
  real(pREAL),   intent(out), dimension(3,3,3,3) :: &
    dLi_dTstar                                                                                      !< derivative of Li with respect to Tstar (4th-order tensor defined to be zero)

  real(pREAL) :: T, dot_T
  real(pREAL), dimension(3,3) :: Alpha


  T     = thermal_T(ph,me)
  dot_T = thermal_dot_T(ph,me)

  associate(prm => param(kinematics_thermal_expansion_instance(ph)))

    Alpha = 0.0_pREAL
    Alpha(1,1) = prm%Alpha_11%at(T)
    if (any(phase_lattice(ph) == ['hP','tI'])) Alpha(3,3) = prm%Alpha_33%at(T)
    Alpha = crystal_symmetrize_33(Alpha,phase_lattice(ph))
    Li = dot_T * Alpha

  end associate
  dLi_dTstar = 0.0_pREAL

end subroutine thermalexpansion_LiAndItsTangent

end submodule thermalexpansion
