Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
28385 Discussions

How can I check "if the array contains element A and does not contains element B"?

Yeon__Jejoon
Novice
3,043 Views

I have an array that includes 2D arrays. I wish to set some elements of a specific array to a specific variable based on some conditions. 

All arrays are read from the input file. I read the columns and stored them into the arrays. Input columns look like this: 

open(1,file="rawdata.txt",status='old')

nat=9000 ! integer

do i=1,nat
read (1,*) field1(i),numelem(i),(contents1(i,j),j=1,numelem(i)),(contents2(i,j),j=1,numelem(i))
enddo

Here, numelem(i) array provides the total number of elements of the secondary array contents1 and contents2 so I used it. 

 

Now, I wish to perform 

...


r1 = 3 ! this is an integer number I gather from other arrays 

r2 = 5 ! this is an integer number I gather from other arrays 

do i=1,nat

if (field1(i).eq.3) then 

! If-loop 1 = If the value of field1 array is 3

do j=1,numelem(i) 

if (((field1(contents1(i,j)).eq.4).and.(contents2(i,j).ge.0.5)).or.((field1(contents1(i,j)).eq.1).and.(contents2(i,j).ge.0.3))) then

! If-loop 2 = If the value of field1 array at contents1(i,j)-th index is 4 and corresponding contents2(i,j) value is greater than 0.5. Or, if the value of field1 array at contents1(i,j)-th index is 1 and corresponding conetents2(i,j) value is greater than 0.3 then 

if ((r1/=contents1(i,j)).and.(r2==contents1(i,j))) then

! If-loop 3 = Among elements of contents1 array that satisfy if-loop 1 and if-loop 2, check if the r1 is not included among the selected elements of contents1 array, and r2 is included in the selected elements of contents1 array. 

if (contents1(i,j)/=r2) then

r3 = contents1(i,j)

! If-loop 4 = If the if-loop 1, if-loop 2, and if-loop 3 are satisfied, then assign/store the elements of contents1(i,j) to new variable r3, except the element r2. 

...

 

But I don't think I wrote properly to get what I want. Especially, I think if-loop 3 and if-loop 4 are definitely wrong. 

For if-loop 3, I tried ANY command but this gives me the error

An array-valued argument is required in this context.

So I just used if loop and logical operator /= and ==. But I'm not sure if I'm doing this correctly. 

 

If-loop 4 is wrong, assign/store the elements of the array to a variable except for a specific element. But I'm not sure how can I achieve this... 

 

Is there any better way to achieve such loops without using a lot of if-loops? Data size is really huge, I wish to reduce the number of if loops, but I'm not sure if there are any better ways. 

 

0 Kudos
23 Replies
jimdempseyatthecove
Honored Contributor III
388 Views

Consider using a contained procedure (contained function) as filter, and use CYCLE to improve clarity in coding:

 

*** Untested code follows:

subroutine YourSubroutineNameHere(your args here)
...
open(1,file="rawdata.txt",status='old')
iframe=0
100  continue
iframe=iframe+1

nat=9000 ! integer

do i=1,nat
  read (1,*)
  field1(i),numelem(i),(neighborlist1(i,j),j=1,numelem(i)),(neighborlist2(i,j),j=1,numelem(i))
enddo

r1=0
r2=0
r3=0
r4=0
ncount=0


