The script scans the source code and creates an internal map of all the files/modules/procedures/quantities defined in Quantum Espresso.
The goal is to convert each module into a datatype. The script already does most of the work, but some manual input is needed.
I have already generated a set of blueprints for each module in Quantum Espresso. You can find them in the folders Modules/
and PW/src/
of this git repository.
Below I will be denoting relative paths with the wildcard *
, which may stand for either Modules/
or PW/src/
.
I a providing a detailed example what to do for a standard module that can be objectified semi-automatically (e.g. gvect
).
- A module
module
will be converted tomodule_type
data type and be stored inmodule_type_module
module - It is possible that
module
already containsdata
data type. In that case, declaration ofdata_type
will be located inmodule_type_module
in a type definition block located above themodule_type
block definition
The gvect
module is defined in Modules/recvec.f90
.
For the gvect
module (and every other module), the script creates the _definition
files in the */autogenerated_0/
directory. In this case, the file gvect_definition.f90
is generated.
Any file in the autogenerated_0
directory can and will be overwritten at any time. When you modify a file first copy it into the */human/
folder.
The type definition will need to look like the following:
alloc_args = {"fft_base": {"type": "type(fft_base_type)", "dimension": null}, "ions_base": {"type": "type(ions_base_type)", "dimension": null}}
init_args = {"ngm": {"type": "integer", "dimension": null}, "ngm_g": {"type": "integer", "dimension": null}, "ngl": {"type": "integer", "dimension": null}, "ngmx": {"type": "integer", "dimension": null}, "ecutrho": {"type": "real(dp)", "dimension": null}, "gcutm": {"type": "real(dp)", "dimension": null}, "nl": {"type": "integer", "dimension": ":"}, "nlm": {"type": "integer", "dimension": ":"}, "gstart": {"type": "integer", "dimension": null}, "gg": {"type": "real(dp)", "dimension": ":"}, "gl": {"type": "real(dp)", "dimension": ":"}, "igtongl": {"type": "integer", "dimension": ":"}, "g": {"type": "real(dp)", "dimension": ":,:"}, "mill": {"type": "integer", "dimension": ":,:"}, "ig_l2g": {"type": "integer", "dimension": ":"}, "sortedig_l2g": {"type": "integer", "dimension": ":"}, "mill_g": {"type": "integer", "dimension": ":,:"}, "eigts1": {"type": "complex(dp)", "dimension": ":,:"}, "eigts2": {"type": "complex(dp)", "dimension": ":,:"}, "eigts3": {"type": "complex(dp)", "dimension": ":,:"}}
type :: gvect_type
logical :: is_alloc = .false.
logical :: is_init = .false.
integer :: ngm = 0
integer :: ngm_g = 0
integer :: ngl = 0
integer :: ngmx = 0
real(dp) :: ecutrho = 0.0_dp
real(dp) :: gcutm = 0.0_dp
integer, allocatable, dimension(:) :: nl ! dimensions = ["this%ngm"]
integer, allocatable, dimension(:) :: nlm ! dimensions = ["this%ngm"]
integer :: gstart = 2
real(dp), allocatable, dimension(:) :: gg ! dimensions = ["this%ngm"]
real(dp), dimension(:), pointer :: gl
integer, allocatable, dimension(:) :: igtongl ! dimensions = ["this%ngm"]
real(dp), allocatable, dimension(:,:) :: g ! dimensions = ["3","this%ngm"]
integer, allocatable, dimension(:,:) :: mill ! dimensions = ["3","this%ngm"]
integer, allocatable, dimension(:) :: ig_l2g ! dimensions = ["this%ngm"]
integer, allocatable, dimension(:) :: sortedig_l2g ! dimensions = ["this%ngm"]
integer, allocatable, dimension(:,:) :: mill_g ! dimensions = ["3","this%ngm"]
complex(dp), allocatable, dimension(:,:) :: eigts1 ! dimensions = ["-fft_base%nr1:fft_base%nr1","ions_base%nat"]
complex(dp), allocatable, dimension(:,:) :: eigts2 ! dimensions = ["-fft_base%nr2:fft_base%nr2","ions_base%nat"]
complex(dp), allocatable, dimension(:,:) :: eigts3 ! dimensions = ["-fft_base%nr3:fft_base%nr3","ions_base%nat"]
contains
procedure, pass :: alloc
procedure, pass :: init
procedure, pass :: update
procedure, pass :: dealloc
end type gvect_type
Please, mind that any modification made directly in the autogenerated
folder will be overwritten.
In order to move to STEP 1
human
needs to execute the following code from the /human/
folder
$> python ../../declaration_to_procedure.py gvect
This will look for gvect_definition.f90
and generate in the folder ../autogenerated/
a file called gvect_bound_procedures.f90
.
Files with a _procedures
suffix are just blueprint, and are expected to be manually modified. Alessandro will provide the scheleton to make it faster (esprecially the alloc
method), but the actual logic has to be written by human
.
To modify a *_procedures.f90
file, copy it to the human
directory first (making sure you're not overwriting somebody else's work).
Every datatype is automatically given three bound methods, alloc
, init
, and dealloc
(any suggestion on better names?):
alloc
is where allocatable arrays defined in the datatype are allocated.init
is where the actual quantities are set.dealloc
is where allocatable arrays defined in the datatype are deallocated.
If you want to add other procedures to the datatype, add them below, and also add the name of the procedure to the list in the first line (this is important to automate everything).
['alloc', 'init', 'dealloc']
subroutine alloc(this, fft_base, ions_base )
use memory_manager_module, only: memory_manager
implicit none
class(gvect_type), intent(inout) :: this
type(fft_base_type), intent(in) :: fft_base
type(ions_base_type), intent(in) :: ions_base
integer :: istat
allocate( nl(this%ngm), stat=istat )
call memory_manager('gvect%alloc', 'nl', nl(:), 1, istat)
allocate( nlm(this%ngm), stat=istat )
call memory_manager('gvect%alloc', 'nlm', nlm(:), 1, istat)
allocate( gg(this%ngm), stat=istat )
call memory_manager('gvect%alloc', 'gg', gg(:), 1, istat)
allocate( igtongl(this%ngm), stat=istat )
call memory_manager('gvect%alloc', 'igtongl', igtongl(:), 1, istat)
...
allocate( eigts1(-fft_base%nr1:fft_base%nr1,ions_base%nat), stat=istat )
call memory_manager('gvect%alloc', 'eigts1', eigts1(:), 1, istat)
...
this%is_alloc = .true.
return
end subroutine alloc
subroutine init(this,ngm,ngm_g,ngl,ngmx,gstart,...,nl,...)
implicit none
type(gvect_type), intent(inout) :: this
integer, optional :: ngm
integer, optional :: ngm_g
integer, optional :: ngl
integer, optional :: ngmx
integer, optional :: gstart
integer, optional :: gcutm
integer, optional :: ecutrho
...
integer, dimension(:), optional :: nl
...
if (present(ngm)) this%ngm = ngm
if (present(ngm_g)) this%ngm_g = ngm_g
if (present(ngl)) this%ngl = ngl
if (present(ngmx)) this%ngmx = ngmx
if (present(gstart)) this%gstart = gstart
if (present(gcutm)) this%gcutm = gcutm
if (present(ecutrho)) this%ecutrho = ecutrho
...
if (present(nl)), this%nl = nl
...
this%is_init = .true.
return
end subroutine init
subroutine dealloc(this)
use memory_manager_module, only: memory_manager
implicit none
class(gvect_type), intent(inout) :: this
integer :: istat
deallocate( nl, stat=istat )
call memory_manager('gvect%dealloc', 'nl', nl(:), -1, istat)
deallocate( nlm, stat=istat )
call memory_manager('gvect%dealloc', 'nlm', nlm(:), -1, istat)
deallocate( gg, stat=istat )
call memory_manager('gvect%dealloc', 'gg', gg(:), -1, istat)
deallocate( igtongl, stat=istat )
call memory_manager('gvect%dealloc', 'igtongl', igtongl(:), -1, istat)
...
this%is_alloc = .false.
return
end subroutine dealloc
The actual f90
that will be passed to the compiler will be an automatic combination of the autogenerated gvect_definition.f90
and of the human-patched gvect_procedures.f90
The structure will be something like this:
module gvect
implicit none
type :: gvect_type
...
contains
procedure, pass :: alloc
...
end type gvect_type
contains
subroutine alloc(...)
...
end subroutine alloc
...
end module gvect
Add the name of the file you wish to blacklist to a new line in the */human/skip_files.txt
.
Any file that gets blacklisted will not be scanned by the script, and its modules/procedures won't be patched.
Add as many f90
files as you wish to the */human/additional_files/
folder.
Files in this folder will be treated as if they came directly with the upstream source code.
It is understood that any file added this way will either:
- need no patching
- OR be patchable with the usual automatic procedure.
By default the script generates a datatype for each and every module in Quantum Espresso.
If for whatever reason, turns out a module shouldn't undergo this transformation, add its name to the */human/skip_modules.txt
file.
This could be useful if the module only contains parameters and procedures, or if you want to blacklist a module without blacklisting the entire source file containing it (i.e. if there are other modules in the same file that you want to be included instead.)
Whenever you complete the objectification of a module, add the name of the module to the */human/completed_modules.txt
file.
Datatypes mimic the structure of the original modules. It is possible that some quantities (or even modules) have eluded my poorly written regular expressions.
If you come across such cases, please notify it to me so I can fix the script upstream.