【问题标题】:How to Construct Multidimensional Array with Variable Assignment如何用变量赋值构造多维数组
【发布时间】:2018-03-29 14:08:17
【问题描述】:

所以我遇到了数组执行循环的问题。

我有一个包含 976 个原子 X、Y、Z 坐标数组的列表。在另一个文本文件中,我有一个数组,每个原子有 976 个电荷。此外,我有 Xo、Yo、Zo 的数组,有 2,971 个点(13x13,x13)。我需要计算的是原子的实际 X、Y、Z 坐标与 2,197 个网格点之间的距离。最初我用 33 个原子做的计算是这样的。

  do i=1,2197
 D1(i) = (((X(1)-Xi(i))**2 + (Y(1)-Yi(i))**2 + (Z(1)-Zi(i))**2)**0.5)*10E-10
 D2(i) = (((X(2)-Xi(i))**2 + (Y(2)-Yi(i))**2 + (Z(2)-Zi(i))**2)**0.5)*10E-10
 D3(i) = (((X(3)-Xi(i))**2 + (Y(3)-Yi(i))**2 + (Z(3) Zi(i))**2)**0.5)*10E-10
 D4(i) = (((X(4)-Xi(i))**2 + (Y(4)-Yi(i))**2 + (Z(4)- 
   Zi(i))**2)**0.5)*10E-10 

....等等等等

但由于现在我有 976 个原子,手动执行此操作将是一场噩梦。 我的问题是:由于我需要为每个包含 2197 个点的原子创建一个数组,有没有一种方法可以生成它而无需手动写出来?

我尝试过这样的事情:

do i=1,2197
    Do n=1,976
        DO j=1,976
D(j,i) = (((X(n)-Xi(i))**2 + (Y(n)-Yi(i))**2 + (Z(n)- 
Zi(i))**2)**0.5)*10E-10
        End do
    End Do
Enddo

我的逻辑是基本上计算每个原子的 2,197 个距离点,以便它们像这样存储D(j,i) 其中 j 表示它来自数字 1-976 的哪个原子,变量 i 将是 2197 个不同的点由公式计算。这个想法是拥有 967 个原子,其中每个原子有 2197 个点,我可以访问这些点来稍后计算其他事物。然后将电荷除以距离以找到电压电位,所以基本上我需要能够调用距离,如D(atom#,charge.)

我的逻辑是否正确,或者我应该使用其他数组组合以不同的方式解决这个问题?

【问题讨论】:

  • 没有关注这一切,但这里的n 循环没有意义,因为d(j,i) 将始终只有n=976 传递的值。并且对于所有j 都是相同的,因为j 不在表达式中。
  • 我明白你在说什么。例如,告诉程序将 D(1,2197) 的所有 2,197 个点相加,然后将数组的下一个 2,197 点加起来为 D(2,2197),有什么方法?基本上是一个循环,它将创建一个不同的数组 D(1+n,i),它将基本上将每个 do 循环存储到最左边下标的一个新数组中?
  • 你的主要问题是你没有清楚地描述你想要做什么。 “加起来”是什么意思?您似乎没有尝试在循环中添加任何内容。如果d(j,i)jth粒子和ith网格点之间的距离,n是什么?
  • 我想我应该事先说明我的主要目标。本质上,我有 976 个原子,每个原子都有 XYZ 坐标和电荷。我需要计算实际原子 XYZ 坐标和网格点之间的距离(在这种情况下,我制作了一个 13x13x13 网格,因此有 2197 个唯一点)。对于几个原子,实际计算很简单,因为我只是创建了 D1(2197),而 D1 指的是 ATOM1 的距离公式生成的 2197 个唯一距离。对于原子 2,我会做 D2(2197),而 D3 会有 2197 个点等。目的是构建一个自动编号 D1-D976 的循环。
  • - 自动无需我说明 D1(i) = 距离公式,D2(i)= 距离 ... D976(i) = 距离公式。我本可以手动完成并为每个原子分配其数组,但我想知道是否有一种方法可以通过 do 循环自动完成,结果证明确实存在。现在我遇到的唯一问题是让程序从文本文件中正确读取数据,但我想我很快就会弄清楚。

标签: arrays fortran physics


【解决方案1】:

至少对我来说,您的问题相当难以理解,但假设这不是一个家庭作业问题并且您只是 Fortran 爱好者或新开发人员,这里有一个非面向对象但现代有效的解决方案:

program confusingQuestion

use, intrinsic :: iso_fortran_env, only: int32,real64
implicit none

integer(int32), parameter :: nAtom = 976
integer(int32), parameter :: nGrid = 2197
real(real64)              :: AtomCrd(3,nAtom), AtomCharge(nAtom)
real(real64)              :: GridCrd(3,nGrid), Atom2GridDistance(nGrid,nAtom)
integer(int32)            :: iAtom,iGrid

! for demonstration purposes, I assign random values to variables
call random_number(AtomCrd)
call random_number(GridCrd)
call random_number(AtomCharge)

do concurrent (iAtom = 1:nAtom,iGrid=1:nGrid)
  Atom2GridDistance(iGrid,iAtom) = norm2( AtomCrd(:,iAtom)-GridCrd(:,iGrid) )
end do

! print a sample:
write(*,'(*(g0.4,","))') Atom2GridDistance(:,1) ! print distances for the first atom
end program confusingQuestion

请注意,我在这里故意使用了do concurrent 构造。与串行执行的常规 do-loop 不同,do concurrent 告诉编译器循环中的所有元素计算都是相互独立的,它们可以同时计算。因此,根据您的编译器,此代码可能会并行执行,这将提高您的代码速度。

此外,无需在您的代码中手动实现 L2-norm。 Fortran 已经有不止一种计算 L2-norm 的方法,其中之一是 norm2 (x [ , dim ] ),它返回一个实数数组 x 的 L2-norm;结果是一个与 x 相同类型的实标量。 请注意,您将需要 Fortran 2008 编译器来编译此代码。这是一个在线 2008 编译器,您可以使用它尝试代码 https://www.tutorialspoint.com/compile_fortran_online.php

请记住,为了让编译器为您自动并行化并发循环,您很可能需要使用 O2 或更好的 O3 优化标志以及自动并行化标志(例如以英特尔的 ifort 为例)。我不确定 GNU gfortran 中 do concurrent 的状态。但是,在使用 Intel ifort 编译器时,我已经看到了明显的性能差异。这是关于此主题的相关但旧线程:https://stackoverflow.com/a/25860047/2088694

【讨论】:

  • 哇!我从来没有想过要走这条路。这不是一个家庭作业问题,我实际上是一名生物化学本科生(不是程序员哈哈)。我只是想找到一种方法来加快我对大型蛋白质系统的计算(准确地说是电压门控钾和钠通道。我知道所有需要完成的计算,我只是不擅长弄清楚如何编码它是正确的。尽管如此,do concurrent 是我什至不知道的事情,我相信它会加快计算速度,而不是走我的老恐龙时代路线。谢谢你的洞察力!
  • 很高兴听到这些信息很有用。我在上面的答案中添加了关于这个主题的新段落。干杯。
  • 只是要注意基本的do 循环没有根本性的错误。作为一个新手,你真的应该学习如何正确地写一个。
猜你喜欢
  • 2021-10-08
  • 1970-01-01
  • 1970-01-01
  • 2021-04-03
  • 2020-06-10
  • 2019-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多