(git:0de0cc2)
qs_tddfpt_utils.F
Go to the documentation of this file.
1 !--------------------------------------------------------------------------------------------------!
2 ! CP2K: A general program to perform molecular dynamics simulations !
3 ! Copyright 2000-2024 CP2K developers group <https://cp2k.org> !
4 ! !
5 ! SPDX-License-Identifier: GPL-2.0-or-later !
6 !--------------------------------------------------------------------------------------------------!
7 
8 ! **************************************************************************************************
9 !> \par History
10 !> 09-JUL-2002, TCH, development started
11 ! **************************************************************************************************
13  USE cp_control_types, ONLY: dft_control_type,&
14  tddfpt_control_type
17  USE cp_fm_basic_linalg, ONLY: cp_fm_scale,&
19  cp_fm_trace
22  USE cp_fm_types, ONLY: cp_fm_create,&
25  cp_fm_release,&
28  cp_fm_to_fm,&
29  cp_fm_type
31  USE dbcsr_api, ONLY: dbcsr_p_type
32  USE kinds, ONLY: dp
33  USE physcon, ONLY: evolt
34  USE qs_environment_types, ONLY: get_qs_env,&
35  qs_environment_type
36  USE qs_mo_types, ONLY: get_mo_set
37  USE qs_p_env_methods, ONLY: p_env_create,&
39  USE qs_p_env_types, ONLY: p_env_release,&
40  qs_p_env_type
44 #include "./base/base_uses.f90"
45 
46  IMPLICIT NONE
47 
48  CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qs_tddfpt_utils'
49  LOGICAL, PARAMETER :: debug_this_module = .true.
50 
51 ! **************************************************************************************************
53  INTEGER :: orbit
54  INTEGER :: lumo
55  REAL(kind=dp) :: value
56  TYPE(simple_solution_sorter), POINTER :: next
57  END TYPE simple_solution_sorter
58 
59  PRIVATE
60 
61  ! METHODS
62  PUBLIC :: tddfpt_cleanup, &
63  tddfpt_init, &
66  normalize, &
68 
69 CONTAINS
70 
71 ! **************************************************************************************************
72 !> \brief Initialize some necessary structures for a tddfpt calculation.
73 !> \param p_env perturbation environment to be initialized
74 !> \param t_env tddfpt environment to be initialized
75 !> \param qs_env Quickstep environment with the results of a
76 !> ground state calcualtion
77 ! **************************************************************************************************
78  SUBROUTINE tddfpt_init(p_env, t_env, qs_env)
79 
80  TYPE(qs_p_env_type), INTENT(INOUT) :: p_env
81  TYPE(tddfpt_env_type), INTENT(out) :: t_env
82  TYPE(qs_environment_type), POINTER :: qs_env
83 
84 !------------------!
85 ! create the p_env !
86 !------------------!
87 
88  CALL p_env_create(p_env, qs_env, orthogonal_orbitals=.true.)
89  CALL p_env_psi0_changed(p_env, qs_env) ! update the m_epsilon matrix
90 
91  !------------------!
92  ! create the t_env !
93  !------------------!
94  CALL tddfpt_env_allocate(t_env, p_env, qs_env)
95  CALL tddfpt_env_init(t_env, qs_env)
96 
97  END SUBROUTINE tddfpt_init
98 
99 ! **************************************************************************************************
100 !> \brief Initialize t_env with meaningfull values.
101 !> \param t_env ...
102 !> \param qs_env ...
103 ! **************************************************************************************************
104  SUBROUTINE tddfpt_env_init(t_env, qs_env)
105 
106  TYPE(tddfpt_env_type), INTENT(inout) :: t_env
107  TYPE(qs_environment_type), POINTER :: qs_env
108 
109  INTEGER :: n_spins, spin
110  TYPE(dbcsr_p_type), DIMENSION(:), POINTER :: matrix_s
111  TYPE(dft_control_type), POINTER :: dft_control
112 
113  NULLIFY (matrix_s, dft_control)
114 
115  CALL get_qs_env(qs_env, matrix_s=matrix_s, dft_control=dft_control)
116  n_spins = dft_control%nspins
117  IF (dft_control%tddfpt_control%invert_S) THEN
118  DO spin = 1, n_spins
119  CALL copy_dbcsr_to_fm(matrix_s(1)%matrix, t_env%invS(spin))
120  CALL cp_fm_cholesky_decompose(t_env%invS(spin))
121  CALL cp_fm_cholesky_invert(t_env%invS(spin))
122  END DO
123  END IF
124 
125  END SUBROUTINE tddfpt_env_init
126 
127 ! **************************************************************************************************
128 !> \brief ...
129 !> \param t_env ...
130 !> \param p_env ...
131 ! **************************************************************************************************
132  SUBROUTINE tddfpt_cleanup(t_env, p_env)
133 
134  TYPE(tddfpt_env_type), INTENT(inout) :: t_env
135  TYPE(qs_p_env_type), INTENT(INOUT) :: p_env
136 
137  CALL tddfpt_env_deallocate(t_env)
138  CALL p_env_release(p_env)
139 
140  END SUBROUTINE tddfpt_cleanup
141 
142 ! **************************************************************************************************
143 !> \brief ...
144 !> \param matrices ...
145 !> \param energies ...
146 !> \param n_v ...
147 !> \param qs_env ...
148 ! **************************************************************************************************
149  SUBROUTINE co_initial_guess(matrices, energies, n_v, qs_env)
150 
151  TYPE(cp_fm_type), DIMENSION(:, :), POINTER :: matrices
152  REAL(kind=dp), DIMENSION(:), INTENT(OUT) :: energies
153  INTEGER, INTENT(IN) :: n_v
154  TYPE(qs_environment_type), POINTER :: qs_env
155 
156  INTEGER :: i, n_cols, n_lumos, n_orbits, n_rows, &
157  n_spins, oo, spin, vo
158  REAL(kind=dp) :: evd
159  REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: guess, lumos
160  REAL(kind=dp), DIMENSION(:), POINTER :: orbital_eigenvalues
161  TYPE(dft_control_type), POINTER :: dft_control
162  TYPE(simple_solution_sorter), POINTER :: sorter_iterator, sorter_pointer, &
163  sorter_start
164  TYPE(tddfpt_control_type), POINTER :: tddfpt_control
165 
166 ! number of vectors to initialize
167 
168  NULLIFY (dft_control)
169 
170  CALL get_qs_env(qs_env, dft_control=dft_control)
171  tddfpt_control => dft_control%tddfpt_control
172  n_spins = dft_control%nspins
173  energies = 0.0_dp
174 
175  IF (.NOT. ASSOCIATED(tddfpt_control%lumos)) THEN
176  cpabort("LUMOS missing")
177  END IF
178 
179  DO spin = 1, n_spins
180 
181  n_cols = matrices(1, spin)%matrix_struct%ncol_global
182  n_rows = matrices(1, spin)%matrix_struct%nrow_global
183 
184  DO i = 1, n_v
185  CALL cp_fm_set_all(matrices(i, spin), 0.0_dp)
186  END DO
187 
188  CALL get_mo_set(qs_env%mos(spin), eigenvalues=orbital_eigenvalues)
189 
190  n_lumos = tddfpt_control%lumos(spin)%matrix_struct%ncol_global
191 
192  n_orbits = SIZE(orbital_eigenvalues)
193 
194  !-----------------------------------------!
195  ! create a SORTED list of initial guesses !
196  !-----------------------------------------!
197  ! first element
198  evd = tddfpt_control%lumos_eigenvalues(1, spin) - orbital_eigenvalues(n_orbits)
199  ALLOCATE (sorter_start)
200  sorter_start%orbit = n_orbits
201  sorter_start%lumo = 1
202  sorter_start%value = evd
203  NULLIFY (sorter_start%next)
204  ! rest of the elements
205  DO oo = n_orbits, 1, -1
206  DO vo = 1, n_lumos
207 
208  IF (oo == n_orbits .AND. vo == 1) cycle ! already in list
209 
210  evd = tddfpt_control%lumos_eigenvalues(vo, spin) - orbital_eigenvalues(oo)
211 
212  sorter_iterator => sorter_start
213  NULLIFY (sorter_pointer)
214  DO WHILE (ASSOCIATED(sorter_iterator%next))
215  IF (sorter_iterator%next%value > evd) THEN
216  sorter_pointer => sorter_iterator%next
217  EXIT
218  END IF
219  sorter_iterator => sorter_iterator%next
220  END DO
221 
222  ALLOCATE (sorter_iterator%next)
223  sorter_iterator%next%orbit = oo
224  sorter_iterator%next%lumo = vo
225  sorter_iterator%next%value = evd
226  sorter_iterator%next%next => sorter_pointer
227 
228  END DO
229  END DO
230 
231  ALLOCATE (lumos(n_rows, n_lumos), guess(n_rows, n_orbits))
232  CALL cp_fm_get_submatrix(tddfpt_control%lumos(spin), lumos, &
233  start_col=1, n_cols=n_lumos)
234 
235  !-------------------!
236  ! fill the matrices !
237  !-------------------!
238  sorter_iterator => sorter_start
239  DO i = 1, min(n_v, n_orbits*n_lumos)
240  guess(:, :) = 0.0_dp
241  CALL dcopy(n_rows, lumos(:, sorter_iterator%lumo), 1, &
242  guess(:, sorter_iterator%orbit), 1)
243  CALL cp_fm_set_submatrix(matrices(i, spin), guess)
244  energies(i) = energies(i) + sorter_iterator%value/real(n_spins, dp)
245  sorter_iterator => sorter_iterator%next
246  END DO
247  IF (n_v > n_orbits*n_lumos) THEN
248  DO i = n_orbits*n_lumos + 1, n_v
249  CALL cp_fm_init_random(matrices(i, spin), n_orbits)
250  energies(i) = 1.0e38_dp
251  END DO
252  END IF
253 
254  !--------------!
255  ! some cleanup !
256  !--------------!
257  DEALLOCATE (lumos, guess)
258  sorter_iterator => sorter_start
259  DO WHILE (ASSOCIATED(sorter_iterator))
260  sorter_pointer => sorter_iterator
261  sorter_iterator => sorter_iterator%next
262  DEALLOCATE (sorter_pointer)
263  END DO
264 
265  END DO
266 
267  END SUBROUTINE co_initial_guess
268 
269 ! **************************************************************************************************
270 !> \brief ...
271 !> \param qs_env ...
272 !> \param t_env ...
273 ! **************************************************************************************************
274  SUBROUTINE find_contributions(qs_env, t_env)
275 
276  TYPE(qs_environment_type), POINTER :: qs_env
277  TYPE(tddfpt_env_type), INTENT(IN) :: t_env
278 
279  INTEGER :: i, j, n_ev, n_spins, occ, output_unit, &
280  spin, virt
281  INTEGER, DIMENSION(2) :: nhomos, nlumos, nrows
282  REAL(kind=dp) :: contribution, summed_contributions
283  REAL(kind=dp), ALLOCATABLE, DIMENSION(:, :) :: homo_coeff_col, lumo_coeff_col
284  TYPE(cp_fm_type), ALLOCATABLE, DIMENSION(:) :: s_lumos
285  TYPE(dbcsr_p_type), DIMENSION(:), POINTER :: matrix_s
286  TYPE(dft_control_type), POINTER :: dft_control
287  TYPE(tddfpt_control_type) :: t_control
288 
289  NULLIFY (matrix_s, dft_control)
290  output_unit = cp_logger_get_default_io_unit()
291  CALL get_qs_env(qs_env, matrix_s=matrix_s, dft_control=dft_control)
292 
293  IF (output_unit > 0) WRITE (output_unit, *)
294  IF (output_unit > 0) WRITE (output_unit, *)
295 
296  t_control = dft_control%tddfpt_control
297  n_ev = t_control%n_ev
298  n_spins = dft_control%nspins
299 
300  ALLOCATE (s_lumos(n_spins))
301 
302  DO spin = 1, n_spins
303  nrows(spin) = t_control%lumos(spin)%matrix_struct%nrow_global
304  nhomos(spin) = t_env%evecs(1, spin)%matrix_struct%ncol_global
305  nlumos(spin) = t_control%lumos(spin)%matrix_struct%ncol_global
306  CALL cp_fm_create(s_lumos(spin), t_control%lumos(spin)%matrix_struct, &
307  "S times lumos")
308  CALL cp_dbcsr_sm_fm_multiply(matrix_s(1)%matrix, t_control%lumos(spin), &
309  s_lumos(spin), nlumos(spin), 1.0_dp, 0.0_dp)
310  END DO
311 
312  ALLOCATE (homo_coeff_col(maxval(nrows(1:n_spins)), 1), &
313  lumo_coeff_col(maxval(nrows(1:n_spins)), 1))
314  DO i = 1, n_ev
315  IF (output_unit > 0) THEN
316  WRITE (output_unit, '(A,I3,5X,F15.6)') " excited state : ", i, t_env%evals(i)*evolt
317  WRITE (output_unit, *)
318  END IF
319  summed_contributions = 0.0_dp
320  DO spin = 1, n_spins
321  IF (n_spins == 2) THEN
322  IF (spin == 1) THEN
323  IF (output_unit > 0) WRITE (output_unit, *) 'alpha:'
324  ELSE
325  IF (output_unit > 0) WRITE (output_unit, *) 'beta:'
326  END IF
327  END IF
328  searchloop: DO occ = nhomos(spin), 1, -1
329  CALL cp_fm_get_submatrix(t_env%evecs(i, spin), homo_coeff_col, &
330  1, occ, nrows(spin), 1)
331  DO virt = 1, nlumos(spin)
332  CALL cp_fm_get_submatrix(s_lumos(spin), lumo_coeff_col, &
333  1, virt, nrows(spin), 1)
334  contribution = 0.0_dp
335  DO j = 1, nrows(spin)
336  contribution = contribution + homo_coeff_col(j, 1)*lumo_coeff_col(j, 1)
337  END DO
338  summed_contributions = summed_contributions + (contribution)**2
339  IF (abs(contribution) > 5.0e-2_dp) THEN
340  IF (output_unit > 0) WRITE (output_unit, '(14X,I5,A,I5,10X,F8.3,5X,F8.3)') &
341  occ, " ->", nhomos(spin) + virt, abs(contribution), summed_contributions
342  END IF
343  IF (abs(summed_contributions - 1.0_dp) < 1.0e-3_dp) cycle searchloop
344  END DO
345  END DO searchloop
346  END DO
347  IF (output_unit > 0) WRITE (output_unit, *)
348  END DO
349 
350  !
351  ! punch a checksum for the regs
352  IF (output_unit > 0) THEN
353  WRITE (output_unit, '(T2,A,E14.6)') ' TDDFPT : CheckSum =', sqrt(sum(t_env%evals**2))
354  END IF
355 
356  CALL cp_fm_release(s_lumos)
357 
358  DEALLOCATE (homo_coeff_col, lumo_coeff_col)
359 
360  END SUBROUTINE find_contributions
361 
362 ! **************************************************************************************************
363 !> \brief ...
364 !> \param X ...
365 !> \param tmp_vec ...
366 !> \param metric ...
367 ! **************************************************************************************************
368  SUBROUTINE normalize(X, tmp_vec, metric)
369 
370  TYPE(cp_fm_type), DIMENSION(:), INTENT(IN) :: x, tmp_vec
371  TYPE(dbcsr_p_type), DIMENSION(:), POINTER :: metric
372 
373  INTEGER :: n_spins, spin
374  REAL(kind=dp) :: norm, tmp
375 
376  n_spins = SIZE(x)
377  norm = 0.0_dp
378 
379  DO spin = 1, n_spins
380  tmp = 0.0_dp
381  CALL cp_dbcsr_sm_fm_multiply(metric(1)%matrix, x(spin), &
382  tmp_vec(spin), &
383  x(spin)%matrix_struct%ncol_global, &
384  1.0_dp, 0.0_dp)
385  CALL cp_fm_trace(x(spin), tmp_vec(spin), tmp)
386  norm = norm + tmp
387  END DO
388 
389  norm = sqrt(norm)
390  DO spin = 1, n_spins
391  CALL cp_fm_scale((1.0_dp/norm), x(spin))
392  END DO
393 
394  END SUBROUTINE normalize
395 
396  !---------------------------------------!
397  ! x must not be changed in this routine !
398  ! tmp_vec may be changed !
399  !---------------------------------------!
400 ! **************************************************************************************************
401 !> \brief ...
402 !> \param X ...
403 !> \param V_set ...
404 !> \param SV_set ...
405 !> \param work ...
406 !> \param n ...
407 ! **************************************************************************************************
408  SUBROUTINE reorthogonalize(X, V_set, SV_set, work, n)
409 
410  TYPE(cp_fm_type), DIMENSION(:), INTENT(IN) :: x
411  TYPE(cp_fm_type), DIMENSION(:, :), INTENT(IN) :: v_set, sv_set
412  TYPE(cp_fm_type), DIMENSION(:), INTENT(IN) :: work
413  INTEGER, INTENT(IN) :: n
414 
415  CHARACTER(LEN=*), PARAMETER :: routinen = 'reorthogonalize'
416 
417  INTEGER :: handle, i, n_spins, spin
418  REAL(dp) :: dot_product, tmp
419 
420  CALL timeset(routinen, handle)
421 
422  IF (n > 0) THEN
423 
424  n_spins = SIZE(x)
425  DO spin = 1, n_spins
426  CALL cp_fm_to_fm(x(spin), work(spin))
427  END DO
428 
429  DO i = 1, n
430  dot_product = 0.0_dp
431  DO spin = 1, n_spins
432  CALL cp_fm_trace(sv_set(i, spin), work(spin), tmp)
433  dot_product = dot_product + tmp
434  END DO
435  DO spin = 1, n_spins
436  CALL cp_fm_scale_and_add(1.0_dp, x(spin), &
437  -1.0_dp*dot_product, v_set(i, spin))
438  END DO
439  END DO
440 
441  END IF
442 
443  CALL timestop(handle)
444 
445  END SUBROUTINE reorthogonalize
446 
447 ! **************************************************************************************************
448 
449 END MODULE qs_tddfpt_utils
Defines control structures, which contain the parameters and the settings for the DFT-based calculati...
DBCSR operations in CP2K.
subroutine, public cp_dbcsr_sm_fm_multiply(matrix, fm_in, fm_out, ncol, alpha, beta)
multiply a dbcsr with a fm matrix
subroutine, public copy_dbcsr_to_fm(matrix, fm)
Copy a DBCSR matrix to a BLACS matrix.
basic linear algebra operations for full matrices
subroutine, public cp_fm_scale_and_add(alpha, matrix_a, beta, matrix_b)
calc A <- alpha*A + beta*B optimized for alpha == 1.0 (just add beta*B) and beta == 0....
subroutine, public cp_fm_scale(alpha, matrix_a)
scales a matrix matrix_a = alpha * matrix_b
various cholesky decomposition related routines
subroutine, public cp_fm_cholesky_invert(matrix, n, info_out)
used to replace the cholesky decomposition by the inverse
subroutine, public cp_fm_cholesky_decompose(matrix, n, info_out)
used to replace a symmetric positive def. matrix M with its cholesky decomposition U: M = U^T * U,...
represent a full matrix distributed on many processors
Definition: cp_fm_types.F:15
subroutine, public cp_fm_set_submatrix(fm, new_values, start_row, start_col, n_rows, n_cols, alpha, beta, transpose)
sets a submatrix of a full matrix fm(start_row:start_row+n_rows,start_col:start_col+n_cols) = alpha*o...
Definition: cp_fm_types.F:768
subroutine, public cp_fm_set_all(matrix, alpha, beta)
set all elements of a matrix to the same value, and optionally the diagonal to a different one
Definition: cp_fm_types.F:535
subroutine, public cp_fm_get_submatrix(fm, target_m, start_row, start_col, n_rows, n_cols, transpose)
gets a submatrix of a full matrix op(target_m)(1:n_rows,1:n_cols) =fm(start_row:start_row+n_rows,...
Definition: cp_fm_types.F:901
subroutine, public cp_fm_init_random(matrix, ncol, start_col)
fills a matrix with random numbers
Definition: cp_fm_types.F:452
subroutine, public cp_fm_create(matrix, matrix_struct, name, use_sp)
creates a new full matrix with the given structure
Definition: cp_fm_types.F:167
various routines to log and control the output. The idea is that decisions about where to log should ...
integer function, public cp_logger_get_default_io_unit(logger)
returns the unit nr for the ionode (-1 on all other processors) skips as well checks if the procs cal...
Defines the basic variable types.
Definition: kinds.F:23
integer, parameter, public dp
Definition: kinds.F:34
Definition of physical constants:
Definition: physcon.F:68
real(kind=dp), parameter, public evolt
Definition: physcon.F:183
subroutine, public get_qs_env(qs_env, atomic_kind_set, qs_kind_set, cell, super_cell, cell_ref, use_ref_cell, kpoints, dft_control, mos, sab_orb, sab_all, qmmm, qmmm_periodic, sac_ae, sac_ppl, sac_lri, sap_ppnl, sab_vdw, sab_scp, sap_oce, sab_lrc, sab_se, sab_xtbe, sab_tbe, sab_core, sab_xb, sab_xtb_nonbond, sab_almo, sab_kp, sab_kp_nosym, particle_set, energy, force, matrix_h, matrix_h_im, matrix_ks, matrix_ks_im, matrix_vxc, run_rtp, rtp, matrix_h_kp, matrix_h_im_kp, matrix_ks_kp, matrix_ks_im_kp, matrix_vxc_kp, kinetic_kp, matrix_s_kp, matrix_w_kp, matrix_s_RI_aux_kp, matrix_s, matrix_s_RI_aux, matrix_w, matrix_p_mp2, matrix_p_mp2_admm, rho, rho_xc, pw_env, ewald_env, ewald_pw, active_space, mpools, input, para_env, blacs_env, scf_control, rel_control, kinetic, qs_charges, vppl, rho_core, rho_nlcc, rho_nlcc_g, ks_env, ks_qmmm_env, wf_history, scf_env, local_particles, local_molecules, distribution_2d, dbcsr_dist, molecule_kind_set, molecule_set, subsys, cp_subsys, oce, local_rho_set, rho_atom_set, task_list, task_list_soft, rho0_atom_set, rho0_mpole, rhoz_set, ecoul_1c, rho0_s_rs, rho0_s_gs, do_kpoints, has_unit_metric, requires_mo_derivs, mo_derivs, mo_loc_history, nkind, natom, nelectron_total, nelectron_spin, efield, neighbor_list_id, linres_control, xas_env, virial, cp_ddapc_env, cp_ddapc_ewald, outer_scf_history, outer_scf_ihistory, x_data, et_coupling, dftb_potential, results, se_taper, se_store_int_env, se_nddo_mpole, se_nonbond_env, admm_env, lri_env, lri_density, exstate_env, ec_env, dispersion_env, gcp_env, vee, rho_external, external_vxc, mask, mp2_env, bs_env, kg_env, WannierCentres, atprop, ls_scf_env, do_transport, transport_env, v_hartree_rspace, s_mstruct_changed, rho_changed, potential_changed, forces_up_to_date, mscfg_env, almo_scf_env, gradient_history, variable_history, embed_pot, spin_embed_pot, polar_env, mos_last_converged, rhs)
Get the QUICKSTEP environment.
Definition and initialisation of the mo data type.
Definition: qs_mo_types.F:22
subroutine, public get_mo_set(mo_set, maxocc, homo, lfomo, nao, nelectron, n_el_f, nmo, eigenvalues, occupation_numbers, mo_coeff, mo_coeff_b, uniform_occupation, kTS, mu, flexible_electron_count)
Get the components of a MO set data structure.
Definition: qs_mo_types.F:397
Utility functions for the perturbation calculations.
subroutine, public p_env_psi0_changed(p_env, qs_env)
To be called after the value of psi0 has changed. Recalculates the quantities S_psi0 and m_epsilon.
subroutine, public p_env_create(p_env, qs_env, p1_option, p1_admm_option, orthogonal_orbitals, linres_control)
allocates and initializes the perturbation environment (no setup)
basis types for the calculation of the perturbation of density theory.
subroutine, public p_env_release(p_env)
relases the given p_env (see doc/ReferenceCounting.html)
subroutine, public tddfpt_env_deallocate(t_env)
...
subroutine, public tddfpt_env_allocate(t_env, p_env, qs_env)
...
subroutine, public reorthogonalize(X, V_set, SV_set, work, n)
...
subroutine, public find_contributions(qs_env, t_env)
...
logical, parameter debug_this_module
subroutine, public tddfpt_init(p_env, t_env, qs_env)
Initialize some necessary structures for a tddfpt calculation.
subroutine, public co_initial_guess(matrices, energies, n_v, qs_env)
...
subroutine, public tddfpt_cleanup(t_env, p_env)
...
subroutine, public normalize(X, tmp_vec, metric)
...