使 SUM 函数与派生类型一起工作

Making SUM function work with derived types

我有一个用户类型,其中包含两个或多个不同大小的数组。

type state    
    real(real64) :: pos(3), ori(4), vee(3), omg(3)
end type

并且我定义了 (*)(+) 运算符以便能够进行代数计算

interface operator (+)
    procedure st_add
end interface
interface operator (*)
    procedure st_scale1, st_scale2
end interface

contains

elemental function st_add(a,b) result(c)
type(state), intent(in) :: a,b
type(state) :: c
    c = state( &
        a%pos + b%pos, &
        a%ori + b%ori, &
        a%vee + b%vee, &
        a%omg + b%omg)
    
end function

elemental function st_scale1(a,b) result(c)
real(real64), intent(in) :: a
type(state), intent(in) :: b
type(state) :: c

    c = state( &
        a * b%pos, &
        a * b%ori, &
        a * b%vee, &
        a * b%omg)
    
end function

elemental function st_scale2(b,a) result(c)
real(real64), intent(in) :: a
type(state), intent(in) :: b
type(state) :: c

    c = state( &
        a * b%pos, &
        a * b%ori, &
        a * b%vee, &
        a * b%omg)
    
end function

现在我在线性代数运算中使用上述内容,例如

    real(real64), parameter :: c(4) = [1/6d0, 2/6d0, 2/6d0, 1/6d0]        
    type(state) :: k(4),k_step

    k_step = c(1)*k(1)+c(2)*k(2)+c(3)*k(3)+c(4)*k(4)

但为了简洁和代码的灵活性,我想要使用以下内容

    k_step = sum( c(:)*k(:) )    ! error

导致以下错误 error #6362: The data types of the argument(s) are invalid. [SUM].

那么我有什么选择呢? sum 调用 st_add 是否需要通用接口?或者我需要一些其他定义吗?

我正在使用英特尔® Fortran 经典编译器 2021.1.2(oneAPI HPC 的一部分)。


解决方案

最有效的解决方案是添加以下内容

interface sum
    procedure st_sum
end interface

contains

pure function st_sum(a) result (s)
type(state), intent(in) :: a(:)
type(state) :: s
integer :: i, n
    n = size(a)        
    s = a(1)
    do i=2, n
        s = s + a(i)
    end do
end function

和用法

k_step = sum(c(:)*k(:))
st = this + h*k_step

嗯,其实我把上面的两种说法合二为一了

st = this + sum( (h*c(:))*k(:) )

总的来说,我得到了相同的结果,但有轻微的性能损失(大约 10%)。它必须是函数结果的所有数组复制值。 IDK.

我需要一个通用接口来求和st_add吗?

您确实需要向 sum 通用接口添加特定功能。该函数在内部调用什么并不重要。它必须是适用于 state 类型数组的函数,这很重要。

interface sum
  procedure your_function
end interface


function your_function(a)
   type(state) :: your_function
   type(state), intent(in) :: a(:)
   ...
end function

通用 operator(+) 的用户定义特定过程不(也不应该)暗示 sum 的实现。如果您希望为通用 sum 提供特定程序,那么您必须自己提供一个。

在 Fortran 中,内部 sum(对于内部数字类型)不是根据 + 定义的,因此派生类型没有自然的类似物。

同样,productnorm2 不是根据 *+ 定义的。

题主似乎喜欢dot_product的想法。尽管此函数 根据 *(和 sum)定义的,但这没有用:内部函数仅采用内部类型并且不考虑特定sumoperator(*).

的程序