element_square_second.F90 Source File


This file depends on

sourcefile~~element_square_second.f90~~EfferentGraph sourcefile~element_square_second.f90 element_square_second.F90 sourcefile~element_interface.f90 element_interface.F90 sourcefile~element_square_second.f90->sourcefile~element_interface.f90 sourcefile~core.f90 core.F90 sourcefile~element_interface.f90->sourcefile~core.f90 sourcefile~input.f90 input.F90 sourcefile~element_interface.f90->sourcefile~input.f90 sourcefile~mesh_interface.f90 mesh_interface.F90 sourcefile~element_interface.f90->sourcefile~mesh_interface.f90 sourcefile~allocate.f90 allocate.F90 sourcefile~core.f90->sourcefile~allocate.f90 sourcefile~check_range.f90 check_range.F90 sourcefile~core.f90->sourcefile~check_range.f90 sourcefile~deallocate.f90 deallocate.F90 sourcefile~core.f90->sourcefile~deallocate.f90 sourcefile~error.f90 error.F90 sourcefile~core.f90->sourcefile~error.f90 sourcefile~fortran_utils.f90 fortran_utils.F90 sourcefile~core.f90->sourcefile~fortran_utils.f90 sourcefile~string_utils.f90 string_utils.F90 sourcefile~core.f90->sourcefile~string_utils.f90 sourcefile~types.f90 types.F90 sourcefile~core.f90->sourcefile~types.f90 sourcefile~unique.f90 unique.F90 sourcefile~core.f90->sourcefile~unique.f90 sourcefile~vtk.f90 vtk.F90 sourcefile~core.f90->sourcefile~vtk.f90 sourcefile~vtk_constants.f90 vtk_constants.F90 sourcefile~core.f90->sourcefile~vtk_constants.f90 sourcefile~input_interface.f90 input_interface.F90 sourcefile~input.f90->sourcefile~input_interface.f90 sourcefile~mesh_interface.f90->sourcefile~core.f90 sourcefile~allocate.f90->sourcefile~error.f90 sourcefile~deallocate.f90->sourcefile~error.f90 sourcefile~memory_stats_wrapper.f90 memory_stats_wrapper.F90 sourcefile~fortran_utils.f90->sourcefile~memory_stats_wrapper.f90 sourcefile~signal_flag_wrapper.f90 signal_flag_wrapper.F90 sourcefile~fortran_utils.f90->sourcefile~signal_flag_wrapper.f90 sourcefile~system_info_wrapper.f90 system_info_wrapper.F90 sourcefile~fortran_utils.f90->sourcefile~system_info_wrapper.f90 sourcefile~input_interface.f90->sourcefile~core.f90 sourcefile~project_settings.f90 project_settings.F90 sourcefile~input_interface.f90->sourcefile~project_settings.f90 sourcefile~string_utils.f90->sourcefile~allocate.f90 sourcefile~array.f90 array.F90 sourcefile~types.f90->sourcefile~array.f90 sourcefile~gauss.f90 gauss.F90 sourcefile~types.f90->sourcefile~gauss.f90 sourcefile~matrix.f90 matrix.F90 sourcefile~types.f90->sourcefile~matrix.f90 sourcefile~pointer.f90 pointer.F90 sourcefile~types.f90->sourcefile~pointer.f90 sourcefile~variable.f90 variable.F90 sourcefile~types.f90->sourcefile~variable.f90 sourcefile~vector.f90 vector.F90 sourcefile~types.f90->sourcefile~vector.f90 sourcefile~unique.f90->sourcefile~allocate.f90 sourcefile~vtk.f90->sourcefile~allocate.f90 sourcefile~vtk.f90->sourcefile~deallocate.f90 sourcefile~vtk.f90->sourcefile~types.f90 sourcefile~vtk.f90->sourcefile~unique.f90 sourcefile~vtk.f90->sourcefile~vtk_constants.f90 sourcefile~vtk_wrapper.f90 vtk_wrapper.F90 sourcefile~vtk.f90->sourcefile~vtk_wrapper.f90 sourcefile~vtu_wrapper.f90 vtu_wrapper.F90 sourcefile~vtk.f90->sourcefile~vtu_wrapper.f90 sourcefile~array.f90->sourcefile~allocate.f90 sourcefile~array.f90->sourcefile~deallocate.f90 sourcefile~matrix_coo.f90 matrix_coo.F90 sourcefile~matrix.f90->sourcefile~matrix_coo.f90 sourcefile~matrix_crs.f90 matrix_crs.F90 sourcefile~matrix.f90->sourcefile~matrix_crs.f90 sourcefile~matrix_dense.f90 matrix_dense.F90 sourcefile~matrix.f90->sourcefile~matrix_dense.f90 sourcefile~matrix_interface.f90 matrix_interface.F90 sourcefile~matrix.f90->sourcefile~matrix_interface.f90 sourcefile~c_utils.f90 c_utils.F90 sourcefile~memory_stats_wrapper.f90->sourcefile~c_utils.f90 sourcefile~project_settings.f90->sourcefile~core.f90 sourcefile~signal_flag.f90 signal_flag.F90 sourcefile~signal_flag_wrapper.f90->sourcefile~signal_flag.f90 sourcefile~system_info_wrapper.f90->sourcefile~c_utils.f90 sourcefile~variable.f90->sourcefile~allocate.f90 sourcefile~c_utils.f90->sourcefile~signal_flag.f90 sourcefile~memory_stats.f90 memory_stats.F90 sourcefile~c_utils.f90->sourcefile~memory_stats.f90 sourcefile~system_info.f90 system_info.F90 sourcefile~c_utils.f90->sourcefile~system_info.f90 sourcefile~matrix_coo.f90->sourcefile~allocate.f90 sourcefile~matrix_coo.f90->sourcefile~deallocate.f90 sourcefile~matrix_coo.f90->sourcefile~matrix_interface.f90 sourcefile~matrix_crs.f90->sourcefile~allocate.f90 sourcefile~matrix_crs.f90->sourcefile~deallocate.f90 sourcefile~matrix_crs.f90->sourcefile~matrix_interface.f90 sourcefile~matrix_dense.f90->sourcefile~allocate.f90 sourcefile~matrix_dense.f90->sourcefile~deallocate.f90 sourcefile~matrix_dense.f90->sourcefile~matrix_interface.f90