! First do loop for the first oxygen
next_i: do i=1,nat
  if (field1(i).ne.3) cycle ! next i
  ! If-loop 1 = If the value of field1 array is 3
  do j=1,numelem(i)
    if (isNotCandidate(i,j)) cycle
    ! If ((the value of field1 array at neighborlist1(i,j)-th index is 4 and
    !        corresponding neighborlist2(i,j) value is greater than 0.5.)
    !     Or, (the value of field1 array at neighborlist1(i,j)-th index is 1 and
    !        corresponding neighborlist2(i,j) value is greater than 0.3)) then
    r1=neighborlist1(i,j)

    ! Second do loop for the second oxygen. Use i+1 for indexing to prevent
    ! the counting of the same oxygen atom i.
    do k=i+1,nat
      if (field1(k).ne.3) cycle ! next k
      ! If-loop 1 = If the value of field1 array is 3
      do l=1,numelem(k)
        if (isNotCandidate(k,l)) cycle ! next l
        ! If-loop 2 = If the value of field1 array at neighborlist1(k,l)-th index is
        ! 4 and corresponding neighborlist2(k,l) value is greater than 0.5. Or, if
        ! the value of field1 array at neighborlist1(k,l)-th index is 1 and
        ! corresponding neighborlist2(k,l) value is greater than 0.3 then
        if (r1.ne.neighborlist1(k,l)) cycle
        ! If-loop3 = If the neighborlist1(k,l) contains the r1 =>
        ! This means the i, r1, and k, three elements are connected.
        ! But I'm not sure if this is a proper way to check include/exclude
        r2=neighborlist1(k,l) ! but only if the r2 is not r1
        ! If all three loops are satisfied, then store the element of neighborlist1(
        ! k,l) to the r2. While doing this, I wish to exclude r1 from r2.
        ! But I don't know how to do this.

        ! Third do loop for the third oxygen. Use k+1 for indexing to prevent
        ! the counting of the same oxygen i and k
        do m=k+1,nat
          if (field1(m).ne.3) cycle ! next m
          ! If-loop 1 = If the value of field1 array is 3
          do n=1,numelem(m)
            if (isNotCandidate(m,n)) cycle ! next n
            ! If-loop 2 = If the value of field1 array at neighborlist1(m,n)-th index is
            ! 4 and corresponding neighborlist2(m,n) value is greater than 0.5. Or, if
            ! the value of field1 array at neighborlist1(m,n)-th index is 1 and
            ! corresponding neighborlist2(m,n) value is greater than 0.3 then
            if ((r1==neighborlist1(m,n)).or.(r2==neighborlist1(m,n))) cycle
            ! If-loop3 = If the neighborlist1(m,n) contains the r2 but not r1 =>
            ! This means the i, r1, k, and r2, four elements are connected.
            ! But I'm not sure if this is a proper way to check include/exclude
            r3=neighborlist1(m,n) ! but only if the r3 is not r2
            ! If all three loops are satisfied, then store the element of neighborlist1(
            ! m,n) to the r3. While doing this, I wish to exclude r2 from r3.
            ! But I don't know how to do this.

            ! Fourth do loop for the fourth oxygen. Use m+1 for indexing to prevent
            ! the counting of the same oxygen i, k, and m
            do o=m+1,nat
              if (field1(o).ne.3) cycle ! next o
              ! If-loop 1 = If the value of field1 array is 3
              do p=1,numelem(o)
                if (isNotCandidate(o,p)) cycle ! next p
                ! If-loop 2 = If the value of field1 array at neighborlist1(o,p)-th index is
                ! 4 and corresponding neighborlist2(o,p) value is greater than 0.5. Or, if
                ! the value of field1 array at neighborlist1(o,p)-th index is 1 and
                ! corresponding neighborlist2(o,p) value is greater than 0.3 then
                if ((r1==neighborlist1(o,p)).or.(r2==neighborlist1(o,p)).or.(r3==neighborlist1(o,p))) cycle
                ! If-loop3 = If the neighborlist1(o,p) contains the r3 but not r1 & r2  =>
                ! This means the i, r1, k, r2, m, and r3, six elements are connected.
                ! But I'm not sure if this is a proper way to check include/exclude
                r4=neighborlist1(o,p) ! but only if the r4 is not r3
                ! If all three loops are satisfied, then store the element of neighborlist1(
                ! o,p) to the r4. While doing this, I wish to exclude r3 from r4.
                ! But I don't know how to do this.
                if (r4=neighborlist1(i,j)) then
                  ! Now, if the neighborlist1(i,j), the neighbor list of atom i contains r4
                  ! That means we found the ring of i-r1-k-r2-m-r3-o-r4-i. Count +1
                  ncount=ncount+1
                  cycle next_i
                endif ! if (r4=neighborlist1(i,j)) then
              end do ! do p=1,numelem(o)
            end do ! do o=m+1,nat
          end do ! do n=1,numelem(m)
        end do ! do m=k+1,nat
      end do ! do l=1,numelem(k)
    end do ! do k=i+1,nat
  end do ! do j=1,numelem(i)
end do ! do i=1,nat
write(151,40) iframe,ncount
goto 100 ! read in next batch (consider replacing with DO loop exiting on EOF or flag in data file)
! ...
return ! from subroutine
! At bottom of procedure (but not after end ....) insert a contained procedure
contains
logical function isNotCandidate(x, y)
  integer :: x, y
  isNotCandidate = .not. (((field1(neighborlist1(x,y)).eq.4).and.(neighborlist2(x,y).ge.0.5)).or.((field1(neighborlist1(x,y)).eq.1).and.(neighborlist2(x,y).ge.0.3)))
