(git:6344619)
Loading...
Searching...
No Matches
generic_function_input.F
Go to the documentation of this file.
1!--------------------------------------------------------------------------------------------------!
2! CP2K: A general program to perform molecular dynamics simulations !
3! Copyright 2000-2026 CP2K developers group <https://cp2k.org> !
4! !
5! SPDX-License-Identifier: GPL-2.0-or-later !
6!--------------------------------------------------------------------------------------------------!
7
8! **************************************************************************************************
9!> \brief Creates and parses an input section to define a generic function
10!> \note
11!> This module handles the input definition and retrieval, while the task of actually parsing
12!> and evaluating a function is handled by common/fparser module. The general workflow is:
13!>
14!> 1. In the module that creates input sections, use the subroutine to add the subsection
15!> &GENERIC_FUNCTION with keywords FUNCTION, PARAMETERS, UNITS, VALUES and (optionally)
16!> VARIABLES. If a list of symbols has been specified (like input_variables=["X", "Y", "Z"])
17!> then they will be reserved as the variables and the VARIABLES keyword will not appear.
18!> If no such list is given then the VARIABLES keyword requesting input of symbol(s) for
19!> the variable(s) will appear, with no restriction on the number of symbols.
20!> If in addition to the function itself, the numerical derivatives are also needed, set
21!> do_deriv=.TRUE. which will create two additional keywords, DX and ERROR_LIMIT.
22!> NULLIFY(subsection)
23!> CALL create_function_section(subsection, "GENERIC_FUNCTION", "FUNCTION", &
24!> "This subsection defines a generic function.")
25!> ! Add extra keywords here if needed
26!> ! NULLIFY (keyword)
27!> ! CALL keyword_create(keyword, __LOCATION__, ...)
28!> ! CALL section_add_keyword(subsection, keyword)
29!> ! CALL keyword_release(keyword)
30!> ! ...
31!> ! Done adding extra keywords
32!> CALL section_add_subsection(section, subsection)
33!> CALL section_release(subsection)
34!>
35!> 2. Define a type that stores information about the function.
36!> TYPE my_func_type
37!> CHARACTER(LEN=default_path_length) :: function = ""
38!> CHARACTER(LEN=default_string_length), DIMENSION(:), ALLOCATABLE :: parameters
39!> REAL(KIND=dp), DIMENSION(:), ALLOCATABLE :: values
40!> ! To evaluate numerical derivatives also store DX and ERROR_LIMIT
41!> REAL(KIND=dp) :: dx = 0.0_dp, lerr = 0.0_dp
42!> END TYPE my_func_type
43!>
44!> 3. In the module that parses input, use the subroutine to get and save the definitions. The
45!> usage of input_variables should be consistent with above. Note that the variables and the
46!> parameters are all saved in my_func%parameters and my_func%values; with n_var variables
47!> and n_par parameters, both my_func%parameters and my_func%values will have a dimension of
48!> (n_var + n_par), variables first. To evaluate numerical derivatives, also pass
49!> dx=my_func%dx and error_limit=my_func%lerr.
50!> TYPE(my_func_type), POINTER :: my_func
51!> NULLIFY (my_func)
52!> CALL parse_function_section(gen_section, "FUNCTION", my_func%function, &
53!> my_func%parameters, my_func%values)
54!>
55!> 4. Use the subroutines from the fparser module to evaluate the function or its derivative.
56!> CALL initf(1)
57!> CALL parsef(1, TRIM(my_func%function), my_func%parameters)
58!> ! Substitute new values for the variables if needed
59!> ! my_func%values(1) = ...
60!> ! Done substituting
61!> result = evalf(1, my_func%values)
62!> CPASSERT(EvalErrType <= 0)
63!> ! To evaluate numerical derivative with respect to the k-th element of my_func%values:
64!> ! derivative = evalfd(1, k, my_func%values, my_func%dx, err)
65!> ! IF (ABS(err) > my_func%lerr) THEN
66!> ! CALL cp_warn(__LOCATION__, &
67!> ! "Error in computing numerical derivatives larger than allowed")
68!> ! END IF
69!> ! Done evaluating derivative
70!> CALL finalizef()
71!>
72!> \par History
73!> 10.2026 created
74!> \author Zilong He
75! **************************************************************************************************
77 USE cp_units, ONLY: cp_unit_to_cp2k
78 USE fparser, ONLY: docf
87 USE input_val_types, ONLY: char_t,&
88 lchar_t,&
89 real_t
90 USE kinds, ONLY: default_path_length,&
92 dp
94 USE string_utilities, ONLY: compress,&
97#include "./base/base_uses.f90"
98
99 IMPLICIT NONE
100 PRIVATE
101
102 LOGICAL, PRIVATE, PARAMETER :: debug_this_module = .true.
103 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'generic_function_input'
104
105 CHARACTER(len=*), PARAMETER, PRIVATE :: name_kw_dx = "DX", &
106 name_kw_error = "ERROR_LIMIT", &
107 name_kw_parameters = "PARAMETERS", &
108 name_kw_units = "UNITS", &
109 name_kw_values = "VALUES", &
110 name_kw_variables = "VARIABLES"
111
113
114CONTAINS
115
116! **************************************************************************************************
117!> \brief Create the input section for a generic function
118!> \param gen_section the section to be created for the generic function
119!> \param section_name the name of the subsection for the generic function
120!> \param func_name the name of the keyword for the function
121!> \param description a custom description for the subsection
122!> \param repeats whether the section can be repeated
123!> \param do_deriv whether keywords for evaluating numerical derivative are needed
124!> \param input_variables a list of symbols that will be parsed as variables
125!> \par History
126!> 10.2026 created from scratch matching argument names with parse_function_section
127! **************************************************************************************************
128 SUBROUTINE create_function_section(gen_section, section_name, func_name, description, &
129 repeats, do_deriv, input_variables)
130 TYPE(section_type), POINTER :: gen_section
131 CHARACTER(LEN=*), INTENT(IN) :: section_name, func_name, description
132 LOGICAL, INTENT(IN), OPTIONAL :: repeats, do_deriv
133 CHARACTER(LEN=*), DIMENSION(:), INTENT(IN), &
134 OPTIONAL :: input_variables
135
136 CHARACTER(LEN=*), PARAMETER :: default_values_unit_str = "internal_cp2k"
137
138 CHARACTER(LEN=:), ALLOCATABLE :: input_variables_info, my_description
139 CHARACTER(LEN=default_string_length) :: length, my_size_default, my_size_var
140 INTEGER :: i, mydim
141 LOGICAL :: has_kw_variables, my_do_deriv, my_repeats
142 TYPE(keyword_type), POINTER :: keyword
143
144 my_repeats = .false.
145 IF (PRESENT(repeats)) my_repeats = repeats
146 my_do_deriv = .false.
147 IF (PRESENT(do_deriv)) my_do_deriv = do_deriv
148 has_kw_variables = .false.
149 my_description = trim(description)
150 mydim = 0
151 CALL integer_to_string(default_string_length, my_size_default)
153
154 IF (PRESENT(input_variables)) THEN
155 has_kw_variables = .true.
156 mydim = SIZE(input_variables)
157 CALL integer_to_string(mydim, my_size_var)
158 input_variables_info = "The following "//trim(my_size_var)//" symbol(s) will be reserved "// &
159 "as the variable(s) to be substituted for internal evaluation: "
160 DO i = 1, SIZE(input_variables)
161 input_variables_info = input_variables_info//"`"//trim(input_variables(i))//"`"
162 IF (i < SIZE(input_variables)) THEN
163 input_variables_info = input_variables_info//", "
164 ELSE
165 input_variables_info = input_variables_info//". "
166 END IF
167 END DO
168 input_variables_info = input_variables_info//"They cannot be used as parameters."
169 my_description = my_description//newline//newline//input_variables_info
170 END IF
171
172 NULLIFY (keyword)
173 cpassert(.NOT. ASSOCIATED(gen_section))
174
175 CALL section_create(gen_section, __location__, name=trim(section_name), &
176 description=my_description, &
177 n_keywords=0, n_subsections=0, repeats=my_repeats)
178
179 CALL keyword_create(keyword, __location__, name=trim(func_name), &
180 description="Specifies a functional form. The maximum length "// &
181 "is "//trim(adjustl(length))//" characters. "//docf(), &
182 usage=trim(func_name)//" A*X+B*(2/(1+EXP(-0.001*Y))-1)", &
183 type_of_var=lchar_t, n_var=1)
184 CALL section_add_keyword(gen_section, keyword)
185 CALL keyword_release(keyword)
186
187 CALL keyword_create(keyword, __location__, name=name_kw_parameters, &
188 description="Defines the symbols for the parameter(s) in the functional "// &
189 "form, delimited by whitespaces. Symbols are case sensitive and each is "// &
190 "at most "//trim(my_size_default)//" characters long.", &
191 usage=name_kw_parameters//" A B", type_of_var=char_t, &
192 n_var=-1, repeats=.true.)
193 CALL section_add_keyword(gen_section, keyword)
194 CALL keyword_release(keyword)
195
196 CALL keyword_create(keyword, __location__, name=name_kw_units, &
197 description="Optionally, defines CP2K unit string for each of the given "// &
198 "parameter value(s). A valid CP2K unit string is one of the strings that "// &
199 "is listed under the References/Units section of the CP2K manual, or a "// &
200 "combination of multiple of these strings with `*`, `/` and `^`. It is "// &
201 "assumed that the corresponding parameter value is specified in this unit "// &
202 "which needs to be converted first to the internal unit during input parsing. "// &
203 "Note that the meaningfulness of the unit is not checked. If omitted, all "// &
204 "values will be treated as already in the internal unit and used literally.", &
205 usage=name_kw_units//" eV*angstrom^-1 angstrom", type_of_var=char_t, &
206 n_var=-1, repeats=.true.)
207 CALL section_add_keyword(gen_section, keyword)
208 CALL keyword_release(keyword)
209
210 CALL keyword_create(keyword, __location__, name=name_kw_values, &
211 description="Defines the value(s) of the parameter in the functional form.", &
212 usage=name_kw_values//" 50 1.0E+2", type_of_var=real_t, &
213 n_var=-1, repeats=.true., unit_str=default_values_unit_str)
214 CALL section_add_keyword(gen_section, keyword)
215 CALL keyword_release(keyword)
216
217 IF (.NOT. has_kw_variables) THEN
218 CALL keyword_create(keyword, __location__, name=name_kw_variables, &
219 description="Defines the symbols for the variable(s) in the functional "// &
220 "form, delimited by whitespaces. Symbols are case sensitive and each is "// &
221 "at most "//trim(my_size_default)//" characters long.", &
222 usage=name_kw_variables//" X Y", type_of_var=char_t, &
223 n_var=-1)
224 CALL section_add_keyword(gen_section, keyword)
225 CALL keyword_release(keyword)
226 END IF
227
228 IF (my_do_deriv) THEN
229 CALL keyword_create(keyword, __location__, name=name_kw_dx, &
230 description="Parameter used for computing the derivative with the Ridders' method.", &
231 usage=name_kw_dx//" <REAL>", default_r_val=0.1_dp, unit_str="bohr")
232 CALL section_add_keyword(gen_section, keyword)
233 CALL keyword_release(keyword)
234
235 CALL keyword_create(keyword, __location__, name=name_kw_error, &
236 description="Checks that the error in computing the derivative is not larger than "// &
237 "the value set; in case error is larger a warning message is printed.", &
238 usage=name_kw_error//" <REAL>", default_r_val=1.0e-12_dp)
239 CALL section_add_keyword(gen_section, keyword)
240 CALL keyword_release(keyword)
241 END IF
242
243 END SUBROUTINE create_function_section
244
245! **************************************************************************************************
246!> \brief Reads from the input structure all information for generic functions
247!> \param gen_section the name of the section for the generic function
248!> \param func_name the name of the keyword for the function
249!> \param xfunction the function
250!> \param parameters the list of symbols for the parameters in the function
251!> \param values the list of values for the parameters in the function
252!> \param var_values the values of the variables in the function
253!> \param i_rep_sec the index of the section if it may be repeated
254!> \param input_variables the list of symbols for the variables in the function
255!> \param dx the dx for evaluating numerical derivatives of the function
256!> \param error_limit the error limit for evaluating numerical derivatives of the function
257!> \par History
258!> 10.2026 created by migrating the get_generic_info in force_fields_util.F to here
259! **************************************************************************************************
260 SUBROUTINE parse_function_section(gen_section, func_name, xfunction, parameters, values, &
261 var_values, i_rep_sec, input_variables, dx, error_limit)
262 TYPE(section_vals_type), POINTER :: gen_section
263 CHARACTER(LEN=*), INTENT(IN) :: func_name
264 CHARACTER(LEN=default_path_length), INTENT(OUT) :: xfunction
265 CHARACTER(LEN=default_string_length), &
266 ALLOCATABLE, DIMENSION(:), INTENT(OUT) :: parameters
267 REAL(kind=dp), ALLOCATABLE, DIMENSION(:), &
268 INTENT(OUT) :: values
269 REAL(kind=dp), DIMENSION(:), INTENT(IN), OPTIONAL :: var_values
270 INTEGER, INTENT(IN), OPTIONAL :: i_rep_sec
271 CHARACTER(LEN=*), DIMENSION(:), INTENT(IN), &
272 OPTIONAL :: input_variables
273 REAL(kind=dp), INTENT(OUT), OPTIONAL :: dx, error_limit
274
275 CHARACTER(LEN=default_string_length), &
276 DIMENSION(:), POINTER :: my_par, my_par_tmp, my_units, &
277 my_units_tmp, my_var
278 INTEGER :: i, ind, irep, isize, j, mydim, n_par, &
279 n_units, n_val, nblank
280 LOGICAL :: check
281 REAL(kind=dp), DIMENSION(:), POINTER :: my_val, my_val_tmp
282
283 NULLIFY (my_var, my_par, my_val, my_par_tmp, my_val_tmp)
284 NULLIFY (my_units)
285 NULLIFY (my_units_tmp)
286 irep = 1
287 IF (PRESENT(i_rep_sec)) irep = i_rep_sec
288 mydim = 0
289 CALL section_vals_val_get(gen_section, trim(func_name), i_rep_section=irep, c_val=xfunction)
290 CALL compress(xfunction, full=.true.)
291 IF (PRESENT(dx)) THEN
292 CALL section_vals_val_get(gen_section, name_kw_dx, i_rep_section=irep, r_val=dx)
293 END IF
294 IF (PRESENT(error_limit)) THEN
295 CALL section_vals_val_get(gen_section, name_kw_error, i_rep_section=irep, r_val=error_limit)
296 END IF
297 IF (PRESENT(input_variables)) THEN
298 ALLOCATE (my_var(SIZE(input_variables)))
299 my_var = input_variables
300 ELSE
301 CALL section_vals_val_get(gen_section, name_kw_variables, i_rep_section=irep, c_vals=my_var)
302 END IF
303 IF (ASSOCIATED(my_var)) THEN
304 mydim = SIZE(my_var)
305 END IF
306 ! Check for presence of Parameters
307 CALL section_vals_val_get(gen_section, name_kw_parameters, i_rep_section=irep, n_rep_val=n_par)
308 CALL section_vals_val_get(gen_section, name_kw_values, i_rep_section=irep, n_rep_val=n_val)
309 check = (n_par > 0) .EQV. (n_val > 0)
310 cpassert(check)
311 CALL section_vals_val_get(gen_section, name_kw_units, i_rep_section=irep, n_rep_val=n_units)
312 IF (n_par > 0) THEN
313 ! Parameters
314 ALLOCATE (my_par(0))
315 ALLOCATE (my_val(0))
316 DO i = 1, n_par
317 isize = SIZE(my_par)
318 CALL section_vals_val_get(gen_section, name_kw_parameters, i_rep_section=irep, i_rep_val=i, c_vals=my_par_tmp)
319 nblank = count(my_par_tmp == "")
320 CALL reallocate(my_par, 1, isize + SIZE(my_par_tmp) - nblank)
321 ind = 0
322 DO j = 1, SIZE(my_par_tmp)
323 IF (my_par_tmp(j) == "") cycle
324 ind = ind + 1
325 my_par(isize + ind) = my_par_tmp(j)
326 END DO
327 END DO
328 DO i = 1, n_val
329 isize = SIZE(my_val)
330 CALL section_vals_val_get(gen_section, name_kw_values, i_rep_section=irep, i_rep_val=i, r_vals=my_val_tmp)
331 CALL reallocate(my_val, 1, isize + SIZE(my_val_tmp))
332 my_val(isize + 1:isize + SIZE(my_val_tmp)) = my_val_tmp
333 END DO
334 cpassert(SIZE(my_par) == SIZE(my_val))
335 ! Optionally read the units for each parameter value
336 ALLOCATE (my_units(0))
337 IF (n_units > 0) THEN
338 DO i = 1, n_units
339 isize = SIZE(my_units)
340 CALL section_vals_val_get(gen_section, name_kw_units, i_rep_section=irep, i_rep_val=i, c_vals=my_units_tmp)
341 nblank = count(my_units_tmp == "")
342 CALL reallocate(my_units, 1, isize + SIZE(my_units_tmp) - nblank)
343 ind = 0
344 DO j = 1, SIZE(my_units_tmp)
345 IF (my_units_tmp(j) == "") cycle
346 ind = ind + 1
347 my_units(isize + ind) = my_units_tmp(j)
348 END DO
349 END DO
350 cpassert(SIZE(my_units) == SIZE(my_val))
351 END IF
352 mydim = mydim + SIZE(my_val)
353 IF (SIZE(my_val) == 0) THEN
354 DEALLOCATE (my_par)
355 DEALLOCATE (my_val)
356 DEALLOCATE (my_units)
357 END IF
358 END IF
359 ! Handle trivial case of a null function defined
360 ALLOCATE (parameters(mydim))
361 ALLOCATE (values(mydim))
362 IF (mydim > 0) THEN
363 parameters(1:SIZE(my_var)) = my_var
364 values(1:SIZE(my_var)) = 0.0_dp
365 IF (PRESENT(var_values)) THEN
366 cpassert(SIZE(var_values) == SIZE(my_var))
367 values(1:SIZE(my_var)) = var_values
368 END IF
369 IF (ASSOCIATED(my_val)) THEN
370 DO i = 1, SIZE(my_val)
371 parameters(SIZE(my_var) + i) = my_par(i)
372 IF (n_units > 0) THEN
373 values(SIZE(my_var) + i) = cp_unit_to_cp2k(my_val(i), trim(adjustl(my_units(i))))
374 ELSE
375 values(SIZE(my_var) + i) = my_val(i)
376 END IF
377 END DO
378 END IF
379 END IF
380 IF (ASSOCIATED(my_par)) THEN
381 DEALLOCATE (my_par)
382 END IF
383 IF (ASSOCIATED(my_val)) THEN
384 DEALLOCATE (my_val)
385 END IF
386 IF (ASSOCIATED(my_units)) THEN
387 DEALLOCATE (my_units)
388 END IF
389 IF (PRESENT(input_variables)) THEN
390 DEALLOCATE (my_var)
391 END IF
392 END SUBROUTINE parse_function_section
393
394END MODULE generic_function_input
unit conversion facility
Definition cp_units.F:30
real(kind=dp) function, public cp_unit_to_cp2k(value, unit_str, defaults, power)
converts to the internal cp2k units to the given unit
Definition cp_units.F:1282
This public domain function parser module is intended for applications where a set of mathematical ex...
Definition fparser.F:17
character(len=:) function, allocatable, public docf()
...
Definition fparser.F:1151
Creates and parses an input section to define a generic function.
subroutine, public create_function_section(gen_section, section_name, func_name, description, repeats, do_deriv, input_variables)
Create the input section for a generic function.
subroutine, public parse_function_section(gen_section, func_name, xfunction, parameters, values, var_values, i_rep_sec, input_variables, dx, error_limit)
Reads from the input structure all information for generic functions.
represents keywords in an input
subroutine, public keyword_release(keyword)
releases the given keyword (see doc/ReferenceCounting.html)
subroutine, public keyword_create(keyword, location, name, description, usage, type_of_var, n_var, repeats, variants, default_val, default_l_val, default_r_val, default_lc_val, default_c_val, default_i_val, default_l_vals, default_r_vals, default_c_vals, default_i_vals, lone_keyword_val, lone_keyword_l_val, lone_keyword_r_val, lone_keyword_c_val, lone_keyword_i_val, lone_keyword_l_vals, lone_keyword_r_vals, lone_keyword_c_vals, lone_keyword_i_vals, enum_c_vals, enum_i_vals, enum, enum_strict, enum_desc, unit_str, citations, deprecation_notice, removed)
creates a keyword object
objects that represent the structure of input sections and the data contained in an input section
subroutine, public section_create(section, location, name, description, n_keywords, n_subsections, repeats, citations, deprecation_notice)
creates a list of keywords
subroutine, public section_add_keyword(section, keyword)
adds a keyword to the given section
subroutine, public section_vals_val_get(section_vals, keyword_name, i_rep_section, i_rep_val, n_rep_val, val, l_val, i_val, r_val, c_val, l_vals, i_vals, r_vals, c_vals, explicit)
returns the requested value
a wrapper for basic fortran types.
integer, parameter, public real_t
integer, parameter, public lchar_t
integer, parameter, public char_t
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public dp
Definition kinds.F:34
integer, parameter, public default_string_length
Definition kinds.F:57
integer, parameter, public default_path_length
Definition kinds.F:58
Utility routines for the memory handling.
Utilities for string manipulations.
subroutine, public integer_to_string(inumber, string)
Converts an integer number to a string. The WRITE statement will return an error message,...
subroutine, public compress(string, full)
Eliminate multiple space characters in a string. If full is .TRUE., then all spaces are eliminated.
character(len=1), parameter, public newline
subroutine check(condition, label)
Stops with an error message if a test fails.
represent a keyword in the input
represent a section of the input file