alesgenova / quantum-corretto

Python script to create a map of the Quantum Espresso fortran code base and automatically patch the source code to create reusable routines and custom datatypes.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Quantum Corretto Generator

Overview

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).

Nomenclature definitions

  • A module module will be converted to module_type data type and be stored in module_type_module module
  • It is possible that module already contains data data type. In that case, declaration of data_type will be located in module_type_module in a type definition block located above the module_type block definition

STEP 0

Teach by Example: from module gvect to type gvect_type)

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:

gvect_definition.f90

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.

Move to STEP 1

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.

STEP 1

Bound procedures

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 bound procedure

['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

Init bound procedure

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

Dealloc bound procedure

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

gvect_type.f90

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

Blacklisting a file

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.

Providing an additional / overriding file

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.

Exclude modules from the objectification 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.)

Mark an objectified module as completed

Whenever you complete the objectification of a module, add the name of the module to the */human/completed_modules.txt file.

Errors

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.

About

Python script to create a map of the Quantum Espresso fortran code base and automatically patch the source code to create reusable routines and custom datatypes.

License:GNU General Public License v3.0


Languages

Language:Python 64.7%Language:Fortran 35.3%