I have a little problem with array.
I would like to be able to access it according to coordinates relating to a table of rank 3 AND also according to coordinates of table of rank 1.
Here is an example below:
integer, dimension(2,2,2) , target:: Int_array_xyz integer, dimension(8), pointer:: Int_array_xxx Int_array_xyz = RESHAPE((I,I=1,8),(/2,2,2/)) Int_array_xxx(1) => Int_array_xyz(1,1,1)
This approach is not accepted.
It is also not possible to do the following:
integer, dimension(2,2,2) :: Int_array_xyz integer, dimension(2) :: Int_array_xxx EQUIVALENCE (Int_array_xxx(1) , Int_array_xyz(1,1,1) )
I’m trying to improve a computational tool, and for some reason, the previous developer chose to reference some values in a 3-dimensional space, and other values in a 1-dimensional space.
When someone wishes to perform matrix operations by combining certain values, we are obliged to carry out an expensive and dangerous calculation of index matches.
If I could access to some of my table values alternately according to the two index systems (1 dimension and 3 dimensions) I would save a lot of development time.
I thought about using the RESHAPE function to be able to switch from a 3D system to a 1D system, but it is not possible to make a RESHAPE on itself!
XXX(:,:,:) = RESHAPE(XXX(:),(/2,2,2/))
So I have to create two tables. So that I double the memory space used, and also I need to synchronize the tables when one or the other is modified.
XYZ(:,:,:) = RESHAPE(XXX(:),(/2,2,2/))
Does anyone have any ideas?
Is that possible? Do I have to look further, on the side of "type" or "class"?
I have also try by using the PACK instruction, but not with any success.
Does anyone have an idea.
INTEGER, DIMENSION (10,3) :: MAT_XY INTEGER, DIMENSION (30) :: PACK_XX LOGICAL, DIMENSION (10,3) :: MASK ! PACK_XXX(:) = 0 ! for further try MASK(:,:) = .TRUE. MAT_XY=RESHAPE((/ (i,i=1,30) /),(/10,3/)) PACK(MAT_XY,MASK,PACK_XX) ! PACK(MAT_XY,.TRUE.,PACK_XX) ! I think, this is supposed to also work
Thank you for raising this query. As your query has reached inappropriate forum, I will help you in routing this topic to the correct forum for better discussion. We appreciate your patience.
Intel Developer Zone Support
S R, Arvind (Intel) wrote:
I will help you in routing this topic to the correct forum for better discussion.
Thanks Arvind !
Et voila !
PROGRAM ESSAI_POINTER !!---------------------------------------------------------------------- !! *** PROGRAM ESSAI_POINTER *** !! !! ** PURPOSE : LITTLE EXERCISE ON POINTER USES !! !!---------------------------------------------------------------------- IMPLICIT NONE INTEGER, DIMENSION (10,3) , TARGET :: MAT_I INTEGER, DIMENSION (:) , POINTER :: AD1_MAT_I MAT_I(:,:)=0 AD1_MAT_I(1:30) => MAT_I(:,:) MAT_I(:,:)=0 AD1_MAT_I(5:11)=3 print *,MAT_I(1:5 ,1) print *,MAT_I(6:10,1) print *,MAT_I(1:5 ,2) print *,MAT_I(6:10,2) print *,MAT_I(1:5 ,3) print *,MAT_I(6:10,3) MAT_I(3,:) = 2 print *,"" print *,AD1_MAT_I END PROGRAM ESSAI_POINTER
Not after the first try, but finally, I'm now able to do exactly what i need, with both method ! Pointer et EQUIVALENCE.
Thanks everyone for your support.
Thank you Dr. I really appreciate this kind of lighting! Moreover, EQUIVALENCE seems to be easier to use ...
So, I really enjoy not making bad choices in my early development.
for my knowledge, that it is the bad point of using EQUIVALENCE ?
EQUIVALENCE has been deemed obsolete in the standard. Its use can encourage errors and hard-to-maintain applications. There are almost always better ways to accomplish what EQUIVALENCE does.
But the EQUIVALENCE has deep roots in the assembler language i.e associate the same address to different variables and is very simple to understand.
The RESHAPE statement is in my opinion absolutely obscure: which other language has a similar function ?
People in charge of defining Fortran standard, who have made such a fuss with POINTER, TARGET, and so on ..., are continuing to mark as obsolete useful features and add new features uselessly complicated.
>>EQUIVALENCE has been deemed obsolete in the standard. Its use can encourage errors and hard-to-maintain applications.
Then what is the functional difference with: pointer(...) => RESHAPE(other(...)) or the ASSOCIATE equivalent?
To me, the pointer route is not only confusing, and complicated (Luigi's sentiments), but from my viewpoint is more "hard-to-maintain".
My reasoning is the EQUIVILENCE is placed with the variable declarations. Thus providing some indication that the variable/array is multi-purposed as well as how it is repurposed, whereas using a pointer hides this multi-purposes-ness from all places except where it is used. IOW pointer can tend towards being blindsided. I suppose one could argue that the foundation array having TARGET provides the heads up to some extent for pointer=> but no such heads up for ASSOCIATE.
I would say that most users of EQUIVALENCE don't understand how it works and aren't aware of the rules the compiler assumes are valid. For example, if you EQUIVALENCE an INTEGER and a REAL, and define the INTEGER, the REAL becomes undefined. The typical use of EQUIVALENCE in the past was to save storage by overlaying variables on the same memory location. It was never intended to be used for reinterpreting a value. I have seen programs misuse EQUIVALENCE and COMMON in ways that the standard prohibits and then the programmers complain when the code no longer works due to an optimization.
Pointer bounds remapping is the proper solution for this case. Some of the other substitutes for EQUIVALENCE are clunky, such as TRANSFER, but the meaning is clear and both the user and the compiler are clear as to what is happening.
I don't get the gripe about ASSOCIATE.
>>I don't get the gripe about ASSOCIATE
While ASSOCIATE does not provide for re-typing a variable/array, it does provide for repurposing the, or part of the, variable/array.
The non-portable UNION can also do the same. There are many cases in programming where it is expedient to represent the same memory location(s) as differing types. Reading data files form old systems with non-conforming data layouts.
I eliminated equivalence from all my code many tears ago, I found it had been used in some very cludgy ways and was a source of some unpredictable results. Irrespective of if you like it or not, It has been declared obsolete and thus it is not wise to be developing new code that uses it. In my world all new code should only be compiled with standards checking on. Simillarly I won't use a circular saw without the safety guard ot drive my car without a seat belt.
>>ASSOCIATE can do much more than UNION or EQUIVALENCE...
Correct, however, it cannot retype a variable/array/array section, or re-layout a record, whereas a UNION (or a "cast") can.
Thank you for all your answers, which I read with great interest.
Your opinions seem to differ. So here is how I construct my conclusion:
jimdempseyatthecove (Blackbelt) wrote:
[…] My reasoning is the EQUIVALENCE is placed with the variable declarations. […]
That's a very positive point!
Equivalence has a very simple and clear use for developers and the variables will be very clearly identified.
In addition, the associated variables can no longer be changed by mistake in the rest of the developments.
In our developments, all variables are declared and initialized in modules separate from the rest of the code. So there is very little risk of REAL/INTEGER confusion.
Steve Lionel (Ret.) (Blackbelt)
[…] if you EQUIVALENCE an INTEGER and a REAL, and define the INTEGER, the REAL becomes undefined. […]
This means that the compiler or the execution will inform me of the improper use of an undefined variable in case of an error.
Luigi R. wrote:
Not using EQUIVALENCE that has a deep root in the assembler language can prevents you to create efficient software architectures.
The performance of my code is a real priority.
It seems to me that efficiency and performance have more or less the same meaning.
My conclusion is that EQUIVALENCE is an excellent solution to my problem. But I'm just a paper belt... so I'm very interested in your explanations!
Thanks thanks thanks :-)
jimdempseyatthecove (Blackbelt) wrote:
>>ASSOCIATE can do much more than UNION or EQUIVALENCE...
Correct, however, it cannot retype a variable/array/array section, or re-layout a record, whereas a UNION (or a "cast") can. ..
Jim, what do you mean by "re-layout a record"?
TYPE MyRecord UNION MAP REAL(8) :: BLA END MAP MAP CHARACTER(LEN=8) :: NAME END MAP MAP INTEGER(4) :: COUNT END MAP END UNION ... ! other record items REAL(8) :: OTHER(nnn) END TYPE MyRecord
And use that type to read records from a file (as binary).
When the data file contains POD (plain old data), and when you cannot tell what the record is until after you read the record, it is useful to be able to describe the record in a manner that has a multiplicity of declarations. A similar issue when writing arbitrary POD files.
Thanks, I presumed you were referring to UNIONs but I wanted to be sure.
Would you by any chance have a fully worked out, real-life example of record processing using this non-standard UNION type along with some measure of performance you get, either in terms of speed and/or data size that you think is important to consider?
Now, one can achieve something similar to the above use case of UNIONs involving POD via polymorphiic types, TRANSFER, etc. in Fortran. But then the issue brought up often is performance. But as far as I know, there has been no objective quantification of any difference in performance with UNIONs vs some standard-conforming one, just words and references to inefficient assembler code, etc. That does not help move beyond the status quo with Fortran. With an actual example, if someone can illustrate substantial difference in non-standard UNION and what the standard offers now, that could help contemplate what to consider in a future revision to the standard - it's worth an online discussion (see below) among the interested parties.
Readers: please see this recent site at GitHub: https://github.com/j3-fortran/fortran_proposals
I can show you parts of an example, but it would be difficult to provide metrics. Description follows:
I have a physics simulation program Space Elevator, tether emulation, body motion, rigid beam object, planetary, etc... Part of the simulation program includes the ability to periodically log data to a database of my design. The database store, and subsequent playback read, has the capability to selectively include, exclude, and type conversion (precision changing). IOW some data is not interested in saving (e.g. transient and temporary) and other data as an example may be adequately converted from REAL(8) to REAL(4). e.g. for the rotation of a body I would want to integrate many steps using REAL(8) but snapshot using REAL(4). Or integer variables may execute fastest in native bit-ness (INTEGER(4)) but are known to be competently stored using a lesser precision (INTEGER(2)).
I also wanted the conversions to be declared dynamically. Meaning on one simulation run, store in low precision and filtered down to minimal data for quick analysis, and later in a different simulation run store in high precision and filtered up to include additional data.
The simulation program will not know the type sizes and/or packing/unpacking filters until run time, or until it opens a prior written database.
Consider the following record which can be used as both a header record with data of a blob (bucket) of data as well as a "stream reader" of the data within the blob following the header portion of the blob.
type TypeAVRDBBlobContext ! Align real 8 at front of context union map real(8) :: valREAL8 end map map real(4) :: valREAL4 end map map integer(8) :: valINT8 end map map integer(4) :: valINT4 end map map integer(2) :: valINT2 end map map integer(1) :: valINT1 end map map integer(1) :: ArrayINT1(8) end map map character(8) :: Name end map end union ! continue with real real(8) :: theMin, theMax, theRange, theMidpoint, theDelta ! Next integers integer :: IOUnit integer :: ByteCount integer :: Mode ! current SaveAs mode integer :: BlobNumber integer :: FrameNumber ! vvvvv duplicate into header integer(4) :: BlobSize ! number of bytes per record (512, 1024, 2048, ...) integer(4) :: BlobNumberFrame1 integer(4) :: NumberOfBlobsPerFrame integer(4) :: NumberOfBlobs integer(4) :: NumberOfFrames ! ^^^^^ duplicate into header ! last allocatable buffer for Blob(BlobSize) integer(1), pointer :: Blob(:) end type TypeAVRDBBlobContext
The pointer Blob is messaged on read/write such that the blob data immediately follows this header layout in the database.
To use the data I have get/put functions to, in conjunction with the filter and size conversion information, to read or write data in stream-like manner using one of the types listed in the front of the record. (then advance the pointer to the Blob)
In the simple case of saving a REAL(8), default real specified as real(8)
! routine to save REALs in arbitrary mode subroutine AVRDB_Save_REAL(SaveAs, value) use MOD_AVRDB implicit none ! input args integer(1) :: SaveAs real :: value ! local variables integer :: valueAsINTEGER ! AVRDB%out%bc%Mode = SaveAs select case(SaveAs) case(NotRecorded) return case(AsINT1:AsINT8) valueAsINTEGER = value call AVRDB_Save_INTEGERquick(valueAsINTEGER) return case(AsREAL4:AsREAL8) call AVRDB_Save_REALquick(value) return case(AsCompressed1REAL4rd:AsCompressed4REAL8d) ! compression performed for arrays only CALL DOSTOP('MOD_AVRDBcode - invalid AVRDB_Save_Mode') return case default CALL DOSTOP('MOD_AVRDBcode - unassigned AVRDB_Save_Mode') end select end subroutine AVRDB_Save_REAL
And for the case of ASREAL4:ASREAL8
subroutine AVRDB_Save_REALquick(val) use MOD_AVRDB implicit none real :: val ! local variables ! code select case(AVRDB%out%bc%Mode) case(AsREAL4) AVRDB%out%bc%valREAL4 = val if((AVRDB%out%bc%ByteCount+4) .gt. AVRDB%out%bc%BlobSize) call AVRDB_WriteBlob(AVRDB%out%bc) AVRDB%out%bc%Blob(AVRDB%out%bc%ByteCount+1:AVRDB%out%bc%ByteCount+4) = AVRDB%out%bc%ArrayINT1(1:4) AVRDB%out%bc%ByteCount = AVRDB%out%bc%ByteCount + 4 return case(AsREAL8) AVRDB%out%bc%valREAL8 = val if((AVRDB%out%bc%ByteCount+8) .gt. AVRDB%out%bc%BlobSize) call AVRDB_WriteBlob(AVRDB%out%bc) AVRDB%out%bc%Blob(AVRDB%out%bc%ByteCount+1:AVRDB%out%bc%ByteCount+8) = AVRDB%out%bc%ArrayINT1(1:8) AVRDB%out%bc%ByteCount = AVRDB%out%bc%ByteCount + 8 return case(AsCompressed1REAL4rd,AsCompressed1REAL4d,AsCompressed1REAL8rd,AsCompressed1REAL8d) if(AVRDB%out%bc%theDelta .ne. 0.0) then AVRDB%out%bc%valINT1 = INT1((val-AVRDB%out%bc%theMidpoint) / AVRDB%out%bc%theDelta) else AVRDB%out%bc%valINT1 = 0 endif if((AVRDB%out%bc%ByteCount+1) .gt. AVRDB%out%bc%BlobSize) call AVRDB_WriteBlob(AVRDB%out%bc) AVRDB%out%bc%Blob(AVRDB%out%bc%ByteCount) = AVRDB%out%bc%ArrayINT1(1) AVRDB%out%bc%ByteCount = AVRDB%out%bc%ByteCount + 1 return case(AsCompressed2REAL4rd,AsCompressed2REAL4d,AsCompressed2REAL8rd,AsCompressed2REAL8d) if(AVRDB%out%bc%theDelta .ne. 0.0) then AVRDB%out%bc%valINT2 = INT2((val-AVRDB%out%bc%theMidpoint) / AVRDB%out%bc%theDelta) else AVRDB%out%bc%valINT2 = 0 endif if((AVRDB%out%bc%ByteCount+2) .gt. AVRDB%out%bc%BlobSize) call AVRDB_WriteBlob(AVRDB%out%bc) AVRDB%out%bc%Blob(AVRDB%out%bc%ByteCount+1:AVRDB%out%bc%ByteCount+2) = AVRDB%out%bc%ArrayINT1(1:2) AVRDB%out%bc%ByteCount = AVRDB%out%bc%ByteCount + 2 return case(AsCompressed4REAL4rd,AsCompressed4REAL4d,AsCompressed4REAL8rd,AsCompressed4REAL8d) if(AVRDB%out%bc%theDelta .ne. 0.0) then AVRDB%out%bc%valINT4 = INT4((val-AVRDB%out%bc%theMidpoint) / AVRDB%out%bc%theDelta) else AVRDB%out%bc%valINT4 = 0 endif if((AVRDB%out%bc%ByteCount+4) .gt. AVRDB%out%bc%BlobSize) call AVRDB_WriteBlob(AVRDB%out%bc) AVRDB%out%bc%Blob(AVRDB%out%bc%ByteCount+1:AVRDB%out%bc%ByteCount+4) = AVRDB%out%bc%ArrayINT1(1:4) AVRDB%out%bc%ByteCount = AVRDB%out%bc%ByteCount + 4 return case default CALL DOSTOP('MOD_AVRDBcode - unassigned AVRDB_Save_Mode') end select end subroutine AVRDB_Save_REALquick
in the case of ASREAL4, the value val is converted from real (real(8)) to real(4) in the process of being written into header union valREAL4. Then using the UNION's ArrayINT1(8), 4 bytes are copied into the integer(1) array Blob beginning at index ByteCount+1. Then the ByteCount is advanced.
With the UNION, all this hoop jumping, is simple table driven. Without UNION I would not have this flexability.