- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Please help me to resolve a problem.
I have function, which accept an array as input data:
[fortran]real function MixtureDensity( MassFrac ) real, intent(in) :: MassFrac(4) ! - mass fractions of H2, CO, H2O, CO2 .... end function[/fortran]
I have declared the structure:
[fortran]type TSubstContent real H2 real CO real H2O real CO2 end type[/fortran]
This structure has identical construction as an array required in the function MixtureDensity. For some reasons I do not want to declare MassFrac as an array. The problem is: how to pass MassFrac of type TSubstContent to the function MixtureDensity:
[fortran]type(TSubstContent) MassFrac call MixtureDensity( MassFrac ) ! - error by compilation[/fortran]
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The way I would get round this is to map any array over the structure elements eg.
type Arr
sequence
union
map
real(4) :: MassData(4)
end map
map
real(4) :: h2
real(4) :: CO
real(4) :: H2O
real(4) :: CO2
end map
end union
end type
Type(Arr) :: MassFracArray
Then you can call your function MixtureDensity(MassFracArray%MassData)
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You say the error is in compilation, but I would expect the error would be in the link stage where it would complain about an unsatisfied external.
You really should make the two parts agree. If you want to use TSubstContent in the calling program,
you should use it in the subroutine definition. Otherwise, even though the memory layout is the same, the linker which connects the various routines into an executable program will not see them as matching, and
complain that it doesn't know anything about MixtureDensity with an argument of TSubstContent.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
First of all, the Fortran standard does not guarantee that the storage of the derived type is laid out the same way an array would be. A compiler is allowed to reorder the components and vary their spacing any way it wants to. You could add SEQUENCE to the type and this would disable reordering, but does not guarantee spacing. You could add BIND(C) and this would specify the same memory layout as the "companion C processor" would use in a struct, which in most cases would be closest to what you want.
But you'd still get an error because the type of the actual argument doesn't match the type of the dummy argument.
I could give you several ways around this, but I feel obligated to ask what the "some reasons" are and why the function can't have its argument declared as the derived type.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
-There is a function, which accepts a realarray as input data
- The function is called with a type structure as argument
It doesnt matter if your structure consists of 4 real values. When you dont want to declare MassFrac as an array, you have to create a seperate array with has the values, something like this:
[bash]real MassFracArray(4) MassFracArray(1) = MassFrac%H2 MassFracArray(2) = MassFrac%CO MassFracArray(3) = MassFrac%H2O MassFracArray(4) = MassFrac%CO2 call MixtureDensity(MassFracArray)[/bash]Markus
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Steve Lionel (Intel) said:
"Basically, you want a variable of type TSubstContent to be treated as if it were an array of four REAL values"
- that is exactly, what i mean (thanks for understanding)
Steve Lionel (Intel) said:
"but I feel obligated to ask what the "some reasons" are and why the function can't have its argument declared as the derived type"
Inside many functions like the MixtureDensity, mentioned above, operating with arrays let to get all the power and comfort in coding as well perfomance (cycles do - end do, vectorized operations and many instrinc functions for arrays). But outside or in some other subroutines (and especially by debugging) there is a need to treat array as a collection (structure) of named elements. Yes i could declare named constants:
[fortran] integer, parameter :: H2 = 1 integer, parameter :: CO = 2 integer, parameter :: H2O = 3 integer, parameter :: CO2 = 4[/fortran]so it let me to operate with separate elements very simple using the form:
[bash]MassFrac(H2) = 0.23[/bash]it is a way (i do not need to remember what index stays for H2). But i would like to see in debuging the values of named elements (variables inspector) - and that is not possible for arrays (indexes of array are always integer).
Steve Lionel (Intel) said:
"You could add SEQUENCE to the type and this would disable reordering, but does not guarantee spacing."
Array sections (include that from derived types) and common arrays often have different spacing, but it makes not a problem, when they are as operands in a same vectorized expression
John B. Walter said:
"If you want to use TSubstContent in the calling program,
you should use it in the subroutine definition"
Making TSubstContent as a type of input data in functions have many disadvantages: no more vectorizing, not possible to use cycles (do - end do) and some others
onkelhotte
Thanks, your solution is correct (i new that) but i hope there is a more natural way
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The way I would get round this is to map any array over the structure elements eg.
type Arr
sequence
union
map
real(4) :: MassData(4)
end map
map
real(4) :: h2
real(4) :: CO
real(4) :: H2O
real(4) :: CO2
end map
end union
end type
Type(Arr) :: MassFracArray
Then you can call your function MixtureDensity(MassFracArray%MassData)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
dannycat
Using combination of sequence - union - map seems to be the right solution. Thank you very much for answer .
By the way dous using of sequence or union or map decrease compilator's possibility to make perfomance code, optimization and so on?
Steve Lionel (Intel) said:
"I could give you several ways around this"
If you give some more ways I will thank you. I think some ways could be connected with using of pointers so as it would be done in C. But this way is not organic with fortran and probably will decrease optimization possibility.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- integer,parameter::H2=1
- integer,parameter::CO=2
- integer,parameter::H2O=3
- integer,parameter::CO2=4
[fortran] integer, parameter :: H2 = 1 integer, parameter :: CO = 2 integer, parameter :: H2O = 3 integer, parameter :: CO2 = 4[/fortran]so it let me to operate with separate elements very simple using the form:
- MassFrac(H2)=0.23
[bash]MassFrac(H2) = 0.23[/bash]it is a way (i do not need to remember what index stays for H2). But i would like to see in debuging the values of named elements (variables inspector) - and that is not possible for arrays (indexes of array are always integer).
The last bit is not necessarily true - see pic below. The watch window shows an array reference that involves a parameter.

