屏蔽 Fortran 数组的更好方法?
Better way to mask a Fortran array?
我想屏蔽 Fortran 数组。这是我目前正在做的方式...
where (my_array <=15.0)
mask_array = 1
elsewhere
mask_array = 0
end where
然后我得到我的掩码数组:
masked = my_array * mask_array
有更简洁的方法吗?
使用 MERGE 内部函数:
masked = my_array * merge(1,0,my_array<=15.0)
或者,坚持 where
,
masked = 0
where (my_array <=15.0) masked = my_array
我预计 where
的使用和 merge
的使用在速度和内存消耗方面存在差异,但我不知道它们是什么是。
这里已经给出了两种不同的方法:一种是保留 and one using 。首先,High Performance Mark 提到在速度和内存使用方面可能存在差异(考虑临时数组)。我将指出另一个潜在的考虑因素(不进行价值判断)。
subroutine work_with_masked_where(my_array)
real, intent(in) :: my_array(:)
real, allocatable :: masked(:)
allocate(masked(SIZE(my_array)), source=0.)
where (my_array <=15.0) masked = my_array
! ...
end subroutine
subroutine work_with_masked_merge(my_array)
real, intent(in) :: my_array(:)
real, allocatable :: masked(:)
masked = MERGE(my_array, 0., my_array<=15.)
! ...
end subroutine
即merge
方案可以使用自动分配。当然,有时人们不希望这样做(例如当处理大量相同大小的 my_array
时:在这些情况下检查数组大小时通常会产生开销):使用 masked(:) = MERGE(...)
处理分配后(这甚至可能与问题代码相关)。
我发现定义一个函数 where
很有用,它采用 logical
数组和 returns integer
值的 .true.
索引, 例如
x = where([.true., .false., .false., .true.]) ! sets `x` to [1, 4].
这个函数可以定义为
function where(input) result(output)
logical, intent(in) :: input(:)
integer, allocatable :: output(:)
integer :: i
output = pack([(i, i=1, size(input))], input)
end function
有了这个where
功能,你的问题就可以解决
my_array(where(my_array>15.0)) = 0
这可能不是执行此操作的最高效方法,但我认为它非常可读且简洁。这个 where
函数也可以比 where
内在函数更灵活,因为它可以用于例如对于多维数组的特定维度。
限制:
但是请注意(正如@francescalus 指出的那样)这不适用于非 1 索引的数组。这种限制无法轻易避免,因为对此类数组执行比较操作会丢弃索引信息,例如
real :: my_array(-2,2)
integer, allocatable :: indices(:)
my_array(-2:2) = [1,2,3,4,5]
indices = my_array>3
write(*,*) lbound(indices), ubound(indices) ! Prints "1 5".
对于非 1 索引的数组,为了使用此 where
函数,您需要相当丑陋的
my_array(where(my_array>15.0)+lbound(my_array)-1) = 0
我想屏蔽 Fortran 数组。这是我目前正在做的方式...
where (my_array <=15.0)
mask_array = 1
elsewhere
mask_array = 0
end where
然后我得到我的掩码数组:
masked = my_array * mask_array
有更简洁的方法吗?
使用 MERGE 内部函数:
masked = my_array * merge(1,0,my_array<=15.0)
或者,坚持 where
,
masked = 0
where (my_array <=15.0) masked = my_array
我预计 where
的使用和 merge
的使用在速度和内存消耗方面存在差异,但我不知道它们是什么是。
这里已经给出了两种不同的方法:一种是保留
subroutine work_with_masked_where(my_array)
real, intent(in) :: my_array(:)
real, allocatable :: masked(:)
allocate(masked(SIZE(my_array)), source=0.)
where (my_array <=15.0) masked = my_array
! ...
end subroutine
subroutine work_with_masked_merge(my_array)
real, intent(in) :: my_array(:)
real, allocatable :: masked(:)
masked = MERGE(my_array, 0., my_array<=15.)
! ...
end subroutine
即merge
方案可以使用自动分配。当然,有时人们不希望这样做(例如当处理大量相同大小的 my_array
时:在这些情况下检查数组大小时通常会产生开销):使用 masked(:) = MERGE(...)
处理分配后(这甚至可能与问题代码相关)。
我发现定义一个函数 where
很有用,它采用 logical
数组和 returns integer
值的 .true.
索引, 例如
x = where([.true., .false., .false., .true.]) ! sets `x` to [1, 4].
这个函数可以定义为
function where(input) result(output)
logical, intent(in) :: input(:)
integer, allocatable :: output(:)
integer :: i
output = pack([(i, i=1, size(input))], input)
end function
有了这个where
功能,你的问题就可以解决
my_array(where(my_array>15.0)) = 0
这可能不是执行此操作的最高效方法,但我认为它非常可读且简洁。这个 where
函数也可以比 where
内在函数更灵活,因为它可以用于例如对于多维数组的特定维度。
限制:
但是请注意(正如@francescalus 指出的那样)这不适用于非 1 索引的数组。这种限制无法轻易避免,因为对此类数组执行比较操作会丢弃索引信息,例如
real :: my_array(-2,2)
integer, allocatable :: indices(:)
my_array(-2:2) = [1,2,3,4,5]
indices = my_array>3
write(*,*) lbound(indices), ubound(indices) ! Prints "1 5".
对于非 1 索引的数组,为了使用此 where
函数,您需要相当丑陋的
my_array(where(my_array>15.0)+lbound(my_array)-1) = 0