I store parameters in a derived type and I want to load them from a file through a namelist at run time. This is a sketch of the code:
module options_definitions
implicit none
type :: opts
integer :: option1
integer :: option2
contains
procedure :: load => load_options
end type opts
contains
subroutine load_options(this,path_to_profiles_folder,profile_name)
class(opts), intent(inout) :: this
character(len=*), intent(in) :: path_to_profiles_folder
character(len=*), intent(in) :: profile_name
integer :: err, ios
logical :: file_exists
character(len=255) :: err_message
character(len=255) :: path_to_profile_folder
ASSOCIATE( option1 => this%option1, &
option2 => this%option2)
namelist /options_nml/ option1, option2
! the following is INESSENTIAL added for completeness
!-----------------------------------------------------
path_to_profile_folder=trim(path_to_profiles_folder)//trim(profile_name)
! load options from the configuration file (namelist set above)
INQUIRE(FILE=trim(path_to_profile_folder)//'/'//trim(opt_file_name), EXIST=file_exists) ! 'file_exists' will be TRUE if the file exists and FALSE otherwise
if (file_exists) then
call my_open(111,trim(path_to_profile_folder)//'/'//trim(opt_file_name),&
'Options module could not open the options configuration file')
read(111,NML=options_nml,iostat=ios,iomsg=err_message)
call check_read_success_and_close(111,trim(path_to_profile_folder)//'/'//trim(opt_file_name),ios,err_message,&
'Options module could not read from the options configuration file')
else
print*,'Warning: The required configuration file containing options &
(options.nml) does not exist. The inbuilt options will be &
used instead.'
endif
!---------------------------------------
end associate
end subroutine load_options
The compiler complains that option1
, option2
, ... have not been declared ( (iFort) error #6404: This name does not have a type, and must have an explicit type. [OPTION1]
).
From the description of the namelist construct (https://software.intel.com/sites/products/documentation/doclib/stdxe/2013/composerxe/compiler/fortran-mac/GUID-EAC90ABA-859A-4745-B9FC-B8D66B5B6FF0.htm) I read that
each variable in var-list must be accessed by USE or host association
which I would say is the case here as this
is a dummy argument with intent(inout)
. Isn't this the use association?
What is wrong? Is there a workaround such that I don't have to declare the same stuff many times in different places?
(e.g. declaring option1
as module private and copying it in this
at the end of the load_options
routine rather than using the associate
construct would work but I would prefer something less prone to omissions when adding options later)
Thanks in advance for any comments.
PS: Just before posting I found this question. The code looks like it could be doing what I want but without seeing the definition of the unit
type I cannot understand what it does.
EDIT1: Follow up on Vladimir's comments
Now I quite understand what is going on. In the question I linked the author uses a pointer to this
not to individual fields and this would be my preferred option if I could make it work. When I tried it the compiler was fine with the namelist declaration but refused to read from the input file saying
error #5498: Allocatable or pointer derived-type fields require a user-defined I/O procedure. read(111,NML=options_nml,iostat=ios,iomsg=err_message)
Any suggestions what such an I/O procedure could look like? I am confused, if it is possible to add a derived-type (or a pointer to it) in a namelist declaration then I would assume that the variables in the input file should be referred to as this%option1
and that would be it. I have found another discussion here where Steve Lionel from Intel supports this view saying
The Fortran standard allows only "variable-name" in the namelist object list. You can, however, put T in the namelist and reference T%I in namelist input (and it is shown on output).
But the compiler complains without even seeing the file.
EDIT2: What I changed in the code above (that lead to the compiler error in first edit) was this:
class(opts), intent(inout), target :: THIS
TYPE(opts), POINTER :: THIS_NML
namelist /options_nml/ THIS_NML
THIS_NML => THIS
The associate construct deleted, the original namelist declaration deleted. The error occurred in the read
statement in the if (file_exists)
block. So what I did was to use a pointer to a structure in the namelist declaration. That was all.
For ifort you can actually use the solution from my answer you linked. The exact definition of unit
is not important there.
select type(t => this)
type is (opts)
associate( option1 => t%option1, &
option2 => t%option2)
namelist /options_nml/ option1, option2
read...
end associate
end select
This is because this
is polymorphic in your example.
I investigated, whether this usage is standard conforming, and it is not. Ifort clearly warns, when asked to follow the standard
ifort -stand f08 namelist.f90
namelist.f90(8): warning #7775: Placement of NAMELIST statement is non-standard.
There are many other workarounds you can do. You can create local pointers and read them in the namelist read, that is exactly what the answer linked by you does. That would compile also with gfortran
. I would go this way.
integer, pointer :: option1, option2
namelist /options_nml/ option1, option2
option1 => this%option1
option2 => this%option2
read...
Or use normal local variables and copy the values to this
.