Debugging information, including value, is available for parameters that are referenced in a program unit. Make sure that you have the /debug-parameters:all option (Under the property Fortran > Debugging > Information for PARAMETER constants) set appropriately. Unfortunately (Dear Intel Compiler Developers - it would be nice if this was "fixed" in some way - this use case is quite common in my experience) while "used" means "used", "all" doesn't really mean "all" - you do need to have that reference somewhere in that program unit. The reference can be pretty irrelevant though - in the example I've used an otherwise pointless ONLY clause in a dummy subroutine.
So... now we have a reasonable work around for displaying array elements - why are you tracking the species concentrations in individual components again?
The use of arrays for things that are array-like in nature is the natural fortran way, particularly since F90 with all those nice intrinsic array operations. If you are tracking anything more than one or two species, then chemical species concentrations are a classic case of an an array-like thing. Using arrays wins hands down from the point of view of code clarity and maintainability - think about how easy it becomes to add and remove species as your model evolves.
I would completely avoid the non-standard MAP/UNION thing. It may solve the problem that you've posed, but it is just asking for trouble down the track. I have similar reservations about using TRANSFER or POINTERS (fractionally less evil because they are at least standard fortran features) - why obfuscate your code, make it harder to maintain, make it harder for the processor to work out what your intent is, make future readers of your code wonder what sort of substances your were consuming when your designed this software, etc, etc, when you fundamentally don't need to?
(If Dr Fortran is still listening - I can get an ICE out of the compiler (12.1.2) with the attached example if I uncomment the erroneous function result clause on the function statement for the density procedure.)
DebugParameters.f90
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
So... now we have a reasonable work around for displaying array elements
.. "all" doesn't really mean "all" - you do need to have that reference somewhere in that program unit. The reference can be pretty irrelevant though - in the example I've used an otherwise pointless ONLY clause in a dummy subroutine
This problem could be resolved as followes (adopted from pascal). Let it would be possible to declare:
[fortran]type TSubstIndexes = ( H2, H2O, CO, CO2, O2, N2, Al, Ar, CH4 ) real MassFrac( TSubstIndexes ) ! - array of 9 real values, but indexes are from TSubstIndexes real ShortMassFrac( CO:N2 ) ! - array of 4 real values and indexes are from subrange of TSubstIndexes[/fortran]So compilator will know for sure what Indexes to show in Watch Window for MassFrac and ShortMassFrac (no need in debuging options). So it would be possible then to write:
[fortran]program Example
! ....
print *, MixtureRg(MassFrac) ! is possible
call DoSomething (MassFrac) ! is possible too
! may be written:
do I = H2, CH4
MassFrac(i) = 0
end do
! or may be written:
do I = Lbound(TSubstIndexes), Ubound(TSubstIndexes)
MassFrac(i) = 0
end do
contains
pure real function MixtureRg(MassFrac) result(Res)
real , intent(in) :: MassFrac(9)
! here are used pure integers for indexes: MassFrac(i)
end function
subroutine DoSomething(Array)
real , intent(in) :: Array(:)
! ...
end subroutine
end program[/fortran] IanH wrote:
think about how easy it becomes to add and remove species as your model evolves
The examples above are simplified. Naturally the set of species can be expanded. I can explain it.
IanH wrote:
why are you tracking the species concentrations in individual components again?
Once it is by initialization (using experimental data), once it is calculation of separate reactions between two substances. So there many situations when it is needed to deel with separate elements. In such cases they should be named.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You could use pointers, or you could use TRANSFER if the arguments were read and not written.
thanks for more suggestions. i will try them
IanH wrote:
I would completely avoid the non-standard MAP/UNION thing
It should be included in standard
dannycat
I have tried union - map and it works even without sequence.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Even though someone already provided a solution using the non-standard UNION/MAP, a simpler alternative is to use generics:
[fortran]interface MixtureDensity
module procedure MixtureDensity, MixtureDensity_TSubstContent
end interface
...
contains
...
real function MixtureDensity( MassFrac )
real, intent(in) :: MassFrac(4) ! - mass fractions of H2, CO, H2O, CO2
....
end function
real function MixtureDensity_TSubstContent( MassFrac )
type(TSubstContent), intent(in) :: MassFrac ! - mass fractions of H2, CO, H2O, CO2
MixtureDensity_TSubstContent = MixtureDensity([MassFrac%H2, MassFrac%CO, &
MassFrac%H2O,MassFrac%CO2])
end function
[/fortran] EDIT:
If the problem is about using loops to go through the components of the derived type, then, as suggested by Steve Lionel, use pointers:
[fortran]type TSubstContent real, pointer :: H2 => NULL() real, pointer :: CO => NULL() real, pointer :: H2O => NULL() real, pointer :: CO2 => NULL() real :: array(4) = 0 end type contains subroutine initialize(TSC) type(TSubstContent), target, intent(OUT) :: TSC TSC%H2 => TSC%array(1) TSC%CO => TSC%array(2) TSC%H2O => TSC%array(3) TSC%CO2 => TSC%array(4) end subroutine [/fortran]
That way, you can loop through the contents using the array component.
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page