end function isNotCandidate
end subroutine YourSubroutineNameHere

Also...

The above does not consider (permit) joined rings (where a same neighbor pair is a segment of two or more rings).

A fair amount of work is performed counting the number of rings, you might want to have an array (e.g. integer::ring(8,:)) to store the values of i, r1, k, r2, m, r3, o, r4 in index ncount after incrementing ncount.

Jim Dempsey

0 Kudos
Yeon__Jejoon
Novice
315 Views

I really appreciate your help and suggestions for my question. May I ask questions about your replies? 

 

1) Example code structures. 

I found that the direction of logical operators is opposite from my example. For example, you are using .ne., not .eq., for field1 check. Is there any reason behind this? Or is this just you modified the script to use it as an example? 

 

2) Regarding modifying/changing field 1 to exclude searched atoms

Thanks for the suggestion. 

Do you think would it be better to perform this exclusion right after if if-structure such as

 

 

 


do i=1,nat

if (field1(i).eq.3) then

field1(i).eq.-3

...

do m=1,nat

if (field1(m).eq.3) then

field1(m).eq.-3

...

r2=neighborlist1(k,l)

field1(r2).eq.-3

...

 

 

 

Or, would it be better to exclude all together after the ring is found, such as your 

field1([i,r1,k,r2,m,r3,o,r4])=-3 (after ncount+1) 

example? 

 

3) Regarding the 8-shaped ring comment.

I also thought the prevention of re-use of an atom would resolve this issue. That is why I was escaping from the use of the same oxygen by indexing i, k=i+1, m=k+1... But I was not sure (and still I'm not sure) about r1, r2, r3, and r4. That was why I asked the following question in the original post: how can I store/assign the elements of the array to the value except for a specific value(s) I declare? I wrote this in the comment of the example code also. 

For example, I really wish to exclude r1 and r2 when I define r3 as r3=neighborlist1(m,n). There are some atoms that include r1, r2, and r3 in the neighborlist1. But if I include r1 and r2 to r3, that will make an 8-shape ring. But I don't know how can I exclude r1 and r2 when I define r3 as r3=neighborlist1(m,n). I really don't know how to do that. That is one of the questions that I really wish to know the answer.

If I finish the logic for such escaping from the use of the same atom, including r1, r2, r3, and r4, then my code should be able to prevent an 8-shaped ring. 

 

You suggested modifying field1 values. If I use the exclusion you suggested, like 

field1([i,r1,k,r2,m,r3,o,r4])=-3

or

field1(r2)=-3

Would this resolve the situation I asked about the "store/assign the elements of the array to the value except for a specific value"? 

 

Again, thank you so much for your suggestions. 

0 Kudos
jimdempseyatthecove
Honored Contributor III
306 Views

>>I found that the direction of logical operators is opposite from my example. For example, you are using .ne., not .eq., for field1 check. Is there any reason behind this?

I though the code example was self-obvious. You had:

do loopIndexing

  if (satisfyingExpression) then

    ...

    do otherLoopIndexing

       ... etc nested several layers deep

   end do otherLoopIndexing

  end do loopIndexing

 

in your case you span many statements (with increasing IF level nesting) before it becomes obvious that the non-matching expression simply advances the loop indexing.

 

Whereas in my suggestion, CYCLE is used on the non-matching expression, thus eliminating the IF nesting. This makes the code much easier to read by eliminating the IF nesting.

>>2) (when to flag field1 entry with "don't consider"

This really needs to be done after identifying a ring *** provided that you are not also counting double rings (where a segment of one ring, is also a segment of another ring).

 

>>3 Regarding the 8-shaped ring comment.

I also thought the prevention of re-use of an atom would resolve this issue. 

The question I asked was if you wanted to permit the counting of 8-shape rings (not how best to exclude counting such rings).

If you require that no atom be (counted as) a member of multiple rings, then set all ring atoms to the "exclude flag". Doing so takes the CYCLE on the next DO I iteration (very fast path).

Note, if you are looking for double/triple/... rings and/or rings of different lengths, then consider changing the ring detection algorithm to a recursive method then have a list of array of ring finds where you store the ring path integer :: rings(maxAtomsInRing, nAtoms/3). You would need to terminate recursion at maxAtomeInRing (and preclude counting larger rings, that are not of interest), The ring attoms are stored: first, next, ..., last, 0,0,...,0,ncount). Or in place of an integer array, store the list of indexes as (allocatable) CHARACTERs.

Jim Dempsey

0 Kudos
Reply