Source Code

submodule(domain_mesh_element) domain_mesh_element_square_second
    implicit none
contains

    !----------------------------------------------------------------------!
    ! construct_square_second:
    !----------------------------------------------------------------------!
    ! This function constructs a square_second element object based on the
    ! given element index, global nodal coordinates, connectivity, and
    ! spatial dimension type.
    !
    ! Arguments:
    !   iElem             : Element index (int32).
    !                       Identifies the target element.
    !
    !   Global_Coordinate : type_dp_3d type pointer containing the global coordinates
    !                       of all nodes in the mesh.
    !
    !   Connectivity      : Integer array (size 4) specifying the indices of
    !                       nodes that form the square element.
    !
    ! Return Value:
    !   element         : Allocated polymorphic object of type
    !                       square_second (extends Abstract_ElementType).
    !
    ! Function Details:
    !   - Allocates a new square_second element object.
    !   - Stores element ID and connectivity information.
    !   - Links to the corresponding global coordinates for each node.
    !   - Initializes Gauss point and weight for integration.
    !
    !----------------------------------------------------------------------!
    module function construct_square_second(id, global_coordinate, input) result(element)
        implicit none
        integer(int32), intent(in) :: id
        type(type_dp_3d), pointer, intent(in) :: global_coordinate
        type(type_input), intent(in) :: input
        class(abst_element), allocatable :: element

        integer(int32) :: i
        integer(int32) :: num_nodes, num_gauss
        real(real64), allocatable :: weight(:)
        real(real64), allocatable :: gauss(:, :)

        allocate (type_square_second :: element)

        num_nodes = input%geometry%vtk%cells(id)%num_nodes_in_cell

        select case (input%basic%geometry_settings%integration_type)
        case ("full")
            num_gauss = 9_int32
            call allocate_array(weight, num_gauss)
            call allocate_array(gauss, 3_int32, num_gauss)

            weight(:) = [25.0d0 / 81.0d0, 40.0d0 / 81.0d0, 25.0d0 / 81.0d0, 40.0d0 / 81.0d0, &
                         64.0d0 / 81.0d0, 40.0d0 / 81.0d0, 25.0d0 / 81.0d0, 40.0d0 / 81.0d0, &
                         25.0d0 / 81.0d0]

            gauss(:, 1) = [-sqrt(3.0d0 / 5.0d0), -sqrt(3.0d0 / 5.0d0), 0.0d0]
            gauss(:, 2) = [0.0d0, -sqrt(3.0d0 / 5.0d0), 0.0d0]
            gauss(:, 3) = [sqrt(3.0d0 / 5.0d0), -sqrt(3.0d0 / 5.0d0), 0.0d0]
            gauss(:, 4) = [-sqrt(3.0d0 / 5.0d0), 0.0d0, 0.0d0]
            gauss(:, 5) = [0.0d0, 0.0d0, 0.0d0]
            gauss(:, 6) = [sqrt(3.0d0 / 5.0d0), 0.0d0, 0.0d0]
            gauss(:, 7) = [-sqrt(3.0d0 / 5.0d0), sqrt(3.0d0 / 5.0d0), 0.0d0]
            gauss(:, 8) = [0.0d0, sqrt(3.0d0 / 5.0d0), 0.0d0]
            gauss(:, 9) = [sqrt(3.0d0 / 5.0d0), sqrt(3.0d0 / 5.0d0), 0.0d0]
        case ("reduced")
            num_gauss = 4_int32
            call allocate_array(weight, num_gauss)
            call allocate_array(gauss, 3_int32, num_gauss)

            weight(:) = [1.0d0, 1.0d0, 1.0d0, 1.0d0]
            gauss(:, 1) = [-sqrt(1.0d0 / 3.0d0), -sqrt(1.0d0 / 3.0d0), 0.0d0]
            gauss(:, 2) = [-sqrt(1.0d0 / 3.0d0), sqrt(1.0d0 / 3.0d0), 0.0d0]
            gauss(:, 3) = [sqrt(1.0d0 / 3.0d0), sqrt(1.0d0 / 3.0d0), 0.0d0]
            gauss(:, 4) = [sqrt(1.0d0 / 3.0d0), -sqrt(1.0d0 / 3.0d0), 0.0d0]
        case ("free")
            num_gauss = 4_int32
            call allocate_array(weight, num_gauss)
            call allocate_array(gauss, 3_int32, num_gauss)

            weight(:) = [1.0d0, 1.0d0, 1.0d0, 1.0d0]
            gauss(:, 1) = [-input%basic%geometry_settings%integration_points, -input%basic%geometry_settings%integration_points, 0.0d0]
            gauss(:, 2) = [-input%basic%geometry_settings%integration_points, input%basic%geometry_settings%integration_points, 0.0d0]
            gauss(:, 3) = [input%basic%geometry_settings%integration_points, input%basic%geometry_settings%integration_points, 0.0d0]
            gauss(:, 4) = [input%basic%geometry_settings%integration_points, -input%basic%geometry_settings%integration_points, 0.0d0]
        end select

        call element%initialize(id=id, &
                                type=input%geometry%vtk%cells(id)%cell_type, &
                                group=input%geometry%vtk%cells(id)%cell_entity_id, &
                                dimension=input%geometry%vtk%cells(id)%get_dimension(), &
                                order=input%geometry%vtk%cells(id)%get_order(), &
                                num_nodes=num_nodes, &
                                connectivity=input%geometry%vtk%cells(id)%connectivity(1:num_nodes), &
                                num_gauss=num_gauss, &
                                weight=weight, &
                                gauss=gauss, &
                                global_coordinate=global_coordinate)

    end function construct_square_second

    pure module function get_area_square_second(self) result(area)
        implicit none
        class(type_square_second), intent(in) :: self
        real(real64) :: area
        type(type_dp_vector_3d) :: r

        ! 初期化
        area = 0.0d0
        r%z = 0.0d0

        ! ガウス点 1
        r%x = -sqrt(3.0d0 / 5.0d0)
        r%y = -sqrt(3.0d0 / 5.0d0)
        area = area + 25.0d0 / 81.0d0 * self%jacobian_det(r)

        ! ガウス点 2
        r%x = 0.0d0
        r%y = -sqrt(3.0d0 / 5.0d0)
        area = area + 40.0d0 / 81.0d0 * self%jacobian_det(r)

        ! ガウス点 3
        r%x = sqrt(3.0d0 / 5.0d0)
        r%y = -sqrt(3.0d0 / 5.0d0)
        area = area + 25.0d0 / 81.0d0 * self%jacobian_det(r)

        ! ガウス点 4
        r%x = -sqrt(3.0d0 / 5.0d0)
        r%y = 0.0d0
        area = area + 40.0d0 / 81.0d0 * self%jacobian_det(r)

        ! ガウス点 5
        r%x = 0.0d0
        r%y = 0.0d0
        area = area + 64.0d0 / 81.0d0 * self%jacobian_det(r)

        ! ガウス点 6
        r%x = sqrt(3.0d0 / 5.0d0)
        r%y = 0.0d0
        area = area + 40.0d0 / 81.0d0 * self%jacobian_det(r)

        ! ガウス点 7
        r%x = -sqrt(3.0d0 / 5.0d0)
        r%y = sqrt(3.0d0 / 5.0d0)
        area = area + 25.0d0 / 81.0d0 * self%jacobian_det(r)

        ! ガウス点 8
        r%x = 0.0d0
        r%y = sqrt(3.0d0 / 5.0d0)
        area = area + 40.0d0 / 81.0d0 * self%jacobian_det(r)

        ! ガウス点 9
        r%x = sqrt(3.0d0 / 5.0d0)
        r%y = sqrt(3.0d0 / 5.0d0)
        area = area + 25.0d0 / 81.0d0 * self%jacobian_det(r)

    end function get_area_square_second

    !----------------------------------------------------------------------!
    ! psi_square_second:
    !----------------------------------------------------------------------!
    ! This function evaluates the shape function ψ_i(ξ, η) for a linear
    ! square element at the given natural coordinates (ξ, η).
    !
    ! Arguments:
    !   self : square_second type object.
    !          Represents the square element for which the shape
    !          function is evaluated.
    !
    !   i    : Integer (int32), index of the shape function (i = 1 ~ 8).
    !          Each index corresponds to a vertex of the square.
    !
    !   xi   : Real(real64), the ξ  coordinate in the natural coordinate
    !          system.
    !
    !   eta  : Real(real64), the η coordinate in the natural coordinate
    !          system.
    !
    ! Return Value:
    !   psi  : Real(real64), value of the i-th shape function ψ_i at (ξ, η).
    !
    ! Function Details:
    !   - For a linear square element, the shape functions are:
    !       ψ₁(ξ, η) = 0.25 * (1 - ξ) * (1 - η) * (-ξ - η - 1)
    !       ψ₂(ξ, η) = 0.25 * (1 + ξ) * (1 - η) * (ξ - η - 1)
    !       ψ₃(ξ, η) = 0.25 * (1 + ξ) * (1 + η) * (ξ + η - 1)
    !       ψ₄(ξ, η) = 0.25 * (1 - ξ) * (1 + η) * (-ξ + η - 1)
    !       ψ₅(ξ, η) = 0.5 * (1 - ξ) * (1 + ξ) * (1 - η)
    !       ψ₆(ξ, η) = 0.5 * (1 + ξ) * (1 - η) * (1 + η)
    !       ψ₇(ξ, η) = 0.5 * (1 - ξ) * (1 + ξ) * (1 + η)
    !       ψ₈(ξ, η) = 0.5 * (1 - ξ) * (1 - η) * (1 + η)
    !   - Returns 0.0d0 for indices outside the range [1, 8].
    !
    !----------------------------------------------------------------------!
    pure elemental module function psi_square_second(self, i, r) result(psi)
        implicit none
        class(type_square_second), intent(in) :: self
        integer(int32), intent(in) :: i
        type(type_dp_vector_3d), intent(in) :: r
        real(real64) :: psi

        select case (i)
        case (1)
            psi = 0.25d0 * (1.0d0 - r%x) * (1.0d0 - r%y) * (-r%x - r%y - 1.0d0)
        case (2)
            psi = 0.25d0 * (1.0d0 + r%x) * (1.0d0 - r%y) * (r%x - r%y - 1.0d0)
        case (3)
            psi = 0.25d0 * (1.0d0 + r%x) * (1.0d0 + r%y) * (r%x + r%y - 1.0d0)
        case (4)
            psi = 0.25d0 * (1.0d0 - r%x) * (1.0d0 + r%y) * (-r%x + r%y - 1.0d0)
        case (5)
            psi = 0.5d0 * (1.0d0 - r%x) * (1.0d0 + r%x) * (1.0d0 - r%y)
        case (6)
            psi = 0.5d0 * (1.0d0 + r%x) * (1.0d0 - r%y) * (1.0d0 + r%y)
        case (7)
            psi = 0.5d0 * (1.0d0 - r%x) * (1.0d0 + r%x) * (1.0d0 + r%y)
        case (8)
            psi = 0.5d0 * (1.0d0 - r%x) * (1.0d0 - r%y) * (1.0d0 + r%y)
        case default
            psi = 0.0d0
        end select
    end function psi_square_second

    !----------------------------------------------------------------------!
    ! dpsi_square_second:
    !----------------------------------------------------------------------!
    ! This function evaluates the partial derivative ∂ψ_i/∂ξ of the i-th
    ! shape function for a linear square element with respect to ξ
    ! at a given η coordinate.
    !
    ! Arguments:
    !   self : square_second type object.
    !          Represents the square element for which the derivative
    !          is being evaluated.
    !
    !   i    : Integer (int32), index of the shape function (i = 1 ~ 8).
    !
    !   xi   : Real(real64), the ξ coordinate in the natural coordinate
    !          system.
    !
    !   eta  : Real(real64), the η coordinate in the natural coordinate
    !          system.
    !
    ! Return Value:
    !   dpsi : Real(real64), value of ∂ψ_i/∂ξ evaluated at (ξ, η).
    !
    ! Function Details:
    !   - For a bilinear square element:
    !       ∂ψ₁/∂ξ = 0.25 * (1 - η) * (2ξ + η)
    !       ∂ψ₂/∂ξ = 0.25 * (1 - η) * (2ξ - η)
    !       ∂ψ₃/∂ξ = 0.25 * (1 + η) * (2ξ + η)
    !       ∂ψ₄/∂ξ = 0.25 * (1 + η) * (2ξ - η)
    !       ∂ψ₅/∂ξ = -ξ * (1 - η)
    !       ∂ψ₆/∂ξ = 0.5 * (1 + ξ) * (1 - ξ)
    !       ∂ψ₇/∂ξ = -ξ * (1 + η)
    !       ∂ψ₈/∂ξ = -0.5 * (1 + η) * (1 - η)
    !       ∂ψ₁/∂η = 0.25 * (1 - ξ) * (2η + ξ)
    !       ∂ψ₂/∂η = 0.25 * (1 + ξ) * (2η - ξ)
    !       ∂ψ₃/∂η = 0.25 * (1 + ξ) * (2η + ξ)
    !       ∂ψ₄/∂η = 0.25 * (1 - ξ) * (2η - ξ)
    !       ∂ψ₅/∂η = -0.5 * (1 + ξ) * (1 - ξ)
    !       ∂ψ₆/∂η = -(1 + ξ) * η
    !       ∂ψ₇/∂η = 0.5 * (1 + ξ) * (1 - ξ)
    !       ∂ψ₈/∂η = -(1 - ξ) * η
    !   - Returns 0.0 for indices outside [1, 8].
    !
    !----------------------------------------------------------------------!
    pure elemental module function dpsi_square_second(self, i, j, r) result(dpsi)
        implicit none
        class(type_square_second), intent(in) :: self
        integer(int32), intent(in) :: i
        integer(int32), intent(in) :: j
        type(type_dp_vector_3d), intent(in) :: r
        real(real64) :: dpsi

        select case (j)
        case (1)
            select case (i)
            case (1)
                dpsi = 0.25d0 * (1.0d0 - r%y) * (2.0d0 * r%x + r%y)
            case (2)
                dpsi = 0.25d0 * (1.0d0 - r%y) * (2.0d0 * r%x - r%y)
            case (3)
                dpsi = 0.25d0 * (1.0d0 + r%y) * (2.0d0 * r%x + r%y)
            case (4)
                dpsi = 0.25d0 * (1.0d0 + r%y) * (2.0d0 * r%x - r%y)
            case (5)
                dpsi = -r%x * (1.0d0 - r%y)
            case (6)
                dpsi = 0.5d0 * (1.0d0 + r%y) * (1.0d0 - r%y)
            case (7)
                dpsi = -r%x * (1.0d0 + r%y)
            case (8)
                dpsi = -0.5d0 * (1.0d0 + r%y) * (1.0d0 - r%y)
            case default
                dpsi = 0.0d0
            end select
        case (2)
            select case (i)
            case (1)
                dpsi = 0.25d0 * (1.0d0 - r%y) * (r%x + 2.0d0 * r%y)
            case (2)
                dpsi = 0.25d0 * (1.0d0 - r%y) * (-r%x + 2.0d0 * r%y)
            case (3)
                dpsi = 0.25d0 * (1.0d0 + r%y) * (r%x + 2.0d0 * r%y)
            case (4)
                dpsi = 0.25d0 * (1.0d0 + r%y) * (-r%x + 2.0d0 * r%y)
            case (5)
                dpsi = -0.5d0 * (1.0d0 + r%x) * (1.0d0 - r%x)
            case (6)
                dpsi = -(1.0d0 + r%x) * r%y
            case (7)
                dpsi = 0.5d0 * (1.0d0 + r%x) * (1.0d0 - r%x)
            case (8)
                dpsi = -(1.0d0 - r%x) * r%y
            case default
                dpsi = 0.0d0
            end select
        end select
    end function dpsi_square_second

    !----------------------------------------------------------------------!
    ! jacobian_square_second:
    !----------------------------------------------------------------------!
    ! This function computes the (i,j) component of the Jacobian matrix J
    ! for a linear square finite element at a given natural coordinate
    ! (ξ, η). The Jacobian maps natural coordinates (ξ, η) to physical
    ! coordinates (x, y).
    !
    ! Arguments:
    !   self : square_second type object.
    !          Represents the element whose Jacobian is being evaluated.
    !
    !   i    : Integer (int32), the row index of the Jacobian component.
    !          i = 1 → corresponds to x-component (dx/dξ or dx/dη),
    !          i = 2 → corresponds to y-component (dy/dξ or dy/dη).
    !
    !   j    : Integer (int32), the column index of the Jacobian component.
    !          j = 1 → partial derivative w.r.t ξ,
    !          j = 2 → partial derivative w.r.t η.
    !
    !   xi   : Real(real64), ξ coordinate in natural coordinate system.
    !
    !   eta  : Real(real64), η coordinate in natural coordinate system.
    !
    ! Return Value:
    !   jacobian : Real(real64), the (i,j) component of the Jacobian matrix.
    !
    ! Function Details:
    !   - The Jacobian matrix J is a 2×2 matrix defined as:
    !         [ ∂x/∂ξ  ∂x/∂η ]
    !         [ ∂y/∂ξ  ∂y/∂η ]
    !
    !   - Each entry is computed as a weighted sum over the shape function
    !     derivatives with respect to ξ or η, multiplied by the physical
    !     coordinates (X or Y) of the element's nodes.
    !
    !   - The derivatives of shape functions are accessed via:
    !         self%dpsi(ii,1, eta)
    !         self%dpsi(ii,2, xi)
    !
    !   - For example:
    !       ∂x/∂ξ = Σ (∂ψ_i/∂ξ) * x_i
    !       ∂y/∂η = Σ (∂ψ_i/∂η) * y_i
    !
    !   - This function supports 2D problems.
    !
    !----------------------------------------------------------------------!
    pure elemental module function jacobian_square_second(self, i, j, r) result(jacobian)
        implicit none
        class(type_square_second), intent(in) :: self
        integer(int32), intent(in) :: i, j
        type(type_dp_vector_3d), intent(in) :: r
        real(real64) :: jacobian

        integer(int32) :: ii
        type(type_dp_vector_3d) :: coordinate

        jacobian = 0
        !! dx
        select case (i)
        case (1)
            select case (j)
            case (1)
                !! dx_dxi
                do ii = 1, self%get_num_nodes()
                    coordinate = self%get_coordinate(ii)
                    jacobian = jacobian + self%dpsi(ii, 1, r) * coordinate%x
                end do
            case (2)
                !! dx_deta
                do ii = 1, self%get_num_nodes()
                    coordinate = self%get_coordinate(ii)
                    jacobian = jacobian + self%dpsi(ii, 2, r) * coordinate%x
                end do
            end select

        !! dy
        case (2)
            select case (j)
            case (1)
                !! dy_dxi
                do ii = 1, self%get_num_nodes()
                    coordinate = self%get_coordinate(ii)
                    jacobian = jacobian + self%dpsi(ii, 1, r) * coordinate%y
                end do
            case (2)
                !! dy_deta
                do ii = 1, self%get_num_nodes()
                    coordinate = self%get_coordinate(ii)
                    jacobian = jacobian + self%dpsi(ii, 2, r) * coordinate%y
                end do
            end select
        end select

    end function jacobian_square_second

    !----------------------------------------------------------------------!
    ! jacobian_det_square_second:
    !----------------------------------------------------------------------!
    ! This function computes the determinant of the Jacobian matrix J
    ! for a linear square element at a specified point (ξ, η) in
    ! the natural coordinate system.
    !
    ! Arguments:
    !   self : square_second type object.
    !          Represents the finite element whose Jacobian is evaluated.
    !
    !   xi   : Real(real64), ξ coordinate in the natural coordinate system.
    !
    !   eta  : Real(real64), η coordinate in the natural coordinate system.
    !
    ! Return Value:
    !   jacobian_det : Real(real64), the determinant of the Jacobian matrix J.
    !
    ! Function Details:
    !   - The Jacobian matrix J is a 2×2 matrix defined as:
    !         [ ∂x/∂ξ  ∂x/∂η ]
    !         [ ∂y/∂ξ  ∂y/∂η ]
    !
    !   - The determinant is calculated using:
    !         det(J) = (∂x/∂ξ)(∂y/∂η) - (∂x/∂η)(∂y/∂ξ)
    !
    !   - This determinant gives the area scaling factor for transformation
    !     from natural to physical coordinates and is used in numerical
    !     integration (e.g., Gauss quadrature) on the element.
    !
    !   - A zero or negative determinant typically indicates a problem
    !     with the element geometry (e.g., inverted element).
    !
    !----------------------------------------------------------------------!
    pure elemental module function jacobian_det_square_second(self, r) result(jacobian_det)
        implicit none
        class(type_square_second), intent(in) :: self
        type(type_dp_vector_3d), intent(in) :: r
        real(real64) :: jacobian_det

        real(real64) :: dx_xi, dx_eta
        real(real64) :: dy_xi, dy_eta

        dx_xi  = self%jacobian(1, 1, r) !&
        dx_eta = self%jacobian(1, 2, r) !&
        dy_xi  = self%jacobian(2, 1, r) !&
        dy_eta = self%jacobian(2, 2, r) !&

        jacobian_det = dx_xi * dy_eta - dx_eta * dy_xi

    end function jacobian_det_square_second

    !--------------------------------------------------------------------------------------
    ! is_in_square_second:
    !--------------------------------------------------------------------------------------
    ! This subroutine checks if the given physical coordinates (px, py) lie
    ! within the boundaries of a square element.
    ! The subroutine uses a reverse mapping (Newton-Raphson method) to map
    ! the physical coordinates to natural coordinates (ξ, η) and then
    ! checks if the point lies within the square element.
    !
    ! Arguments:
    !   self  : square_second type object. Represents a square element.
    !           It contains the coordinates (X, Y, Z) and connectivity
    !           information (conn) of the element.
    !
    !   px    : x-coordinate (real64 type) in the physical coordinate system.
    !           This coordinate is checked to see if it lies inside the square element.
    !
    !   py    : y-coordinate (real64 type) in the physical coordinate system.
    !           This coordinate is checked to see if it lies inside the square element.
    !
    ! Return Value:
    !   is_in : .true. if the point lies within the square element,
    !           .false. otherwise.
    !           The subroutine also returns .false. if the Newton-Raphson method
    !           does not converge or if the natural coordinates fall outside
    !           the square element's domain.
    !
    ! Algorithm:
    !   - The subroutine uses the Newton-Raphson method to map the physical
    !     coordinates (px, py) to the natural coordinates (ξ, η).
    !   - The subroutine then checks if the natural coordinates (ξ, η) are
    !     within the valid range [-1, 1]. If they are, the point is inside
    !     the square element.
    !   - If the method does not converge, or the natural coordinates fall
    !     outside the valid range, the subroutine returns .false.
    !
    !--------------------------------------------------------------------------------------
    module subroutine is_in_square_second(self, cartesian, normalized, is_in)
        class(type_square_second), intent(in) :: self
        type(type_dp_vector_3d), intent(in) :: cartesian
        type(type_dp_vector_3d), intent(inout) :: normalized
        logical, intent(inout) :: is_in

        type(type_dp_vector_3d) :: r
        type(type_dp_vector_3d) :: coordinate
        real(real64) :: x0, y0
        real(real64) :: dx_xi, dx_eta, dy_xi, dy_eta
        real(real64) :: detJ
        real(real64) :: dx, dy
        integer(int32) :: iter, max_iter
        real(real64) :: tol
        integer(int32) :: i
        logical :: converged

        ! 初期化
        call r%set(0.0d0, 0.0d0, 0.0d0)

        tol = 1.0d-15
        max_iter = 1000
        converged = .false.

        ! Newton-Raphson 法による逆写像
        do iter = 1, max_iter
            x0 = 0.0d0
            y0 = 0.0d0

            do i = 1, self%get_num_nodes()
                coordinate = self%get_coordinate(i)
                x0 = x0 + self%psi(i, r) * coordinate%x
                y0 = y0 + self%psi(i, r) * coordinate%y
            end do

            dx = cartesian%x - x0
            dy = cartesian%y - y0

            if (sqrt(dx * dx + dy * dy) < tol) then
                converged = .true.
                exit
            end if

            dx_xi = self%jacobian(1, 1, r)
            dx_eta = self%jacobian(1, 2, r)
            dy_xi = self%jacobian(2, 1, r)
            dy_eta = self%jacobian(2, 2, r)

            detJ = self%jacobian_det(r)
            if (abs(detJ) < 1.0d-20) exit ! ヤコビ行列の特異性チェック

            ! Newton-Raphson 更新
            r%x = r%x + (dy_eta * dx - dx_eta * dy) / detJ
            r%y = r%y + (-dy_xi * dx + dx_xi * dy) / detJ
        end do

        ! 最終判定:収束かつ自然座標が範囲内
        is_in = converged .and. (abs(r%x) <= 1.0d0) .and. (abs(r%y) <= 1.0d0)
        if (is_in) normalized = r

    end subroutine is_in_square_second

end submodule domain_mesh_element_square_second