1!> \brief YAML output of SIESTA variables for external post-processors.
2!!
3!! This module provides facilities to output SIESTA variables into a YAML
4!! file at the end of a run.
5!!
6!! Variables are dumped in YAML format to provide structured data that
7!! can be read partially or fully, in any order, at the option of the user.
8!!
9!! The resulting output can be processed in C/C++ using LibYAML (found at
10!! https://github.com/yaml/libyaml), in Python through the ruamel.yaml
11!! package (found at https://pypi.python.org/pypi/ruamel.yaml), and in Perl
12!! thanks to the YAML::XS module (found at
13!! http://search.cpan.org/~tinita/YAML-LibYAML-0.69/lib/YAML/XS.pod).
14!!
15!! \author Yann Pouillon
16!! \date 2017-2018
17!! \copyright GNU General Public License version 3
18!!
19!! \note The interest of using the YAML format is that output can be achieved
20!!       without introducing new external dependencies.
21!!
22module m_io_yaml
23
24  use precision, only: dp
25
26  implicit none
27
28  private
29
30  ! Ensure compliance with the YAML 1.2 file format
31  character(len=*), parameter :: CH10 = achar(10)   !< New line
32  character(len=*), parameter :: CH34 = achar(34)   !< Double quote
33  character(len=*), parameter :: YAML_HEADER = "%YAML 1.2"//CH10//"---"//CH10
34  character(len=*), parameter :: YAML_FOOTER = CH10//"..."
35
36  public :: siesta_write_yaml
37
38contains
39
40  !> \brief Creates a YAML file containing SIESTA build parameters and
41  !!        final energy values.
42  !!
43  !! This routine calls io_assign() to get a free unit number and outputs
44  !! valid YAML data structures into the associated OUTVARS.yml file. The
45  !! data consists in two dictionaries:
46  !!   - siesta: build parameters and status of optional features;
47  !!   - energies: full decomposition of the total energy, using exactly
48  !!     the same naming conventions as in the \ref m_energies module.
49  !!
50  !! The resulting file can easily be parsed using the ruamel.yaml Python
51  !! module.
52  !!
53  !! \todo Output forces and stress tensor.
54  subroutine siesta_write_yaml()
55
56    use m_energies
57    use version_info
58
59    implicit none
60
61    logical :: trigger
62    integer :: ierr, yaml_fd
63
64    ! Open YAML document
65    call io_assign(yaml_fd)
66    open(unit=yaml_fd, file='OUTVARS.yml', status='new', action='write', &
67&     access='sequential', form='formatted', iostat=ierr)
68    ! FIXME: Find out why the system sometines reports a failure while things
69    !        have gone perfectly well.
70    !if ( ierr .ne. 0 ) call die('could not open OUTVARS.yml')
71    write(unit=yaml_fd, fmt='(A)') YAML_HEADER
72
73    ! Dump SIESTA information
74    write(unit=yaml_fd, fmt='(A)') "siesta:"
75    write(unit=yaml_fd, fmt='(2X,A,":",1X,3(A))') "version", &
76&     CH34, trim(adjustl(version_str)), CH34
77    write(unit=yaml_fd, fmt='(2X,A,":",1X,3(A))') "arch", &
78&     CH34, trim(adjustl(siesta_arch)), CH34
79    write(unit=yaml_fd, fmt='(2X,A,":",1X,3(A))') "compiler", &
80&     CH34, trim(adjustl(compiler_version)), CH34
81    write(unit=yaml_fd, fmt='(2X,A,":",1X,3(A))') "fflags", &
82&     CH34, trim(adjustl(fflags)), CH34
83    write(unit=yaml_fd, fmt='(2X,A,":",1X,3(A))') "fppflags", &
84&     CH34, trim(adjustl(fppflags)), CH34
85    write(unit=yaml_fd, fmt='(2X,A,":",1X,3(A))') "libs", &
86&     CH34, trim(adjustl(libs)), CH34
87#ifdef MPI
88    write(unit=yaml_fd, fmt='(2X,A,":",1X,3(A))') "build", &
89&     CH34, "mpi", CH34
90#else
91    write(unit=yaml_fd, fmt='(2X,A,":",1X,3(A))') "build", &
92&     CH34, "serial", CH34
93#endif
94#ifdef _OPENMP
95    trigger = .true.
96#else
97    trigger = .false.
98#endif
99    write(unit=yaml_fd, fmt='(2X,A,":",1X,3(A))') "openmp", &
100&     CH34, trim(yesno(trigger)), CH34
101#ifdef USE_GEMM3M
102    trigger = .true.
103#else
104    trigger = .false.
105#endif
106    write(unit=yaml_fd, fmt='(2X,A,":",1X,3(A))') "gemm3m", &
107&     CH34, trim(yesno(trigger)), CH34
108#ifdef CDF
109    trigger = .true.
110#else
111    trigger = .false.
112#endif
113    write(unit=yaml_fd, fmt='(2X,A,":",1X,3(A))') "netcdf", &
114&     CH34, trim(yesno(trigger)), CH34
115#ifdef NCDF_4
116    trigger = .true.
117#else
118    trigger = .false.
119#endif
120    write(unit=yaml_fd, fmt='(2X,A,":",1X,3(A))') "netcdf4", &
121&     CH34, trim(yesno(trigger)), CH34
122#ifdef NCDF_PARALLEL
123    trigger = .true.
124#else
125    trigger = .false.
126#endif
127    write(unit=yaml_fd, fmt='(2X,A,":",1X,3(A))') "netcdf4_mpi", &
128&     CH34, trim(yesno(trigger)), CH34
129#if defined(ON_DOMAIN_DECOMP) || defined(SIESTA__METIS)
130    trigger = .true.
131#else
132    trigger = .false.
133#endif
134    write(unit=yaml_fd, fmt='(2X,A,":",1X,3(A))') "metis", &
135&     CH34, trim(yesno(trigger)), CH34
136#ifdef SIESTA__CHESS
137    trigger = .true.
138#else
139    trigger = .false.
140#endif
141    write(unit=yaml_fd, fmt='(2X,A,":",1X,3(A))') "chess", &
142&     CH34, trim(yesno(trigger)), CH34
143#ifdef SIESTA__ELPA
144    trigger = .true.
145#else
146    trigger = .false.
147#endif
148    write(unit=yaml_fd, fmt='(2X,A,":",1X,3(A))') "elpa", &
149&     CH34, trim(yesno(trigger)), CH34
150#ifdef SIESTA__FLOOK
151    trigger = .true.
152#else
153    trigger = .false.
154#endif
155    write(unit=yaml_fd, fmt='(2X,A,":",1X,3(A))') "flook", &
156&     CH34, trim(yesno(trigger)), CH34
157#ifdef SIESTA__PEXSI
158    trigger = .true.
159#else
160    trigger = .false.
161#endif
162    write(unit=yaml_fd, fmt='(2X,A,":",1X,3(A))') "pexsi", &
163&     CH34, trim(yesno(trigger)), CH34
164
165    ! Dump energies
166    write(unit=yaml_fd, fmt='(A,A)') CH10, "energies:"
167    write(unit=yaml_fd, fmt='(2X,A,":",1X,E24.8)') "Ebs", Ebs
168    write(unit=yaml_fd, fmt='(2X,A,":",1X,E24.8)') "Eions", Eions
169    write(unit=yaml_fd, fmt='(2X,A,":",1X,E24.8)') "Ena", Ena
170    write(unit=yaml_fd, fmt='(2X,A,":",1X,E24.8)') "Ekin", Ekin
171    write(unit=yaml_fd, fmt='(2X,A,":",1X,E24.8)') "Enl", Enl
172    write(unit=yaml_fd, fmt='(2X,A,":",1X,E24.8)') "Eso", Eso
173    write(unit=yaml_fd, fmt='(2X,A,":",1X,E24.8)') "Edftu", Edftu
174    write(unit=yaml_fd, fmt='(2X,A,":",1X,E24.8)') "DEna", DEna
175    write(unit=yaml_fd, fmt='(2X,A,":",1X,E24.8)') "DUscf", DUscf
176    write(unit=yaml_fd, fmt='(2X,A,":",1X,E24.8)') "DUext", DUext
177    write(unit=yaml_fd, fmt='(2X,A,":",1X,E24.8)') "Exc", Exc
178    write(unit=yaml_fd, fmt='(2X,A,":",1X,E24.8)') "Ecorrec", Ecorrec
179    write(unit=yaml_fd, fmt='(2X,A,":",1X,E24.8)') "Emadel", Emad
180    write(unit=yaml_fd, fmt='(2X,A,":",1X,E24.8)') "Emeta", Emeta
181    write(unit=yaml_fd, fmt='(2X,A,":",1X,E24.8)') "Emolmec", Emm
182    write(unit=yaml_fd, fmt='(2X,A,":",1X,E24.8)') "Ekinion", Ekinion
183    write(unit=yaml_fd, fmt='(2X,A,":",1X,E24.8)') "Eharris", Eharrs+Ekinion
184    write(unit=yaml_fd, fmt='(2X,A,":",1X,E24.8)') "Etot", Etot+Ekinion
185    write(unit=yaml_fd, fmt='(2X,A,":",1X,E24.8)') "FreeEng", FreeE+Ekinion
186
187    ! Close YAML document
188    write(unit=yaml_fd, fmt='(A)') YAML_FOOTER
189    call io_close(yaml_fd)
190
191  end subroutine siesta_write_yaml
192
193  !> \brief Internal function to translate booleans into "yes"/"no" strings.
194  !!
195  !! This function takes a boolean condition as input and returns a string
196  !! corresponding to the boolean value.
197  !!
198  !! \param[in] cond: boolean condition
199  !! \return string equal to "yes" for .true., and "no" for .false.
200  function yesno(cond) result(word)
201
202    logical, intent(in) :: cond
203
204    character(len=3) :: word
205
206    if ( cond ) then
207      word = "yes"
208    else
209      word = "no "
210    end if
211
212  end function yesno
213
214end module m_io_yaml
215