diff --git a/docs/advanced/input_files/input-main.md b/docs/advanced/input_files/input-main.md index b4aaed03a4..0ed67ff2b7 100644 --- a/docs/advanced/input_files/input-main.md +++ b/docs/advanced/input_files/input-main.md @@ -476,6 +476,12 @@ - [td\_trigo\_amp](#td_trigo_amp) - [td\_heavi\_t0](#td_heavi_t0) - [td\_heavi\_amp](#td_heavi_amp) + - [td\_supsine\_amp](#td_supsine_amp) + - [td\_supsine\_freq](#td_supsine_freq) + - [td\_supsine\_phase](#td_supsine_phase) + - [td\_supsine\_sigma](#td_supsine_sigma) + - [td\_supsine\_tstart](#td_supsine_tstart) + - [td\_supsine\_tend](#td_supsine_tend) - [init\_vecpot\_file](#init_vecpot_file) - [ocp](#ocp) - [ocp\_set](#ocp_set) @@ -4143,48 +4149,51 @@ ### td_dt - **Type**: Real -- **Description**: The time step used in electronic propagation. Setting td_dt will reset the value of md_dt to td_dt * estep_per_md. +- **Description**: The time step used for electronic propagation. If td_dt is not specified, it is set to md_dt / estep_per_md. If td_dt is specified explicitly, md_dt is reset to td_dt * estep_per_md. - **Default**: md_dt / estep_per_md - **Unit**: fs ### td_edm - **Type**: Integer -- **Description**: Method to calculate the energy-density matrix, mainly affects the calculation of force and stress. - - 0: Using the original formula. - - 1: Using the formula for ground state (deprecated). Note that this usually does not hold if wave function is not the eigenstate of the Hamiltonian. +- **Description**: Method used to calculate the energy-density matrix for the overlap contribution to forces in LCAO RT-TDDFT. + - 0: Use $\mathrm{EDM}_{\boldsymbol{k}}=\frac{1}{2}\left(S_{\boldsymbol{k}}^{-1}H_{\boldsymbol{k}}\rho_{\boldsymbol{k}}+\rho_{\boldsymbol{k}}H_{\boldsymbol{k}}S_{\boldsymbol{k}}^{-1}\right)$. + - 1: Use the ground-state eigenvalue-weighted expression $\mathrm{EDM}_{\mu\nu,\boldsymbol{k}}=\sum_i w_{i\boldsymbol{k}}\epsilon_{i\boldsymbol{k}}C_{\mu i,\boldsymbol{k}}C_{\nu i,\boldsymbol{k}}^*$. This expression is deprecated for RT-TDDFT and is generally not valid when the propagated wave functions are not Hamiltonian eigenstates. - **Default**: 0 ### td_print_eij - **Type**: Real -- **Description**: Controls the printing of Hamiltonian matrix elements. - - < 0: Suppress all output. - - >= 0: Print only elements with either i or j exceeding td_print_eij. +- **Description**: Controls output of the propagated-state Hamiltonian matrix elements $E_{ij}=\Braket{\psi_i | \hat{H} | \psi_j}$ to the running log. The printed band indices $i$ and $j$ are one-based global indices. Both the threshold and the printed matrix elements are in Ry. + - $\lt 0$: Disable the output. + - $\geqslant 0$: Print an element when either $\left|\operatorname{Re}E_{ij}\right|$ or $\left|\operatorname{Im}E_{ij}\right|$ is greater than or equal to td_print_eij. - **Default**: -1 - **Unit**: Ry ### td_propagator - **Type**: Integer -- **Description**: Methods of electronic propagation. - - 0: Crank-Nicolson, based on matrix inversion. - - 1: 4th-order Taylor expansion of exponential. - - 2: Enforced time-reversal symmetry (ETRS). - - 3: Crank-Nicolson, based on solving linear equation. +- **Description**: Method used to propagate the electronic states in a nonorthogonal LCAO basis. The formulas below use Hartree atomic units, with $S$, $H$, and $\Delta t=\mathtt{td\_dt}$ evaluated as required by each approximation. + - 0: Crank-Nicolson through an explicitly constructed evolution matrix, $U=\left[S+\mathrm{i}H\Delta t/2\right]^{-1}\left[S-\mathrm{i}H\Delta t/2\right]$. + - 1: Fourth-order Taylor approximation to the exponential. With $\mathcal{A}=-\mathrm{i}S^{-1}H\Delta t$, $U=I+\mathcal{A}+\mathcal{A}^2/2+\mathcal{A}^3/6+\mathcal{A}^4/24$. + - 2: Enforced time-reversal symmetry (ETRS), $U(t+\Delta t,t)=\exp\left[-\mathrm{i}S^{-1}H(t+\Delta t)\Delta t/2\right]\exp\left[-\mathrm{i}S^{-1}H(t)\Delta t/2\right]$. In the implementation, each exponential is replaced by the fourth-order Taylor polynomial from method 1 evaluated with a half time step. + - 3: Crank-Nicolson by directly solving $\left[S+\mathrm{i}H\Delta t/2\right]\psi(t+\Delta t)=\left[S-\mathrm{i}H\Delta t/2\right]\psi(t)$. + + > Note: GPU execution currently supports only method 0 in both single-GPU and multi-GPU solver configurations. CPU execution supports methods 0 through 3. - **Default**: 0 ### td_vext - **Type**: Boolean -- **Description**: - True: Add a laser-material interaction (external electric field). +- **Description**: Controls whether a time-dependent external electric field is applied. + - True: Add a laser-material interaction (external electric field). - False: No external electric field. - **Default**: False ### td_vext_dire -- **Type**: String -- **Description**: Specifies the direction(s) of the external electric field when td_vext is enabled. For example, td_vext_dire 1 2 indicates that external electric fields are applied to both the x and y directions simultaneously. Electric field parameters can also be written as strings. For example, td_gauss_phase 0 1.5707963 indicates that the Gaussian type electric fields in the x and y directions have a phase delay of pi/2. +- **Type**: Vector of Integer +- **Description**: Specifies one absolute Cartesian direction for each external electric field when td_vext is enabled. Unlike the ground-state efield_dir parameter, these directions are not defined by lattice or reciprocal-lattice vectors. The number of values must equal that of td_ttype, and repeated directions are allowed; fields assigned to the same direction are added. For example, td_vext_dire 1 2 applies one field along Cartesian x and one along Cartesian y. - 1: The external field direction is along the x-axis. - 2: The external field direction is along the y-axis. - 3: The external field direction is along the z-axis. @@ -4201,217 +4210,296 @@ ### td_ttype -- **Type**: String -- **Description**: Type of electric field in the time domain. - - 0: Gaussian type function. - - 1: Trapezoid type function. - - 2: Trigonometric type function. - - 3: Heaviside type function. +- **Type**: Vector of Integer +- **Description**: Specifies one time-domain type for each external electric field. Its number of values must equal that of td_vext_dire. Parameters belonging to each type must provide exactly one value for every occurrence of that type, in occurrence order; fields with a repeated direction are added. + + The formulas below use Hartree atomic units. For every ordinary input frequency $f$, $\omega=2\pi f$; $\Delta t=\mathtt{td\_dt}$; and $E_0$ denotes the corresponding amplitude parameter. A step-valued parameter $n_q$ represents the physical time $t_q=n_q\Delta t$. + + - 0: Gaussian pulse, $E(t)=E_0\cos\left[\omega(t-t_0)+\varphi\right]\mathrm{e}^{-(t-t_0)^2/(2\sigma^2)}$, where $t_0=\mathtt{td\_gauss\_t0}\Delta t$. + - 1: Trapezoid pulse, $E(t)=E_0g(t)\cos(\omega t+\varphi)$. With $t_1=\mathtt{td\_trape\_t1}\Delta t$, $t_2=\mathtt{td\_trape\_t2}\Delta t$, and $t_3=\mathtt{td\_trape\_t3}\Delta t$, the envelope is $g(t)=t/t_1$ for $0\leqslant t\lt t_1$, $g(t)=1$ for $t_1\leqslant t\lt t_2$, $g(t)=(t_3-t)/(t_3-t_2)$ for $t_2\leqslant t\lt t_3$, and $g(t)=0$ otherwise. + - 2: Trigonometric pulse, $E(t)=E_0\cos(\omega_1t+\varphi_1)\sin^2(\omega_2t+\varphi_2)$. + - 3: Heaviside pulse defined on electronic steps. With $n_0=\mathtt{td\_heavi\_t0}$, $E(n)=E_0$ for $n\lt n_0$ and $E(n)=0$ for $n\geqslant n_0$. + - 4: Finite-support supersine pulse. For $t_{\mathrm{s}}\lt t\lt t_{\mathrm{e}}$, the envelope is $f(t)=\left\{\sin\left[\pi\frac{t-t_{\mathrm{s}}}{t_{\mathrm{e}}-t_{\mathrm{s}}}\right]\right\}^{\frac{\pi}{\sigma}\left|\frac{t-t_{\mathrm{s}}}{t_{\mathrm{e}}-t_{\mathrm{s}}}-\frac{1}{2}\right|}$ and the electric field is $E(t)=E_0\left\{f(t)\cos\left[\omega\left(t-\frac{t_{\mathrm{s}}+t_{\mathrm{e}}}{2}\right)+\varphi\right]+\frac{\dot{f}(t)}{\omega}\sin\left[\omega\left(t-\frac{t_{\mathrm{s}}+t_{\mathrm{e}}}{2}\right)+\varphi\right]\right\}$. The corresponding analytic vector potential is $\boldsymbol{A}(t)=-\frac{E_0}{\omega}f(t)\sin\left[\omega\left(t-\frac{t_{\mathrm{s}}+t_{\mathrm{e}}}{2}\right)+\varphi\right]\hat{\boldsymbol{e}}$, with $\boldsymbol{E}(t)=-\partial\boldsymbol{A}(t)/\partial t$. The envelope, electric field, and vector potential are zero at the pulse boundaries and outside the interval. + + In the velocity and hybrid gauges, ABACUS obtains the vector potential actually used in propagation by Simpson integration of the selected electric fields, including the supersine field, so a residual at the numerical-quadrature accuracy scale may remain. - **Default**: 0 ### td_tstart - **Type**: Integer -- **Description**: The initial time step when the time-dependent electric field is activated. +- **Description**: First electronic step at which the time-dependent electric field is active. The interval from td_tstart through td_tend includes both endpoints. On each active step $n$, the velocity and hybrid gauges integrate the field over $[n\Delta t,(n+1)\Delta t]$, where $\Delta t=\mathtt{td\_dt}$. - **Default**: 1 ### td_tend - **Type**: Integer -- **Description**: The final time step when the time-dependent electric field is deactivated. The field remains active between td_tstart and td_tend. +- **Description**: Last electronic step at which the time-dependent electric field is active. The interval from td_tstart through td_tend includes both endpoints. On each active step $n$, the velocity and hybrid gauges integrate the field over $[n\Delta t,(n+1)\Delta t]$, where $\Delta t=\mathtt{td\_dt}$. - **Default**: 1000 ### td_lcut1 - **Type**: Real -- **Description**: The lower bound of the interval in the length gauge RT-TDDFT, where the coordinate is the fractional coordinate. +- **Description**: Lower fractional-coordinate cutoff for the periodic spatial modulation used in the length gauge. Let $c_1=\mathtt{td\_lcut1}$, $c_2=\mathtt{td\_lcut2}$, $D=c_2-c_1$, and $G=c_1+1-c_2$. For a fractional coordinate $x$, the field factor is $\eta(x)=1$ when $c_1\leqslant x\lt c_2$ and $\eta(x)=-D/G$ elsewhere. The reversed outer interval makes the potential periodic and continuous and gives the field zero cell average. - **Default**: 0.05 ### td_lcut2 - **Type**: Real -- **Description**: The upper bound of the interval in the length gauge RT-TDDFT, where the coordinate is the fractional coordinate. +- **Description**: Upper fractional-coordinate cutoff for the periodic spatial modulation used in the length gauge. Let $c_1=\mathtt{td\_lcut1}$, $c_2=\mathtt{td\_lcut2}$, $D=c_2-c_1$, and $G=c_1+1-c_2$. For a fractional coordinate $x$, the field factor is $\eta(x)=1$ when $c_1\leqslant x\lt c_2$ and $\eta(x)=-D/G$ elsewhere. The reversed outer interval makes the potential periodic and continuous and gives the field zero cell average. - **Default**: 0.95 ### td_gauss_freq -- **Type**: String -- **Description**: Frequency of the Gaussian type electric field. +- **Type**: Vector of Real +- **Availability**: *td_ttype contains 0* +- **Description**: Ordinary frequency $f$ in the Gaussian-pulse formula, with $\omega=2\pi f$. Supply exactly one value for each td_ttype 0 occurrence, in occurrence order. - **Default**: 22.13 - **Unit**: 1/fs ### td_gauss_phase -- **Type**: String -- **Description**: Phase of the Gaussian type electric field. +- **Type**: Vector of Real +- **Availability**: *td_ttype contains 0* +- **Description**: Carrier phase $\varphi$ in the Gaussian-pulse formula. Supply exactly one value for each td_ttype 0 occurrence, in occurrence order. - **Default**: 0.0 +- **Unit**: rad ### td_gauss_sigma -- **Type**: String -- **Description**: Pulse width (standard deviation) of the Gaussian type electric field. +- **Type**: Vector of Real +- **Availability**: *td_ttype contains 0* +- **Description**: Nonzero standard deviation $\sigma$ of the Gaussian envelope. Supply exactly one value for each td_ttype 0 occurrence, in occurrence order. - **Default**: 30.0 - **Unit**: fs ### td_gauss_t0 -- **Type**: String -- **Description**: Step number of the time center of the Gaussian type electric field. +- **Type**: Vector of Real +- **Availability**: *td_ttype contains 0* +- **Description**: Electronic-step position of the Gaussian center, which defines $t_0=\mathtt{td\_gauss\_t0}\Delta t$. Supply exactly one value for each td_ttype 0 occurrence, in occurrence order. - **Default**: 100 ### td_gauss_amp -- **Type**: String -- **Description**: Amplitude of the Gaussian type electric field. +- **Type**: Vector of Real +- **Availability**: *td_ttype contains 0* +- **Description**: Electric-field scale $E_0$ in the Gaussian-pulse formula. Supply exactly one value for each td_ttype 0 occurrence, in occurrence order. - **Default**: 0.25 - **Unit**: V/Angstrom ### td_trape_freq -- **Type**: String -- **Description**: Frequency of the trapezoid type electric field. +- **Type**: Vector of Real +- **Availability**: *td_ttype contains 1* +- **Description**: Ordinary carrier frequency $f$ in the trapezoid-pulse formula, with $\omega=2\pi f$. Supply exactly one value for each td_ttype 1 occurrence, in occurrence order. - **Default**: 1.60 - **Unit**: 1/fs ### td_trape_phase -- **Type**: String -- **Description**: Phase of the trapezoid type electric field. +- **Type**: Vector of Real +- **Availability**: *td_ttype contains 1* +- **Description**: Carrier phase $\varphi$ in the trapezoid-pulse formula. Supply exactly one value for each td_ttype 1 occurrence, in occurrence order. - **Default**: 0.0 +- **Unit**: rad ### td_trape_t1 -- **Type**: String -- **Description**: Step number of the time interval t1 of the trapezoid type electric field. +- **Type**: Vector of Real +- **Availability**: *td_ttype contains 1* +- **Description**: Electronic step defining the end of the linear rise, $t_1=\mathtt{td\_trape\_t1}\Delta t$. Each field must satisfy td_trape_t1 <= td_trape_t2 <= td_trape_t3. Supply exactly one value for each td_ttype 1 occurrence, in occurrence order. - **Default**: 1875 ### td_trape_t2 -- **Type**: String -- **Description**: Step number of the time interval t2 of the trapezoid type electric field. +- **Type**: Vector of Real +- **Availability**: *td_ttype contains 1* +- **Description**: Electronic step defining the end of the plateau, $t_2=\mathtt{td\_trape\_t2}\Delta t$. Each field must satisfy td_trape_t1 <= td_trape_t2 <= td_trape_t3. Supply exactly one value for each td_ttype 1 occurrence, in occurrence order. - **Default**: 5625 ### td_trape_t3 -- **Type**: String -- **Description**: Step number of the time interval t3 of the trapezoid type electric field. +- **Type**: Vector of Real +- **Availability**: *td_ttype contains 1* +- **Description**: Electronic step defining the end of the linear fall, $t_3=\mathtt{td\_trape\_t3}\Delta t$. Each field must satisfy td_trape_t1 <= td_trape_t2 <= td_trape_t3. Supply exactly one value for each td_ttype 1 occurrence, in occurrence order. - **Default**: 7500 ### td_trape_amp -- **Type**: String -- **Description**: Amplitude of the trapezoid type electric field. +- **Type**: Vector of Real +- **Availability**: *td_ttype contains 1* +- **Description**: Electric-field scale $E_0$ in the trapezoid-pulse formula. Supply exactly one value for each td_ttype 1 occurrence, in occurrence order. - **Default**: 2.74 - **Unit**: V/Angstrom ### td_trigo_freq1 -- **Type**: String -- **Description**: Frequency 1 of the trigonometric type electric field. +- **Type**: Vector of Real +- **Availability**: *td_ttype contains 2* +- **Description**: First ordinary frequency $f_1$ in the trigonometric-pulse formula, with $\omega_1=2\pi f_1$. Supply exactly one value for each td_ttype 2 occurrence, in occurrence order. - **Default**: 1.164656 - **Unit**: 1/fs ### td_trigo_freq2 -- **Type**: String -- **Description**: Frequency 2 of the trigonometric type electric field. +- **Type**: Vector of Real +- **Availability**: *td_ttype contains 2* +- **Description**: Second ordinary frequency $f_2$ in the trigonometric-pulse formula, with $\omega_2=2\pi f_2$. Supply exactly one value for each td_ttype 2 occurrence, in occurrence order. - **Default**: 0.029116 - **Unit**: 1/fs ### td_trigo_phase1 -- **Type**: String -- **Description**: Phase 1 of the trigonometric type electric field. +- **Type**: Vector of Real +- **Availability**: *td_ttype contains 2* +- **Description**: Carrier phase $\varphi_1$ in the cosine factor of the trigonometric-pulse formula. Supply exactly one value for each td_ttype 2 occurrence, in occurrence order. - **Default**: 0.0 +- **Unit**: rad ### td_trigo_phase2 -- **Type**: String -- **Description**: Phase 2 of the trigonometric type electric field. +- **Type**: Vector of Real +- **Availability**: *td_ttype contains 2* +- **Description**: Envelope phase $\varphi_2$ in the sine-squared factor of the trigonometric-pulse formula. Supply exactly one value for each td_ttype 2 occurrence, in occurrence order. - **Default**: 0.0 +- **Unit**: rad ### td_trigo_amp -- **Type**: String -- **Description**: Amplitude of the trigonometric type electric field. +- **Type**: Vector of Real +- **Availability**: *td_ttype contains 2* +- **Description**: Electric-field scale $E_0$ in the trigonometric-pulse formula. Supply exactly one value for each td_ttype 2 occurrence, in occurrence order. - **Default**: 2.74 - **Unit**: V/Angstrom ### td_heavi_t0 -- **Type**: String -- **Description**: Step number of the switch time of the Heaviside type electric field. +- **Type**: Vector of Real +- **Availability**: *td_ttype contains 3* +- **Description**: Electronic switch step $n_0$ in the Heaviside-pulse definition. The field is $E_0$ for $n\lt n_0$ and zero for $n\geqslant n_0$. Supply exactly one value for each td_ttype 3 occurrence, in occurrence order. - **Default**: 100 ### td_heavi_amp -- **Type**: String -- **Description**: Amplitude of the Heaviside type electric field. +- **Type**: Vector of Real +- **Availability**: *td_ttype contains 3* +- **Description**: Electric-field scale $E_0$ in the Heaviside-pulse definition. Supply exactly one value for each td_ttype 3 occurrence, in occurrence order. - **Default**: 1.0 - **Unit**: V/Angstrom +### td_supsine_amp + +- **Type**: Vector of Real +- **Availability**: *td_ttype contains 4* +- **Description**: Carrier electric-field scale $E_0$ of each supersine pulse. This is not a normalization of the complete waveform maximum, because the envelope-derivative term also contributes. Supply exactly one value for each td_ttype 4 occurrence, in occurrence order. +- **Default**: 0.27 +- **Unit**: V/Angstrom + +### td_supsine_freq + +- **Type**: Vector of Real +- **Availability**: *td_ttype contains 4* +- **Description**: Nonzero ordinary carrier frequency $f$ of each supersine pulse, with $\omega=2\pi f$. Supply exactly one value for each td_ttype 4 occurrence, in occurrence order. +- **Default**: 0.18737028625 +- **Unit**: 1/fs + +### td_supsine_phase + +- **Type**: Vector of Real +- **Availability**: *td_ttype contains 4* +- **Description**: Electric-field carrier phase $\varphi$ at the center of each supersine envelope. A value of 0 places a cosine carrier maximum at the envelope center. Supply exactly one value for each td_ttype 4 occurrence, in occurrence order. +- **Default**: 0.0 +- **Unit**: rad + +### td_supsine_sigma + +- **Type**: Vector of Real +- **Availability**: *td_ttype contains 4* +- **Description**: Dimensionless shape parameter $\sigma$ of each supersine envelope. It must satisfy $0\lt\sigma\lt\pi/2$ so that the electric field approaches zero at the pulse boundaries. Supply exactly one value for each td_ttype 4 occurrence, in occurrence order. +- **Default**: 0.75 + +### td_supsine_tstart + +- **Type**: Vector of String +- **Availability**: *td_ttype contains 4* +- **Description**: Integer electronic step at the left, exactly zero boundary of each supersine pulse, defining $t_{\mathrm{s}}=\mathtt{td\_supsine\_tstart}\Delta t$. Supply exactly one integer or default token for each td_ttype 4 occurrence, in occurrence order; each default token inherits td_tstart. The complete pulse support must lie inside the inclusive global td_tstart to td_tend interval; hard truncation of a supersine pulse is rejected. +- **Default**: default + +### td_supsine_tend + +- **Type**: Vector of String +- **Availability**: *td_ttype contains 4* +- **Description**: Integer electronic step at the right, exactly zero boundary of each supersine pulse, defining $t_{\mathrm{e}}=\mathtt{td\_supsine\_tend}\Delta t$. Supply exactly one integer or default token for each td_ttype 4 occurrence, in occurrence order; each default token inherits td_tend. The complete pulse support must lie inside the inclusive global td_tstart to td_tend interval; hard truncation of a supersine pulse is rejected. +- **Default**: default + ### init_vecpot_file - **Type**: Boolean -- **Description**: Initialize vector potential through file or not. - - True: Initialize vector potential from file At.dat (unit: a.u.). It consists of four columns, representing the step number and vector potential on each direction. - - False: Calculate vector potential by integrating the electric field. +- **Description**: Selects the source of the Cartesian vector potential used by LCAO RT-TDDFT. + - True: Read vector_pot.txt from the calculation working directory. Each non-comment line must contain four columns: a conventionally one-based electronic-step label followed by $A_x$, $A_y$, and $A_z$ in atomic units. Rows are consumed sequentially; the first column is read as a label and is not used for lookup. If propagation continues beyond the available rows, the last row is reused. + - False: Obtain the vector potential by integrating the configured electric field. - **Default**: False ### ocp - **Type**: Boolean -- **Description**: - True: Fixes the band occupations based on the values specified in ocp_set. - - False: Does not fix the band occupations. +- **Description**: Controls fixed band occupations. In calculations other than LCAO RT-TDDFT, fixed values are applied during electronic-state setup. In LCAO RT-TDDFT, the initial ground-state SCF determines occupations normally, and fixed values from ocp_set are applied during the subsequent real-time propagation steps. + - True: Use the fixed occupations specified by ocp_set during propagation. + - False: Keep the occupations determined by the initial SCF. - **Default**: False ### ocp_set - **Type**: String -- **Description**: If ocp is set to 1, ocp_set must be provided as a string specifying the occupation numbers for each band across all k-points. The format follows a space-separated pattern, where occupations are assigned sequentially to bands for each k-point. A shorthand notation Nx can be used to repeat a value x for N bands. - - Example: - 1 10*1 0 1 represents occupations for 13 bands, where the 12th band is fully unoccupied (0), and all others are occupied (1). - - For a system with multiple k-points, the occupations must be specified for all k-points, following their order in the output file kpoints (may lead to fractional occupations). - - Incorrect specification of ocp_set could lead to inconsistencies in electron counting, causing the calculation to terminate with an error. +- **Description**: Fixed occupation weights used when ocp is true. Values are assigned band by band for each k-point, following k-point order. In LCAO RT-TDDFT, the initial ground-state SCF uses its normally determined occupations, and this array is applied only during subsequent real-time propagation steps. The repetition syntax N*x expands to N copies of x. + - Example: 1 10*1 0 1 expands to 13 values, with the 12th value equal to 0 and all other values equal to 1. + - After expansion, the array length must equal nks * nbands. + - The sum of all weights must equal nelec; otherwise the calculation terminates with an error. - **Default**: None ### out_dipole - **Type**: Boolean -- **Description**: - True: Output electric dipole moment. - - False: Do not output electric dipole moment. +- **Description**: Controls electric-dipole output. In RT-TDDFT, each enabled spin channel is written to OUT.{suffix}/dipole_s[spin].txt using a one-based spin number. Every row contains the one-based electronic-step index followed by the Cartesian electronic-dipole components $P_x$, $P_y$, and $P_z$ in atomic units. The running log additionally reports the electronic, ionic, and total dipoles and the norm of the total dipole. + - True: Output the electric dipole information. + - False: Do not output the electric dipole information. - **Default**: False ### out_current - **Type**: Integer -- **Description**: Controls the current-density output method for LCAO RT-TDDFT. +- **Availability**: *basis_type==lcao and esolver_type==tddft* +- **Description**: Controls the current-density output method for LCAO RT-TDDFT. Output rows contain the one-based electronic-step index followed by $J_x$, $J_y$, and $J_z$ in atomic units. - 0: Do not output current. - - 1: Explicitly construct the velocity operator from the momentum, vector-potential, and KB nonlocal-pseudopotential terms using two-center integral / spherical grid integral: $\hat{v}_{\alpha}=-\mathrm{i}\nabla_{\alpha}+A_{\alpha}(t)+\mathrm{i}\left[\widetilde{V}_{\mathrm{NL}}^{\mathrm{KB}},r_{\alpha}\right]$, where $\widetilde{V}_{\mathrm{NL}}^{\mathrm{KB}}=\mathrm{e}^{-\mathrm{i}\boldsymbol{A}(t)\cdot\boldsymbol{r}}\hat{V}_{\mathrm{NL}}^{\mathrm{KB}}\mathrm{e}^{\mathrm{i}\boldsymbol{A}(t)\cdot\boldsymbol{r}}$. $\boldsymbol{A}(t)$ is nonzero only for the velocity gauge (td_stype=1); otherwise $\boldsymbol{A}(t)=0$. Other nonlocal Hamiltonian terms (e.g., EXX) are not included explicitly. - - 2: Use the full Hamiltonian to construct the generalized velocity matrix in a nonorthogonal NAO basis: $\widetilde{v}_{\alpha}=\partial_{\alpha}H+\mathrm{i}HS^{-1}\mathcal{R}_{\alpha}-\mathrm{i}\mathcal{R}_{\alpha}S^{-1}H-HS^{-1}\partial_{\alpha}S$. This includes all contributions available in the real-space Hamiltonian matrix when enabled. This method is more general but more expensive. + - 1: Explicitly construct the velocity operator from the momentum, vector-potential, and KB nonlocal-pseudopotential terms using two-center and spherical-grid integrals: $\hat{v}_{\alpha}=-\mathrm{i}\nabla_{\alpha}+A_{\alpha}(t)+\mathrm{i}\left[\widetilde{V}_{\mathrm{NL}}^{\mathrm{KB}},r_{\alpha}\right]$, where $\widetilde{V}_{\mathrm{NL}}^{\mathrm{KB}}=\mathrm{e}^{-\mathrm{i}\boldsymbol{A}(t)\cdot\boldsymbol{r}}\hat{V}_{\mathrm{NL}}^{\mathrm{KB}}\mathrm{e}^{\mathrm{i}\boldsymbol{A}(t)\cdot\boldsymbol{r}}$. $\boldsymbol{A}(t)$ is nonzero only for the velocity gauge (td_stype=1); otherwise $\boldsymbol{A}(t)=0$. Other nonlocal Hamiltonian terms, such as EXX, are not included explicitly. The total current is written to OUT.{suffix}/current_tot.txt. + - 2: Use the full Hamiltonian to construct the generalized velocity matrix in a nonorthogonal NAO basis, $\widetilde{v}_{\alpha}=\partial_{\alpha}H+\mathrm{i}HS^{-1}\mathcal{R}_{\alpha}-\mathrm{i}\mathcal{R}_{\alpha}S^{-1}H-HS^{-1}\partial_{\alpha}S$. This includes all contributions available in the real-space Hamiltonian matrix when enabled. This method is more general but more expensive. The total current is written to OUT.{suffix}/current_tot_comm.txt. - **Default**: 0 ### out_current_k - **Type**: Boolean -- **Description**: - True: Output current for each k-points separately. - - False: Output current in total. +- **Availability**: *basis_type==lcao and esolver_type==tddft and out_current>0* +- **Description**: Controls whether LCAO RT-TDDFT current density is also resolved by spin and k-point. The total-current file is always written when out_current is 1 or 2. + - True: In addition to the total, out_current=1 writes OUT.{suffix}/current_s[spin]k[kpoint].txt; out_current=2 writes OUT.{suffix}/current_s[spin]k[kpoint]_comm.txt. Both use one-based spin and k-point numbers, with k-points numbered independently within each spin channel. Each row contains the one-based electronic-step index followed by $J_x$, $J_y$, and $J_z$ in atomic units. + - False: Output only current_tot.txt for out_current=1 or current_tot_comm.txt for out_current=2. - **Default**: False ### out_efield - **Type**: Boolean -- **Description**: Whether to output the electric field data to files. When enabled, writes real-time electric field values (unit: V/A) into files named efield_[num].txt, where [num] is the sequential index of the electric field ranges from 0 to N-1 for N configured fields. It is noteworthy that the field type sequence follows td_ttype, while the direction sequence follows td_vext_dire. - - True: Output electric field. - - False: Do not output electric field. +- **Availability**: *esolver_type==tddft and td_vext==true* +- **Description**: Controls time-dependent electric-field output. For each configured field, OUT.{suffix}/efield_[index].txt contains two columns: physical time in fs and the field value in V/Angstrom. The one-based field index follows the occurrence order shared by td_ttype and td_vext_dire, so fields assigned to the same direction remain in separate files. At initialization, a fresh calculation with md_restart=False truncates the files corresponding to the currently configured fields, whereas a calculation with md_restart=True preserves them and appends new samples. + - True: Output electric-field values on active electronic steps. + - False: Do not output electric-field values. - **Default**: False ### out_vecpot - **Type**: Boolean -- **Description**: Output vector potential or not (unit: a.u.). - - True: Output vector potential into file At.dat. - - False: Do not output vector potential. +- **Availability**: *basis_type==lcao and esolver_type==tddft* +- **Description**: Controls Cartesian vector-potential output for LCAO RT-TDDFT. OUT.{suffix}/vector_pot.txt contains four columns: the one-based electronic-step index followed by $A_x$, $A_y$, and $A_z$ in atomic units. At initialization, a fresh calculation with md_restart=False truncates the file and writes a new header, whereas a calculation with md_restart=True preserves a nonempty existing file and appends new samples. If the restart output file is missing or empty, a new file with a header is created. + - True: Write vector-potential samples on electronic propagation steps. + - False: Do not output the vector potential. - **Default**: False [back to top](#full-list-of-input-keywords) diff --git a/docs/parameters.yaml b/docs/parameters.yaml index a87375c033..7c142a45cb 100644 --- a/docs/parameters.yaml +++ b/docs/parameters.yaml @@ -2407,7 +2407,7 @@ parameters: category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" type: Real description: | - The time step used in electronic propagation. Setting td_dt will reset the value of md_dt to td_dt * estep_per_md. + The time step used for electronic propagation. If td_dt is not specified, it is set to md_dt / estep_per_md. If td_dt is specified explicitly, md_dt is reset to td_dt * estep_per_md. default_value: md_dt / estep_per_md unit: fs availability: "" @@ -2415,9 +2415,9 @@ parameters: category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" type: Integer description: | - Method to calculate the energy-density matrix, mainly affects the calculation of force and stress. - * 0: Using the original formula. - * 1: Using the formula for ground state (deprecated). Note that this usually does not hold if wave function is not the eigenstate of the Hamiltonian. + Method used to calculate the energy-density matrix for the overlap contribution to forces in LCAO RT-TDDFT. + * 0: Use $\mathrm{EDM}_{\boldsymbol{k}}=\frac{1}{2}\left(S_{\boldsymbol{k}}^{-1}H_{\boldsymbol{k}}\rho_{\boldsymbol{k}}+\rho_{\boldsymbol{k}}H_{\boldsymbol{k}}S_{\boldsymbol{k}}^{-1}\right)$. + * 1: Use the ground-state eigenvalue-weighted expression $\mathrm{EDM}_{\mu\nu,\boldsymbol{k}}=\sum_i w_{i\boldsymbol{k}}\epsilon_{i\boldsymbol{k}}C_{\mu i,\boldsymbol{k}}C_{\nu i,\boldsymbol{k}}^*$. This expression is deprecated for RT-TDDFT and is generally not valid when the propagated wave functions are not Hamiltonian eigenstates. default_value: "0" unit: "" availability: "" @@ -2425,9 +2425,9 @@ parameters: category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" type: Real description: | - Controls the printing of Hamiltonian matrix elements. - * < 0: Suppress all output. - * >= 0: Print only elements with either i or j exceeding td_print_eij. + Controls output of the propagated-state Hamiltonian matrix elements $E_{ij}=\Braket{\psi_i | \hat{H} | \psi_j}$ to the running log. The printed band indices $i$ and $j$ are one-based global indices. Both the threshold and the printed matrix elements are in Ry. + * $\lt 0$: Disable the output. + * $\geqslant 0$: Print an element when either $\left|\operatorname{Re}E_{ij}\right|$ or $\left|\operatorname{Im}E_{ij}\right|$ is greater than or equal to td_print_eij. default_value: "-1" unit: Ry availability: "" @@ -2435,11 +2435,13 @@ parameters: category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" type: Integer description: | - Methods of electronic propagation. - * 0: Crank-Nicolson, based on matrix inversion. - * 1: 4th-order Taylor expansion of exponential. - * 2: Enforced time-reversal symmetry (ETRS). - * 3: Crank-Nicolson, based on solving linear equation. + Method used to propagate the electronic states in a nonorthogonal LCAO basis. The formulas below use Hartree atomic units, with $S$, $H$, and $\Delta t=\mathtt{td\_dt}$ evaluated as required by each approximation. + * 0: Crank-Nicolson through an explicitly constructed evolution matrix, $U=\left[S+\mathrm{i}H\Delta t/2\right]^{-1}\left[S-\mathrm{i}H\Delta t/2\right]$. + * 1: Fourth-order Taylor approximation to the exponential. With $\mathcal{A}=-\mathrm{i}S^{-1}H\Delta t$, $U=I+\mathcal{A}+\mathcal{A}^2/2+\mathcal{A}^3/6+\mathcal{A}^4/24$. + * 2: Enforced time-reversal symmetry (ETRS), $U(t+\Delta t,t)=\exp\left[-\mathrm{i}S^{-1}H(t+\Delta t)\Delta t/2\right]\exp\left[-\mathrm{i}S^{-1}H(t)\Delta t/2\right]$. In the implementation, each exponential is replaced by the fourth-order Taylor polynomial from method 1 evaluated with a half time step. + * 3: Crank-Nicolson by directly solving $\left[S+\mathrm{i}H\Delta t/2\right]\psi(t+\Delta t)=\left[S-\mathrm{i}H\Delta t/2\right]\psi(t)$. + + [NOTE] GPU execution currently supports only method 0 in both single-GPU and multi-GPU solver configurations. CPU execution supports methods 0 through 3. default_value: "0" unit: "" availability: "" @@ -2447,6 +2449,7 @@ parameters: category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" type: Boolean description: | + Controls whether a time-dependent external electric field is applied. * True: Add a laser-material interaction (external electric field). * False: No external electric field. default_value: "False" @@ -2454,9 +2457,9 @@ parameters: availability: "" - name: td_vext_dire category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" - type: String + type: Vector of Integer description: | - Specifies the direction(s) of the external electric field when td_vext is enabled. For example, td_vext_dire 1 2 indicates that external electric fields are applied to both the x and y directions simultaneously. Electric field parameters can also be written as strings. For example, td_gauss_phase 0 1.5707963 indicates that the Gaussian type electric fields in the x and y directions have a phase delay of pi/2. + Specifies one absolute Cartesian direction for each external electric field when td_vext is enabled. Unlike the ground-state efield_dir parameter, these directions are not defined by lattice or reciprocal-lattice vectors. The number of values must equal that of td_ttype, and repeated directions are allowed; fields assigned to the same direction are added. For example, td_vext_dire 1 2 applies one field along Cartesian x and one along Cartesian y. * 1: The external field direction is along the x-axis. * 2: The external field direction is along the y-axis. * 3: The external field direction is along the z-axis. @@ -2476,13 +2479,18 @@ parameters: availability: "" - name: td_ttype category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" - type: String + type: Vector of Integer description: | - Type of electric field in the time domain. - * 0: Gaussian type function. - * 1: Trapezoid type function. - * 2: Trigonometric type function. - * 3: Heaviside type function. + Specifies one time-domain type for each external electric field. Its number of values must equal that of td_vext_dire. Parameters belonging to each type must provide exactly one value for every occurrence of that type, in occurrence order; fields with a repeated direction are added. + + The formulas below use Hartree atomic units. For every ordinary input frequency $f$, $\omega=2\pi f$; $\Delta t=\mathtt{td\_dt}$; and $E_0$ denotes the corresponding amplitude parameter. A step-valued parameter $n_q$ represents the physical time $t_q=n_q\Delta t$. + * 0: Gaussian pulse, $E(t)=E_0\cos\left[\omega(t-t_0)+\varphi\right]\mathrm{e}^{-(t-t_0)^2/(2\sigma^2)}$, where $t_0=\mathtt{td\_gauss\_t0}\Delta t$. + * 1: Trapezoid pulse, $E(t)=E_0g(t)\cos(\omega t+\varphi)$. With $t_1=\mathtt{td\_trape\_t1}\Delta t$, $t_2=\mathtt{td\_trape\_t2}\Delta t$, and $t_3=\mathtt{td\_trape\_t3}\Delta t$, the envelope is $g(t)=t/t_1$ for $0\leqslant t\lt t_1$, $g(t)=1$ for $t_1\leqslant t\lt t_2$, $g(t)=(t_3-t)/(t_3-t_2)$ for $t_2\leqslant t\lt t_3$, and $g(t)=0$ otherwise. + * 2: Trigonometric pulse, $E(t)=E_0\cos(\omega_1t+\varphi_1)\sin^2(\omega_2t+\varphi_2)$. + * 3: Heaviside pulse defined on electronic steps. With $n_0=\mathtt{td\_heavi\_t0}$, $E(n)=E_0$ for $n\lt n_0$ and $E(n)=0$ for $n\geqslant n_0$. + * 4: Finite-support supersine pulse. For $t_{\mathrm{s}}\lt t\lt t_{\mathrm{e}}$, the envelope is $f(t)=\left\{\sin\left[\pi\frac{t-t_{\mathrm{s}}}{t_{\mathrm{e}}-t_{\mathrm{s}}}\right]\right\}^{\frac{\pi}{\sigma}\left|\frac{t-t_{\mathrm{s}}}{t_{\mathrm{e}}-t_{\mathrm{s}}}-\frac{1}{2}\right|}$ and the electric field is $E(t)=E_0\left\{f(t)\cos\left[\omega\left(t-\frac{t_{\mathrm{s}}+t_{\mathrm{e}}}{2}\right)+\varphi\right]+\frac{\dot{f}(t)}{\omega}\sin\left[\omega\left(t-\frac{t_{\mathrm{s}}+t_{\mathrm{e}}}{2}\right)+\varphi\right]\right\}$. The corresponding analytic vector potential is $\boldsymbol{A}(t)=-\frac{E_0}{\omega}f(t)\sin\left[\omega\left(t-\frac{t_{\mathrm{s}}+t_{\mathrm{e}}}{2}\right)+\varphi\right]\hat{\boldsymbol{e}}$, with $\boldsymbol{E}(t)=-\partial\boldsymbol{A}(t)/\partial t$. The envelope, electric field, and vector potential are zero at the pulse boundaries and outside the interval. + + In the velocity and hybrid gauges, ABACUS obtains the vector potential actually used in propagation by Simpson integration of the selected electric fields, including the supersine field, so a residual at the numerical-quadrature accuracy scale may remain. default_value: "0" unit: "" availability: "" @@ -2490,7 +2498,7 @@ parameters: category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" type: Integer description: | - The initial time step when the time-dependent electric field is activated. + First electronic step at which the time-dependent electric field is active. The interval from td_tstart through td_tend includes both endpoints. On each active step $n$, the velocity and hybrid gauges integrate the field over $[n\Delta t,(n+1)\Delta t]$, where $\Delta t=\mathtt{td\_dt}$. default_value: "1" unit: "" availability: "" @@ -2498,7 +2506,7 @@ parameters: category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" type: Integer description: | - The final time step when the time-dependent electric field is deactivated. The field remains active between td_tstart and td_tend. + Last electronic step at which the time-dependent electric field is active. The interval from td_tstart through td_tend includes both endpoints. On each active step $n$, the velocity and hybrid gauges integrate the field over $[n\Delta t,(n+1)\Delta t]$, where $\Delta t=\mathtt{td\_dt}$. default_value: "1000" unit: "" availability: "" @@ -2506,7 +2514,7 @@ parameters: category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" type: Real description: | - The lower bound of the interval in the length gauge RT-TDDFT, where the coordinate is the fractional coordinate. + Lower fractional-coordinate cutoff for the periodic spatial modulation used in the length gauge. Let $c_1=\mathtt{td\_lcut1}$, $c_2=\mathtt{td\_lcut2}$, $D=c_2-c_1$, and $G=c_1+1-c_2$. For a fractional coordinate $x$, the field factor is $\eta(x)=1$ when $c_1\leqslant x\lt c_2$ and $\eta(x)=-D/G$ elsewhere. The reversed outer interval makes the potential periodic and continuous and gives the field zero cell average. default_value: "0.05" unit: "" availability: "" @@ -2514,161 +2522,209 @@ parameters: category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" type: Real description: | - The upper bound of the interval in the length gauge RT-TDDFT, where the coordinate is the fractional coordinate. + Upper fractional-coordinate cutoff for the periodic spatial modulation used in the length gauge. Let $c_1=\mathtt{td\_lcut1}$, $c_2=\mathtt{td\_lcut2}$, $D=c_2-c_1$, and $G=c_1+1-c_2$. For a fractional coordinate $x$, the field factor is $\eta(x)=1$ when $c_1\leqslant x\lt c_2$ and $\eta(x)=-D/G$ elsewhere. The reversed outer interval makes the potential periodic and continuous and gives the field zero cell average. default_value: "0.95" unit: "" availability: "" - name: td_gauss_freq category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" - type: String + type: Vector of Real description: | - Frequency of the Gaussian type electric field. + Ordinary frequency $f$ in the Gaussian-pulse formula, with $\omega=2\pi f$. Supply exactly one value for each td_ttype 0 occurrence, in occurrence order. default_value: "22.13" unit: 1/fs - availability: "" + availability: td_ttype contains 0 - name: td_gauss_phase category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" - type: String + type: Vector of Real description: | - Phase of the Gaussian type electric field. + Carrier phase $\varphi$ in the Gaussian-pulse formula. Supply exactly one value for each td_ttype 0 occurrence, in occurrence order. default_value: "0.0" - unit: "" - availability: "" + unit: rad + availability: td_ttype contains 0 - name: td_gauss_sigma category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" - type: String + type: Vector of Real description: | - Pulse width (standard deviation) of the Gaussian type electric field. + Nonzero standard deviation $\sigma$ of the Gaussian envelope. Supply exactly one value for each td_ttype 0 occurrence, in occurrence order. default_value: "30.0" unit: fs - availability: "" + availability: td_ttype contains 0 - name: td_gauss_t0 category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" - type: String + type: Vector of Real description: | - Step number of the time center of the Gaussian type electric field. + Electronic-step position of the Gaussian center, which defines $t_0=\mathtt{td\_gauss\_t0}\Delta t$. Supply exactly one value for each td_ttype 0 occurrence, in occurrence order. default_value: "100" unit: "" - availability: "" + availability: td_ttype contains 0 - name: td_gauss_amp category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" - type: String + type: Vector of Real description: | - Amplitude of the Gaussian type electric field. + Electric-field scale $E_0$ in the Gaussian-pulse formula. Supply exactly one value for each td_ttype 0 occurrence, in occurrence order. default_value: "0.25" unit: V/Angstrom - availability: "" + availability: td_ttype contains 0 - name: td_trape_freq category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" - type: String + type: Vector of Real description: | - Frequency of the trapezoid type electric field. + Ordinary carrier frequency $f$ in the trapezoid-pulse formula, with $\omega=2\pi f$. Supply exactly one value for each td_ttype 1 occurrence, in occurrence order. default_value: "1.60" unit: 1/fs - availability: "" + availability: td_ttype contains 1 - name: td_trape_phase category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" - type: String + type: Vector of Real description: | - Phase of the trapezoid type electric field. + Carrier phase $\varphi$ in the trapezoid-pulse formula. Supply exactly one value for each td_ttype 1 occurrence, in occurrence order. default_value: "0.0" - unit: "" - availability: "" + unit: rad + availability: td_ttype contains 1 - name: td_trape_t1 category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" - type: String + type: Vector of Real description: | - Step number of the time interval t1 of the trapezoid type electric field. + Electronic step defining the end of the linear rise, $t_1=\mathtt{td\_trape\_t1}\Delta t$. Each field must satisfy td_trape_t1 <= td_trape_t2 <= td_trape_t3. Supply exactly one value for each td_ttype 1 occurrence, in occurrence order. default_value: "1875" unit: "" - availability: "" + availability: td_ttype contains 1 - name: td_trape_t2 category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" - type: String + type: Vector of Real description: | - Step number of the time interval t2 of the trapezoid type electric field. + Electronic step defining the end of the plateau, $t_2=\mathtt{td\_trape\_t2}\Delta t$. Each field must satisfy td_trape_t1 <= td_trape_t2 <= td_trape_t3. Supply exactly one value for each td_ttype 1 occurrence, in occurrence order. default_value: "5625" unit: "" - availability: "" + availability: td_ttype contains 1 - name: td_trape_t3 category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" - type: String + type: Vector of Real description: | - Step number of the time interval t3 of the trapezoid type electric field. + Electronic step defining the end of the linear fall, $t_3=\mathtt{td\_trape\_t3}\Delta t$. Each field must satisfy td_trape_t1 <= td_trape_t2 <= td_trape_t3. Supply exactly one value for each td_ttype 1 occurrence, in occurrence order. default_value: "7500" unit: "" - availability: "" + availability: td_ttype contains 1 - name: td_trape_amp category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" - type: String + type: Vector of Real description: | - Amplitude of the trapezoid type electric field. + Electric-field scale $E_0$ in the trapezoid-pulse formula. Supply exactly one value for each td_ttype 1 occurrence, in occurrence order. default_value: "2.74" unit: V/Angstrom - availability: "" + availability: td_ttype contains 1 - name: td_trigo_freq1 category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" - type: String + type: Vector of Real description: | - Frequency 1 of the trigonometric type electric field. + First ordinary frequency $f_1$ in the trigonometric-pulse formula, with $\omega_1=2\pi f_1$. Supply exactly one value for each td_ttype 2 occurrence, in occurrence order. default_value: "1.164656" unit: 1/fs - availability: "" + availability: td_ttype contains 2 - name: td_trigo_freq2 category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" - type: String + type: Vector of Real description: | - Frequency 2 of the trigonometric type electric field. + Second ordinary frequency $f_2$ in the trigonometric-pulse formula, with $\omega_2=2\pi f_2$. Supply exactly one value for each td_ttype 2 occurrence, in occurrence order. default_value: "0.029116" unit: 1/fs - availability: "" + availability: td_ttype contains 2 - name: td_trigo_phase1 category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" - type: String + type: Vector of Real description: | - Phase 1 of the trigonometric type electric field. + Carrier phase $\varphi_1$ in the cosine factor of the trigonometric-pulse formula. Supply exactly one value for each td_ttype 2 occurrence, in occurrence order. default_value: "0.0" - unit: "" - availability: "" + unit: rad + availability: td_ttype contains 2 - name: td_trigo_phase2 category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" - type: String + type: Vector of Real description: | - Phase 2 of the trigonometric type electric field. + Envelope phase $\varphi_2$ in the sine-squared factor of the trigonometric-pulse formula. Supply exactly one value for each td_ttype 2 occurrence, in occurrence order. default_value: "0.0" - unit: "" - availability: "" + unit: rad + availability: td_ttype contains 2 - name: td_trigo_amp category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" - type: String + type: Vector of Real description: | - Amplitude of the trigonometric type electric field. + Electric-field scale $E_0$ in the trigonometric-pulse formula. Supply exactly one value for each td_ttype 2 occurrence, in occurrence order. default_value: "2.74" unit: V/Angstrom - availability: "" + availability: td_ttype contains 2 - name: td_heavi_t0 category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" - type: String + type: Vector of Real description: | - Step number of the switch time of the Heaviside type electric field. + Electronic switch step $n_0$ in the Heaviside-pulse definition. The field is $E_0$ for $n\lt n_0$ and zero for $n\geqslant n_0$. Supply exactly one value for each td_ttype 3 occurrence, in occurrence order. default_value: "100" unit: "" - availability: "" + availability: td_ttype contains 3 - name: td_heavi_amp category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" - type: String + type: Vector of Real description: | - Amplitude of the Heaviside type electric field. + Electric-field scale $E_0$ in the Heaviside-pulse definition. Supply exactly one value for each td_ttype 3 occurrence, in occurrence order. default_value: "1.0" unit: V/Angstrom - availability: "" + availability: td_ttype contains 3 + - name: td_supsine_amp + category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" + type: Vector of Real + description: | + Carrier electric-field scale $E_0$ of each supersine pulse. This is not a normalization of the complete waveform maximum, because the envelope-derivative term also contributes. Supply exactly one value for each td_ttype 4 occurrence, in occurrence order. + default_value: "0.27" + unit: V/Angstrom + availability: td_ttype contains 4 + - name: td_supsine_freq + category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" + type: Vector of Real + description: | + Nonzero ordinary carrier frequency $f$ of each supersine pulse, with $\omega=2\pi f$. Supply exactly one value for each td_ttype 4 occurrence, in occurrence order. + default_value: "0.18737028625" + unit: 1/fs + availability: td_ttype contains 4 + - name: td_supsine_phase + category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" + type: Vector of Real + description: | + Electric-field carrier phase $\varphi$ at the center of each supersine envelope. A value of 0 places a cosine carrier maximum at the envelope center. Supply exactly one value for each td_ttype 4 occurrence, in occurrence order. + default_value: "0.0" + unit: rad + availability: td_ttype contains 4 + - name: td_supsine_sigma + category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" + type: Vector of Real + description: | + Dimensionless shape parameter $\sigma$ of each supersine envelope. It must satisfy $0\lt\sigma\lt\pi/2$ so that the electric field approaches zero at the pulse boundaries. Supply exactly one value for each td_ttype 4 occurrence, in occurrence order. + default_value: "0.75" + unit: "" + availability: td_ttype contains 4 + - name: td_supsine_tstart + category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" + type: Vector of String + description: | + Integer electronic step at the left, exactly zero boundary of each supersine pulse, defining $t_{\mathrm{s}}=\mathtt{td\_supsine\_tstart}\Delta t$. Supply exactly one integer or default token for each td_ttype 4 occurrence, in occurrence order; each default token inherits td_tstart. The complete pulse support must lie inside the inclusive global td_tstart to td_tend interval; hard truncation of a supersine pulse is rejected. + default_value: default + unit: "" + availability: td_ttype contains 4 + - name: td_supsine_tend + category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" + type: Vector of String + description: | + Integer electronic step at the right, exactly zero boundary of each supersine pulse, defining $t_{\mathrm{e}}=\mathtt{td\_supsine\_tend}\Delta t$. Supply exactly one integer or default token for each td_ttype 4 occurrence, in occurrence order; each default token inherits td_tend. The complete pulse support must lie inside the inclusive global td_tstart to td_tend interval; hard truncation of a supersine pulse is rejected. + default_value: default + unit: "" + availability: td_ttype contains 4 - name: init_vecpot_file category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" type: Boolean description: | - Initialize vector potential through file or not. - * True: Initialize vector potential from file At.dat (unit: a.u.). It consists of four columns, representing the step number and vector potential on each direction. - * False: Calculate vector potential by integrating the electric field. + Selects the source of the Cartesian vector potential used by LCAO RT-TDDFT. + * True: Read vector_pot.txt from the calculation working directory. Each non-comment line must contain four columns: a conventionally one-based electronic-step label followed by $A_x$, $A_y$, and $A_z$ in atomic units. Rows are consumed sequentially; the first column is read as a label and is not used for lookup. If propagation continues beyond the available rows, the last row is reused. + * False: Obtain the vector potential by integrating the configured electric field. default_value: "False" unit: "" availability: "" @@ -2676,8 +2732,9 @@ parameters: category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" type: Boolean description: | - * True: Fixes the band occupations based on the values specified in ocp_set. - * False: Does not fix the band occupations. + Controls fixed band occupations. In calculations other than LCAO RT-TDDFT, fixed values are applied during electronic-state setup. In LCAO RT-TDDFT, the initial ground-state SCF determines occupations normally, and fixed values from ocp_set are applied during the subsequent real-time propagation steps. + * True: Use the fixed occupations specified by ocp_set during propagation. + * False: Keep the occupations determined by the initial SCF. default_value: "False" unit: "" availability: "" @@ -2685,11 +2742,10 @@ parameters: category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" type: String description: | - If ocp is set to 1, ocp_set must be provided as a string specifying the occupation numbers for each band across all k-points. The format follows a space-separated pattern, where occupations are assigned sequentially to bands for each k-point. A shorthand notation Nx can be used to repeat a value x for N bands. - * Example: - 1 10*1 0 1 represents occupations for 13 bands, where the 12th band is fully unoccupied (0), and all others are occupied (1). - * For a system with multiple k-points, the occupations must be specified for all k-points, following their order in the output file kpoints (may lead to fractional occupations). - * Incorrect specification of ocp_set could lead to inconsistencies in electron counting, causing the calculation to terminate with an error. + Fixed occupation weights used when ocp is true. Values are assigned band by band for each k-point, following k-point order. In LCAO RT-TDDFT, the initial ground-state SCF uses its normally determined occupations, and this array is applied only during subsequent real-time propagation steps. The repetition syntax N*x expands to N copies of x. + * Example: 1 10*1 0 1 expands to 13 values, with the 12th value equal to 0 and all other values equal to 1. + * After expansion, the array length must equal nks * nbands. + * The sum of all weights must equal nelec; otherwise the calculation terminates with an error. default_value: None unit: "" availability: "" @@ -3471,8 +3527,9 @@ parameters: category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" type: Boolean description: | - * True: Output electric dipole moment. - * False: Do not output electric dipole moment. + Controls electric-dipole output. In RT-TDDFT, each enabled spin channel is written to OUT.{suffix}/dipole_s[spin].txt using a one-based spin number. Every row contains the one-based electronic-step index followed by the Cartesian electronic-dipole components $P_x$, $P_y$, and $P_z$ in atomic units. The running log additionally reports the electronic, ionic, and total dipoles and the norm of the total dipole. + * True: Output the electric dipole information. + * False: Do not output the electric dipole information. default_value: "False" unit: "" availability: "" @@ -3480,42 +3537,43 @@ parameters: category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" type: Integer description: | - Controls the current-density output method for LCAO RT-TDDFT. + Controls the current-density output method for LCAO RT-TDDFT. Output rows contain the one-based electronic-step index followed by $J_x$, $J_y$, and $J_z$ in atomic units. * 0: Do not output current. - * 1: Explicitly construct the velocity operator from the momentum, vector-potential, and KB nonlocal-pseudopotential terms using two-center integral / spherical grid integral: $\hat{v}_{\alpha}=-\mathrm{i}\nabla_{\alpha}+A_{\alpha}(t)+\mathrm{i}\left[\widetilde{V}_{\mathrm{NL}}^{\mathrm{KB}},r_{\alpha}\right]$, where $\widetilde{V}_{\mathrm{NL}}^{\mathrm{KB}}=\mathrm{e}^{-\mathrm{i}\boldsymbol{A}(t)\cdot\boldsymbol{r}}\hat{V}_{\mathrm{NL}}^{\mathrm{KB}}\mathrm{e}^{\mathrm{i}\boldsymbol{A}(t)\cdot\boldsymbol{r}}$. $\boldsymbol{A}(t)$ is nonzero only for the velocity gauge (td_stype=1); otherwise $\boldsymbol{A}(t)=0$. Other nonlocal Hamiltonian terms (e.g., EXX) are not included explicitly. - * 2: Use the full Hamiltonian to construct the generalized velocity matrix in a nonorthogonal NAO basis: $\widetilde{v}_{\alpha}=\partial_{\alpha}H+\mathrm{i}HS^{-1}\mathcal{R}_{\alpha}-\mathrm{i}\mathcal{R}_{\alpha}S^{-1}H-HS^{-1}\partial_{\alpha}S$. This includes all contributions available in the real-space Hamiltonian matrix when enabled. This method is more general but more expensive. + * 1: Explicitly construct the velocity operator from the momentum, vector-potential, and KB nonlocal-pseudopotential terms using two-center and spherical-grid integrals: $\hat{v}_{\alpha}=-\mathrm{i}\nabla_{\alpha}+A_{\alpha}(t)+\mathrm{i}\left[\widetilde{V}_{\mathrm{NL}}^{\mathrm{KB}},r_{\alpha}\right]$, where $\widetilde{V}_{\mathrm{NL}}^{\mathrm{KB}}=\mathrm{e}^{-\mathrm{i}\boldsymbol{A}(t)\cdot\boldsymbol{r}}\hat{V}_{\mathrm{NL}}^{\mathrm{KB}}\mathrm{e}^{\mathrm{i}\boldsymbol{A}(t)\cdot\boldsymbol{r}}$. $\boldsymbol{A}(t)$ is nonzero only for the velocity gauge (td_stype=1); otherwise $\boldsymbol{A}(t)=0$. Other nonlocal Hamiltonian terms, such as EXX, are not included explicitly. The total current is written to OUT.{suffix}/current_tot.txt. + * 2: Use the full Hamiltonian to construct the generalized velocity matrix in a nonorthogonal NAO basis, $\widetilde{v}_{\alpha}=\partial_{\alpha}H+\mathrm{i}HS^{-1}\mathcal{R}_{\alpha}-\mathrm{i}\mathcal{R}_{\alpha}S^{-1}H-HS^{-1}\partial_{\alpha}S$. This includes all contributions available in the real-space Hamiltonian matrix when enabled. This method is more general but more expensive. The total current is written to OUT.{suffix}/current_tot_comm.txt. default_value: "0" unit: "" - availability: "" + availability: basis_type==lcao and esolver_type==tddft - name: out_current_k category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" type: Boolean description: | - * True: Output current for each k-points separately. - * False: Output current in total. + Controls whether LCAO RT-TDDFT current density is also resolved by spin and k-point. The total-current file is always written when out_current is 1 or 2. + * True: In addition to the total, out_current=1 writes OUT.{suffix}/current_s[spin]k[kpoint].txt; out_current=2 writes OUT.{suffix}/current_s[spin]k[kpoint]_comm.txt. Both use one-based spin and k-point numbers, with k-points numbered independently within each spin channel. Each row contains the one-based electronic-step index followed by $J_x$, $J_y$, and $J_z$ in atomic units. + * False: Output only current_tot.txt for out_current=1 or current_tot_comm.txt for out_current=2. default_value: "False" unit: "" - availability: "" + availability: "basis_type==lcao and esolver_type==tddft and out_current>0" - name: out_efield category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" type: Boolean description: | - Whether to output the electric field data to files. When enabled, writes real-time electric field values (unit: V/A) into files named efield_[num].txt, where [num] is the sequential index of the electric field ranges from 0 to N-1 for N configured fields. It is noteworthy that the field type sequence follows td_ttype, while the direction sequence follows td_vext_dire. - * True: Output electric field. - * False: Do not output electric field. + Controls time-dependent electric-field output. For each configured field, OUT.{suffix}/efield_[index].txt contains two columns: physical time in fs and the field value in V/Angstrom. The one-based field index follows the occurrence order shared by td_ttype and td_vext_dire, so fields assigned to the same direction remain in separate files. At initialization, a fresh calculation with md_restart=False truncates the files corresponding to the currently configured fields, whereas a calculation with md_restart=True preserves them and appends new samples. + * True: Output electric-field values on active electronic steps. + * False: Do not output electric-field values. default_value: "False" unit: "" - availability: "" + availability: esolver_type==tddft and td_vext==true - name: out_vecpot category: "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory" type: Boolean description: | - Output vector potential or not (unit: a.u.). - * True: Output vector potential into file At.dat. - * False: Do not output vector potential. + Controls Cartesian vector-potential output for LCAO RT-TDDFT. OUT.{suffix}/vector_pot.txt contains four columns: the one-based electronic-step index followed by $A_x$, $A_y$, and $A_z$ in atomic units. At initialization, a fresh calculation with md_restart=False truncates the file and writes a new header, whereas a calculation with md_restart=True preserves a nonempty existing file and appends new samples. If the restart output file is missing or empty, a new file with a header is created. + * True: Write vector-potential samples on electronic propagation steps. + * False: Do not output the vector potential. default_value: "False" unit: "" - availability: "" + availability: basis_type==lcao and esolver_type==tddft - name: cal_symm_repr category: System variables type: "Integer \\[Integer\\](optional)" diff --git a/source/Makefile.Objects b/source/Makefile.Objects index 6aeaa0c75a..771396233d 100644 --- a/source/Makefile.Objects +++ b/source/Makefile.Objects @@ -69,6 +69,7 @@ VPATH=./src_global:\ ./src_ri:\ ./source_lcao/module_ri:\ ./source_io/module_parameter:\ +./source_io/module_efield:\ ./source_lcao/module_lr:\ ./source_lcao/module_lr/ao_to_mo_transformer:\ ./source_lcao/module_lr/dm_trans:\ @@ -578,6 +579,8 @@ OBJS_IO=module_parameter/input_conv.o\ write_init.o\ td_current_io.o\ td_current_io_comm.o\ + td_efield_io.o\ + td_vector_pot_io.o\ write_libxc_r.o\ output_log.o\ output_mat_sparse.o\ diff --git a/source/source_esolver/esolver_ks_lcao_tddft.cpp b/source/source_esolver/esolver_ks_lcao_tddft.cpp index 154a3a65e2..44c835c12a 100644 --- a/source/source_esolver/esolver_ks_lcao_tddft.cpp +++ b/source/source_esolver/esolver_ks_lcao_tddft.cpp @@ -1,11 +1,15 @@ #include "esolver_ks_lcao_tddft.h" +#include "source_base/constants.h" #include "source_lcao/module_rt/boundary_fix.h" //----------------IO----------------- #include "source_base/global_variable.h" #include "source_io/module_ctrl/ctrl_output_td.h" +#include "source_io/module_efield/td_efield_io.h" +#include "source_io/module_efield/td_vector_pot_io.h" #include "source_io/module_output/output_log.h" +#include "source_io/module_parameter/parameter.h" #include "source_io/module_wf/read_wfc_nao.h" //------LCAO HSolver ElecState------- #include "source_estate/elecstate_tools.h" @@ -13,6 +17,8 @@ #include "source_estate/module_dm/cal_dm_psi.h" #include "source_estate/module_dm/cal_edm_tddft.h" #include "source_estate/module_pot/H_TDDFT_pw.h" +#include "source_estate/module_pot/potential_new.h" +#include "source_estate/module_pot/td_field_manager.h" #include "source_hsolver/hsolver_lcao.h" #include "source_lcao/module_rt/evolve_elec.h" #include "source_lcao/rho_tau_lcao.h" @@ -72,8 +78,27 @@ void ESolver_KS_LCAO_TDDFT::before_all_runners(BaseCell& basecell, c basecell.require_kind(BaseCell::Kind::unit_cell, __FUNCTION__); UnitCell& ucell = static_cast(basecell); + // Build the shared field state before the base ESolver registers potential + // components. Both propagation gauges and the length-gauge potential must + // observe the same electronic-step counter. + td_field_manager_ = elecstate::create_td_field_manager(inp); + if (inp.mdp.md_restart) + { + td_field_manager_->read_restart(PARAM.globalv.global_readin_dir); + } + if (inp.out_efield && GlobalV::MY_RANK == 0) + { + ModuleIO::prepare_td_field_output(PARAM.globalv.global_out_dir, td_field_manager_->fields().size(), inp.mdp.md_restart); + } + if (inp.out_vecpot && GlobalV::MY_RANK == 0) + { + ModuleIO::prepare_td_vector_pot_output(PARAM.globalv.global_out_dir, inp.mdp.md_restart); + } + elecstate::H_TDDFT_pw::sync_compatibility_state(*td_field_manager_); + // Run before_all_runners in ESolver_KS_LCAO ESolver_KS_LCAO, TR>::before_all_runners(ucell, inp); + this->pelec->pot->set_td_field_manager(td_field_manager_); td_p = new TD_info(&ucell, this->pv, this->orb_); TD_info::td_vel_op = td_p; @@ -110,13 +135,8 @@ void ESolver_KS_LCAO_TDDFT::runner(BaseCell& basecell, const int ist // 1) before_scf (electronic iteration loops) //---------------------------------------------------------------- this->before_scf(ucell, istep); // From ESolver_KS_LCAO - td_p->initialize_phase_hybrid(ucell, - dynamic_cast, TR>*>(this->p_hamilt)->getHR()); - td_p->calculate_grad_overlap(this->pv, - ucell, - this->gd, - this->orb_.cutoffs(), - this->two_center_bundle_.overlap_orb.get()); + td_p->initialize_phase_hybrid(ucell, dynamic_cast, TR>*>(this->p_hamilt)->getHR()); + td_p->calculate_grad_overlap(this->pv, ucell, this->gd, this->orb_.cutoffs(), this->two_center_bundle_.overlap_orb.get()); // Initialize the moving spatial gauge if (use_td_moving_gauge && this->td_mg_ == nullptr) { @@ -140,11 +160,7 @@ void ESolver_KS_LCAO_TDDFT::runner(BaseCell& basecell, const int ist if (PARAM.inp.td_stype != 1 && TD_info::out_current == 1) { // initialize the velocity operator - velocity_mat = new Velocity_op(&ucell, - &(this->gd), - &this->pv, - this->orb_, - this->two_center_bundle_.overlap_orb.get()); + velocity_mat = new Velocity_op(&ucell, &(this->gd), &this->pv, this->orb_, this->two_center_bundle_.overlap_orb.get()); // calculate velocity operator velocity_mat->calculate_grad_term(); velocity_mat->calculate_vcomm_r(); @@ -159,13 +175,7 @@ void ESolver_KS_LCAO_TDDFT::runner(BaseCell& basecell, const int ist // Reset laststep matrix and wfc, if any atom cross the boundary // Apply a phase correction to H, S, and psi to keep consistency when atoms cross periodic boundaries const size_t len_hs_ik = use_tensor && use_lapack ? PARAM.globalv.nlocal * PARAM.globalv.nlocal : this->pv.nloc; - module_rt::reset_matrix_boundary(ucell, - this->kv, - &(this->pv), - this->Hk_laststep, - this->Sk_laststep, - this->psi_laststep, - len_hs_ik); + module_rt::reset_matrix_boundary(ucell, this->kv, &(this->pv), this->Hk_laststep, this->Sk_laststep, this->psi_laststep, len_hs_ik); for (int estep = 0; estep < estep_max; estep++) { @@ -175,7 +185,14 @@ void ESolver_KS_LCAO_TDDFT::runner(BaseCell& basecell, const int ist // update At if (PARAM.inp.td_stype > 0) { - elecstate::H_TDDFT_pw::update_At(); + // TDFieldManager owns the midpoint-vector-potential update. Static + // mirrors are synchronized afterward for legacy RT-TDDFT clients. + td_field_manager_->advance_vector_gauge(); + elecstate::H_TDDFT_pw::sync_compatibility_state(*td_field_manager_); + if (PARAM.inp.out_efield && GlobalV::MY_RANK == 0) + { + ModuleIO::write_td_field_values(*td_field_manager_, PARAM.globalv.global_out_dir); + } td_p->cal_cart_At(elecstate::H_TDDFT_pw::At); ModuleBase::GlobalFunc::OUT(GlobalV::ofs_running, "Cartesian vector potential Ax(t)", TD_info::cart_At[0]); ModuleBase::GlobalFunc::OUT(GlobalV::ofs_running, "Cartesian vector potential Ay(t)", TD_info::cart_At[1]); @@ -185,12 +202,7 @@ void ESolver_KS_LCAO_TDDFT::runner(BaseCell& basecell, const int ist if (estep != 0) { this->CE.update_all_dis(ucell); - this->CE.extrapolate_charge(&this->Pgrid, - ucell, - &this->chr, - &this->sf, - GlobalV::ofs_running, - GlobalV::ofs_warning); + this->CE.extrapolate_charge(&this->Pgrid, ucell, &this->chr, &this->sf, GlobalV::ofs_running, GlobalV::ofs_warning); this->exx_nao.before_scf(ucell, this->kv, this->orb_, this->p_chgmix, totstep, PARAM.inp); elecstate::init_scf(ucell, this->Pgrid, @@ -276,10 +288,7 @@ void ESolver_KS_LCAO_TDDFT::print_step() } template -void ESolver_KS_LCAO_TDDFT::hamilt2rho_single(UnitCell& ucell, - const int istep, - const int iter, - const double ethr) +void ESolver_KS_LCAO_TDDFT::hamilt2rho_single(UnitCell& ucell, const int istep, const int iter, const double ethr) { // Update the moving spatial gauge if (use_td_moving_gauge) @@ -293,26 +302,25 @@ void ESolver_KS_LCAO_TDDFT::hamilt2rho_single(UnitCell& ucell, { if (istep >= TD_info::estep_shift + 1) { - module_rt::Evolve_elec::solve_psi( - istep, - PARAM.inp.nbands, - PARAM.globalv.nlocal, - this->kv.get_nks(), - static_cast>*>(this->p_hamilt), - this->pv, - this->psi, - this->psi_laststep, - this->Hk_laststep, - this->Sk_laststep, - this->pelec->ekb, - GlobalV::ofs_running, - PARAM.inp.propagator, - use_tensor, - use_lapack, - this->td_mg_, - &ucell, - this->kv.kvec_d, - use_td_moving_gauge); + module_rt::Evolve_elec::solve_psi(istep, + PARAM.inp.nbands, + PARAM.globalv.nlocal, + this->kv.get_nks(), + static_cast>*>(this->p_hamilt), + this->pv, + this->psi, + this->psi_laststep, + this->Hk_laststep, + this->Sk_laststep, + this->pelec->ekb, + GlobalV::ofs_running, + PARAM.inp.propagator, + use_tensor, + use_lapack, + this->td_mg_, + &ucell, + this->kv.kvec_d, + use_td_moving_gauge); } this->weight_dm_rho(ucell); } @@ -397,8 +405,8 @@ void ESolver_KS_LCAO_TDDFT::iter_finish(UnitCell& ucell, { for (int ib = 0; ib < PARAM.inp.nbands; ib++) { - GlobalV::ofs_running << " " << std::setw(9) << ik + 1 << std::setw(8) << ib + 1 << std::setw(12) - << this->pelec->wg(ik, ib) << std::endl; + GlobalV::ofs_running << " " << std::setw(9) << ik + 1 << std::setw(8) << ib + 1 << std::setw(12) << this->pelec->wg(ik, ib) + << std::endl; } } GlobalV::ofs_running << std::endl; @@ -412,32 +420,25 @@ void ESolver_KS_LCAO_TDDFT::iter_finish(UnitCell& ucell, this->store_h_s_psi(ucell, istep, iter, force_save); // Calculate energy-density matrix for RT-TDDFT - if (conv_esolver && estep == estep_max - 1 && istep >= (PARAM.inp.init_wfc == "file" ? 0 : 1) - && PARAM.inp.td_edm == 0 && PARAM.inp.td_stype != 2) + if (conv_esolver && estep == estep_max - 1 && istep >= (PARAM.inp.init_wfc == "file" ? 0 : 1) && PARAM.inp.td_edm == 0 + && PARAM.inp.td_stype != 2) { if (use_tensor && use_lapack) { - elecstate::cal_edm_tddft_tensor_lapack( - this->pv, - this->dmat, - this->kv, - static_cast>*>(this->p_hamilt)); + elecstate::cal_edm_tddft_tensor_lapack(this->pv, + this->dmat, + this->kv, + static_cast>*>(this->p_hamilt)); } else { - elecstate::cal_edm_tddft(this->pv, - this->dmat, - this->kv, - static_cast>*>(this->p_hamilt)); + elecstate::cal_edm_tddft(this->pv, this->dmat, this->kv, static_cast>*>(this->p_hamilt)); } } } template -void ESolver_KS_LCAO_TDDFT::store_h_s_psi(UnitCell& ucell, - const int istep, - const int iter, - const bool conv_esolver) +void ESolver_KS_LCAO_TDDFT::store_h_s_psi(UnitCell& ucell, const int istep, const int iter, const bool conv_esolver) { const int nbands = PARAM.inp.nbands; const int nlocal = PARAM.globalv.nlocal; @@ -466,18 +467,16 @@ void ESolver_KS_LCAO_TDDFT::store_h_s_psi(UnitCell& ucell, // Allocate memory for Hk_laststep, if (use_tensor && use_lapack), should be global if (this->Hk_laststep.NumElements() != len_HS_all) { - this->Hk_laststep = ct::Tensor(ct::DataType::DT_COMPLEX_DOUBLE, - ct_device_type_hs, - ct::TensorShape({this->kv.get_nks(), len_HS_ik})); + this->Hk_laststep + = ct::Tensor(ct::DataType::DT_COMPLEX_DOUBLE, ct_device_type_hs, ct::TensorShape({this->kv.get_nks(), len_HS_ik})); this->Hk_laststep.zero(); } // Allocate memory for Sk_laststep, if (use_tensor && use_lapack), should be global if (this->Sk_laststep.NumElements() != len_HS_all) { - this->Sk_laststep = ct::Tensor(ct::DataType::DT_COMPLEX_DOUBLE, - ct_device_type_hs, - ct::TensorShape({this->kv.get_nks(), len_HS_ik})); + this->Sk_laststep + = ct::Tensor(ct::DataType::DT_COMPLEX_DOUBLE, ct_device_type_hs, ct::TensorShape({this->kv.get_nks(), len_HS_ik})); this->Sk_laststep.zero(); } @@ -537,34 +536,18 @@ void ESolver_KS_LCAO_TDDFT::store_h_s_psi(UnitCell& ucell, // Only the root process (myid == 0) performs the copy if (myid == 0 && h_ptr != nullptr && s_ptr != nullptr) { - BlasConnector::copy(len_HS_ik, - h_ptr, - 1, - this->Hk_laststep.template data>() + ik * len_HS_ik, - 1); - BlasConnector::copy(len_HS_ik, - s_ptr, - 1, - this->Sk_laststep.template data>() + ik * len_HS_ik, - 1); + BlasConnector::copy(len_HS_ik, h_ptr, 1, this->Hk_laststep.template data>() + ik * len_HS_ik, 1); + BlasConnector::copy(len_HS_ik, s_ptr, 1, this->Sk_laststep.template data>() + ik * len_HS_ik, 1); } #endif } else { - BlasConnector::copy(len_HS_ik, - h_mat.p, - 1, - this->Hk_laststep.template data>() + ik * len_HS_ik, - 1); - BlasConnector::copy(len_HS_ik, - s_mat.p, - 1, - this->Sk_laststep.template data>() + ik * len_HS_ik, - 1); + BlasConnector::copy(len_HS_ik, h_mat.p, 1, this->Hk_laststep.template data>() + ik * len_HS_ik, 1); + BlasConnector::copy(len_HS_ik, s_mat.p, 1, this->Sk_laststep.template data>() + ik * len_HS_ik, 1); } // end use_tensor - } // end ik - } // conv_esolver + } // end ik + } // conv_esolver } template diff --git a/source/source_esolver/esolver_ks_lcao_tddft.h b/source/source_esolver/esolver_ks_lcao_tddft.h index 07c8199cca..b321d60117 100644 --- a/source/source_esolver/esolver_ks_lcao_tddft.h +++ b/source/source_esolver/esolver_ks_lcao_tddft.h @@ -8,6 +8,13 @@ #include "source_lcao/module_rt/td_moving_gauge.h" #include "source_lcao/module_rt/velocity_op.h" +#include + +namespace elecstate +{ +class TDFieldManager; +} + namespace ModuleESolver { @@ -28,12 +35,7 @@ class ESolver_KS_LCAO_TDDFT : public ESolver_KS_LCAO, TR> void store_h_s_psi(UnitCell& ucell, const int istep, const int iter, const bool conv_esolver); - void iter_finish(UnitCell& ucell, - const int istep, - const int estep, - const int estep_max, - int& iter, - bool& conv_esolver); + void iter_finish(UnitCell& ucell, const int istep, const int estep, const int estep_max, int& iter, bool& conv_esolver); virtual void after_scf(UnitCell& ucell, const int istep, const bool conv_esolver) override; @@ -72,6 +74,8 @@ class ESolver_KS_LCAO_TDDFT : public ESolver_KS_LCAO, TR> //! Restart flag bool restart_done = false; + std::shared_ptr td_field_manager_; + private: void weight_dm_rho(const UnitCell& ucell); }; diff --git a/source/source_estate/CMakeLists.txt b/source/source_estate/CMakeLists.txt index 7d8e836bb1..34bf85cb6c 100644 --- a/source/source_estate/CMakeLists.txt +++ b/source/source_estate/CMakeLists.txt @@ -13,6 +13,7 @@ list(APPEND objects module_pot/gatefield.cpp module_pot/efield.cpp module_pot/H_Hartree_pw.cpp + module_pot/H_TDDFT_pw.cpp module_pot/pot_xc.cpp module_pot/pot_local.cpp module_pot/potential_new.cpp @@ -22,6 +23,9 @@ list(APPEND objects module_pot/pot_sep.cpp module_pot/pot_xc_fdm.cpp module_pot/pot_cosikr.cpp + module_pot/td_field.cpp + module_pot/td_field_profiles.cpp + module_pot/td_field_manager.cpp module_charge/chgmixing.cpp module_charge/gint_precision_controller.cpp module_charge/charge.cpp @@ -46,7 +50,6 @@ list(APPEND objects if(ENABLE_LCAO) list(APPEND objects elecstate_lcao.cpp - module_pot/H_TDDFT_pw.cpp module_dm/init_dm.cpp module_dm/density_matrix.cpp module_dm/density_matrix_io.cpp diff --git a/source/source_estate/module_pot/H_TDDFT_pw.cpp b/source/source_estate/module_pot/H_TDDFT_pw.cpp index 5d2e0a940a..d56bcd13a8 100644 --- a/source/source_estate/module_pot/H_TDDFT_pw.cpp +++ b/source/source_estate/module_pot/H_TDDFT_pw.cpp @@ -1,490 +1,149 @@ #include "H_TDDFT_pw.h" -#include "source_base/constants.h" -#include "source_base/math_integral.h" +#include "source_base/global_variable.h" #include "source_base/timer.h" +#include "source_base/tool_quit.h" +#include "source_io/module_efield/td_efield_io.h" #include "source_io/module_parameter/parameter.h" +#include "td_field_manager.h" namespace elecstate { -int H_TDDFT_pw::istep = -1; -bool H_TDDFT_pw::is_initialized = false; - -double H_TDDFT_pw::amp; - -// Used for calculating electric field force on ions, summing over directions -std::vector H_TDDFT_pw::global_vext_time = {0.0, 0.0, 0.0}; - -int H_TDDFT_pw::stype; // 0 : length gauge 1: velocity gauge - -std::vector H_TDDFT_pw::ttype; -// 0: Gaussian type function. -// 1: Trapezoid type function. -// 2: Trigonometric functions, sin^2. -// 3: Heaviside step function. - -int H_TDDFT_pw::tstart; -int H_TDDFT_pw::tend; -double H_TDDFT_pw::dt; -// cut dt for integral -double H_TDDFT_pw::dt_int; -int H_TDDFT_pw::istep_int; -// space domain parameters - -// length gauge -double H_TDDFT_pw::lcut1; -double H_TDDFT_pw::lcut2; - -// velocity gauge +int H_TDDFT_pw::stype = 0; ModuleBase::Vector3 H_TDDFT_pw::At; ModuleBase::Vector3 H_TDDFT_pw::At_laststep; -// hybrid gauge ModuleBase::Vector3 H_TDDFT_pw::Et; -// time domain parameters - -// Gauss -int H_TDDFT_pw::gauss_count; -std::vector H_TDDFT_pw::gauss_omega; // time(a.u.)^-1 -std::vector H_TDDFT_pw::gauss_phase; -std::vector H_TDDFT_pw::gauss_sigma; // time(a.u.) -std::vector H_TDDFT_pw::gauss_t0; -std::vector H_TDDFT_pw::gauss_amp; // Ry/bohr -std::vector H_TDDFT_pw::gauss_ncut; // cut for integral - -// trapezoid -int H_TDDFT_pw::trape_count; -std::vector H_TDDFT_pw::trape_omega; // time(a.u.)^-1 -std::vector H_TDDFT_pw::trape_phase; -std::vector H_TDDFT_pw::trape_t1; -std::vector H_TDDFT_pw::trape_t2; -std::vector H_TDDFT_pw::trape_t3; -std::vector H_TDDFT_pw::trape_amp; // Ry/bohr -std::vector H_TDDFT_pw::trape_ncut; // cut for integral - -// Trigonometric -int H_TDDFT_pw::trigo_count; -std::vector H_TDDFT_pw::trigo_omega1; // time(a.u.)^-1 -std::vector H_TDDFT_pw::trigo_omega2; // time(a.u.)^-1 -std::vector H_TDDFT_pw::trigo_phase1; -std::vector H_TDDFT_pw::trigo_phase2; -std::vector H_TDDFT_pw::trigo_amp; // Ry/bohr -std::vector H_TDDFT_pw::trigo_ncut; // cut for integral - -// Heaviside -int H_TDDFT_pw::heavi_count; -std::vector H_TDDFT_pw::heavi_t0; -std::vector H_TDDFT_pw::heavi_amp; // Ry/bohr +std::vector H_TDDFT_pw::global_vext_time = {0.0, 0.0, 0.0}; -void H_TDDFT_pw::current_step_info(const std::string& file_dir, int& istep) +H_TDDFT_pw::H_TDDFT_pw(const ModulePW::PW_Basis* rho_basis_in, + const UnitCell* ucell_in, + const std::shared_ptr& field_manager) + : ucell_(ucell_in), field_manager_(field_manager) { - std::stringstream ssc; - ssc << file_dir << "Restart_td.txt"; - std::ifstream file(ssc.str().c_str()); + this->dynamic_mode = false; + this->fixed_mode = true; + this->rho_basis_ = rho_basis_in; - if (!file) + if (!field_manager_) { - ModuleBase::WARNING_QUIT("H_TDDFT_pw::current_step_info", "No Restart_td.txt!"); + ModuleBase::WARNING_QUIT("H_TDDFT_pw", "RT-TDDFT field manager is not initialized."); } + sync_compatibility_state(*field_manager_); +} - file >> istep; - file >> At[0] >> At[1] >> At[2]; - file >> At_laststep[0] >> At_laststep[1] >> At_laststep[2]; - At_laststep = -At_laststep; - file.close(); +void H_TDDFT_pw::sync_compatibility_state(const TDFieldManager& manager) +{ + stype = manager.gauge(); + At = manager.vector_potential(); + At_laststep = manager.vector_potential_laststep(); + Et = manager.electric_field(); + const ModuleBase::Vector3& total_field = manager.total_electric_field(); + global_vext_time = {total_field[0], total_field[1], total_field[2]}; } void H_TDDFT_pw::cal_fixed_v(double* vl_pseudo) { ModuleBase::TITLE("H_TDDFT_pw", "cal_fixed_v"); - - // skip if not length gauge - if (stype != 0) + if (field_manager_->gauge() != 0) { return; } - // time evolve - H_TDDFT_pw::istep++; - H_TDDFT_pw::istep_int = istep; - global_vext_time = {0.0, 0.0, 0.0}; - - // judgement to skip vext - if (!PARAM.inp.td_vext || istep > tend || istep < tstart) + // Advance exactly once per rebuilt fixed potential. The potential then + // consumes the same per-occurrence samples exposed to field output. + field_manager_->advance_length_gauge(); + sync_compatibility_state(*field_manager_); + if (!field_manager_->active()) { return; } ModuleBase::timer::start("H_TDDFT_pw", "cal_fixed_v"); - - int count = 0; - gauss_count = 0; - trape_count = 0; - trigo_count = 0; - heavi_count = 0; - - - - for (auto direc: PARAM.inp.td_vext_dire) + const std::vector& fields = field_manager_->fields(); + const std::vector& field_values = field_manager_->field_values(); + for (std::size_t field_index = 0; field_index < fields.size(); ++field_index) { std::vector vext_space(this->rho_basis_->nrxx, 0.0); - double vext_time = cal_v_time(ttype[count], true); - - global_vext_time[direc - 1] += vext_time; - - if (PARAM.inp.out_efield && GlobalV::MY_RANK == 0) - { - std::stringstream as; - as << PARAM.globalv.global_out_dir << "efield_" << count << ".txt"; - std::ofstream ofs(as.str().c_str(), std::ofstream::app); - ofs << H_TDDFT_pw::istep * dt * ModuleBase::AU_to_FS << "\t" - << vext_time * ModuleBase::Ry_to_eV / ModuleBase::BOHR_TO_A << std::endl; - ofs.close(); - } + const double field_value = field_values[field_index]; - cal_v_space(vext_space, direc); - for (size_t ir = 0; ir < this->rho_basis_->nrxx; ++ir) + cal_v_space_length(vext_space, fields[field_index].direction() + 1); + for (std::size_t ir = 0; ir < static_cast(this->rho_basis_->nrxx); ++ir) { - vl_pseudo[ir] += vext_space[ir] * vext_time; + vl_pseudo[ir] += vext_space[ir] * field_value; } - count++; } - - ModuleBase::timer::end("H_TDDFT_pw", "cal_fixed_v"); - return; -} - -void H_TDDFT_pw::cal_v_space(std::vector& vext_space, int direc) -{ - ModuleBase::TITLE("H_TDDFT_pw", "cal_v_space"); - ModuleBase::timer::start("H_TDDFT_pw", "cal_v_space"); - - switch (stype) + if (PARAM.inp.out_efield && GlobalV::MY_RANK == 0) { - case 0: - cal_v_space_length(vext_space, direc); - break; - default: - std::cout << "space_domain_type of electric field is wrong" << std::endl; - break; + ModuleIO::write_td_field_values(*field_manager_, PARAM.globalv.global_out_dir); } - - ModuleBase::timer::end("H_TDDFT_pw", "cal_v_space"); - return; + ModuleBase::timer::end("H_TDDFT_pw", "cal_fixed_v"); } -void H_TDDFT_pw::cal_v_space_length(std::vector& vext_space, int direc) +void H_TDDFT_pw::cal_v_space_length(std::vector& vext_space, const int direction) { ModuleBase::TITLE("H_TDDFT_pw", "cal_v_space_length"); ModuleBase::timer::start("H_TDDFT_pw", "cal_v_space_length"); for (int ir = 0; ir < this->rho_basis_->nrxx; ++ir) { - int i = ir / (this->rho_basis_->ny * this->rho_basis_->nplane); - int j = ir / this->rho_basis_->nplane - i * this->rho_basis_->ny; - int k = ir % this->rho_basis_->nplane + this->rho_basis_->startz_current; - double x = (double)i / this->rho_basis_->nx; - double y = (double)j / this->rho_basis_->ny; - double z = (double)k / this->rho_basis_->nz; + const int i = ir / (this->rho_basis_->ny * this->rho_basis_->nplane); + const int j = ir / this->rho_basis_->nplane - i * this->rho_basis_->ny; + const int k = ir % this->rho_basis_->nplane + this->rho_basis_->startz_current; + const double x = static_cast(i) / this->rho_basis_->nx; + const double y = static_cast(j) / this->rho_basis_->ny; + const double z = static_cast(k) / this->rho_basis_->nz; - switch (direc) + if (direction == 1) { - case 1: vext_space[ir] = cal_v_space_length_potential(x) * this->ucell_->latvec.e11 + cal_v_space_length_potential(y) * this->ucell_->latvec.e21 + cal_v_space_length_potential(z) * this->ucell_->latvec.e31; - break; - - case 2: + } + else if (direction == 2) + { vext_space[ir] = cal_v_space_length_potential(x) * this->ucell_->latvec.e12 + cal_v_space_length_potential(y) * this->ucell_->latvec.e22 + cal_v_space_length_potential(z) * this->ucell_->latvec.e32; - break; - - case 3: + } + else + { vext_space[ir] = cal_v_space_length_potential(x) * this->ucell_->latvec.e13 + cal_v_space_length_potential(y) * this->ucell_->latvec.e23 + cal_v_space_length_potential(z) * this->ucell_->latvec.e33; - break; - - default: - std::cout << "direction of electric field is wrong" << std::endl; - break; } } ModuleBase::timer::end("H_TDDFT_pw", "cal_v_space_length"); - return; } -double H_TDDFT_pw::cal_v_space_length_potential(double i) +double H_TDDFT_pw::cal_v_space_length_potential(const double coordinate) const { - double vext_space = 0.0; - if (i < lcut1) + const double lower_cut = field_manager_->length_cut1(); + const double upper_cut = field_manager_->length_cut2(); + if (coordinate < lower_cut) { - vext_space = -((i - lcut1) * (lcut2 - lcut1) / (lcut1 + 1.0 - lcut2) - lcut1) * this->ucell_->lat0; + return -((coordinate - lower_cut) * (upper_cut - lower_cut) / (lower_cut + 1.0 - upper_cut) - lower_cut) * this->ucell_->lat0; } - else if (i >= lcut1 && i < lcut2) + if (coordinate < upper_cut) { - vext_space = i * this->ucell_->lat0; + return coordinate * this->ucell_->lat0; } - else if (i >= lcut2) - { - vext_space = -((i - lcut2) * (lcut2 - lcut1) / (lcut1 + 1.0 - lcut2) - lcut2) * this->ucell_->lat0; - } - return vext_space; -} - -int H_TDDFT_pw::check_ncut(int t_type) -{ - int ncut = 0; - switch (t_type) - { - case 0: - ncut = *(gauss_ncut.begin() + gauss_count); - break; - - case 1: - ncut = *(trape_ncut.begin() + trape_count); - break; - - case 2: - ncut = *(trigo_ncut.begin() + trigo_count); - break; - - case 3: - ncut = 2; - break; - - default: - std::cout << "time_domain_type of electric field is wrong" << std::endl; - break; - } - return ncut; -} - -void H_TDDFT_pw::update_At() -{ - // time evolve - H_TDDFT_pw::istep++; - // midpoint rule should be used both in Hamiltonian and here. - At = At + At_laststep / 2.0; - At_laststep.set(0.0, 0.0, 0.0); - Et.set(0.0, 0.0, 0.0); - global_vext_time = { 0.0, 0.0, 0.0 }; - - // judgement to skip vext - if (!PARAM.inp.td_vext || istep > tend || istep < tstart) - { - return; - } - - ModuleBase::timer::start("H_TDDFT_pw", "update_At"); - - int count = 0; - gauss_count = 0; - trape_count = 0; - trigo_count = 0; - heavi_count = 0; - // parameters for integral - int ncut = 1; - bool last = false; - double out = 0.0; - - for (auto direc: PARAM.inp.td_vext_dire) - { - last = false; - // cut the integral space and initialize relevant parameters - ncut = check_ncut(ttype[count]); - istep_int = istep * ncut; - dt_int = dt / double(ncut); - - // store vext_time for each time point, include the first and last point - std::vector vext_time(ncut + 1, 0.0); // Use std::vector to manage memory - for (int i = 0; i <= ncut; i++) - { - // if this is the last point, type_count++ - if (i == ncut) - { - last = true; - } - vext_time[i] = cal_v_time(ttype[count], last); - istep_int++; - } - // Call the Simpson's rule integration using std::vector data - ModuleBase::Integral::Simpson_Integral(ncut + 1, vext_time.data(), dt_int, out); - - // update At value for its direction - switch (stype) - { - case 1: - At_laststep[direc - 1] -= out; - break; - case 2: - At_laststep[direc - 1] -= out; - Et[direc - 1] += vext_time[0]; - break; - default: - std::cout << "space_domain_type of electric field is wrong" << std::endl; - break; - } - - // output Efield - if (PARAM.inp.out_efield && GlobalV::MY_RANK == 0) - { - std::stringstream as; - as << PARAM.globalv.global_out_dir << "efield_" << count << ".txt"; - std::ofstream ofs(as.str().c_str(), std::ofstream::app); - ofs << H_TDDFT_pw::istep * dt * ModuleBase::AU_to_FS << "\t" - << vext_time[0] * ModuleBase::Ry_to_eV / ModuleBase::BOHR_TO_A << std::endl; - ofs.close(); - } - // total count++ - count++; - } - At = At + At_laststep / 2.0; - if(stype==2)global_vext_time = { Et[0],Et[1],Et[2] }; - - ModuleBase::timer::end("H_TDDFT_pw", "update_At"); - return; -} - -double H_TDDFT_pw::cal_v_time(int t_type, const bool last) -{ - double vext_time = 0.0; - - switch (t_type) - { - case 0: - vext_time = cal_v_time_Gauss(last); - break; - - case 1: - vext_time = cal_v_time_trapezoid(last); - break; - - case 2: - vext_time = cal_v_time_trigonometric(last); - break; - - case 3: - vext_time = cal_v_time_heaviside(last); - break; - - default: - std::cout << "time_domain_type of electric field is wrong" << std::endl; - break; - } - return vext_time; -} - -double H_TDDFT_pw::cal_v_time_Gauss(const bool last) -{ - double vext_time = 0.0; - double t0 = *(gauss_t0.begin() + gauss_count); - double omega = *(gauss_omega.begin() + gauss_count); - double sigma = *(gauss_sigma.begin() + gauss_count); - double phase = *(gauss_phase.begin() + gauss_count); - double amp = *(gauss_amp.begin() + gauss_count); - double ncut = *(gauss_ncut.begin() + gauss_count); - - double gauss_t = (istep_int - t0 * ncut) * dt_int; - vext_time = cos(omega * gauss_t + phase) * exp(-gauss_t * gauss_t * 0.5 / (sigma * sigma)) * amp; - if (last) - { - gauss_count++; - } - - return vext_time; -} - -double H_TDDFT_pw::cal_v_time_trapezoid(const bool last) -{ - double vext_time = 0.0; - double t1 = *(trape_t1.begin() + trape_count); - double t2 = *(trape_t2.begin() + trape_count); - double t3 = *(trape_t3.begin() + trape_count); - double omega = *(trape_omega.begin() + trape_count); - double phase = *(trape_phase.begin() + trape_count); - double amp = *(trape_amp.begin() + trape_count); - double ncut = *(trape_ncut.begin() + trape_count); - - if (istep < t1) - { - vext_time = istep_int / ncut / t1; - } - else if (istep < t2) - { - vext_time = 1.0; - } - else if (istep < t3) - { - vext_time = (t3 - istep_int / ncut) / (t3 - t2); - } - - vext_time = vext_time * amp * cos(omega * istep_int * dt_int + phase); - if (last) - { - trape_count++; - } - - return vext_time; -} - -double H_TDDFT_pw::cal_v_time_trigonometric(const bool last) -{ - double vext_time = 0.0; - double omega1 = *(trigo_omega1.begin() + trigo_count); - double phase1 = *(trigo_phase1.begin() + trigo_count); - double omega2 = *(trigo_omega2.begin() + trigo_count); - double phase2 = *(trigo_phase2.begin() + trigo_count); - double amp = *(trigo_amp.begin() + trigo_count); - - const double timenow = istep_int * dt_int; - - vext_time = amp * cos(omega1 * timenow + phase1) * sin(omega2 * timenow + phase2) * sin(omega2 * timenow + phase2); - if (last) - { - trigo_count++; - } - - return vext_time; -} - -double H_TDDFT_pw::cal_v_time_heaviside(const bool last) -{ - double t0 = *(heavi_t0.begin() + heavi_count); - double amp = *(heavi_amp.begin() + heavi_count); - double vext_time = 0.0; - if (istep < t0) - { - vext_time = amp; - } - else if (istep >= t0) - { - vext_time = 0.0; - } - if (last) - { - heavi_count++; - } - - return vext_time; + return -((coordinate - upper_cut) * (upper_cut - lower_cut) / (lower_cut + 1.0 - upper_cut) - upper_cut) * this->ucell_->lat0; } -void H_TDDFT_pw::compute_force(const UnitCell& cell, ModuleBase::matrix& fe) +void H_TDDFT_pw::compute_force(const UnitCell& cell, ModuleBase::matrix& force) { - int iat = 0; - for (int it = 0; it < cell.ntype; ++it) + int atom_index = 0; + for (int type = 0; type < cell.ntype; ++type) { - for (int ia = 0; ia < cell.atoms[it].na; ++ia) + for (int atom = 0; atom < cell.atoms[type].na; ++atom) { - for (int direc = 0; direc < 3; ++direc) + for (int direction = 0; direction < 3; ++direction) { - // No need to multiply ModuleBase::e2, since the unit of force is Ry/Bohr - fe(iat, direc) = global_vext_time[direc] * cell.atoms[it].ncpp.zv; + force(atom_index, direction) = global_vext_time[direction] * cell.atoms[type].ncpp.zv; } - ++iat; + ++atom_index; } } } diff --git a/source/source_estate/module_pot/H_TDDFT_pw.h b/source/source_estate/module_pot/H_TDDFT_pw.h index 38463b2a95..b609bcfc31 100644 --- a/source/source_estate/module_pot/H_TDDFT_pw.h +++ b/source/source_estate/module_pot/H_TDDFT_pw.h @@ -2,147 +2,82 @@ #define H_TDDFT_PW_H #include "pot_base.h" -#include "source_io/module_parameter/parameter.h" +#include "source_base/vector3.h" +#include #include namespace elecstate { +class TDFieldManager; + +/** + * @brief Length-gauge RT-TDDFT potential backed by TDFieldManager. + * + * The class consumes the manager's current field samples to construct the + * periodic real-space potential. Static members are retained only as mirrors + * for legacy propagation, current, force, and restart interfaces. + */ class H_TDDFT_pw : public PotBase { public: - H_TDDFT_pw(const ModulePW::PW_Basis* rho_basis_in, const UnitCell* ucell_in) : ucell_(ucell_in) + /** + * @brief Construct the time-dependent potential component. + * + * @param rho_basis_in Real-space grid on which the potential is evaluated. + * @param ucell_in Unit cell defining Cartesian coordinates and lattice size. + * @param field_manager Shared source of RT-TDDFT field state. + */ + H_TDDFT_pw(const ModulePW::PW_Basis* rho_basis_in, const UnitCell* ucell_in, const std::shared_ptr& field_manager); + + /** @brief Destroy the time-dependent potential component. */ + ~H_TDDFT_pw() { - this->dynamic_mode = false; - this->fixed_mode = true; - - this->rho_basis_ = rho_basis_in; - - // If it is the first time to create an H_TDDFT_pw instance and is restart calculation, - // initialize istep using current_step_info - if (!is_initialized && PARAM.inp.mdp.md_restart) - { - int restart_istep = -1; - std::string file_dir = PARAM.globalv.global_readin_dir; - current_step_info(file_dir, restart_istep); - - if (restart_istep >= 0) - { - H_TDDFT_pw::istep = restart_istep - 1; // Update istep - } - - is_initialized = true; // Mark as initialized, so that istep will not be initialized again - } } - ~H_TDDFT_pw() {}; - + /** + * @brief Add the current length-gauge external potential to the fixed part. + * + * @param vl_pseudo Real-space fixed potential updated in place. + */ void cal_fixed_v(double* vl_pseudo) override; /** - * @brief Compute ionic force of electric field + * @brief Synchronize legacy static mirrors with the field manager state. * - * @param[in] cell Information of cell - * @param[out] fe Force of electric field F = qE + * @param manager Active RT-TDDFT field manager. */ - static void compute_force(const UnitCell& cell, ModuleBase::matrix& fe); - - // parameters - static int stype; // 0: length gauge; 1: velocity gauge; 2: hybrid gauge - - static std::vector ttype; - // 0: Gaussian type function. - // 1: Trapezoid type function. - // 2: Trigonometric functions, sin^2. - // 3: Heaviside step function. - - static int tstart; - static int tend; - static double dt; - // cut dt for integral - static double dt_int; - static int istep_int; + static void sync_compatibility_state(const TDFieldManager& manager); - // Space domain parameters + /** + * @brief Compute ionic force of electric field. + * + * @param[in] cell Information of cell. + * @param[out] fe Electric-field force, F = qE. + */ + static void compute_force(const UnitCell& cell, ModuleBase::matrix& fe); - // length gauge - static double lcut1; - static double lcut2; + /** @brief Legacy mirror of the spatial-gauge selector. */ + static int stype; - // velocity gauge, vector potential + /** @brief Legacy mirror of the midpoint vector potential. */ static ModuleBase::Vector3 At; + + /** @brief Legacy mirror of the current vector-potential increment. */ static ModuleBase::Vector3 At_laststep; - static ModuleBase::Vector3 Et; - // Time domain parameters - - // Gauss - static int gauss_count; - static std::vector gauss_omega; // time(a.u.)^-1 - static std::vector gauss_phase; - static std::vector gauss_sigma; // time(a.u.) - static std::vector gauss_t0; - static std::vector gauss_amp; // Ry/bohr - // add for velocity gauge, recut dt into n pieces to make sure the integral is accurate enough - // must be even, thus would get odd number of points for Simpson integral - static std::vector gauss_ncut; - - // Trapezoid - static int trape_count; - static std::vector trape_omega; // time(a.u.)^-1 - static std::vector trape_phase; - static std::vector trape_t1; - static std::vector trape_t2; - static std::vector trape_t3; - static std::vector trape_amp; // Ry/bohr - // add for velocity gauge, recut dt into n pieces to make sure the integral is accurate enough - static std::vector trape_ncut; - - // Trigonometric - static int trigo_count; - static std::vector trigo_omega1; // time(a.u.)^-1 - static std::vector trigo_omega2; // time(a.u.)^-1 - static std::vector trigo_phase1; - static std::vector trigo_phase2; - static std::vector trigo_amp; // Ry/bohr - // add for velocity gauge, recut dt into n pieces to make sure the integral is accurate enough - static std::vector trigo_ncut; - - // Heaviside - static int heavi_count; - static std::vector heavi_t0; - static std::vector heavi_amp; // Ry/bohr - - // update At for velocity gauge by intergral of E(t)dt - static void update_At(); + /** @brief Legacy mirror of the instantaneous hybrid-gauge field. */ + static ModuleBase::Vector3 Et; private: - static int istep; - static bool is_initialized; // static flag variable, used to ensure initialization only once - - static double amp; static std::vector global_vext_time; - const UnitCell* ucell_ = nullptr; - - // Obtain the current MD step information, used for restart calculation - void current_step_info(const std::string& file_dir, int& istep); + void cal_v_space_length(std::vector& vext_space, int direction); + double cal_v_space_length_potential(double coordinate) const; - // Potential of electric field in space domain: for length gauge only - void cal_v_space(std::vector& vext_space, int direc); - void cal_v_space_length(std::vector& vext_space, int direc); - double cal_v_space_length_potential(double i); - - // Potential of electric field in time domain: Gaussian, trapezoid, trigonometric, Heaviside - static double cal_v_time(int t_type, const bool last); - static double cal_v_time_Gauss(const bool last); - static double cal_v_time_trapezoid(const bool last); - static double cal_v_time_trigonometric(const bool last); - static double cal_v_time_heaviside(const bool last); - - // Get ncut number for At integral - static int check_ncut(int t_type); + const UnitCell* ucell_ = nullptr; + std::shared_ptr field_manager_; }; } // namespace elecstate diff --git a/source/source_estate/module_pot/potential_new.cpp b/source/source_estate/module_pot/potential_new.cpp index 70b35cdf00..150dfe893a 100644 --- a/source/source_estate/module_pot/potential_new.cpp +++ b/source/source_estate/module_pot/potential_new.cpp @@ -1,5 +1,6 @@ #include "potential_new.h" +#include "pot_ml_exx.h" #include "source_base/global_function.h" #include "source_base/global_variable.h" #include "source_base/memory_recorder.h" @@ -8,7 +9,6 @@ #include "source_base/tool_title.h" #include "source_hamilt/module_xc/xc_functional.h" #include "source_io/module_parameter/parameter.h" -#include "pot_ml_exx.h" #include @@ -24,9 +24,8 @@ Potential::Potential(const ModulePW::PW_Basis* rho_basis_in, double* etxc_in, double* vtxc_in, VSep* vsep_cell_in) - : ucell_(ucell_in), vloc_(vloc_in), structure_factors_(structure_factors_in), - solvent_(solvent_in), vsep_cell(vsep_cell_in), etxc_(etxc_in), - vtxc_(vtxc_in) + : ucell_(ucell_in), vloc_(vloc_in), structure_factors_(structure_factors_in), solvent_(solvent_in), vsep_cell(vsep_cell_in), + etxc_(etxc_in), vtxc_(vtxc_in) { this->rho_basis_ = rho_basis_in; this->rho_basis_smooth_ = rho_basis_smooth_in; @@ -96,19 +95,19 @@ void Potential::allocate() ModuleBase::TITLE("Potential", "allocate"); const int nspin = PARAM.inp.nspin; - assert(nspin==1 || nspin==2 || nspin==4); + assert(nspin == 1 || nspin == 2 || nspin == 4); const int nrxx = this->rho_basis_->nrxx; const int nrxx_smooth = this->rho_basis_smooth_->nrxx; if (nrxx == 0) - { - return; - } - if (nrxx_smooth == 0) - { - return; - } + { + return; + } + if (nrxx_smooth == 0) + { + return; + } this->v_eff_fixed.resize(nrxx); ModuleBase::Memory::record("Pot::veff_fix", sizeof(double) * nrxx); @@ -156,10 +155,9 @@ void Potential::allocate() } } -void Potential::update_from_charge(const Charge*const chg, const UnitCell*const ucell) +void Potential::update_from_charge(const Charge* const chg, const UnitCell* const ucell) { ModuleBase::TITLE("Potential", "update_from_charge"); - //ModuleBase::timer::start("Potential", "update_from_charge"); if (!this->fixed_done) { @@ -194,8 +192,6 @@ void Potential::update_from_charge(const Charge*const chg, const UnitCell*const } // There's no need to synchronize memory for double precision pointers while in a CPU environment } - - //ModuleBase::timer::end("Potential", "update_from_charge"); } void Potential::cal_fixed_v(double* vl_pseudo) @@ -215,7 +211,7 @@ void Potential::cal_fixed_v(double* vl_pseudo) ModuleBase::timer::end("Potential", "cal_fixed_v"); } -void Potential::cal_v_eff(const Charge*const chg, const UnitCell*const ucell, ModuleBase::matrix& v_eff) +void Potential::cal_v_eff(const Charge* const chg, const UnitCell* const ucell, ModuleBase::matrix& v_eff) { ModuleBase::TITLE("Potential", "cal_veff"); ModuleBase::timer::start("Potential", "cal_veff"); @@ -248,7 +244,7 @@ void Potential::cal_v_eff(const Charge*const chg, const UnitCell*const ucell, Mo ModuleBase::timer::end("Potential", "cal_veff"); } -void Potential::init_pot(const Charge*const chg) +void Potential::init_pot(const Charge* const chg) { ModuleBase::TITLE("Potential", "init_pot"); ModuleBase::timer::start("Potential", "init_pot"); @@ -284,9 +280,9 @@ void Potential::interpolate_vrs(void) ModuleBase::timer::start("Potential", "interpolate_vrs"); const int nspin = PARAM.inp.nspin; - assert(nspin==1 || nspin==2 || nspin==4); + assert(nspin == 1 || nspin == 2 || nspin == 4); - if (PARAM.globalv.double_grid) + if (rho_basis_ != rho_basis_smooth_) { if (rho_basis_->gamma_only != rho_basis_smooth_->gamma_only) { diff --git a/source/source_estate/module_pot/potential_new.h b/source/source_estate/module_pot/potential_new.h index d9fc33839c..ae549cdb08 100644 --- a/source/source_estate/module_pot/potential_new.h +++ b/source/source_estate/module_pot/potential_new.h @@ -1,16 +1,19 @@ #ifndef POTENTIALNEW_H #define POTENTIALNEW_H +#include "pot_base.h" #include "source_base/complexmatrix.h" #include "source_hamilt/module_surchem/surchem.h" -#include "source_pw/module_pwdft/vsep_pw.h" #include "source_pw/module_pwdft/structure_factor.h" -#include "pot_base.h" +#include "source_pw/module_pwdft/vsep_pw.h" +#include #include namespace elecstate { +class TDFieldManager; + /** * Potential is the main class of potentials module, it contains: * 1. Constructors and deconstructor @@ -48,7 +51,7 @@ class Potential : public PotBase { public: // default constructor for UT - Potential(){}; + Potential() {}; // In constructor, size of every potential components should be allocated // rho_basis_in is the dense grids, rho_basis_smooth_in is the smooth grids in USPP // charge density and potential are defined on dense grids, @@ -66,16 +69,26 @@ class Potential : public PotBase ~Potential(); // initialize potential when SCF begin - void init_pot(const Charge*const chg); + void init_pot(const Charge* const chg); // initialize potential components before SCF void pot_register(const std::vector& components_list); // update potential from current charge - void update_from_charge(const Charge*const chg, const UnitCell*const ucell); + void update_from_charge(const Charge* const chg, const UnitCell* const ucell); // interface for SCF-converged, etxc vtxc for Energy, vnew for force_scc void get_vnew(const Charge* chg, ModuleBase::matrix& vnew); PotBase* get_pot_type(const std::string& pot_type); + /** + * @brief Inject the shared RT-TDDFT field state before potential setup. + * + * @param field_manager Manager shared with the RT-TDDFT ESolver. + */ + void set_td_field_manager(const std::shared_ptr& field_manager) + { + this->td_field_manager_ = field_manager; + } + // interfaces to get values ModuleBase::matrix& get_eff_v() { @@ -171,7 +184,7 @@ class Potential : public PotBase { return this->v_eff_fixed.data(); } - const ModulePW::PW_Basis *get_rho_basis() const + const ModulePW::PW_Basis* get_rho_basis() const { return this->rho_basis_; } @@ -187,7 +200,6 @@ class Potential : public PotBase // What about adding a function to get the wfc? // This is useful for the calculation of the exx energy - /// @brief get the value of vloc at G=0; /// @return vl(0) double get_vl_of_0() const @@ -200,7 +212,7 @@ class Potential : public PotBase double get_ml_exx_energy() const; private: - void cal_v_eff(const Charge*const chg, const UnitCell*const ucell, ModuleBase::matrix& v_eff) override; + void cal_v_eff(const Charge* const chg, const UnitCell* const ucell, ModuleBase::matrix& v_eff) override; void cal_fixed_v(double* vl_pseudo) override; // interpolate potential on the smooth mesh if necessary void interpolate_vrs(); @@ -215,10 +227,10 @@ class Potential : public PotBase ModuleBase::matrix v_xc; // if PAW is used, vxc must be stored separately - float *s_veff_smooth = nullptr; - float *s_vofk_smooth = nullptr; - double *d_veff_smooth = nullptr; - double *d_vofk_smooth = nullptr; + float* s_veff_smooth = nullptr; + float* s_vofk_smooth = nullptr; + double* d_veff_smooth = nullptr; + double* d_vofk_smooth = nullptr; ModuleBase::matrix vofk_eff; @@ -238,6 +250,7 @@ class Potential : public PotBase surchem* solvent_ = nullptr; VSep* vsep_cell = nullptr; bool use_gpu_ = false; + std::shared_ptr td_field_manager_; }; } // namespace elecstate diff --git a/source/source_estate/module_pot/potential_types.cpp b/source/source_estate/module_pot/potential_types.cpp index 37a9337105..e42e7ca508 100644 --- a/source/source_estate/module_pot/potential_types.cpp +++ b/source/source_estate/module_pot/potential_types.cpp @@ -1,20 +1,18 @@ #include "H_Hartree_pw.h" #include "efield.h" -#include "source_io/module_parameter/parameter.h" #include "gatefield.h" +#include "pot_local.h" +#include "pot_sep.h" +#include "pot_surchem.hpp" +#include "pot_xc.h" +#include "potential_new.h" #include "source_base/global_function.h" #include "source_base/global_variable.h" #include "source_base/timer.h" #include "source_base/tool_quit.h" #include "source_base/tool_title.h" -#include "pot_local.h" -#include "pot_surchem.hpp" -#include "pot_xc.h" -#include "potential_new.h" -#include "pot_sep.h" -#ifdef __LCAO +#include "source_io/module_parameter/parameter.h" #include "H_TDDFT_pw.h" -#endif #ifdef __MLALGO #include "pot_ml_exx.h" #endif @@ -39,10 +37,7 @@ PotBase* Potential::get_pot_type(const std::string& pot_type) } else if (pot_type == "surchem") { - return new PotSurChem(this->rho_basis_, - this->structure_factors_, - this->v_eff_fixed.data(), - this->solvent_); + return new PotSurChem(this->rho_basis_, this->structure_factors_, this->v_eff_fixed.data(), this->solvent_); } else if (pot_type == "efield") { @@ -52,19 +47,20 @@ PotBase* Potential::get_pot_type(const std::string& pot_type) { return new PotGate(this->rho_basis_, this->ucell_); } -#ifdef __LCAO else if (pot_type == "tddft") { - return new H_TDDFT_pw(this->rho_basis_, this->ucell_); + // The RT-TDDFT ESolver injects this manager before potential + // registration so the potential does not own a separate time counter. + return new H_TDDFT_pw(this->rho_basis_, this->ucell_, this->td_field_manager_); } -#endif #ifdef __MLALGO else if (pot_type == "ml_exx") { return new PotML_EXX(this->rho_basis_, this->ucell_); } #endif - else if (pot_type == "dfthalf") { + else if (pot_type == "dfthalf") + { return new PotSep(&(this->structure_factors_->strucFac), this->rho_basis_, this->vsep_cell); } else diff --git a/source/source_estate/module_pot/td_field.cpp b/source/source_estate/module_pot/td_field.cpp new file mode 100644 index 0000000000..b26d3d3b9b --- /dev/null +++ b/source/source_estate/module_pot/td_field.cpp @@ -0,0 +1,33 @@ +#include "td_field.h" + +#include + +namespace elecstate +{ + +double TDFieldSample::step_position() const +{ + return electronic_step + static_cast(simpson_node) / subdivisions; +} + +TDField::TDField(const int direction, std::unique_ptr profile, const int subdivisions) + : direction_(direction), profile_(std::move(profile)), subdivisions_(subdivisions) +{ +} + +int TDField::direction() const +{ + return direction_; +} + +int TDField::subdivisions() const +{ + return subdivisions_; +} + +double TDField::electric_field(const TDFieldSample& sample) const +{ + return profile_->electric_field(sample); +} + +} // namespace elecstate diff --git a/source/source_estate/module_pot/td_field.h b/source/source_estate/module_pot/td_field.h new file mode 100644 index 0000000000..3cad298a92 --- /dev/null +++ b/source/source_estate/module_pot/td_field.h @@ -0,0 +1,107 @@ +#ifndef TD_FIELD_H +#define TD_FIELD_H + +#include + +namespace elecstate +{ + +/** + * @brief Sampling location used to evaluate one time-dependent field profile. + * + * A vector-gauge electronic step is divided into Simpson subintervals. The + * sample represents node `simpson_node` in electronic step `electronic_step`; + * length-gauge evaluation uses node zero directly. + */ +struct TDFieldSample +{ + /** + * @brief Construct a field sampling point. + * + * @param electronic_step_in Zero-based electronic-step index. + * @param simpson_node_in Node index in the current Simpson interval. + * @param subdivisions_in Number of subintervals in one electronic step. + * @param time_in Physical sampling time in internal atomic time units. + */ + TDFieldSample(const int electronic_step_in, const int simpson_node_in, const int subdivisions_in, const double time_in) + : electronic_step(electronic_step_in), simpson_node(simpson_node_in), subdivisions(subdivisions_in), time(time_in) + { + } + + /** + * @brief Return the sampling location in continuous electronic-step units. + */ + double step_position() const; + + int electronic_step; ///< Zero-based electronic-step index. + int simpson_node; ///< Node index within the current electronic step. + int subdivisions; ///< Number of subintervals in one electronic step. + double time; ///< Sampling time in internal atomic time units. +}; + +/** + * @brief Scalar time profile of one configured electric field. + * + * Implementations return the field in ABACUS internal propagation units. A + * profile contains no Cartesian direction; direction handling is owned by + * TDField and TDFieldManager. + */ +class TDFieldProfile +{ + public: + /** @brief Destroy the polymorphic field profile. */ + virtual ~TDFieldProfile() = default; + + /** + * @brief Evaluate the electric field at one sampling point. + * + * @param sample Electronic-step and Simpson-node sampling information. + * @return Scalar field value in internal propagation units. + */ + virtual double electric_field(const TDFieldSample& sample) const = 0; +}; + +/** + * @brief One configured field occurrence and its Cartesian direction. + * + * Each occurrence remains independent even when multiple fields share a + * direction. The class is move-only because it owns a polymorphic profile. + */ +class TDField +{ + public: + /** + * @brief Construct one directed time-dependent field. + * + * @param direction Zero-based absolute Cartesian direction. + * @param profile Scalar field profile owned by this object. + * @param subdivisions Simpson subinterval count for one electronic step. + */ + TDField(int direction, std::unique_ptr profile, int subdivisions); + + /** @brief Move-construct a field while transferring profile ownership. */ + TDField(TDField&& other) = default; + + /** @brief Move-assign a field while transferring profile ownership. */ + TDField& operator=(TDField&& other) = default; + TDField(const TDField&) = delete; + TDField& operator=(const TDField&) = delete; + + /** @brief Return the zero-based absolute Cartesian direction. */ + int direction() const; + + /** @brief Return the Simpson subinterval count for one electronic step. */ + int subdivisions() const; + + /** @brief Evaluate this field's scalar profile at a sampling point. */ + double electric_field(const TDFieldSample& sample) const; + + private: + int direction_; + std::unique_ptr profile_; + int subdivisions_; +}; + +} // namespace elecstate + +#endif diff --git a/source/source_estate/module_pot/td_field_manager.cpp b/source/source_estate/module_pot/td_field_manager.cpp new file mode 100644 index 0000000000..64efe6edbc --- /dev/null +++ b/source/source_estate/module_pot/td_field_manager.cpp @@ -0,0 +1,300 @@ +#include "td_field_manager.h" + +#include "source_base/constants.h" +#include "source_base/math_integral.h" +#include "source_base/tool_quit.h" +#include "source_io/module_parameter/input_parameter.h" +#include "td_field_profiles.h" + +#include +#include +#include +#include + +namespace +{ + +int integration_subdivisions(const double omega, const double dt, const int gauge) +{ + // Length gauge samples only the beginning of each electronic step and does + // not integrate the field in time. + if (gauge == 0) + { + return 1; + } + + // Preserve the legacy frequency-dependent resolution while enforcing the + // positive, even number of subintervals required by Simpson integration. + int subdivisions = static_cast(100.0 * std::abs(omega) * dt / ModuleBase::PI); + subdivisions += subdivisions % 2 == 0 ? 2 : 1; + return std::max(2, subdivisions); +} + +double angular_frequency(const double frequency) +{ + // User frequencies are supplied in fs^-1; profiles use atomic time. + return frequency * 2.0 * ModuleBase::PI * ModuleBase::AU_to_FS; +} + +double field_amplitude(const double amplitude) +{ + // Convert the user-visible V/Angstrom scale to the propagation field unit. + return amplitude * ModuleBase::BOHR_TO_A / ModuleBase::Ry_to_eV; +} + +} // namespace + +namespace elecstate +{ + +TDFieldManager::TDFieldManager(const bool enabled, + const int gauge, + const int start_step, + const int end_step, + const double dt, + const double length_cut1, + const double length_cut2, + std::vector fields) + : enabled_(enabled), gauge_(gauge), start_step_(start_step), end_step_(end_step), dt_(dt), length_cut1_(length_cut1), + length_cut2_(length_cut2), fields_(std::move(fields)), current_step_(-1), active_(false), field_values_(fields_.size(), 0.0) +{ + vector_potential_.set(0.0, 0.0, 0.0); + vector_potential_laststep_.set(0.0, 0.0, 0.0); + electric_field_.set(0.0, 0.0, 0.0); + total_electric_field_.set(0.0, 0.0, 0.0); +} + +void TDFieldManager::advance_length_gauge() +{ + ++current_step_; + active_ = enabled_ && current_step_ >= start_step_ && current_step_ <= end_step_; + std::fill(field_values_.begin(), field_values_.end(), 0.0); + total_electric_field_.set(0.0, 0.0, 0.0); + if (!active_) + { + return; + } + + for (std::size_t index = 0; index < fields_.size(); ++index) + { + const TDField& field = fields_[index]; + const TDFieldSample sample(current_step_, 0, field.subdivisions(), current_step_ * dt_); + // Keep every occurrence for output, but sum repeated directions for + // the physical length-gauge potential and ionic force. + field_values_[index] = field.electric_field(sample); + total_electric_field_[field.direction()] += field_values_[index]; + } +} + +void TDFieldManager::advance_vector_gauge() +{ + ++current_step_; + // Finish the second half of the previous interval before integrating the + // current interval. vector_potential_ therefore remains a midpoint value. + vector_potential_ = vector_potential_ + vector_potential_laststep_ / 2.0; + vector_potential_laststep_.set(0.0, 0.0, 0.0); + electric_field_.set(0.0, 0.0, 0.0); + total_electric_field_.set(0.0, 0.0, 0.0); + std::fill(field_values_.begin(), field_values_.end(), 0.0); + active_ = enabled_ && current_step_ >= start_step_ && current_step_ <= end_step_; + if (!active_) + { + return; + } + + for (std::size_t index = 0; index < fields_.size(); ++index) + { + const TDField& field = fields_[index]; + const int subdivisions = field.subdivisions(); + const double integration_dt = dt_ / subdivisions; + std::vector samples(subdivisions + 1, 0.0); + for (int node = 0; node <= subdivisions; ++node) + { + const double time = (current_step_ + static_cast(node) / subdivisions) * dt_; + samples[node] = field.electric_field(TDFieldSample(current_step_, node, subdivisions, time)); + } + + // Integrate E over [n*dt, (n+1)*dt]. The minus sign implements + // A(t+dt)-A(t) = -integral E(t') dt'. + double integral = 0.0; + ModuleBase::Integral::Simpson_Integral(subdivisions + 1, samples.data(), integration_dt, integral); + vector_potential_laststep_[field.direction()] -= integral; + // Output and the hybrid-gauge scalar potential use E at the interval + // start rather than an average over Simpson nodes. + field_values_[index] = samples.front(); + if (gauge_ == 2) + { + electric_field_[field.direction()] += samples.front(); + } + } + + // Advance from the interval endpoint to its midpoint representation. + vector_potential_ = vector_potential_ + vector_potential_laststep_ / 2.0; + if (gauge_ == 2) + { + total_electric_field_ = electric_field_; + } +} + +void TDFieldManager::read_restart(const std::string& file_dir) +{ + std::ifstream file((file_dir + "Restart_td.txt").c_str()); + if (!file) + { + ModuleBase::WARNING_QUIT("TDFieldManager::read_restart", "No Restart_td.txt!"); + } + + int restart_step = -1; + if (!(file >> restart_step >> vector_potential_[0] >> vector_potential_[1] >> vector_potential_[2] >> vector_potential_laststep_[0] + >> vector_potential_laststep_[1] >> vector_potential_laststep_[2])) + { + ModuleBase::WARNING_QUIT("TDFieldManager::read_restart", "Invalid Restart_td.txt!"); + } + // Retain the legacy restart-file sign convention expected by the first + // half-step update in advance_vector_gauge(). + vector_potential_laststep_ = -vector_potential_laststep_; + current_step_ = restart_step - 1; +} + +int TDFieldManager::gauge() const +{ + return gauge_; +} + +int TDFieldManager::current_step() const +{ + return current_step_; +} + +double TDFieldManager::dt() const +{ + return dt_; +} + +double TDFieldManager::length_cut1() const +{ + return length_cut1_; +} + +double TDFieldManager::length_cut2() const +{ + return length_cut2_; +} + +bool TDFieldManager::active() const +{ + return active_; +} + +const std::vector& TDFieldManager::fields() const +{ + return fields_; +} + +const std::vector& TDFieldManager::field_values() const +{ + return field_values_; +} + +const ModuleBase::Vector3& TDFieldManager::vector_potential() const +{ + return vector_potential_; +} + +const ModuleBase::Vector3& TDFieldManager::vector_potential_laststep() const +{ + return vector_potential_laststep_; +} + +const ModuleBase::Vector3& TDFieldManager::electric_field() const +{ + return electric_field_; +} + +const ModuleBase::Vector3& TDFieldManager::total_electric_field() const +{ + return total_electric_field_; +} + +std::shared_ptr create_td_field_manager(const Input_para& input) +{ + // An explicitly supplied electronic time step takes precedence; otherwise + // derive it from the ionic time step and the electronic-step count. + const double dt + = input.td_dt != -1.0 ? input.td_dt / ModuleBase::AU_to_FS : input.mdp.md_dt / input.estep_per_md / ModuleBase::AU_to_FS; + // Each waveform-specific parameter vector is indexed by occurrences of + // that waveform, not by the overall position in td_ttype. + std::vector occurrences(5, 0); + std::vector fields; + fields.reserve(input.td_ttype.size()); + + for (std::size_t field_index = 0; field_index < input.td_ttype.size(); ++field_index) + { + const int field_type = input.td_ttype[field_index]; + const std::size_t occurrence = occurrences[field_type]++; + std::unique_ptr profile; + int subdivisions = 1; + if (field_type == 0) + { + const double omega = angular_frequency(input.td_gauss_freq.at(occurrence)); + subdivisions = integration_subdivisions(omega, dt, input.td_stype); + profile.reset(new TDGaussianProfile(omega, + input.td_gauss_phase.at(occurrence), + input.td_gauss_sigma.at(occurrence) / ModuleBase::AU_to_FS, + input.td_gauss_t0.at(occurrence), + field_amplitude(input.td_gauss_amp.at(occurrence)), + dt)); + } + else if (field_type == 1) + { + const double omega = angular_frequency(input.td_trape_freq.at(occurrence)); + subdivisions = integration_subdivisions(omega, dt, input.td_stype); + profile.reset(new TDTrapezoidProfile(omega, + input.td_trape_phase.at(occurrence), + input.td_trape_t1.at(occurrence), + input.td_trape_t2.at(occurrence), + input.td_trape_t3.at(occurrence), + field_amplitude(input.td_trape_amp.at(occurrence)))); + } + else if (field_type == 2) + { + const double omega1 = angular_frequency(input.td_trigo_freq1.at(occurrence)); + subdivisions = integration_subdivisions(omega1, dt, input.td_stype); + profile.reset(new TDTrigonometricProfile(omega1, + angular_frequency(input.td_trigo_freq2.at(occurrence)), + input.td_trigo_phase1.at(occurrence), + input.td_trigo_phase2.at(occurrence), + field_amplitude(input.td_trigo_amp.at(occurrence)))); + } + else if (field_type == 3) + { + subdivisions = input.td_stype == 0 ? 1 : 2; + profile.reset(new TDHeavisideProfile(input.td_heavi_t0.at(occurrence), field_amplitude(input.td_heavi_amp.at(occurrence)))); + } + else if (field_type == 4) + { + const double omega = angular_frequency(input.td_supsine_freq.at(occurrence)); + subdivisions = integration_subdivisions(omega, dt, input.td_stype); + profile.reset(new TDSupersineProfile(omega, + input.td_supsine_phase.at(occurrence), + input.td_supsine_sigma.at(occurrence), + input.td_supsine_tstart.at(occurrence), + input.td_supsine_tend.at(occurrence), + field_amplitude(input.td_supsine_amp.at(occurrence)), + dt)); + } + + fields.push_back(TDField(input.td_vext_dire.at(field_index) - 1, std::move(profile), subdivisions)); + } + + return std::shared_ptr(new TDFieldManager(input.td_vext, + input.td_stype, + input.td_tstart, + input.td_tend, + dt, + input.td_lcut1, + input.td_lcut2, + std::move(fields))); +} + +} // namespace elecstate diff --git a/source/source_estate/module_pot/td_field_manager.h b/source/source_estate/module_pot/td_field_manager.h new file mode 100644 index 0000000000..75bf27ceae --- /dev/null +++ b/source/source_estate/module_pot/td_field_manager.h @@ -0,0 +1,121 @@ +#ifndef TD_FIELD_MANAGER_H +#define TD_FIELD_MANAGER_H + +#include "source_base/vector3.h" +#include "td_field.h" + +#include +#include +#include + +struct Input_para; + +namespace elecstate +{ + +/** + * @brief Own and advance all time-dependent electric fields in RT-TDDFT. + * + * The manager preserves per-occurrence values for output while also summing + * fields that share a Cartesian direction. It is the single source of step, + * electric-field, and vector-potential state for all spatial gauges. + */ +class TDFieldManager +{ + public: + /** + * @brief Advance one length-gauge step and sample every field at its start. + */ + void advance_length_gauge(); + + /** + * @brief Advance one velocity/hybrid-gauge step using Simpson integration. + */ + void advance_vector_gauge(); + + /** + * @brief Restore the electronic step and vector-potential state. + * + * @param file_dir Directory containing `Restart_td.txt`. + */ + void read_restart(const std::string& file_dir); + + /** @brief Return the spatial-gauge selector supplied by `td_stype`. */ + int gauge() const; + + /** @brief Return the current zero-based electronic-step index. */ + int current_step() const; + + /** @brief Return the electronic time step in internal atomic time units. */ + double dt() const; + + /** @brief Return the first reduced-coordinate cut of the length gauge. */ + double length_cut1() const; + + /** @brief Return the second reduced-coordinate cut of the length gauge. */ + double length_cut2() const; + + /** @brief Return whether the configured field is active at this step. */ + bool active() const; + + /** @brief Return all configured fields in input-occurrence order. */ + const std::vector& fields() const; + + /** @brief Return per-occurrence field samples for the current step. */ + const std::vector& field_values() const; + + /** @brief Return the midpoint vector potential in propagation units. */ + const ModuleBase::Vector3& vector_potential() const; + + /** @brief Return the integrated vector-potential change for this step. */ + const ModuleBase::Vector3& vector_potential_laststep() const; + + /** @brief Return the direction-summed instantaneous hybrid-gauge field. */ + const ModuleBase::Vector3& electric_field() const; + + /** @brief Return the direction-summed field used by force evaluation. */ + const ModuleBase::Vector3& total_electric_field() const; + + private: + TDFieldManager(bool enabled, + int gauge, + int start_step, + int end_step, + double dt, + double length_cut1, + double length_cut2, + std::vector fields); + + bool enabled_; + int gauge_; + int start_step_; + int end_step_; + double dt_; + double length_cut1_; + double length_cut2_; + std::vector fields_; + int current_step_; + bool active_; + std::vector field_values_; + ModuleBase::Vector3 vector_potential_; + ModuleBase::Vector3 vector_potential_laststep_; + ModuleBase::Vector3 electric_field_; + ModuleBase::Vector3 total_electric_field_; + + friend std::shared_ptr create_td_field_manager(const Input_para& input); +}; + +/** + * @brief Build all field profiles and convert user input to propagation units. + * + * Parameters specific to a waveform are paired with `td_ttype` by occurrence, + * while `td_vext_dire` is paired by the overall field index. + * + * @param input Validated ABACUS input parameters. + * @return Shared manager used by the ESolver and time-dependent potential. + */ +std::shared_ptr create_td_field_manager(const Input_para& input); + +} // namespace elecstate + +#endif diff --git a/source/source_estate/module_pot/td_field_profiles.cpp b/source/source_estate/module_pot/td_field_profiles.cpp new file mode 100644 index 0000000000..b0b8125440 --- /dev/null +++ b/source/source_estate/module_pot/td_field_profiles.cpp @@ -0,0 +1,140 @@ +#include "td_field_profiles.h" + +#include "source_base/constants.h" + +#include + +namespace elecstate +{ + +TDGaussianProfile::TDGaussianProfile(const double omega, + const double phase, + const double sigma, + const double center_step, + const double amplitude, + const double dt) + : omega_(omega), phase_(phase), sigma_(sigma), center_step_(center_step), amplitude_(amplitude), dt_(dt) +{ +} + +double TDGaussianProfile::electric_field(const TDFieldSample& sample) const +{ + const double relative_time = (sample.step_position() - center_step_) * dt_; + return std::cos(omega_ * relative_time + phase_) * std::exp(-0.5 * relative_time * relative_time / (sigma_ * sigma_)) * amplitude_; +} + +TDTrapezoidProfile::TDTrapezoidProfile(const double omega, + const double phase, + const double rise_end_step, + const double plateau_end_step, + const double fall_end_step, + const double amplitude) + : omega_(omega), phase_(phase), rise_end_step_(rise_end_step), plateau_end_step_(plateau_end_step), fall_end_step_(fall_end_step), + amplitude_(amplitude) +{ +} + +double TDTrapezoidProfile::electric_field(const TDFieldSample& sample) const +{ + double envelope = 0.0; + // Segment selection follows the electronic step, while step_position() + // resolves the Simpson nodes used inside a selected linear segment. + if (sample.electronic_step < rise_end_step_) + { + envelope = sample.step_position() / rise_end_step_; + } + else if (sample.electronic_step < plateau_end_step_) + { + envelope = 1.0; + } + else if (sample.electronic_step < fall_end_step_) + { + envelope = (fall_end_step_ - sample.step_position()) / (fall_end_step_ - plateau_end_step_); + } + return envelope * amplitude_ * std::cos(omega_ * sample.time + phase_); +} + +TDTrigonometricProfile::TDTrigonometricProfile(const double omega1, + const double omega2, + const double phase1, + const double phase2, + const double amplitude) + : omega1_(omega1), omega2_(omega2), phase1_(phase1), phase2_(phase2), amplitude_(amplitude) +{ +} + +double TDTrigonometricProfile::electric_field(const TDFieldSample& sample) const +{ + const double envelope = std::sin(omega2_ * sample.time + phase2_); + return amplitude_ * std::cos(omega1_ * sample.time + phase1_) * envelope * envelope; +} + +TDHeavisideProfile::TDHeavisideProfile(const double switch_step, const double amplitude) : switch_step_(switch_step), amplitude_(amplitude) +{ +} + +double TDHeavisideProfile::electric_field(const TDFieldSample& sample) const +{ + return sample.electronic_step < switch_step_ ? amplitude_ : 0.0; +} + +TDSupersineProfile::TDSupersineProfile(const double omega, + const double phase, + const double sigma, + const int start_step, + const int end_step, + const double amplitude, + const double dt) + : omega_(omega), phase_(phase), sigma_(sigma), start_step_(start_step), end_step_(end_step), amplitude_(amplitude), dt_(dt) +{ +} + +double TDSupersineProfile::electric_field(const TDFieldSample& sample) const +{ + // Integer substep coordinates make both pulse boundaries exactly zero and + // avoid floating-point comparisons of independently constructed times. + const long long start_substep = static_cast(start_step_) * sample.subdivisions; + const long long end_substep = static_cast(end_step_) * sample.subdivisions; + const long long sample_substep = static_cast(sample.electronic_step) * sample.subdivisions + sample.simpson_node; + const long long local_substep = sample_substep - start_substep; + const long long duration_substeps = end_substep - start_substep; + const double duration = (end_step_ - start_step_) * dt_; + + double envelope = 0.0; + double envelope_derivative = 0.0; + if (local_substep > 0 && local_substep < duration_substeps) + { + const double x = static_cast(local_substep) / duration_substeps; + const double center_offset = x - 0.5; + if (std::abs(center_offset) <= 1.0e-14) + { + // Symmetry fixes the envelope and its derivative at pulse center. + envelope = 1.0; + } + else + { + const double sine = std::sin(ModuleBase::PI * x); + const double log_sine = std::log(sine); + const double absolute_offset = std::abs(center_offset); + // Evaluate the variable-power envelope in logarithmic form for + // better behavior near the compact-support boundaries. + envelope = std::exp(ModuleBase::PI * absolute_offset * log_sine / sigma_); + if (envelope > 0.0) + { + // Differentiate log(envelope) first, then use f' = f (log f)'. + const double logarithmic_derivative + = ModuleBase::PI * std::copysign(1.0, center_offset) * log_sine / sigma_ + + ModuleBase::PI * ModuleBase::PI * absolute_offset * std::cos(ModuleBase::PI * x) / (sigma_ * sine); + envelope_derivative = envelope * logarithmic_derivative / duration; + } + } + } + + const double time_from_center = (static_cast(local_substep) / sample.subdivisions - 0.5 * (end_step_ - start_step_)) * dt_; + const double carrier_phase = omega_ * time_from_center + phase_; + // The derivative term is required so that this field is exactly -dA/dt + // for the analytic compact-support supersine vector potential. + return amplitude_ * (envelope * std::cos(carrier_phase) + envelope_derivative * std::sin(carrier_phase) / omega_); +} + +} // namespace elecstate diff --git a/source/source_estate/module_pot/td_field_profiles.h b/source/source_estate/module_pot/td_field_profiles.h new file mode 100644 index 0000000000..1039b0f80d --- /dev/null +++ b/source/source_estate/module_pot/td_field_profiles.h @@ -0,0 +1,148 @@ +#ifndef TD_FIELD_PROFILES_H +#define TD_FIELD_PROFILES_H + +#include "td_field.h" + +namespace elecstate +{ + +/** @brief Carrier cosine multiplied by a Gaussian envelope. */ +class TDGaussianProfile : public TDFieldProfile +{ + public: + /** + * @brief Construct a Gaussian field profile in internal units. + * + * @param omega Angular frequency. + * @param phase Carrier phase in radians. + * @param sigma Gaussian width in atomic time units. + * @param center_step Pulse center in electronic-step units. + * @param amplitude Peak field amplitude. + * @param dt Electronic time step in atomic time units. + */ + TDGaussianProfile(double omega, double phase, double sigma, double center_step, double amplitude, double dt); + + /** @copydoc TDFieldProfile::electric_field */ + double electric_field(const TDFieldSample& sample) const override; + + private: + double omega_; + double phase_; + double sigma_; + double center_step_; + double amplitude_; + double dt_; +}; + +/** @brief Carrier cosine with linear-rise, plateau, and linear-fall envelope. */ +class TDTrapezoidProfile : public TDFieldProfile +{ + public: + /** + * @brief Construct a trapezoidal-envelope field profile. + * + * @param omega Angular frequency in internal units. + * @param phase Carrier phase in radians. + * @param rise_end_step End of the linear rise in electronic-step units. + * @param plateau_end_step End of the plateau in electronic-step units. + * @param fall_end_step End of the linear fall in electronic-step units. + * @param amplitude Peak field amplitude in internal units. + */ + TDTrapezoidProfile(double omega, double phase, double rise_end_step, double plateau_end_step, double fall_end_step, double amplitude); + + /** @copydoc TDFieldProfile::electric_field */ + double electric_field(const TDFieldSample& sample) const override; + + private: + double omega_; + double phase_; + double rise_end_step_; + double plateau_end_step_; + double fall_end_step_; + double amplitude_; +}; + +/** @brief Cosine carrier multiplied by a squared-sine envelope. */ +class TDTrigonometricProfile : public TDFieldProfile +{ + public: + /** + * @brief Construct a trigonometric field profile. + * + * @param omega1 Carrier angular frequency in internal units. + * @param omega2 Envelope angular frequency in internal units. + * @param phase1 Carrier phase in radians. + * @param phase2 Envelope phase in radians. + * @param amplitude Peak field amplitude in internal units. + */ + TDTrigonometricProfile(double omega1, double omega2, double phase1, double phase2, double amplitude); + + /** @copydoc TDFieldProfile::electric_field */ + double electric_field(const TDFieldSample& sample) const override; + + private: + double omega1_; + double omega2_; + double phase1_; + double phase2_; + double amplitude_; +}; + +/** @brief Constant field switched off at a specified electronic step. */ +class TDHeavisideProfile : public TDFieldProfile +{ + public: + /** + * @brief Construct a Heaviside field profile. + * + * @param switch_step First electronic step for which the field is zero. + * @param amplitude Field amplitude before the switch. + */ + TDHeavisideProfile(double switch_step, double amplitude); + + /** @copydoc TDFieldProfile::electric_field */ + double electric_field(const TDFieldSample& sample) const override; + + private: + double switch_step_; + double amplitude_; +}; + +/** + * @brief Compact supersine pulse derived from an analytic vector potential. + * + * The electric field includes both the carrier term and the envelope-derivative + * term required by the relation between electric and vector potentials. + */ +class TDSupersineProfile : public TDFieldProfile +{ + public: + /** + * @brief Construct a supersine field profile. + * + * @param omega Carrier angular frequency in internal units. + * @param phase Carrier phase in radians. + * @param sigma Supersine envelope-shape parameter. + * @param start_step First pulse-boundary electronic step. + * @param end_step Last pulse-boundary electronic step. + * @param amplitude Field scale in internal units. + * @param dt Electronic time step in atomic time units. + */ + TDSupersineProfile(double omega, double phase, double sigma, int start_step, int end_step, double amplitude, double dt); + + /** @copydoc TDFieldProfile::electric_field */ + double electric_field(const TDFieldSample& sample) const override; + + private: + double omega_; + double phase_; + double sigma_; + int start_step_; + int end_step_; + double amplitude_; + double dt_; +}; + +} // namespace elecstate + +#endif diff --git a/source/source_estate/test/CMakeLists.txt b/source/source_estate/test/CMakeLists.txt index ae09357ac8..b88e24b86d 100644 --- a/source/source_estate/test/CMakeLists.txt +++ b/source/source_estate/test/CMakeLists.txt @@ -74,7 +74,8 @@ AddTest( AddTest( TARGET MODULE_ESTATE_potentials_new LIBS parameter base device planewave_serial - SOURCES potential_new_test.cpp ../module_pot/potential_new.cpp + SOURCES potential_new_test.cpp td_field_test.cpp ../module_pot/potential_new.cpp + ../module_pot/td_field.cpp ../module_pot/td_field_profiles.cpp ../module_pot/td_field_manager.cpp ) AddTest( diff --git a/source/source_estate/test/potential_new_test.cpp b/source/source_estate/test/potential_new_test.cpp index 7cfeabc6fd..cb95bf6302 100644 --- a/source/source_estate/test/potential_new_test.cpp +++ b/source/source_estate/test/potential_new_test.cpp @@ -1,11 +1,35 @@ -#include "gtest/gtest.h" -#include - -#define private public #include "source_estate/module_pot/potential_new.h" + #include "source_hamilt/module_xc/xc_functional.h" #include "source_io/module_parameter/parameter.h" -// mock functions + +#include "gtest/gtest.h" +#include +#include +#include + +class TestParameters +{ + public: + static void reset() + { + PARAM.input.nspin = 1; + PARAM.input.basis_type = "pw"; + PARAM.input.device = "cpu"; + PARAM.input.precision = "double"; + PARAM.sys.has_float_data = false; + PARAM.sys.has_double_data = true; + } + + static void use_cpu_single() + { + PARAM.input.device = "cpu"; + PARAM.input.precision = "single"; + PARAM.sys.has_float_data = true; + PARAM.sys.has_double_data = false; + } +}; + Structure_Factor::Structure_Factor() { } @@ -24,11 +48,18 @@ Magnetism::Magnetism() Magnetism::~Magnetism() { } -SepPot::SepPot(){} -SepPot::~SepPot(){} -Sep_Cell::Sep_Cell() noexcept {} -Sep_Cell::~Sep_Cell() noexcept {} - +SepPot::SepPot() +{ +} +SepPot::~SepPot() +{ +} +Sep_Cell::Sep_Cell() noexcept +{ +} +Sep_Cell::~Sep_Cell() noexcept +{ +} Charge::Charge() { } @@ -41,631 +72,341 @@ surchem::surchem() surchem::~surchem() { } + int XC_Functional::func_type = 1; bool XC_Functional::ked_flag = false; -namespace elecstate -{ -PotBase* Potential::get_pot_type(const std::string& pot_type) +void XC_Functional::set_xc_type(const std::string xc_func_in) { - return new PotBase; + if (xc_func_in == "meta") + { + func_type = 3; + ked_flag = true; + } + else + { + func_type = 1; + ked_flag = false; + } } -void Set_GlobalV_Default() +namespace elecstate { - PARAM.input.nspin = 1; - PARAM.input.device = "cpu"; - PARAM.input.precision = "double"; -} -} // namespace elecstate - -/************************************************ - * unit test of potential_new.cpp - ***********************************************/ - -/** - * - Tested Functions: - * - Constructor: elecstate::Potential(rhopw, rhopw, ucell, vloc, structure_factors, etxc, vtxc) and - * elecstate::Potential::allocate() - * - potentials are divided into 2 types: fixed and dynamic - * - fixed potentials: loc, gatefield that are independent of rho - * - dynamic potentials: hartree, xc, surchem that are dependent of rho - * - Getters: elecstate::Potential::get_veff_smooth_data() and elecstate::Potential::get_vofk_smooth_data() - * - get the pointers to v_effective and vofk_effective - * - PotRegist: elecstate::Potential::pot_egist(components_list) - * - add new objects of potentials that are derived classes of PotBase - * - CalFixedV: elecstate::Potential::cal_fixed_v() - * - calculate the fixed potentials: v_effective_fixed - * - CalVeff: elecstate::Potential::cal_v_eff() - * - calculate v_effective by adding v_effective_fixed and adding the dynamic potentials - * - UpdateFromCharge: elecstate::Potential::update_from_charge() - * - calls cal_fixed_v and cal_v_eff to update v_effective from rho - * - InitPot: elecstate::Potential::init_pot() - * - using update_from_charge to initialize v_effective - * - GetVnew: elecstate::Potential::get_vnew() - * - used later for scf correction to the forces - * - GetEffective: elecstate::Potential::get_effective_v() - * - get the matrix reference or double pointer v_effective - * - GetEffectiveVOfK: elecstate::Potential::get_effective_vofk() - * - get the matrix reference or double pointer vofk_effective - * - GetFixedV: elecstate::Potential::get_fixed_v() - * - get the double pointer to v_effective_fixed - */ -class PotentialNewTest : public ::testing::Test +class MockPotComponent : public PotBase { - protected: - ModulePW::PW_Basis* rhopw = nullptr; - ModulePW::PW_Basis* rhodpw = nullptr; - UnitCell* ucell = nullptr; - ModuleBase::matrix* vloc = nullptr; - Structure_Factor* structure_factors = nullptr; - surchem* solvent = nullptr; - double* etxc = nullptr; - double* vtxc = nullptr; - elecstate::Potential* pot = nullptr; - virtual void SetUp() + public: + MockPotComponent(const std::string& type, const int grid_size) : type_(type), grid_size_(grid_size) { - rhopw = new ModulePW::PW_Basis; - rhodpw = new ModulePW::PW_Basis; - ucell = new UnitCell; - vloc = new ModuleBase::matrix; - structure_factors = new Structure_Factor(); - solvent = new surchem(); - etxc = new double; - vtxc = new double; - elecstate::Set_GlobalV_Default(); + this->fixed_mode = (type == "fixed"); + this->dynamic_mode = (type == "dynamic" || type == "ramp"); + ++created; } - virtual void TearDown() + + ~MockPotComponent() override { - if (rhopw != nullptr) - { - delete rhopw; - } - if (rhodpw != nullptr) - { - delete rhodpw; - } - if (ucell != nullptr) - { - delete ucell; - } - if (vloc != nullptr) - { - delete vloc; - } - if (structure_factors != nullptr) - { - delete structure_factors; - } - if (solvent != nullptr) - { - delete solvent; - } - if (etxc != nullptr) - { - delete etxc; - } - if (vtxc != nullptr) + ++destroyed; + } + + void cal_fixed_v(double* vl_pseudo) override + { + ++fixed_calls; + for (int ir = 0; ir < grid_size_; ++ir) { - delete vtxc; + vl_pseudo[ir] += 2.0; } - if (pot != nullptr) + } + + void cal_v_eff(const Charge* const chg, const UnitCell* const ucell, ModuleBase::matrix& v_eff) override + { + ++dynamic_calls; + for (int is = 0; is < v_eff.nr; ++is) { - delete pot; + for (int ir = 0; ir < v_eff.nc; ++ir) + { + v_eff(is, ir) += (type_ == "ramp" ? is + ir : dynamic_calls); + } } } -}; - -TEST_F(PotentialNewTest, ConstructorCPUDouble) -{ - rhopw->nrxx = 100; - pot = new elecstate::Potential(rhopw, rhopw, ucell, vloc, structure_factors, solvent, etxc, vtxc); - EXPECT_TRUE(pot->fixed_mode); - EXPECT_TRUE(pot->dynamic_mode); - EXPECT_EQ(pot->v_eff_fixed.size(), 100); - EXPECT_EQ(pot->v_eff.nr, PARAM.input.nspin); - EXPECT_EQ(pot->v_eff.nc, 100); -} -TEST_F(PotentialNewTest, ConstructorCPUSingle) -{ - rhopw->nrxx = 100; - PARAM.input.precision = "single"; - pot = new elecstate::Potential(rhopw, rhopw, ucell, vloc, structure_factors, solvent, etxc, vtxc); - EXPECT_TRUE(pot->fixed_mode); - EXPECT_TRUE(pot->dynamic_mode); - EXPECT_EQ(pot->v_eff_fixed.size(), 100); - EXPECT_EQ(pot->v_eff.nr, PARAM.input.nspin); - EXPECT_EQ(pot->v_eff.nc, 100); -} - -TEST_F(PotentialNewTest, ConstructorNRXX0) -{ - rhopw->nrxx = 0; - pot = new elecstate::Potential(rhopw, rhopw, ucell, vloc, structure_factors, solvent, etxc, vtxc); - EXPECT_TRUE(pot->fixed_mode); - EXPECT_TRUE(pot->dynamic_mode); -} + static void reset() + { + created = 0; + destroyed = 0; + fixed_calls = 0; + dynamic_calls = 0; + } -TEST_F(PotentialNewTest, ConstructorXC3) -{ - XC_Functional::func_type = 3; - XC_Functional::ked_flag = true; - rhopw->nrxx = 100; - pot = new elecstate::Potential(rhopw, rhopw, ucell, vloc, structure_factors, solvent, etxc, vtxc); - EXPECT_TRUE(pot->fixed_mode); - EXPECT_TRUE(pot->dynamic_mode); - EXPECT_EQ(pot->v_eff_fixed.size(), 100); - EXPECT_EQ(pot->v_eff.nr, PARAM.input.nspin); - EXPECT_EQ(pot->v_eff.nc, 100); - EXPECT_EQ(pot->vofk_eff.nr, PARAM.input.nspin); - EXPECT_EQ(pot->vofk_eff.nc, 100); -} + static int created; + static int destroyed; + static int fixed_calls; + static int dynamic_calls; -TEST_F(PotentialNewTest, ConstructorGPUDouble) -{ - // this is just a trivial call to the GPU code - rhopw->nrxx = 100; - PARAM.input.device = "gpu"; - pot = new elecstate::Potential(rhopw, rhopw, ucell, vloc, structure_factors, solvent, etxc, vtxc); - EXPECT_TRUE(pot->fixed_mode); - EXPECT_TRUE(pot->dynamic_mode); - EXPECT_EQ(pot->v_eff_fixed.size(), 100); - EXPECT_EQ(pot->v_eff.nr, PARAM.input.nspin); - EXPECT_EQ(pot->v_eff.nc, 100); -} + private: + std::string type_; + int grid_size_; +}; -TEST_F(PotentialNewTest, ConstructorGPUSingle) -{ - // this is just a trivial call to the GPU code - rhopw->nrxx = 100; - PARAM.input.device = "gpu"; - PARAM.input.precision = "single"; - pot = new elecstate::Potential(rhopw, rhopw, ucell, vloc, structure_factors, solvent, etxc, vtxc); - EXPECT_TRUE(pot->fixed_mode); - EXPECT_TRUE(pot->dynamic_mode); - EXPECT_EQ(pot->v_eff_fixed.size(), 100); - EXPECT_EQ(pot->v_eff.nr, PARAM.input.nspin); - EXPECT_EQ(pot->v_eff.nc, 100); -} +int MockPotComponent::created = 0; +int MockPotComponent::destroyed = 0; +int MockPotComponent::fixed_calls = 0; +int MockPotComponent::dynamic_calls = 0; -TEST_F(PotentialNewTest, Getters) +PotBase* Potential::get_pot_type(const std::string& pot_type) { - pot = new elecstate::Potential; - pot->veff_smooth.create(10, 10); - pot->vofk_smooth.create(10, 10); - float* foo; - foo = pot->get_veff_smooth_data(); - EXPECT_EQ(foo, pot->s_veff_smooth); - foo = pot->get_vofk_smooth_data(); - EXPECT_EQ(foo, pot->s_vofk_smooth); - double* doo; - doo = pot->get_veff_smooth_data(); - EXPECT_EQ(doo, pot->d_veff_smooth); - doo = pot->get_vofk_smooth_data(); - EXPECT_EQ(doo, pot->d_vofk_smooth); - delete foo; - delete doo; + const int grid_size = this->get_rho_basis() == nullptr ? 0 : this->get_rho_basis()->nrxx; + return new MockPotComponent(pot_type, grid_size); } -TEST_F(PotentialNewTest, PotRegister) -{ - pot = new elecstate::Potential; - elecstate::PotBase* pot0 = new elecstate::PotBase; - pot->components.push_back(pot0); - EXPECT_EQ(pot->components.size(), 1); - std::vector compnents_list = {"hartree", "xc"}; - pot->pot_register(compnents_list); - EXPECT_EQ(pot->components.size(), 2); - EXPECT_FALSE(pot->fixed_done); -} +} // namespace elecstate -TEST_F(PotentialNewTest, CalFixedV) +class PotentialNewTest : public ::testing::Test { - // construct potential - rhopw->nrxx = 100; - pot = new elecstate::Potential(rhopw, rhopw, ucell, vloc, structure_factors, solvent, etxc, vtxc); - // - std::vector compnents_list = { - "local", - "hartree", - "xc", - "surchem", - "gatefield" - }; - std::vector fixed = {true, false, false, false, true}; - pot->pot_register(compnents_list); - for (int i = 0; icomponents[i]->fixed_mode = fixed[i]; - } - double* vl_pseudo = new double[1000]; - pot->cal_fixed_v(vl_pseudo); - for (int i = 0; i < pot->v_eff_fixed.size(); i++) + protected: + void SetUp() override { - EXPECT_DOUBLE_EQ(pot->v_eff_fixed[i], 0.0); + TestParameters::reset(); + XC_Functional::set_xc_type("lda"); + elecstate::MockPotComponent::reset(); + + smooth_basis.reset(new ModulePW::PW_Basis); + dense_basis.reset(new ModulePW::PW_Basis_Sup); + ucell.reset(new UnitCell); + vloc.reset(new ModuleBase::matrix); + structure_factors.reset(new Structure_Factor); + solvent.reset(new surchem); + etxc.reset(new double(0.0)); + vtxc.reset(new double(0.0)); } - delete[] vl_pseudo; -} -TEST_F(PotentialNewTest, CalVeff) -{ - // construct potential - rhopw->nrxx = 100; - pot = new elecstate::Potential(rhopw, rhopw, ucell, vloc, structure_factors, solvent, etxc, vtxc); - // - std::vector compnents_list = { - "local", - "hartree", - "xc", - "surchem", - "gatefield" - }; - std::vector dynamic = {false, true, true, true, false}; - pot->pot_register(compnents_list); - for (int i = 0; icomponents[i]->dynamic_mode = dynamic[i]; + potential.reset(); + XC_Functional::set_xc_type("lda"); + TestParameters::reset(); + elecstate::MockPotComponent::reset(); } - Charge* chg = new Charge; - ModuleBase::matrix v_eff; - v_eff.create(2, 100); - pot->cal_v_eff(chg,this->ucell,v_eff); - for (int i = 0; i < pot->v_eff_fixed.size(); i++) + + void create_potential(const ModulePW::PW_Basis* dense, const ModulePW::PW_Basis* smooth) { - EXPECT_DOUBLE_EQ(pot->v_eff_fixed[i], 0.0); + potential.reset(new elecstate::Potential(dense, + smooth, + ucell.get(), + vloc.get(), + structure_factors.get(), + solvent.get(), + etxc.get(), + vtxc.get())); } - delete chg; -} -TEST_F(PotentialNewTest, UpdateFromCharge) -{ - // construct potential - rhopw->nrxx = 100; - pot = new elecstate::Potential(rhopw, rhopw, ucell, vloc, structure_factors, solvent, etxc, vtxc); - // - std::vector compnents_list = { - "local", - "hartree", - "xc", - "surchem", - "gatefield" - }; - std::vector fixed = {true, false, false, false, true}; - std::vector dynamic = {false, true, true, true, false}; - pot->pot_register(compnents_list); - for (int i = 0; icomponents[i]->fixed_mode = fixed[i]; - pot->components[i]->dynamic_mode = dynamic[i]; + smooth_basis->initgrids(4, ModuleBase::Matrix3(1, 0, 0, 0, 1, 0, 0, 0, 1), 4); + smooth_basis->initparameters(false, 4); + smooth_basis->setuptransform(); + smooth_basis->collect_local_pw(); } - Charge* chg = new Charge; - EXPECT_FALSE(pot->fixed_done); - pot->update_from_charge(chg, this->ucell); - EXPECT_TRUE(pot->fixed_done); - delete chg; -} -TEST_F(PotentialNewTest, InitPot) -{ - // construct potential - rhopw->nrxx = 100; - pot = new elecstate::Potential(rhopw, rhopw, ucell, vloc, structure_factors, solvent, etxc, vtxc); - // - std::vector compnents_list = { - "local", - "hartree", - "xc", - "surchem", - "gatefield" - }; - std::vector fixed = {true, false, false, false, true}; - std::vector dynamic = {false, true, true, true, false}; - pot->pot_register(compnents_list); - for (int i = 0; icomponents[i]->fixed_mode = fixed[i]; - pot->components[i]->dynamic_mode = dynamic[i]; + dense_basis->initgrids(4, ModuleBase::Matrix3(1, 0, 0, 0, 1, 0, 0, 0, 1), 6); + dense_basis->initparameters(false, 6); + dense_basis->setuptransform(smooth_basis.get()); + dense_basis->collect_local_pw(); } - Charge* chg = new Charge; - EXPECT_FALSE(pot->fixed_done); - pot->init_pot(chg); - EXPECT_TRUE(pot->fixed_done); - delete chg; -} -TEST_F(PotentialNewTest, GetVnew) + std::unique_ptr smooth_basis; + std::unique_ptr dense_basis; + std::unique_ptr ucell; + std::unique_ptr vloc; + std::unique_ptr structure_factors; + std::unique_ptr solvent; + std::unique_ptr etxc; + std::unique_ptr vtxc; + std::unique_ptr potential; +}; + +TEST_F(PotentialNewTest, ConstructorAndPublicGetters) { - // construct potential - rhopw->nrxx = 100; - pot = new elecstate::Potential(rhopw, rhopw, ucell, vloc, structure_factors, solvent, etxc, vtxc); - // - std::vector compnents_list = { - "local", - "hartree", - "xc", - "surchem", - "gatefield" - }; - std::vector fixed = {true, false, false, false, true}; - std::vector dynamic = {false, true, true, true, false}; - pot->pot_register(compnents_list); - for (int i = 0; icomponents[i]->fixed_mode = fixed[i]; - pot->components[i]->dynamic_mode = dynamic[i]; - } - Charge* chg = new Charge; - ModuleBase::matrix vnew; - pot->get_vnew(chg, vnew); - EXPECT_EQ(vnew.nr, PARAM.input.nspin); - EXPECT_EQ(vnew.nc, 100); - delete chg; + smooth_basis->nrxx = 100; + create_potential(smooth_basis.get(), smooth_basis.get()); + + EXPECT_TRUE(potential->fixed_mode); + EXPECT_TRUE(potential->dynamic_mode); + EXPECT_EQ(potential->get_eff_v().nr, 1); + EXPECT_EQ(potential->get_eff_v().nc, 100); + EXPECT_NE(potential->get_fixed_v(), nullptr); + EXPECT_EQ(potential->get_rho_basis(), smooth_basis.get()); + EXPECT_EQ(potential->get_ucell(), ucell.get()); + EXPECT_EQ(potential->get_vloc(), vloc.get()); + EXPECT_EQ(potential->get_veff_smooth_data(), potential->get_veff_smooth().c); + EXPECT_EQ(potential->get_veff_smooth_data(), nullptr); + + const elecstate::Potential& const_potential = *potential; + EXPECT_EQ(const_potential.get_eff_v().nr, 1); + EXPECT_EQ(const_potential.get_eff_v(0), potential->get_eff_v(0)); + EXPECT_EQ(const_potential.get_fixed_v(), potential->get_fixed_v()); + EXPECT_EQ(const_potential.get_veff_smooth().c, potential->get_veff_smooth().c); } -TEST_F(PotentialNewTest, GetEffectiveVmatrix) +TEST_F(PotentialNewTest, EmptyPotentialReturnsNullData) { - // construct potential - rhopw->nrxx = 100; - pot = new elecstate::Potential(rhopw, rhopw, ucell, vloc, structure_factors, solvent, etxc, vtxc); - // - ModuleBase::matrix v_eff_tmp = pot->get_eff_v(); - const ModuleBase::matrix v_eff_tmp_const = pot->get_eff_v(); - EXPECT_EQ(v_eff_tmp.nr, PARAM.input.nspin); - EXPECT_EQ(v_eff_tmp.nc, 100); - EXPECT_EQ(v_eff_tmp_const.nr, PARAM.input.nspin); - EXPECT_EQ(v_eff_tmp_const.nc, 100); - for (int ir = 0; ir < v_eff_tmp.nr; ir++) - { - for (int ic = 0; ic < v_eff_tmp.nc; ic++) - { - EXPECT_DOUBLE_EQ(v_eff_tmp(ir, ic), pot->v_eff(ir, ic)); - EXPECT_DOUBLE_EQ(v_eff_tmp_const(ir, ic), pot->v_eff(ir, ic)); - } - } + potential.reset(new elecstate::Potential); + const elecstate::Potential& const_potential = *potential; + + EXPECT_EQ(potential->get_eff_v(0), nullptr); + EXPECT_EQ(const_potential.get_eff_v(0), nullptr); + EXPECT_EQ(potential->get_eff_vofk(0), nullptr); + EXPECT_EQ(const_potential.get_eff_vofk(0), nullptr); + EXPECT_EQ(potential->get_veff_smooth_data(), nullptr); + EXPECT_EQ(potential->get_veff_smooth_data(), nullptr); + EXPECT_EQ(potential->get_vofk_smooth_data(), nullptr); + EXPECT_EQ(potential->get_vofk_smooth_data(), nullptr); } -TEST_F(PotentialNewTest, GetEffectiveVarray) +TEST_F(PotentialNewTest, ConstructorCPUSingle) { - // construct potential - rhopw->nrxx = 100; - pot = new elecstate::Potential(rhopw, rhopw, ucell, vloc, structure_factors, solvent, etxc, vtxc); - // - double* v_eff_tmp = pot->get_eff_v(0); - const double* v_eff_tmp_const = pot->get_eff_v(0); - for (int ic = 0; ic < rhopw->nrxx; ic++) - { - EXPECT_DOUBLE_EQ(v_eff_tmp[ic], pot->v_eff(0, ic)); - EXPECT_DOUBLE_EQ(v_eff_tmp_const[ic], pot->v_eff(0, ic)); - } - v_eff_tmp[0] = 1.0; - EXPECT_DOUBLE_EQ(pot->v_eff(0, 0), 1.0); - EXPECT_DOUBLE_EQ(v_eff_tmp_const[0], 1.0); + TestParameters::use_cpu_single(); + smooth_basis->nrxx = 100; + create_potential(smooth_basis.get(), smooth_basis.get()); + + EXPECT_NE(potential->get_veff_smooth_data(), nullptr); + EXPECT_EQ(potential->get_veff_smooth_data(), nullptr); } -TEST_F(PotentialNewTest, GetEffectiveVarrayNullptr) +TEST_F(PotentialNewTest, MetaPotentialPublicGetters) { - pot = new elecstate::Potential; - EXPECT_EQ(pot->v_eff.nc, 0); - double* v_eff_tmp = pot->get_eff_v(0); - const double* v_eff_tmp_const = pot->get_eff_v(0); - EXPECT_EQ(v_eff_tmp, nullptr); - EXPECT_EQ(v_eff_tmp_const, nullptr); + XC_Functional::set_xc_type("meta"); + smooth_basis->nrxx = 100; + create_potential(smooth_basis.get(), smooth_basis.get()); + + EXPECT_EQ(potential->get_eff_vofk().nr, 1); + EXPECT_EQ(potential->get_eff_vofk().nc, 100); + EXPECT_EQ(potential->get_vofk_smooth().nr, 1); + EXPECT_EQ(potential->get_vofk_smooth().nc, 100); + EXPECT_EQ(potential->get_vofk_smooth_data(), potential->get_vofk_smooth().c); + + potential->get_eff_vofk()(0, 0) = 7.0; + EXPECT_DOUBLE_EQ(potential->get_eff_vofk(0)[0], 7.0); + + const elecstate::Potential& const_potential = *potential; + EXPECT_EQ(const_potential.get_eff_vofk(0), potential->get_eff_vofk(0)); + EXPECT_EQ(const_potential.get_vofk_smooth().c, potential->get_vofk_smooth().c); } -TEST_F(PotentialNewTest, GetEffectiveVofkmatrix) +TEST_F(PotentialNewTest, PotRegisterReplacesAndDestroysComponents) { - // construct potential - XC_Functional::func_type = 3; - XC_Functional::ked_flag = true; - rhopw->nrxx = 100; - pot = new elecstate::Potential(rhopw, rhopw, ucell, vloc, structure_factors, solvent, etxc, vtxc); - // - ModuleBase::matrix vofk_eff_tmp = pot->get_eff_vofk(); - const ModuleBase::matrix vofk_eff_tmp_const = pot->get_eff_vofk(); - EXPECT_EQ(vofk_eff_tmp.nr, PARAM.input.nspin); - EXPECT_EQ(vofk_eff_tmp.nc, 100); - EXPECT_EQ(vofk_eff_tmp_const.nr, PARAM.input.nspin); - EXPECT_EQ(vofk_eff_tmp_const.nc, 100); - for (int ir = 0; ir < vofk_eff_tmp.nr; ir++) - { - for (int ic = 0; ic < vofk_eff_tmp.nc; ic++) - { - EXPECT_DOUBLE_EQ(vofk_eff_tmp(ir, ic), pot->vofk_eff(ir, ic)); - EXPECT_DOUBLE_EQ(vofk_eff_tmp_const(ir, ic), pot->vofk_eff(ir, ic)); - } - } + potential.reset(new elecstate::Potential); + potential->pot_register({"fixed", "dynamic"}); + EXPECT_EQ(elecstate::MockPotComponent::created, 2); + EXPECT_EQ(elecstate::MockPotComponent::destroyed, 0); + + potential->pot_register({"dynamic"}); + EXPECT_EQ(elecstate::MockPotComponent::created, 3); + EXPECT_EQ(elecstate::MockPotComponent::destroyed, 2); + + potential.reset(); + EXPECT_EQ(elecstate::MockPotComponent::destroyed, 3); } -TEST_F(PotentialNewTest, GetEffectiveVofkarray) +TEST_F(PotentialNewTest, PublicUpdateFlowsScheduleComponents) { - // construct potential - rhopw->nrxx = 100; - pot = new elecstate::Potential(rhopw, rhopw, ucell, vloc, structure_factors, solvent, etxc, vtxc); - // - double* vofk_eff_tmp = pot->get_eff_vofk(0); - const double* vofk_eff_tmp_const = pot->get_eff_vofk(0); - for (int ic = 0; ic < rhopw->nrxx; ic++) + smooth_basis->nrxx = 8; + create_potential(smooth_basis.get(), smooth_basis.get()); + potential->pot_register({"fixed", "dynamic"}); + Charge charge; + + potential->update_from_charge(&charge, ucell.get()); + EXPECT_EQ(elecstate::MockPotComponent::fixed_calls, 1); + EXPECT_EQ(elecstate::MockPotComponent::dynamic_calls, 1); + for (int ir = 0; ir < smooth_basis->nrxx; ++ir) { - EXPECT_DOUBLE_EQ(vofk_eff_tmp[ic], pot->vofk_eff(0, ic)); - EXPECT_DOUBLE_EQ(vofk_eff_tmp_const[ic], pot->vofk_eff(0, ic)); + EXPECT_DOUBLE_EQ(potential->get_fixed_v()[ir], 2.0); + EXPECT_DOUBLE_EQ(potential->get_eff_v()(0, ir), 3.0); + EXPECT_DOUBLE_EQ(potential->get_veff_smooth()(0, ir), 3.0); } - vofk_eff_tmp[0] = 1.0; - EXPECT_DOUBLE_EQ(pot->vofk_eff(0, 0), 1.0); - EXPECT_DOUBLE_EQ(vofk_eff_tmp_const[0], 1.0); -} -TEST_F(PotentialNewTest, GetEffectiveVofkarrayNullptr) -{ - pot = new elecstate::Potential; - EXPECT_EQ(pot->v_eff.nc, 0); - double* vofk_eff_tmp = pot->get_eff_vofk(0); - const double* vofk_eff_tmp_const = pot->get_eff_vofk(0); - EXPECT_EQ(vofk_eff_tmp, nullptr); - EXPECT_EQ(vofk_eff_tmp_const, nullptr); -} + potential->update_from_charge(&charge, ucell.get()); + EXPECT_EQ(elecstate::MockPotComponent::fixed_calls, 1); + EXPECT_EQ(elecstate::MockPotComponent::dynamic_calls, 2); + EXPECT_DOUBLE_EQ(potential->get_eff_v()(0, 0), 4.0); -TEST_F(PotentialNewTest, GetFixedV) -{ - rhopw->nrxx = 100; - pot = new elecstate::Potential(rhopw, rhopw, ucell, vloc, structure_factors, solvent, etxc, vtxc); - EXPECT_TRUE(pot->fixed_mode); - EXPECT_TRUE(pot->dynamic_mode); - EXPECT_EQ(pot->v_eff_fixed.size(), 100); - double* v_eff_fixed_tmp = pot->get_fixed_v(); - const double* v_eff_fixed_tmp_const = pot->get_fixed_v(); - for (int ic = 0; ic < rhopw->nrxx; ic++) + potential->init_pot(&charge); + EXPECT_EQ(elecstate::MockPotComponent::fixed_calls, 2); + EXPECT_EQ(elecstate::MockPotComponent::dynamic_calls, 3); + EXPECT_DOUBLE_EQ(potential->get_eff_v()(0, 0), 5.0); + + ModuleBase::matrix vnew; + potential->get_vnew(&charge, vnew); + EXPECT_EQ(elecstate::MockPotComponent::fixed_calls, 2); + EXPECT_EQ(elecstate::MockPotComponent::dynamic_calls, 4); + ASSERT_EQ(vnew.nr, 1); + ASSERT_EQ(vnew.nc, smooth_basis->nrxx); + for (int ir = 0; ir < vnew.nc; ++ir) { - v_eff_fixed_tmp[ic] = ic; - EXPECT_DOUBLE_EQ(v_eff_fixed_tmp[ic], pot->v_eff_fixed[ic]); - EXPECT_DOUBLE_EQ(v_eff_fixed_tmp_const[ic], pot->v_eff_fixed[ic]); + EXPECT_DOUBLE_EQ(vnew(0, ir), 1.0); } } -TEST_F(PotentialNewTest, GetVeffSmooth) +TEST_F(PotentialNewTest, DifferentBasisObjectsTriggerInterpolation) { - // construct potential - rhopw->nrxx = 100; - XC_Functional::func_type = 3; - XC_Functional::ked_flag = true; - pot = new elecstate::Potential(rhopw, rhopw, ucell, vloc, structure_factors, solvent, etxc, vtxc); - // - ModuleBase::matrix veff_smooth_tmp = pot->get_veff_smooth(); - const ModuleBase::matrix veff_smooth_const_tmp = pot->get_veff_smooth(); - EXPECT_EQ(veff_smooth_tmp.nr, PARAM.input.nspin); - EXPECT_EQ(veff_smooth_tmp.nc, 100); - EXPECT_EQ(veff_smooth_const_tmp.nr, PARAM.input.nspin); - EXPECT_EQ(veff_smooth_const_tmp.nc, 100); - for (int ir = 0; ir < veff_smooth_tmp.nr; ir++) + XC_Functional::set_xc_type("meta"); + setup_smooth_basis(); + setup_dense_basis(); + create_potential(dense_basis.get(), smooth_basis.get()); + potential->pot_register({"ramp"}); + + for (int ir = 0; ir < potential->get_eff_vofk().nc; ++ir) { - for (int ic = 0; ic < veff_smooth_tmp.nc; ic++) - { - EXPECT_DOUBLE_EQ(veff_smooth_tmp(ir, ic), pot->veff_smooth(ir, ic)); - EXPECT_DOUBLE_EQ(veff_smooth_const_tmp(ir, ic), pot->veff_smooth(ir, ic)); - } + potential->get_eff_vofk()(0, ir) = 2.0 * ir; } -} -TEST_F(PotentialNewTest, GetVofkSmooth) -{ - // construct potential - rhopw->nrxx = 100; - pot = new elecstate::Potential(rhopw, rhopw, ucell, vloc, structure_factors, solvent, etxc, vtxc); - // - ModuleBase::matrix vofk_smooth_tmp = pot->get_veff_smooth(); - const ModuleBase::matrix vofk_smooth_const_tmp = pot->get_veff_smooth(); - EXPECT_EQ(vofk_smooth_tmp.nr, PARAM.input.nspin); - EXPECT_EQ(vofk_smooth_tmp.nc, 100); - EXPECT_EQ(vofk_smooth_const_tmp.nr, PARAM.input.nspin); - EXPECT_EQ(vofk_smooth_const_tmp.nc, 100); - for (int ir = 0; ir < vofk_smooth_tmp.nr; ir++) + Charge charge; + potential->update_from_charge(&charge, ucell.get()); + + const std::vector expected_veff + = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}; + const std::vector expected_vofk + = {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52}; + ASSERT_EQ(potential->get_veff_smooth().nc, expected_veff.size()); + ASSERT_EQ(potential->get_vofk_smooth().nc, expected_vofk.size()); + for (int ir = 0; ir < potential->get_veff_smooth().nc; ++ir) { - for (int ic = 0; ic < vofk_smooth_tmp.nc; ic++) - { - EXPECT_DOUBLE_EQ(vofk_smooth_tmp(ir, ic), pot->vofk_smooth(ir, ic)); - EXPECT_DOUBLE_EQ(vofk_smooth_const_tmp(ir, ic), pot->vofk_smooth(ir, ic)); - } + EXPECT_DOUBLE_EQ(potential->get_veff_smooth()(0, ir), expected_veff[ir]); + EXPECT_DOUBLE_EQ(potential->get_vofk_smooth()(0, ir), expected_vofk[ir]); } } -TEST_F(PotentialNewTest, InterpolateVrsDoubleGrids) +TEST_F(PotentialNewTest, SameBasisObjectCopiesPotentialDirectly) { - PARAM.sys.double_grid = true; - XC_Functional::func_type = 3; - XC_Functional::ked_flag = true; - // Init pw_basis - rhopw->initgrids(4, ModuleBase::Matrix3(1, 0, 0, 0, 1, 0, 0, 0, 1), 4); - rhopw->initparameters(false, 4); - rhopw->setuptransform(); - rhopw->collect_local_pw(); - - rhodpw->initgrids(4, ModuleBase::Matrix3(1, 0, 0, 0, 1, 0, 0, 0, 1), 6); - rhodpw->initparameters(false, 6); - static_cast(rhodpw)->setuptransform(rhopw); - rhodpw->collect_local_pw(); - - pot = new elecstate::Potential(rhodpw, rhopw, ucell, vloc, structure_factors, solvent, etxc, vtxc); - - for (int ir = 0; ir < pot->v_eff.nr; ir++) - { - for (int ic = 0; ic < pot->v_eff.nc; ic++) - { - pot->v_eff(ir, ic) = ir + ic; - pot->vofk_eff(ir, ic) = ir + 2 * ic; - } - } - - pot->interpolate_vrs(); - - std::vector expect_veff = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}; - std::vector expect_vofk = {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52}; - - int index=0; - for (int ir = 0; ir < pot->veff_smooth.nr; ir++) + XC_Functional::set_xc_type("meta"); + setup_smooth_basis(); + create_potential(smooth_basis.get(), smooth_basis.get()); + potential->pot_register({"ramp"}); + + for (int ir = 0; ir < potential->get_eff_vofk().nc; ++ir) { - for (int ic = 0; ic < pot->veff_smooth.nc; ic++) - { - EXPECT_DOUBLE_EQ(pot->veff_smooth(ir,ic), expect_veff[index]); - EXPECT_DOUBLE_EQ(pot->vofk_smooth(ir,ic), expect_vofk[index]); - index++; - } + potential->get_eff_vofk()(0, ir) = 2.0 * ir; } -} + Charge charge; + potential->update_from_charge(&charge, ucell.get()); -TEST_F(PotentialNewTest, InterpolateVrsWarningQuit) -{ - PARAM.sys.double_grid = true; - // Init pw_basis - rhopw->initgrids(4, ModuleBase::Matrix3(1, 0, 0, 0, 1, 0, 0, 0, 1), 4); - rhopw->initparameters(false, 4); - rhopw->setuptransform(); - rhopw->collect_local_pw(); - rhodpw->gamma_only = false; - - rhodpw->initgrids(4, ModuleBase::Matrix3(1, 0, 0, 0, 1, 0, 0, 0, 1), 6); - rhodpw->initparameters(false, 6); - static_cast(rhodpw)->setuptransform(rhopw); - rhodpw->collect_local_pw(); - rhodpw->gamma_only = true; - - pot = new elecstate::Potential(rhodpw, rhopw, ucell, vloc, structure_factors, solvent, etxc, vtxc); - - EXPECT_EXIT(pot->interpolate_vrs(), ::testing::ExitedWithCode(1), ""); + for (int ir = 0; ir < potential->get_veff_smooth().nc; ++ir) + { + EXPECT_DOUBLE_EQ(potential->get_veff_smooth()(0, ir), ir); + EXPECT_DOUBLE_EQ(potential->get_vofk_smooth()(0, ir), 2.0 * ir); + } } -TEST_F(PotentialNewTest, InterpolateVrsSingleGrids) +TEST_F(PotentialNewTest, DifferentBasisObjectsRequireMatchingGammaOnly) { - PARAM.sys.double_grid = false; - XC_Functional::func_type = 3; - XC_Functional::ked_flag = true; - // Init pw_basis - rhopw->initgrids(4, ModuleBase::Matrix3(1, 0, 0, 0, 1, 0, 0, 0, 1), 4); - rhopw->initparameters(false, 4); - rhopw->setuptransform(); - rhopw->collect_local_pw(); - - pot = new elecstate::Potential(rhopw, rhopw, ucell, vloc, structure_factors, solvent, etxc, vtxc); - - for (int ir = 0; ir < pot->v_eff.nr; ir++) - { - for (int ic = 0; ic < pot->v_eff.nc; ic++) - { - pot->v_eff(ir, ic) = ir + ic; - pot->vofk_eff(ir, ic) = ir + 2 * ic; - } - } - - pot->interpolate_vrs(); - - for (int ir = 0; ir < pot->veff_smooth.nr; ir++) - { - for (int ic = 0; ic < pot->veff_smooth.nc; ic++) - { - EXPECT_DOUBLE_EQ(pot->veff_smooth(ir,ic), ir+ic); - EXPECT_DOUBLE_EQ(pot->vofk_smooth(ir,ic), ir+2*ic); - } - } + setup_smooth_basis(); + setup_dense_basis(); + dense_basis->gamma_only = !smooth_basis->gamma_only; + create_potential(dense_basis.get(), smooth_basis.get()); + potential->pot_register({"ramp"}); + Charge charge; + EXPECT_EXIT(potential->update_from_charge(&charge, ucell.get()), ::testing::ExitedWithCode(1), ""); } diff --git a/source/source_estate/test/td_field_test.cpp b/source/source_estate/test/td_field_test.cpp new file mode 100644 index 0000000000..5ce67e53e4 --- /dev/null +++ b/source/source_estate/test/td_field_test.cpp @@ -0,0 +1,129 @@ +#include "source_base/constants.h" +#include "source_estate/module_pot/td_field_manager.h" +#include "source_estate/module_pot/td_field_profiles.h" +#include "source_io/module_parameter/input_parameter.h" + +#include "gtest/gtest.h" +#include +#include +#include + +namespace +{ + +TEST(TDFieldProfileTest, RepresentativeSamples) +{ + elecstate::TDGaussianProfile gaussian(0.5, 0.25, 2.0, 0.0, 2.0, 1.0); + elecstate::TDTrapezoidProfile trapezoid(0.4, 0.4, 1.0, 2.0, 4.0, 3.0); + elecstate::TDTrigonometricProfile trigonometric(0.7, 0.3, 0.2, 0.4, 4.0); + elecstate::TDHeavisideProfile heaviside(3.0, 5.0); + elecstate::TDSupersineProfile supersine(-0.4, 0.3, 0.75, 0, 8, 6.0, 1.0); + + struct ProfileCase + { + const char* name; + double value; + double expected; + }; + const std::vector cases + = {{"Gaussian", gaussian.electric_field(elecstate::TDFieldSample(0, 3, 4, 0.75)), 1.5118014898049421}, + {"Trapezoid", trapezoid.electric_field(elecstate::TDFieldSample(2, 1, 2, 4.0)), -0.93633038223107046}, + {"Trigonometric", trigonometric.electric_field(elecstate::TDFieldSample(1, 1, 2, 1.25)), 0.93167894632639381}, + {"HeavisideBefore", heaviside.electric_field(elecstate::TDFieldSample(2, 1, 2, 2.5)), 5.0}, + {"HeavisideSwitch", heaviside.electric_field(elecstate::TDFieldSample(3, 0, 2, 3.0)), 0.0}, + {"Supersine", supersine.electric_field(elecstate::TDFieldSample(2, 0, 2, 2.0)), -3.6184740935466699}}; + + for (const ProfileCase& profile_case: cases) + { + EXPECT_NEAR(profile_case.value, profile_case.expected, 1.0e-12) << profile_case.name; + } +} + +TEST(TDFieldManagerTest, MixedFieldsSumAndAccumulate) +{ + Input_para input; + input.td_vext = true; + input.td_stype = 2; + input.td_tstart = 0; + input.td_tend = 2; + input.td_dt = ModuleBase::AU_to_FS; + input.td_ttype = {2, 3}; + input.td_vext_dire = {1, 1}; + input.td_trigo_freq1 = {0.0}; + input.td_trigo_freq2 = {0.0}; + input.td_trigo_phase1 = {0.0}; + input.td_trigo_phase2 = {ModuleBase::PI / 2.0}; + input.td_trigo_amp = {2.0}; + input.td_heavi_t0 = {10.0}; + input.td_heavi_amp = {3.0}; + + const double amplitude_conversion = ModuleBase::BOHR_TO_A / ModuleBase::Ry_to_eV; + const double total_field = 5.0 * amplitude_conversion; + std::shared_ptr manager = elecstate::create_td_field_manager(input); + + ASSERT_EQ(manager->fields().size(), 2U); + EXPECT_EQ(manager->fields()[0].direction(), 0); + EXPECT_EQ(manager->fields()[1].direction(), 0); + EXPECT_EQ(manager->fields()[0].subdivisions(), 2); + EXPECT_EQ(manager->fields()[1].subdivisions(), 2); + + manager->advance_vector_gauge(); + ASSERT_EQ(manager->field_values().size(), 2U); + EXPECT_NEAR(manager->field_values()[0], 2.0 * amplitude_conversion, 1.0e-14); + EXPECT_NEAR(manager->field_values()[1], 3.0 * amplitude_conversion, 1.0e-14); + EXPECT_NEAR(manager->electric_field()[0], total_field, 1.0e-14); + EXPECT_NEAR(manager->total_electric_field()[0], total_field, 1.0e-14); + EXPECT_NEAR(manager->vector_potential_laststep()[0], -total_field, 1.0e-14); + EXPECT_NEAR(manager->vector_potential()[0], -0.5 * total_field, 1.0e-14); + + manager->advance_vector_gauge(); + EXPECT_NEAR(manager->vector_potential()[0], -1.5 * total_field, 1.0e-14); + + Input_para oscillatory_input; + oscillatory_input.td_vext = true; + oscillatory_input.td_stype = 1; + oscillatory_input.td_dt = 0.05; + oscillatory_input.td_ttype = {0, 0}; + oscillatory_input.td_vext_dire = {1, 2}; + oscillatory_input.td_gauss_freq = {1.0, -1.0}; + oscillatory_input.td_gauss_phase = {0.0, 0.0}; + oscillatory_input.td_gauss_sigma = {1.0, 1.0}; + oscillatory_input.td_gauss_t0 = {0.0, 0.0}; + oscillatory_input.td_gauss_amp = {1.0, 1.0}; + + std::shared_ptr oscillatory_manager = elecstate::create_td_field_manager(oscillatory_input); + ASSERT_EQ(oscillatory_manager->fields().size(), 2U); + EXPECT_EQ(oscillatory_manager->fields()[0].subdivisions(), 12); + EXPECT_EQ(oscillatory_manager->fields()[1].subdivisions(), 12); +} + +TEST(TDFieldManagerTest, RestartRequiresCompleteState) +{ + Input_para input; + input.td_stype = 1; + std::shared_ptr manager = elecstate::create_td_field_manager(input); + const std::string restart_prefix = "td_field_manager_test_"; + const std::string restart_path = restart_prefix + "Restart_td.txt"; + + { + std::ofstream output(restart_path.c_str()); + output << "7\n1 2 3\n4 5 6\n"; + } + manager->read_restart(restart_prefix); + EXPECT_EQ(manager->current_step(), 6); + EXPECT_DOUBLE_EQ(manager->vector_potential()[0], 1.0); + EXPECT_DOUBLE_EQ(manager->vector_potential()[1], 2.0); + EXPECT_DOUBLE_EQ(manager->vector_potential()[2], 3.0); + EXPECT_DOUBLE_EQ(manager->vector_potential_laststep()[0], -4.0); + EXPECT_DOUBLE_EQ(manager->vector_potential_laststep()[1], -5.0); + EXPECT_DOUBLE_EQ(manager->vector_potential_laststep()[2], -6.0); + + { + std::ofstream output(restart_path.c_str(), std::ofstream::out); + output << "7\n1 2 3\n4 5\n"; + } + EXPECT_EXIT(manager->read_restart(restart_prefix), testing::ExitedWithCode(1), ""); + std::remove(restart_path.c_str()); +} + +} // namespace diff --git a/source/source_io/CMakeLists.txt b/source/source_io/CMakeLists.txt index 4de0b6e88f..ddd5c56f1a 100644 --- a/source/source_io/CMakeLists.txt +++ b/source/source_io/CMakeLists.txt @@ -37,6 +37,8 @@ list(APPEND objects module_ml/write_mlkedf_descriptors.cpp module_current/td_current_io.cpp module_current/td_current_io_comm.cpp + module_efield/td_efield_io.cpp + module_efield/td_vector_pot_io.cpp module_chgpot/write_libxc_r.cpp module_output/output_log.cpp module_json/para_json.cpp diff --git a/source/source_io/module_current/td_current_io_comm.cpp b/source/source_io/module_current/td_current_io_comm.cpp index 9bf62977c8..8e04afb00c 100644 --- a/source/source_io/module_current/td_current_io_comm.cpp +++ b/source/source_io/module_current/td_current_io_comm.cpp @@ -8,8 +8,8 @@ #include "source_base/tool_threading.h" #include "source_base/vector3.h" #include "source_estate/module_pot/H_TDDFT_pw.h" -#include "source_io/module_parameter/parameter.h" #include "source_hamilt/module_hcontainer/hcontainer_funcs.h" +#include "source_io/module_parameter/parameter.h" #include "source_lcao/module_rt/td_folding.h" #include "source_lcao/module_rt/td_info.h" #include "td_current_io.h" @@ -773,20 +773,22 @@ void ModuleIO::write_current(const UnitCell& ucell, } for (int is = 0; is < nspin0; ++is) { + int kpoint_index = 0; for (int ik = 0; ik < kv.get_nks(); ik++) { if (is == kv.isk[ik]) { + ++kpoint_index; if (GlobalV::MY_RANK == 0 && TD_info::out_current_k) { - std::string filename - = PARAM.globalv.global_out_dir + "currents" + std::to_string(is) + "k" + std::to_string(ik) + "comm.txt"; + std::string filename = PARAM.globalv.global_out_dir + "current_s" + std::to_string(is + 1) + "k" + + std::to_string(kpoint_index) + "_comm.txt"; std::ofstream fout; fout.open(filename, std::ios::app); fout << std::setprecision(16); fout << std::scientific; - fout << istep << " " << current_k[ik][0] / omega << " " << current_k[ik][1] / omega << " " << current_k[ik][2] / omega - << std::endl; + fout << istep + 1 << " " << current_k[ik][0] / omega << " " << current_k[ik][1] / omega << " " + << current_k[ik][2] / omega << std::endl; fout.close(); } } @@ -808,7 +810,8 @@ void ModuleIO::write_current(const UnitCell& ucell, fout.open(filename, std::ios::app); fout << std::setprecision(16); fout << std::scientific; - fout << istep << " " << current_total[0] / omega << " " << current_total[1] / omega << " " << current_total[2] / omega << std::endl; + fout << istep + 1 << " " << current_total[0] / omega << " " << current_total[1] / omega << " " << current_total[2] / omega + << std::endl; fout.close(); } diff --git a/source/source_io/module_efield/td_efield_io.cpp b/source/source_io/module_efield/td_efield_io.cpp new file mode 100644 index 0000000000..cf3df4d53b --- /dev/null +++ b/source/source_io/module_efield/td_efield_io.cpp @@ -0,0 +1,69 @@ +#include "td_efield_io.h" + +#include "source_base/constants.h" +#include "source_base/tool_quit.h" +#include "source_estate/module_pot/td_field_manager.h" + +#include +#include + +namespace +{ + +std::string td_field_output_path(const std::string& output_dir, const std::size_t field_index) +{ + std::stringstream path; + path << output_dir << "efield_" << field_index + 1 << ".txt"; + return path.str(); +} + +} // namespace + +namespace ModuleIO +{ + +void prepare_td_field_output(const std::string& output_dir, const std::size_t field_count, const bool restart) +{ + // A restart continues an existing time series. A fresh calculation opens + // every configured file with `out`, which truncates any previous series. + if (restart) + { + return; + } + + for (std::size_t field_index = 0; field_index < field_count; ++field_index) + { + const std::string output_path = td_field_output_path(output_dir, field_index); + std::ofstream output(output_path.c_str(), std::ofstream::out); + if (!output) + { + ModuleBase::WARNING_QUIT("ModuleIO::prepare_td_field_output", "Cannot prepare electric-field file " + output_path + "!"); + } + } +} + +void write_td_field_values(const elecstate::TDFieldManager& manager, const std::string& output_dir) +{ + if (!manager.active()) + { + return; + } + + const std::vector& field_values = manager.field_values(); + for (std::size_t field_index = 0; field_index < field_values.size(); ++field_index) + { + // Keep one file per input occurrence even when directions repeat. + const std::string output_path = td_field_output_path(output_dir, field_index); + std::ofstream output(output_path.c_str(), std::ofstream::app); + if (!output) + { + ModuleBase::WARNING_QUIT("ModuleIO::write_td_field_values", "Cannot append electric-field file " + output_path + "!"); + } + // Convert only at the user-visible output boundary: time to fs and the + // electric field to V/Angstrom. + output << manager.current_step() * manager.dt() * ModuleBase::AU_to_FS << "\t" + << field_values[field_index] * ModuleBase::Ry_to_eV / ModuleBase::BOHR_TO_A << std::endl; + } +} + +} // namespace ModuleIO diff --git a/source/source_io/module_efield/td_efield_io.h b/source/source_io/module_efield/td_efield_io.h new file mode 100644 index 0000000000..ec0e035248 --- /dev/null +++ b/source/source_io/module_efield/td_efield_io.h @@ -0,0 +1,34 @@ +#ifndef TD_EFIELD_IO_H +#define TD_EFIELD_IO_H + +#include +#include + +namespace elecstate +{ +class TDFieldManager; +} + +namespace ModuleIO +{ + +/** + * @brief Prepare electric-field output files for an RT-TDDFT calculation. + * + * @param output_dir Output directory including its trailing path separator. + * @param field_count Number of configured time-dependent electric fields. + * @param restart Whether the calculation continues from an MD/RT-TDDFT restart. + */ +void prepare_td_field_output(const std::string& output_dir, std::size_t field_count, bool restart); + +/** + * @brief Append the current electric-field samples to their output files. + * + * @param manager Time-dependent electric-field state for the current electronic step. + * @param output_dir Output directory including its trailing path separator. + */ +void write_td_field_values(const elecstate::TDFieldManager& manager, const std::string& output_dir); + +} // namespace ModuleIO + +#endif diff --git a/source/source_io/module_efield/td_vector_pot_io.cpp b/source/source_io/module_efield/td_vector_pot_io.cpp new file mode 100644 index 0000000000..ae641003f5 --- /dev/null +++ b/source/source_io/module_efield/td_vector_pot_io.cpp @@ -0,0 +1,117 @@ +#include "td_vector_pot_io.h" + +#include "source_base/tool_quit.h" + +#include +#include +#include + +namespace +{ + +const char* VECTOR_POT_FILENAME = "vector_pot.txt"; + +std::string vector_pot_path(const std::string& directory) +{ + return directory + VECTOR_POT_FILENAME; +} + +void write_vector_pot_header(std::ofstream& output) +{ + output << std::left << std::setw(8) << "#istep" << std::setw(15) << "A_x" << std::setw(15) << "A_y" << std::setw(15) << "A_z" + << std::endl; +} + +bool is_blank_or_comment(const std::string& line) +{ + const std::string::size_type first = line.find_first_not_of(" \t\r\n"); + return first == std::string::npos || line[first] == '#'; +} + +} // namespace + +namespace ModuleIO +{ + +std::vector> read_td_vector_pot(const std::string& input_dir) +{ + const std::string input_path = vector_pot_path(input_dir); + std::ifstream input(input_path.c_str()); + if (!input) + { + ModuleBase::WARNING_QUIT("ModuleIO::read_td_vector_pot", "Cannot open vector-potential file " + input_path + "!"); + } + + std::vector> vector_potentials; + std::string line; + int line_number = 0; + while (std::getline(input, line)) + { + ++line_number; + if (is_blank_or_comment(line)) + { + continue; + } + + std::istringstream row(line); + int step_label = 0; + ModuleBase::Vector3 vector_pot; + if (!(row >> step_label >> vector_pot[0] >> vector_pot[1] >> vector_pot[2])) + { + ModuleBase::WARNING_QUIT("ModuleIO::read_td_vector_pot", + "Invalid vector-potential data on line " + std::to_string(line_number) + " of " + input_path + "!"); + } + row >> std::ws; + if (!row.eof()) + { + ModuleBase::WARNING_QUIT("ModuleIO::read_td_vector_pot", + "Unexpected vector-potential data on line " + std::to_string(line_number) + " of " + input_path + "!"); + } + vector_potentials.push_back(vector_pot); + } + + if (vector_potentials.empty()) + { + ModuleBase::WARNING_QUIT("ModuleIO::read_td_vector_pot", "No vector-potential data found in " + input_path + "!"); + } + return vector_potentials; +} + +void prepare_td_vector_pot_output(const std::string& output_dir, const bool restart) +{ + const std::string output_path = vector_pot_path(output_dir); + if (restart) + { + std::ifstream existing(output_path.c_str(), std::ifstream::binary | std::ifstream::ate); + if (existing && existing.tellg() > 0) + { + return; + } + } + + std::ofstream output(output_path.c_str(), std::ofstream::out); + if (!output) + { + ModuleBase::WARNING_QUIT("ModuleIO::prepare_td_vector_pot_output", "Cannot prepare vector-potential file " + output_path + "!"); + } + write_vector_pot_header(output); +} + +void write_td_vector_pot(const std::string& output_dir, const int electronic_step, const ModuleBase::Vector3& vector_pot) +{ + const std::string output_path = vector_pot_path(output_dir); + std::ofstream output(output_path.c_str(), std::ofstream::app); + if (!output) + { + ModuleBase::WARNING_QUIT("ModuleIO::write_td_vector_pot", "Cannot append vector-potential file " + output_path + "!"); + } + + output << std::left << std::setw(8) << electronic_step + 1; + for (int direction = 0; direction < 3; ++direction) + { + output << std::scientific << std::setprecision(6) << std::setw(15) << vector_pot[direction]; + } + output << std::endl; +} + +} // namespace ModuleIO diff --git a/source/source_io/module_efield/td_vector_pot_io.h b/source/source_io/module_efield/td_vector_pot_io.h new file mode 100644 index 0000000000..826b19286e --- /dev/null +++ b/source/source_io/module_efield/td_vector_pot_io.h @@ -0,0 +1,39 @@ +#ifndef TD_VECTOR_POT_IO_H +#define TD_VECTOR_POT_IO_H + +#include "source_base/vector3.h" + +#include +#include + +namespace ModuleIO +{ + +/** + * @brief Read Cartesian vector potentials for an RT-TDDFT calculation. + * + * @param input_dir Input directory including its trailing path separator. + * @return Vector potentials in atomic units, ordered by file row. + */ +std::vector> read_td_vector_pot(const std::string& input_dir); + +/** + * @brief Prepare the vector-potential output file for an RT-TDDFT calculation. + * + * @param output_dir Output directory including its trailing path separator. + * @param restart Whether the calculation continues from an MD/RT-TDDFT restart. + */ +void prepare_td_vector_pot_output(const std::string& output_dir, bool restart); + +/** + * @brief Append one Cartesian vector-potential sample. + * + * @param output_dir Output directory including its trailing path separator. + * @param electronic_step Zero-based electronic-step index. + * @param vector_pot Cartesian vector potential in atomic units. + */ +void write_td_vector_pot(const std::string& output_dir, int electronic_step, const ModuleBase::Vector3& vector_pot); + +} // namespace ModuleIO + +#endif diff --git a/source/source_io/module_parameter/input_conv.cpp b/source/source_io/module_parameter/input_conv.cpp index 7b44a539da..0b2f23c83e 100644 --- a/source/source_io/module_parameter/input_conv.cpp +++ b/source/source_io/module_parameter/input_conv.cpp @@ -23,7 +23,6 @@ #include "source_lcao/module_dftu/dftu.h" #ifdef __LCAO #include "source_basis/module_ao/ORB_read.h" -#include "source_estate/module_pot/H_TDDFT_pw.h" #include "source_lcao/FORCE_STRESS.h" #include "source_lcao/module_rt/td_info.h" #endif @@ -45,123 +44,6 @@ #include "source_relax/bfgs_basic.h" #include "source_relax/ions_move_cg.h" -#ifdef __LCAO -std::vector Input_Conv::convert_units(std::string params, double c) { - std::vector params_ori; - std::vector params_out; - parse_expression(params, params_ori); - for (auto param: params_ori) - params_out.emplace_back(param * c); - - return params_out; -} - -void Input_Conv::read_td_efield() -{ - elecstate::H_TDDFT_pw::stype = PARAM.inp.td_stype; - const auto& input = PARAM.inp; - if (input.out_hsr[0] == 1 || input.out_hsr[0] == 3 || input.out_hsr_npz_compat) - { - TD_info::out_mat_R = true; - } else { - TD_info::out_mat_R = false; - } - parse_expression(PARAM.inp.td_ttype, elecstate::H_TDDFT_pw::ttype); - - elecstate::H_TDDFT_pw::tstart = PARAM.inp.td_tstart; - elecstate::H_TDDFT_pw::tend = PARAM.inp.td_tend; - if(PARAM.inp.td_dt!=-1.0) - { - elecstate::H_TDDFT_pw::dt = PARAM.inp.td_dt / ModuleBase::AU_to_FS; - } - else - { - elecstate::H_TDDFT_pw::dt = PARAM.mdp.md_dt / PARAM.inp.estep_per_md / ModuleBase::AU_to_FS; - } - elecstate::H_TDDFT_pw::dt_int = elecstate::H_TDDFT_pw::dt; - - // space domain parameters - - // length gauge - elecstate::H_TDDFT_pw::lcut1 = PARAM.inp.td_lcut1; - elecstate::H_TDDFT_pw::lcut2 = PARAM.inp.td_lcut2; - - // time domain parameters - - // Gauss - elecstate::H_TDDFT_pw::gauss_omega = convert_units(PARAM.inp.td_gauss_freq, - 2 * ModuleBase::PI * ModuleBase::AU_to_FS); // time(a.u.)^-1 - elecstate::H_TDDFT_pw::gauss_phase = convert_units(PARAM.inp.td_gauss_phase, 1.0); - elecstate::H_TDDFT_pw::gauss_sigma = convert_units(PARAM.inp.td_gauss_sigma, 1 / ModuleBase::AU_to_FS); - elecstate::H_TDDFT_pw::gauss_t0 = convert_units(PARAM.inp.td_gauss_t0, 1.0); - elecstate::H_TDDFT_pw::gauss_amp = convert_units(PARAM.inp.td_gauss_amp, - ModuleBase::BOHR_TO_A / ModuleBase::Ry_to_eV); // Ry/bohr - // init ncut for velocity gauge integral - for (auto omega: elecstate::H_TDDFT_pw::gauss_omega) { - int ncut - = int(100.0 * omega * elecstate::H_TDDFT_pw::dt / ModuleBase::PI); - if (ncut % 2 == 0) { - ncut += 2; - } else { - ncut += 1; - } - if (elecstate::H_TDDFT_pw::stype == 0) - ncut = 1; - elecstate::H_TDDFT_pw::gauss_ncut.push_back(ncut); - } - // trapezoid - elecstate::H_TDDFT_pw::trape_omega = convert_units(PARAM.inp.td_trape_freq, - 2 * ModuleBase::PI * ModuleBase::AU_to_FS); // time(a.u.)^-1 - elecstate::H_TDDFT_pw::trape_phase = convert_units(PARAM.inp.td_trape_phase, 1.0); - elecstate::H_TDDFT_pw::trape_t1 = convert_units(PARAM.inp.td_trape_t1, 1.0); - elecstate::H_TDDFT_pw::trape_t2 = convert_units(PARAM.inp.td_trape_t2, 1.0); - elecstate::H_TDDFT_pw::trape_t3 = convert_units(PARAM.inp.td_trape_t3, 1.0); - elecstate::H_TDDFT_pw::trape_amp = convert_units(PARAM.inp.td_trape_amp, - ModuleBase::BOHR_TO_A / ModuleBase::Ry_to_eV); // Ry/bohr - // init ncut for velocity gauge integral - for (auto omega: elecstate::H_TDDFT_pw::trape_omega) { - int ncut - = int(100.0 * omega * elecstate::H_TDDFT_pw::dt / ModuleBase::PI); - if (ncut % 2 == 0) { - ncut += 2; - } else { - ncut += 1; - } - if (elecstate::H_TDDFT_pw::stype == 0) - ncut = 1; - elecstate::H_TDDFT_pw::trape_ncut.push_back(ncut); - } - // Trigonometric - elecstate::H_TDDFT_pw::trigo_omega1 = convert_units(PARAM.inp.td_trigo_freq1, - 2 * ModuleBase::PI * ModuleBase::AU_to_FS); // time(a.u.)^-1 - elecstate::H_TDDFT_pw::trigo_omega2 = convert_units(PARAM.inp.td_trigo_freq2, - 2 * ModuleBase::PI * ModuleBase::AU_to_FS); // time(a.u.)^-1 - elecstate::H_TDDFT_pw::trigo_phase1 = convert_units(PARAM.inp.td_trigo_phase1, 1.0); - elecstate::H_TDDFT_pw::trigo_phase2 = convert_units(PARAM.inp.td_trigo_phase2, 1.0); - elecstate::H_TDDFT_pw::trigo_amp = convert_units(PARAM.inp.td_trigo_amp, - ModuleBase::BOHR_TO_A / ModuleBase::Ry_to_eV); // Ry/bohr - // init ncut for velocity gauge integral - for (auto omega: elecstate::H_TDDFT_pw::trigo_omega1) { - int ncut - = int(100.0 * omega * elecstate::H_TDDFT_pw::dt / ModuleBase::PI); - if (ncut % 2 == 0) { - ncut += 2; - } else { - ncut += 1; - } - if (elecstate::H_TDDFT_pw::stype == 0) - ncut = 1; - elecstate::H_TDDFT_pw::trigo_ncut.push_back(ncut); - } - // Heaviside - elecstate::H_TDDFT_pw::heavi_t0 = convert_units(PARAM.inp.td_heavi_t0, 1.0); - elecstate::H_TDDFT_pw::heavi_amp = convert_units(PARAM.inp.td_heavi_amp, - ModuleBase::BOHR_TO_A / ModuleBase::Ry_to_eV); // Ry/bohr - - return; -} -#endif - void Input_Conv::Convert() { ModuleBase::TITLE("Input_Conv", "Convert"); @@ -251,7 +133,7 @@ void Input_Conv::Convert() TD_info::out_current_k = PARAM.inp.out_current_k; TD_info::out_vecpot = PARAM.inp.out_vecpot; TD_info::init_vecpot_file = PARAM.inp.init_vecpot_file; - read_td_efield(); + TD_info::out_mat_R = PARAM.inp.out_hsr[0] == 1 || PARAM.inp.out_hsr[0] == 3 || PARAM.inp.out_hsr_npz_compat; #endif // __LCAO diff --git a/source/source_io/module_parameter/input_conv.h b/source/source_io/module_parameter/input_conv.h index 99db21591c..827d39e2df 100644 --- a/source/source_io/module_parameter/input_conv.h +++ b/source/source_io/module_parameter/input_conv.h @@ -5,19 +5,6 @@ #ifndef INPUT_CONVERT_H #define INPUT_CONVERT_H -#include "source_base/global_function.h" -#include "source_base/global_variable.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - namespace Input_Conv { @@ -33,121 +20,6 @@ void tmp_convert(); */ void Convert(); -/** - * @brief To parse input parameters as expressions into vectors - * - * @tparam T - * @param fn (string): expressions such as "3*1 0 2*0.5 3*0" - * @param vec (vector): stores parsing results, - * for example, "3*1 0 2*0.5 1*1.5" can be parsed as - * [1, 1, 1, 0, 0.5, 0.5, 1.5] - */ -template -void parse_expression(const std::string& fn, std::vector& vec) -{ - ModuleBase::TITLE("Input_Conv", "parse_expression"); - int count = 0; - - // Update the regex pattern to handle scientific notation - std::string pattern("([-+]?[0-9]+\\*[-+]?[0-9.eE+-]+|[-+]?[0-9,.eE+-]+)"); - - std::vector str; - std::stringstream ss(fn); - std::string section; - - // Split the input string into substrings by spaces - while (ss >> section) - { - int index = 0; - if (str.empty()) - { - while (index < section.size() && std::isspace(section[index])) - { - index++; - } - } - section.erase(0, index); - str.push_back(section); - } - - // Compile the regular expression. std::regex (ECMAScript grammar) is - // portable; the previous POSIX implementation did not build on - // Windows/MinGW. The pattern is plain enough to behave identically here. - const std::regex reg(pattern); - std::smatch match; - - // Loop over each section and apply regex to extract numbers - for (size_t i = 0; i < str.size(); ++i) - { - if (str[i] == "") - { - continue; - } - - // Extract the first matched substring (mirrors the old regexec call) - std::string sub_str = ""; - if (std::regex_search(str[i], match, reg)) - { - sub_str = match[0].str(); - } - - // A token that matches nothing is invalid input. Fail fast instead of - // feeding an empty string to the parsers below, which would push an - // indeterminate value into vec. - if (sub_str.empty()) - { - ModuleBase::WARNING_QUIT("Input_Conv::parse_expression", - "invalid token in expression: \"" + str[i] + "\""); - } - - // Check if the substring contains multiplication (e.g., "2*3.14") - if (sub_str.find('*') != std::string::npos) - { - size_t pos = sub_str.find("*"); - int num = stoi(sub_str.substr(0, pos)); - assert(num >= 0); - T occ = static_cast(stof(sub_str.substr(pos + 1, sub_str.size()))); - - // Add the value to the vector `num` times - for (size_t k = 0; k != num; k++) - { - vec.emplace_back(occ); - } - } - else - { - // Handle scientific notation and convert to T. Initialize occ and - // check the extraction so a malformed token fails fast rather than - // pushing an indeterminate value. - std::stringstream convert; - convert << sub_str; - T occ{}; - if (!(convert >> occ)) - { - ModuleBase::WARNING_QUIT("Input_Conv::parse_expression", - "failed to parse number: \"" + sub_str + "\""); - } - vec.emplace_back(occ); - } - } -} - -#ifdef __LCAO -/** - * @brief convert units of different parameters - * - * @param params input parameter - * @param c coefficients of unit conversion - * @return parame*c : parameter after unit vonversion - */ -std::vector convert_units(std::string params, double c); - -/** - * @brief read paramers of electric field for tddft and convert units - */ -void read_td_efield(); -#endif - } // namespace Input_Conv #endif // Input_Convert diff --git a/source/source_io/module_parameter/input_parameter.h b/source/source_io/module_parameter/input_parameter.h index 45f8d65b49..f984a4609d 100644 --- a/source/source_io/module_parameter/input_parameter.h +++ b/source/source_io/module_parameter/input_parameter.h @@ -25,17 +25,17 @@ struct Input_para 0, only basic time reversal would be considered; 1, point group symmetry would be considered*/ std::string symmetry = "default"; - double symmetry_prec = 1.0e-6; ///< LiuXh add 2021-08-12, accuracy for symmetry - bool symmetry_autoclose = true; ///< whether to close symmetry automatically - ///< when error occurs in symmetry analysis - bool cal_force = false; ///< calculate the force - bool cal_stress = false; ///< calculate the stress - int kpar = 1; ///< ecch pool is for one k point - int bndpar = 1; ///< parallel for stochastic/deterministic bands - std::string latname = "user_defined_lattice"; ///< lattice name - std::string assume_isolated = "none"; ///< isolated-system correction: none or makov-payne - double ecutwfc = 0; ///< energy cutoff for wavefunctions - double ecutrho = 0; ///< energy cutoff for charge/potential + double symmetry_prec = 1.0e-6; ///< LiuXh add 2021-08-12, accuracy for symmetry + bool symmetry_autoclose = true; ///< whether to close symmetry automatically + ///< when error occurs in symmetry analysis + bool cal_force = false; ///< calculate the force + bool cal_stress = false; ///< calculate the stress + int kpar = 1; ///< ecch pool is for one k point + int bndpar = 1; ///< parallel for stochastic/deterministic bands + std::string latname = "user_defined_lattice"; ///< lattice name + std::string assume_isolated = "none"; ///< isolated-system correction: none or makov-payne + double ecutwfc = 0; ///< energy cutoff for wavefunctions + double ecutrho = 0; ///< energy cutoff for charge/potential int nx = 0, ny = 0, nz = 0; ///< three dimension of FFT wavefunc int ndx = 0, ndy = 0, ndz = 0; ///< three dimension of FFT smooth charge density @@ -93,7 +93,7 @@ struct Input_para int pw_diag_ndim = 4; ///< dimension of workspace for Davidson diagonalization int diago_cg_prec = 1; ///< mohan add 2012-03-31 int diag_subspace = 0; // 0: Lapack, 1: elpa, 2: scalapack - bool use_k_continuity = false; ///< whether to use k-point continuity for initializing wave functions + bool use_k_continuity = false; ///< whether to use k-point continuity for initializing wave functions std::string smearing_method = "gauss"; ///< "gauss", ///< "mp","methfessel-paxton" @@ -120,7 +120,7 @@ struct Input_para double scf_thr = -1.0; ///< \sum |rhog_out - rhog_in |^2 double scf_ene_thr = -1.0; ///< energy threshold for scf convergence, in eV int scf_thr_type = -1; ///< type of the criterion of scf_thr, 1: reci drho, 2: real drho -// bool final_scf = false; ///< whether to do final scf + // bool final_scf = false; ///< whether to do final scf bool scf_os_stop = false; ///< whether to stop scf when oscillation is detected double scf_os_thr = -0.01; ///< drho threshold for oscillation int scf_os_ndim = 0; ///< number of old iterations used for oscillation detection @@ -167,7 +167,7 @@ struct Input_para double relax_cg_thr = 0.5; ///< threshold when cg to bfgs, pengfei add 2011-08-15 double force_thr = -1; ///< threshold of force in unit (Ry/Bohr) double force_thr_ev = -1; ///< threshold of force in unit (eV/Angstrom) - double force_zero_out = 0; ///< invalid force threshold, mohan add 2011-04-17 + double force_zero_out = 0; ///< invalid force threshold, mohan add 2011-04-17 double stress_thr = 0.5; ///< Pengfei Li 2017-11-01 /// cal_syns = {0, 8}; ///< calculate asynchronous S matrix to output {enable, precision} - double dmax = 0.01; ///< maximum displacement of all atoms in one step (bohr) + double ref_cell_factor = 1; ///< construct a reference cell bigger than the + ///< initial cell liuyu 2023-03-21 + std::vector cal_syns = {0, 8}; ///< calculate asynchronous S matrix to output {enable, precision} + double dmax = 0.01; ///< maximum displacement of all atoms in one step (bohr) // ============== #Parameters (6.OFDFT) =========================== // OFDFT sunliang added on 2022-05-05 - std::string of_kinetic = "wt"; ///< Kinetic energy functional, such as TF, VW, WT, TF+ - std::string of_method = "tn"; ///< optimization method, include cg1, cg2, tn (default), bfgs - std::string of_conv = "energy"; ///< select the convergence criterion, - ///< potential, energy (default), or both - double of_tole = 1e-06; ///< tolerance of the energy change (in Ry) for - ///< determining the convergence, default=2e-6 Ry - double of_tolp = 1e-05; ///< tolerance of potential for determining the - ///< convergence, default=1e-5 in a.u. - double of_tf_weight = 1.0; ///< weight of TF KEDF - double of_vw_weight = 1.0; ///< weight of vW KEDF - double of_wt_alpha = 5. / 6.; ///< parameter alpha of WT KEDF - double of_wt_beta = 5. / 6.; ///< parameter beta of WT KEDF - double of_extwt_kappa = 1.0 - / (2.0 * std::pow(4. / 3., 1. / 3.) - 1.0); - ///< parameter kappa of EXT-WT KEDF + std::string of_kinetic = "wt"; ///< Kinetic energy functional, such as TF, VW, WT, TF+ + std::string of_method = "tn"; ///< optimization method, include cg1, cg2, tn (default), bfgs + std::string of_conv = "energy"; ///< select the convergence criterion, + ///< potential, energy (default), or both + double of_tole = 1e-06; ///< tolerance of the energy change (in Ry) for + ///< determining the convergence, default=2e-6 Ry + double of_tolp = 1e-05; ///< tolerance of potential for determining the + ///< convergence, default=1e-5 in a.u. + double of_tf_weight = 1.0; ///< weight of TF KEDF + double of_vw_weight = 1.0; ///< weight of vW KEDF + double of_wt_alpha = 5. / 6.; ///< parameter alpha of WT KEDF + double of_wt_beta = 5. / 6.; ///< parameter beta of WT KEDF + double of_extwt_kappa = 1.0 / (2.0 * std::pow(4. / 3., 1. / 3.) - 1.0); + ///< parameter kappa of EXT-WT KEDF double of_wt_rho0 = 0.0; ///< set the average density of system, in Bohr^-3 bool of_hold_rho0 = false; ///< If set to 1, the rho0 will be fixed even if the volume of ///< system has changed, it will be set to 1 automatically if @@ -277,20 +276,20 @@ struct Input_para //========================================================== // DeepKS -- added by caoyu and mohan //========================================================== - int deepks_out_labels = 0; ///< (need libnpy) prints energy and force labels and - ///< descriptors for training, wenfei 2022-1-12 - int deepks_out_freq_elec = 0; ///< (need libnpy) frequency of electronic iteration to output - ///< descriptors and labels, default is 0, which means no output until convergence - std::string deepks_out_base = "none"; ///< (need libnpy) base functional for output files - bool deepks_scf = false; ///< (need libnpy and libtorch) if set to true, a trained model - ///< would be needed to calculate V_delta and F_delta - int deepks_bandgap = 0; ///< for bandgap label. QO added 2021-12-15 + int deepks_out_labels = 0; ///< (need libnpy) prints energy and force labels and + ///< descriptors for training, wenfei 2022-1-12 + int deepks_out_freq_elec = 0; ///< (need libnpy) frequency of electronic iteration to output + ///< descriptors and labels, default is 0, which means no output until convergence + std::string deepks_out_base = "none"; ///< (need libnpy) base functional for output files + bool deepks_scf = false; ///< (need libnpy and libtorch) if set to true, a trained model + ///< would be needed to calculate V_delta and F_delta + int deepks_bandgap = 0; ///< for bandgap label. QO added 2021-12-15 std::vector deepks_band_range = {-1, 0}; ///< the range of bands to calculate bandgap - int deepks_v_delta = 0; ///< for v_delta label. xuan added - bool deepks_equiv = false; ///< whether to use equivariant version of DeePKS - bool deepks_out_unittest = false; ///< if set to true, prints intermediate quantities that shall - ///< be used for making unit test - std::string deepks_model = "None"; ///< needed when deepks_scf=1 + int deepks_v_delta = 0; ///< for v_delta label. xuan added + bool deepks_equiv = false; ///< whether to use equivariant version of DeePKS + bool deepks_out_unittest = false; ///< if set to true, prints intermediate quantities that shall + ///< be used for making unit test + std::string deepks_model = "None"; ///< needed when deepks_scf=1 int bessel_descriptor_lmax = 2; ///< lmax used in descriptor std::string bessel_descriptor_ecut = "default"; ///< energy cutoff for spherical bessel functions(Ry) @@ -300,24 +299,24 @@ struct Input_para double bessel_descriptor_sigma = 0.1; ///< spherical bessel smearing_sigma // ============== #Parameters (9.rt-tddft) =========================== - double td_dt = -1.0; ///< time step for propagation - int estep_per_md = 1; ///< number of electronic steps per MD step + double td_dt = -1.0; ///< time step for propagation + int estep_per_md = 1; ///< number of electronic steps per MD step double td_force_dt = 0.02; ///<"fs" bool td_vext = false; ///< add extern potential or not // std::string td_vext_dire = "1"; ///< vext direction std::vector td_vext_dire = {1}; ///< vector of vext direction - bool init_vecpot_file = false; ///< initialize the vector potential, though file or integral - double td_print_eij = -1.0; ///< threshold to output Eij elements - int td_edm = 0; ///< 0: new edm method 1: old edm method - int propagator = 0; ///< method of propagator - int td_stype = 0; ///< type of space domain 0 : length gauge 1: velocity gauge - std::string td_ttype = "0"; ///< type of time domain + bool init_vecpot_file = false; ///< initialize the vector potential, though file or integral + double td_print_eij = -1.0; ///< threshold to output Eij elements + int td_edm = 0; ///< 0: new edm method 1: old edm method + int propagator = 0; ///< method of propagator + int td_stype = 0; ///< type of space domain 0 : length gauge 1: velocity gauge + std::vector td_ttype = {0}; ///< type of time domain ///< 0 Gauss type function. ///< 1 trapezoid type function. ///< 2 Trigonometric functions, sin^2. ///< 3 heaviside function. - ///< 4 HHG function. + ///< 4 supersine function. int td_tstart = 1; int td_tend = 1000; @@ -328,31 +327,39 @@ struct Input_para ///< time domain parameters ///< Gauss - std::string td_gauss_freq = "22.13"; ///< time(fs)^-1 - std::string td_gauss_phase = "0.0"; - std::string td_gauss_sigma = "30.0"; ///< time(fs) - std::string td_gauss_t0 = "100.0"; - std::string td_gauss_amp = "0.25"; ///< V/A + std::vector td_gauss_freq = {22.13}; ///< time(fs)^-1 + std::vector td_gauss_phase = {0.0}; + std::vector td_gauss_sigma = {30.0}; ///< time(fs) + std::vector td_gauss_t0 = {100.0}; + std::vector td_gauss_amp = {0.25}; ///< V/A ///< trapezoid - std::string td_trape_freq = "1.60"; ///< time(fs)^-1 + std::vector td_trape_freq = {1.60}; ///< time(fs)^-1 // Trapezoidal - std::string td_trape_phase = "0.0"; - std::string td_trape_t1 = "1875.0"; - std::string td_trape_t2 = "5625.0"; - std::string td_trape_t3 = "7500.0"; - std::string td_trape_amp = "2.74"; // V/A + std::vector td_trape_phase = {0.0}; + std::vector td_trape_t1 = {1875.0}; + std::vector td_trape_t2 = {5625.0}; + std::vector td_trape_t3 = {7500.0}; + std::vector td_trape_amp = {2.74}; // V/A // Trigonometric - std::string td_trigo_freq1 = "1.164656"; // time(fs)^-1 - std::string td_trigo_freq2 = "0.029116"; // time(fs)^-1 - std::string td_trigo_phase1 = "0.0"; - std::string td_trigo_phase2 = "0.0"; - std::string td_trigo_amp = "2.74"; // V/A + std::vector td_trigo_freq1 = {1.164656}; // time(fs)^-1 + std::vector td_trigo_freq2 = {0.029116}; // time(fs)^-1 + std::vector td_trigo_phase1 = {0.0}; + std::vector td_trigo_phase2 = {0.0}; + std::vector td_trigo_amp = {2.74}; // V/A // Heaviside - std::string td_heavi_t0 = "100.0"; - std::string td_heavi_amp = "1.0"; // V/A + std::vector td_heavi_t0 = {100.0}; + std::vector td_heavi_amp = {1.0}; // V/A + + // Supersine + std::vector td_supsine_amp = {0.27}; ///< V/A + std::vector td_supsine_freq = {0.18737028625}; ///< time(fs)^-1 + std::vector td_supsine_phase = {0.0}; ///< rad + std::vector td_supsine_sigma = {0.75}; + std::vector td_supsine_tstart = {1}; + std::vector td_supsine_tend = {1000}; bool ocp = false; // std::string ocp_set = ""; @@ -362,108 +369,100 @@ struct Input_para int lr_nstates = 1; ///< the number of 2-particle states to be solved std::vector lr_init_xc_kernel = {}; ///< The method to initalize the xc kernel int nocc = -1; ///< the number of occupied orbitals to form the 2-particle basis - int nvirt = 1; ///< the number of virtual orbitals to form the 2-particle basis (nocc + nvirt <= nbands) - std::string xc_kernel = "LDA"; ///< exchange correlation (XC) kernel for LR-TDDFT - std::string lr_solver = "dav"; ///< the eigensolver for LR-TDDFT - double lr_thr = 1e-2; ///< convergence threshold of the LR-TDDFT eigensolver - bool out_wfc_lr = false; ///< whether to output the eigenvectors (excitation amplitudes) in the particle-hole basis + int nvirt = 1; ///< the number of virtual orbitals to form the 2-particle basis (nocc + nvirt <= nbands) + std::string xc_kernel = "LDA"; ///< exchange correlation (XC) kernel for LR-TDDFT + std::string lr_solver = "dav"; ///< the eigensolver for LR-TDDFT + double lr_thr = 1e-2; ///< convergence threshold of the LR-TDDFT eigensolver + bool out_wfc_lr = false; ///< whether to output the eigenvectors (excitation amplitudes) in the particle-hole basis bool lr_unrestricted = false; ///< whether to use the unrestricted construction for LR-TDDFT std::vector abs_wavelen_range = {}; ///< the range of wavelength(nm) to output the absorption spectrum double abs_broadening = 0.01; ///< the broadening (eta) for LR-TDDFT absorption spectrum - std::string abs_gauge - = "length"; ///< whether to use length or velocity gauge to calculate the absorption spectrum in LR-TDDFT + std::string abs_gauge = "length"; ///< whether to use length or velocity gauge to calculate the absorption spectrum in LR-TDDFT std::string ri_hartree_benchmark = "none"; ///< whether to use the RI approximation for the Hartree potential in ///< LR-TDDFT for benchmark (with FHI-aims/ABACUS read-in style) - std::vector aims_nbasis - = {}; ///< the number of basis functions for each atom type used in FHI-aims (for benchmark) + std::vector aims_nbasis = {}; ///< the number of basis functions for each atom type used in FHI-aims (for benchmark) // ============== #Parameters (11.Output) =========================== - int out_stru = 1; ///< output stru file each ion step - ///< 0: no output, 1: STRU format, 2: CIF format - int out_freq_elec = 0; ///< the frequency of electronic iter to output charge and wavefunction - int out_freq_ion = 0; ///< the frequency ( >= 0 ) of ionic step to output charge density; - ///< 0: output only when ion steps are finished - int out_freq_td = 0; ///< output interval in RT-TDDFT - std::vector out_chg = {0, 3}; ///< output charge density. 0: no; 1: yes - std::vector out_xc_r = {-1, 3}; ///< output xc(r). -1: no; >=0: output the order of xc(r) - std::vector out_pot = {0, 8}; ///< output potential - int out_wfc_pw = 0; ///< 0: no; 1: txt; 2: dat - std::vector out_band = {0, 8}; ///< band calculation pengfei 2014-10-13 - int out_dos = 0; ///< dos calculation. mohan add 20090909 - std::vector out_ldos = {0, 3}; ///< ldos calculation - bool out_mul = false; ///< qifeng add 2019-9-10 - bool out_proj_band = false; ///< projected band structure calculation jiyy add 2022-05-11 - std::string out_level = "ie"; ///< control the output information. - std::vector out_dmr = {0, 8}; ///< output density matrix in real space DM(R) - std::vector out_dmk = {0, 8}; ///< output density matrix in reciprocal space DM(k) - bool out_bandgap = false; ///< QO added for bandgap printing - std::vector out_hsk = {0, 8}; ///< output H(k) and S(k): format and text precision - std::vector out_hsr = {0, 8}; ///< output H(R) and S(R): format and text precision - bool out_hsr_npz_compat = false; ///< additional NPZ output for the legacy text-plus-NPZ combination - std::vector out_mat_hs = {0, 8}; ///< legacy alias for text H(k) and S(k) output - std::vector out_mat_tk = {0, 8}; ///< output T(k) matrix in local basis. - std::vector out_mat_l = {0, 8}; ///< output L matrix in local basis. - std::vector out_mat_hs2 = {0, 8}; ///< legacy alias for text H(R) and S(R) output - std::vector out_mat_h_t = {0, 8}; ///< output kinetic energy T(R) matrix - std::vector out_mat_h_vnl = {0, 8}; ///< output nonlocal pseudopotential Vnl(R) matrix - std::vector out_mat_h_vl = {0, 8}; ///< output local pseudopotential Vl(R) matrix - std::vector out_mat_h_vh = {0, 8}; ///< output Hartree Vh(R) matrix - std::vector out_mat_h_vxc = {0, 8}; ///< output XC Vxc(R) matrix - std::vector out_mat_h_exx = {0, 8}; ///< output exact-exchange Vexx(R) matrix - std::vector out_mat_dh = {0, 8}; ///< output dH/dR matrices with precision - std::vector out_mat_dh_t = { 0, 8 }; ///< output kinetic dH/dR (dT/dR) matrices - std::vector out_mat_dh_vl = { 0, 8 }; ///< output local pseudopotential dH/dR (dV^L/dR) matrices - std::vector out_mat_dh_vnl = { 0, 8 }; ///< output nonlocal pseudopotential dH/dR (dV^NL/dR) matrices - std::vector out_mat_dh_vh = { 0, 8 }; ///< output Hartree dH/dR (dV^H/dR) matrices - std::vector out_mat_dh_vxc = { 0, 8 }; ///< output XC dH/dR (dV^XC/dR) matrices - std::vector out_mat_dh_exx = { 0, 8 }; ///< output exact-exchange dH/dR (dV^EXX/dR) matrices - std::vector out_mat_ds = {0, 8}; ///< output dS/dR matrices with precision - bool out_mat_xc = false; ///< output exchange-correlation matrix in - ///< KS-orbital representation. - std::vector out_mat_xc2 = {0, 8}; ///< output Vxc(R) matrix with precision - bool out_eband_terms = false; ///< output the band energy terms separately - bool out_hr_npz = false; ///< output H(R) matrix in npz format - bool out_hsr_npz = false; ///< output H(R) and S(R) matrices in npz format - bool out_dm_npz = false; ///< output DM(R) matrix in npz format + int out_stru = 1; ///< output stru file each ion step + ///< 0: no output, 1: STRU format, 2: CIF format + int out_freq_elec = 0; ///< the frequency of electronic iter to output charge and wavefunction + int out_freq_ion = 0; ///< the frequency ( >= 0 ) of ionic step to output charge density; + ///< 0: output only when ion steps are finished + int out_freq_td = 0; ///< output interval in RT-TDDFT + std::vector out_chg = {0, 3}; ///< output charge density. 0: no; 1: yes + std::vector out_xc_r = {-1, 3}; ///< output xc(r). -1: no; >=0: output the order of xc(r) + std::vector out_pot = {0, 8}; ///< output potential + int out_wfc_pw = 0; ///< 0: no; 1: txt; 2: dat + std::vector out_band = {0, 8}; ///< band calculation pengfei 2014-10-13 + int out_dos = 0; ///< dos calculation. mohan add 20090909 + std::vector out_ldos = {0, 3}; ///< ldos calculation + bool out_mul = false; ///< qifeng add 2019-9-10 + bool out_proj_band = false; ///< projected band structure calculation jiyy add 2022-05-11 + std::string out_level = "ie"; ///< control the output information. + std::vector out_dmr = {0, 8}; ///< output density matrix in real space DM(R) + std::vector out_dmk = {0, 8}; ///< output density matrix in reciprocal space DM(k) + bool out_bandgap = false; ///< QO added for bandgap printing + std::vector out_hsk = {0, 8}; ///< output H(k) and S(k): format and text precision + std::vector out_hsr = {0, 8}; ///< output H(R) and S(R): format and text precision + bool out_hsr_npz_compat = false; ///< additional NPZ output for the legacy text-plus-NPZ combination + std::vector out_mat_hs = {0, 8}; ///< legacy alias for text H(k) and S(k) output + std::vector out_mat_tk = {0, 8}; ///< output T(k) matrix in local basis. + std::vector out_mat_l = {0, 8}; ///< output L matrix in local basis. + std::vector out_mat_hs2 = {0, 8}; ///< legacy alias for text H(R) and S(R) output + std::vector out_mat_h_t = {0, 8}; ///< output kinetic energy T(R) matrix + std::vector out_mat_h_vnl = {0, 8}; ///< output nonlocal pseudopotential Vnl(R) matrix + std::vector out_mat_h_vl = {0, 8}; ///< output local pseudopotential Vl(R) matrix + std::vector out_mat_h_vh = {0, 8}; ///< output Hartree Vh(R) matrix + std::vector out_mat_h_vxc = {0, 8}; ///< output XC Vxc(R) matrix + std::vector out_mat_h_exx = {0, 8}; ///< output exact-exchange Vexx(R) matrix + std::vector out_mat_dh = {0, 8}; ///< output dH/dR matrices with precision + std::vector out_mat_dh_t = {0, 8}; ///< output kinetic dH/dR (dT/dR) matrices + std::vector out_mat_dh_vl = {0, 8}; ///< output local pseudopotential dH/dR (dV^L/dR) matrices + std::vector out_mat_dh_vnl = {0, 8}; ///< output nonlocal pseudopotential dH/dR (dV^NL/dR) matrices + std::vector out_mat_dh_vh = {0, 8}; ///< output Hartree dH/dR (dV^H/dR) matrices + std::vector out_mat_dh_vxc = {0, 8}; ///< output XC dH/dR (dV^XC/dR) matrices + std::vector out_mat_dh_exx = {0, 8}; ///< output exact-exchange dH/dR (dV^EXX/dR) matrices + std::vector out_mat_ds = {0, 8}; ///< output dS/dR matrices with precision + bool out_mat_xc = false; ///< output exchange-correlation matrix in + ///< KS-orbital representation. + std::vector out_mat_xc2 = {0, 8}; ///< output Vxc(R) matrix with precision + bool out_eband_terms = false; ///< output the band energy terms separately + bool out_hr_npz = false; ///< output H(R) matrix in npz format + bool out_hsr_npz = false; ///< output H(R) and S(R) matrices in npz format + bool out_dm_npz = false; ///< output DM(R) matrix in npz format int out_interval = 1; - bool out_app_flag = true; ///< whether output r(R), H(R), S(R), T(R), and dH(R) matrices - ///< in an append manner during MD liuyu 2023-03-20 - int out_ndigits = 8; ///< Assuming 8 digits precision is needed for matrices output - std::vector out_mat_t = {0, 8}; ///< output T(R) matrix with precision - bool out_element_info = false; ///< output information of all elements - std::vector out_mat_r = {0, 8}; ///< output r(R) matrix with precision - int out_wfc_lcao = 0; ///< output the wave functions in local basis. - bool out_dipole = false; ///< output the dipole or not - bool out_efield = false; ///< output the efield or not - int out_current = 0; ///< output the current or not - bool out_current_k = false; ///< output tddft current for all k points - bool out_vecpot = false; ///< output the vector potential or not + bool out_app_flag = true; ///< whether output r(R), H(R), S(R), T(R), and dH(R) matrices + ///< in an append manner during MD liuyu 2023-03-20 + int out_ndigits = 8; ///< Assuming 8 digits precision is needed for matrices output + std::vector out_mat_t = {0, 8}; ///< output T(R) matrix with precision + bool out_element_info = false; ///< output information of all elements + std::vector out_mat_r = {0, 8}; ///< output r(R) matrix with precision + int out_wfc_lcao = 0; ///< output the wave functions in local basis. + bool out_dipole = false; ///< output the dipole or not + bool out_efield = false; ///< output the efield or not + int out_current = 0; ///< output the current or not + bool out_current_k = false; ///< output tddft current for all k points + bool out_vecpot = false; ///< output the vector potential or not std::vector cal_symm_repr = {0, 3}; ///< output the symmetry representation matrix - bool restart_save = false; ///< restart //Peize Lin add 2020-04-04 - bool rpa = false; ///< rpa calculation - std::vector out_pchg = {}; ///< specify the bands to be calculated for partial charge - std::vector out_wfc_norm = {}; ///< specify the bands to be calculated for norm of wfc - std::vector out_wfc_re_im = {}; ///< specify the bands to be calculated for real and imaginary parts of wfc - bool if_separate_k = false; ///< whether to write partial charge for all k-points to individual files or merge them - std::vector out_elf = {0, 3}; ///< output the electron localization function (ELF). 0: no; 1: yes - int out_spillage = 0; ///< output the spillage of the wave function - std::string spillage_outdir = "./"; ///< output directory for spillage + bool restart_save = false; ///< restart //Peize Lin add 2020-04-04 + bool rpa = false; ///< rpa calculation + std::vector out_pchg = {}; ///< specify the bands to be calculated for partial charge + std::vector out_wfc_norm = {}; ///< specify the bands to be calculated for norm of wfc + std::vector out_wfc_re_im = {}; ///< specify the bands to be calculated for real and imaginary parts of wfc + bool if_separate_k = false; ///< whether to write partial charge for all k-points to individual files or merge them + std::vector out_elf = {0, 3}; ///< output the electron localization function (ELF). 0: no; 1: yes + int out_spillage = 0; ///< output the spillage of the wave function + std::string spillage_outdir = "./"; ///< output directory for spillage // ============== #Parameters (12.Postprocess) =========================== double dos_emin_ev = -15.0; double dos_emax_ev = 15.0; double dos_edelta_ev = 0.01; double dos_scale = 0.01; - double dos_sigma = 0.07; ///< pengfei 2014-10-13 - int dos_nche = 100; ///< orders of Chebyshev expansions for dos + double dos_sigma = 0.07; ///< pengfei 2014-10-13 + int dos_nche = 100; ///< orders of Chebyshev expansions for dos std::vector stm_bias = {1.0, 0.1, 1}; ///< bias voltage for STM (start value, step, number) std::vector ldos_line - = {0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 100}; ///< start and end point of the line (direct coordinates) and number of points + = {0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 100}; ///< start and end point of the line (direct coordinates) and number of points bool cal_cond = false; ///< calculate electronic conductivities double cond_che_thr = 1e-8; ///< control the error of Chebyshev expansions @@ -551,8 +550,8 @@ struct Input_para std::string vdw_radius_unit = "Bohr"; ///< unit of radius cutoff for periodic structure double vdw_cn_thr = 40.0; ///< radius cutoff for cn std::string vdw_cn_thr_unit = "Bohr"; ///< unit of cn_thr, Bohr or Angstrom - std::string vdw_d4_xc = "default"; ///< functional name passed to DFT-D4 - std::string vdw_d4_model = "d4"; ///< DFT-D4 dispersion model (d4 or d4s) + std::string vdw_d4_xc = "default"; ///< functional name passed to DFT-D4 + std::string vdw_d4_model = "d4"; ///< DFT-D4 dispersion model (d4 or d4s) ModuleBase::Vector3 vdw_cutoff_period = {3, 3, 3}; ///< periods of periodic structure // ============== #Parameters (15.exx) ==================== @@ -560,44 +559,42 @@ struct Input_para // exx // Peize Lin add 2018-06-20 // ========================================================== - std::vector exx_fock_alpha = {"default"}; ///< fraction of Fock exchange 1/r in hybrid functionals - std::vector exx_fock_lambda = {"default"}; ///< used to compensate for divergence points at G=0 in the - ///< evaluation of Fock exchange using lcao_in_pw method - std::vector exx_erfc_alpha = {"default"}; ///< fraction of exchange erfc(wr)/r in hybrid functionals - std::vector exx_erfc_omega = {"default"}; ///< range-separation parameter in HSE functional - bool exx_separate_loop = true; ///< if 1, a two-step method is employed, else it will start - ///< with a GGA-Loop, and then Hybrid-Loop - std::string exx_singularity_correction = "default"; ///< set the scheme of Coulomb singularity correction - int exx_hybrid_step = 100; ///< the maximal electronic iteration number in - ///< the evaluation of Fock exchange - double exx_mixing_beta = 1.0; ///< mixing_beta for outer-loop when exx_separate_loop=1 - std::string exx_real_number = "default"; ///< exx calculated in real or complex - double exx_pca_threshold = 0.0001; ///< threshold to screen on-site ABFs in exx - double exx_c_threshold = 0.0001; ///< threshold to screen C matrix in exx - double exx_v_threshold = 0.1; ///< threshold to screen C matrix in exx - double exx_dm_threshold = 0.0001; ///< threshold to screen density matrix in exx - double exx_c_grad_threshold = 0.0001; ///< threshold to screen nabla C matrix in exx - double exx_v_grad_threshold = 0.1; ///< threshold to screen nabla V matrix in exx - double exx_c_grad_r_threshold = 0.0001; ///< threshold to screen nabla C * R matrix in exx - double exx_v_grad_r_threshold = 0.1; ///< threshold to screen nabla V * R matrix in exx - std::string exx_ccp_rmesh_times = "default"; ///< how many times larger the radial mesh required for - ///< calculating Columb potential is to that of atomic orbitals - int exx_opt_orb_lmax = 0; ///< the maximum l of the spherical Bessel functions for opt ABFs - double exx_opt_orb_ecut = 0.0; ///< the cut-off of plane wave expansion for opt ABFs - double exx_opt_orb_tolerence = 0.0; ///< the threshold when solving for the zeros of spherical Bessel - ///< functions for opt ABFs - bool exx_symmetry_realspace - = true; ///< whether to reduce the real-space sector in when using symmetry=1 in EXX calculation - double rpa_ccp_rmesh_times = 10.0; ///< how many times larger the radial mesh required for - ///< calculating Columb potential is to that of atomic orbitals - double exx_cs_inv_thr = -1; ///< threshold to inverse Vq in abfs for generating Cs - double shrink_abfs_pca_thr = -1; ///< threshold to shrink auxiliary basis for GW/RPA - double shrink_LU_inv_thr - = 1e-6; ///< threshold to get inverse of overlap matrix by LU decomposition in auxiliary basis representation - bool out_ri_cv = false; ///< Whether to output the coefficient tensor C and ABFs-representation Coulomb matrix V - bool out_unshrinked_v = false; ///< whether to output the large Vq matrix in unshrinked auxiliary basis - bool exx_coul_moment = false; ///< whether to use moment method for Coulomb calculation - bool exx_rotate_abfs = false; ///< whether to rotate auxiliary basis for Coulomb calculation + std::vector exx_fock_alpha = {"default"}; ///< fraction of Fock exchange 1/r in hybrid functionals + std::vector exx_fock_lambda = {"default"}; ///< used to compensate for divergence points at G=0 in the + ///< evaluation of Fock exchange using lcao_in_pw method + std::vector exx_erfc_alpha = {"default"}; ///< fraction of exchange erfc(wr)/r in hybrid functionals + std::vector exx_erfc_omega = {"default"}; ///< range-separation parameter in HSE functional + bool exx_separate_loop = true; ///< if 1, a two-step method is employed, else it will start + ///< with a GGA-Loop, and then Hybrid-Loop + std::string exx_singularity_correction = "default"; ///< set the scheme of Coulomb singularity correction + int exx_hybrid_step = 100; ///< the maximal electronic iteration number in + ///< the evaluation of Fock exchange + double exx_mixing_beta = 1.0; ///< mixing_beta for outer-loop when exx_separate_loop=1 + std::string exx_real_number = "default"; ///< exx calculated in real or complex + double exx_pca_threshold = 0.0001; ///< threshold to screen on-site ABFs in exx + double exx_c_threshold = 0.0001; ///< threshold to screen C matrix in exx + double exx_v_threshold = 0.1; ///< threshold to screen C matrix in exx + double exx_dm_threshold = 0.0001; ///< threshold to screen density matrix in exx + double exx_c_grad_threshold = 0.0001; ///< threshold to screen nabla C matrix in exx + double exx_v_grad_threshold = 0.1; ///< threshold to screen nabla V matrix in exx + double exx_c_grad_r_threshold = 0.0001; ///< threshold to screen nabla C * R matrix in exx + double exx_v_grad_r_threshold = 0.1; ///< threshold to screen nabla V * R matrix in exx + std::string exx_ccp_rmesh_times = "default"; ///< how many times larger the radial mesh required for + ///< calculating Columb potential is to that of atomic orbitals + int exx_opt_orb_lmax = 0; ///< the maximum l of the spherical Bessel functions for opt ABFs + double exx_opt_orb_ecut = 0.0; ///< the cut-off of plane wave expansion for opt ABFs + double exx_opt_orb_tolerence = 0.0; ///< the threshold when solving for the zeros of spherical Bessel + ///< functions for opt ABFs + bool exx_symmetry_realspace = true; ///< whether to reduce the real-space sector in when using symmetry=1 in EXX calculation + double rpa_ccp_rmesh_times = 10.0; ///< how many times larger the radial mesh required for + ///< calculating Columb potential is to that of atomic orbitals + double exx_cs_inv_thr = -1; ///< threshold to inverse Vq in abfs for generating Cs + double shrink_abfs_pca_thr = -1; ///< threshold to shrink auxiliary basis for GW/RPA + double shrink_LU_inv_thr = 1e-6; ///< threshold to get inverse of overlap matrix by LU decomposition in auxiliary basis representation + bool out_ri_cv = false; ///< Whether to output the coefficient tensor C and ABFs-representation Coulomb matrix V + bool out_unshrinked_v = false; ///< whether to output the large Vq matrix in unshrinked auxiliary basis + bool exx_coul_moment = false; ///< whether to use moment method for Coulomb calculation + bool exx_rotate_abfs = false; ///< whether to rotate auxiliary basis for Coulomb calculation double exx_multip_moments_threshold = 1e-10; ///< threshold to screen multipole moments in Coulomb calculation // ============== #Parameters (16.dft+u) ====================== // DFT+U Xin Qu added on 2020-10-29 @@ -628,12 +625,13 @@ struct Input_para double sccut = 3.0; ///< restriction of step size in eV/uB double sc_scf_thr = 1e-3; ///< minimum number of outer scf loop before initial lambda loop double sc_drop_thr = 1e-3; ///< threshold for lambda-loop threshold cutoff in spin-constrained DFT - std::string sc_lambda_strategy = "bfgs"; ///< lambda update strategy: bfgs, bfgs2, linear_response, augmented_lagrangian, hybrid_delayed, linear_scan + std::string sc_lambda_strategy + = "bfgs"; ///< lambda update strategy: bfgs, bfgs2, linear_response, augmented_lagrangian, hybrid_delayed, linear_scan bool sc_direction_only = false; ///< only optimize the direction of magnetization // linear_scan parameters - double sc_scan_lambda_start = 0.0; ///< start value for lambda scan (eV/uB) - double sc_scan_lambda_end = 1.0; ///< end value for lambda scan (eV/uB) - int sc_scan_steps = 20; ///< number of steps in lambda scan + double sc_scan_lambda_start = 0.0; ///< start value for lambda scan (eV/uB) + double sc_scan_lambda_end = 1.0; ///< end value for lambda scan (eV/uB) + int sc_scan_steps = 20; ///< number of steps in lambda scan // ============== #Parameters (18.Quasiatomic Orbital analysis) ========= ///<========================================================== @@ -703,8 +701,8 @@ struct Input_para // ============== #Parameters (22.EXX PW) ===================== // EXX for planewave basis, rhx0820 2025-03-10 - bool exxace = true; // exxace, exact exchange for planewave basis, https://doi.org/10.1021/acs.jctc.6b00092 - bool exx_gamma_extrapolation = true; // gamma point extrapolation for exx, https://doi.org/10.1103/PhysRevB.79.205114 + bool exxace = true; // exxace, exact exchange for planewave basis, https://doi.org/10.1021/acs.jctc.6b00092 + bool exx_gamma_extrapolation = true; // gamma point extrapolation for exx, https://doi.org/10.1103/PhysRevB.79.205114 std::string exx_thr_type = "density"; ///< threshold type for exx outer loop double exx_ene_thr = 1e-5; ///< threshold when exx_thr_type = energy double ecutexx = 0.0; ///< energy cutoff for exx calculation, Ry @@ -714,22 +712,22 @@ struct Input_para * the following two sets of parameters are for the XC parameterization. * The first element should be the LibXC id, to assign the analytical * form of the eXchange and Correlation part of the functional. - * + * * Starting from the second parameter, the parameters are the coefficients * of the functional. For example the M06-L functional, one should refer * to the source file (source code of LibXC) - * + * * src/mgga_x_m06l.c - * + * * the implementation can be found in the file - * + * * src/maple2c/mgga_exc/mgga_x_m06l.c. - * + * * There are 18 parameters for the exchange part, so the whole length of * the xc_exch_ext should be 19. (MGGA_X_M06L, id = 203) - * + * * Likewise, the correlation part can be found in corresponding files. - * + * * These vectors are empty unless the user explicitly requests an * override. This leaves the version-specific default parameters under * Libxc's control. @@ -738,10 +736,10 @@ struct Input_para std::vector xc_corr_ext = {}; // ============== #Parameters (24.td-ofdft) =========================== - bool of_cd = false; ///< add CD potential or not - double of_mCD_alpha = 1.0; ///< parameter of modified CD potential + bool of_cd = false; ///< add CD potential or not + double of_mCD_alpha = 1.0; ///< parameter of modified CD potential // ============== #Parameters (25.uncommon hardware) ================== - int dsp_count = 4; ///< count of DSP hardwares in one node + int dsp_count = 4; ///< count of DSP hardwares in one node }; #endif diff --git a/source/source_io/module_parameter/read_input.h b/source/source_io/module_parameter/read_input.h index b146402a34..c938bdb159 100644 --- a/source/source_io/module_parameter/read_input.h +++ b/source/source_io/module_parameter/read_input.h @@ -4,10 +4,9 @@ #include "input_item.h" #include "source_io/module_parameter/parameter.h" -#include #include #include - +#include namespace ModuleIO { @@ -15,7 +14,7 @@ class ReadInput { public: ReadInput(const int& rank); - ~ReadInput(){}; + ~ReadInput() {}; /** * @brief clear all input items */ @@ -103,7 +102,7 @@ class ReadInput */ void add_item(const Input_Item& item); /// @brief set System_para according to input parameters - /// INPUT and STRU need to refer to each other in ABACUS, + /// INPUT and STRU need to refer to each other in ABACUS, /// so it is necessary to obtain the file paths related to all inputs void set_global_dir(const Input_para& inp, System_para& sys); // set System_para according to input parameters @@ -171,12 +170,16 @@ std::string to_dir(const std::string& str); // return a warning string if the string is not found in the vector std::string nofound_str(std::vector init_chgs, const std::string& str); - // filter non-ASCII characters from ifstream and output to stringstream // return true if successful, false otherwise -bool filter_nonascii_and_comment(std::ifstream& ifs, - std::stringstream& out_ascii_stream); - +bool filter_nonascii_and_comment(std::ifstream& ifs, std::stringstream& out_ascii_stream); + +/** + * @brief Validate the structure and waveform-defining constraints of RT-TDDFT fields. + * + * @param input Input parameters after all values and dynamic defaults are resolved. + */ +void check_td_efield_parameters(const Input_para& input); } // namespace ModuleIO diff --git a/source/source_io/module_parameter/read_input_item_output.cpp b/source/source_io/module_parameter/read_input_item_output.cpp index 93e6ed7be4..2818b9b523 100644 --- a/source/source_io/module_parameter/read_input_item_output.cpp +++ b/source/source_io/module_parameter/read_input_item_output.cpp @@ -1616,8 +1616,9 @@ In molecular dynamics calculations, the output frequency is controlled by out_fr item.annotation = "output dipole or not"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; item.type = "Boolean"; - item.description = R"(* True: Output electric dipole moment. -* False: Do not output electric dipole moment.)"; + item.description = R"(Controls electric-dipole output. In RT-TDDFT, each enabled spin channel is written to OUT.{suffix}/dipole_s[spin].txt using a one-based spin number. Every row contains the one-based electronic-step index followed by the Cartesian electronic-dipole components $P_x$, $P_y$, and $P_z$ in atomic units. The running log additionally reports the electronic, ionic, and total dipoles and the norm of the total dipole. +* True: Output the electric dipole information. +* False: Do not output the electric dipole information.)"; item.default_value = "False"; item.unit = ""; item.availability = ""; @@ -1629,13 +1630,13 @@ In molecular dynamics calculations, the output frequency is controlled by out_fr item.annotation = "output current or not"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; item.type = "Integer"; - item.description = R"(Controls the current-density output method for LCAO RT-TDDFT. + item.description = R"(Controls the current-density output method for LCAO RT-TDDFT. Output rows contain the one-based electronic-step index followed by $J_x$, $J_y$, and $J_z$ in atomic units. * 0: Do not output current. -* 1: Explicitly construct the velocity operator from the momentum, vector-potential, and KB nonlocal-pseudopotential terms using two-center integral / spherical grid integral: $\hat{v}_{\alpha}=-\mathrm{i}\nabla_{\alpha}+A_{\alpha}(t)+\mathrm{i}\left[\widetilde{V}_{\mathrm{NL}}^{\mathrm{KB}},r_{\alpha}\right]$, where $\widetilde{V}_{\mathrm{NL}}^{\mathrm{KB}}=\mathrm{e}^{-\mathrm{i}\boldsymbol{A}(t)\cdot\boldsymbol{r}}\hat{V}_{\mathrm{NL}}^{\mathrm{KB}}\mathrm{e}^{\mathrm{i}\boldsymbol{A}(t)\cdot\boldsymbol{r}}$. $\boldsymbol{A}(t)$ is nonzero only for the velocity gauge (td_stype=1); otherwise $\boldsymbol{A}(t)=0$. Other nonlocal Hamiltonian terms (e.g., EXX) are not included explicitly. -* 2: Use the full Hamiltonian to construct the generalized velocity matrix in a nonorthogonal NAO basis: $\widetilde{v}_{\alpha}=\partial_{\alpha}H+\mathrm{i}HS^{-1}\mathcal{R}_{\alpha}-\mathrm{i}\mathcal{R}_{\alpha}S^{-1}H-HS^{-1}\partial_{\alpha}S$. This includes all contributions available in the real-space Hamiltonian matrix when enabled. This method is more general but more expensive.)"; +* 1: Explicitly construct the velocity operator from the momentum, vector-potential, and KB nonlocal-pseudopotential terms using two-center and spherical-grid integrals: $\hat{v}_{\alpha}=-\mathrm{i}\nabla_{\alpha}+A_{\alpha}(t)+\mathrm{i}\left[\widetilde{V}_{\mathrm{NL}}^{\mathrm{KB}},r_{\alpha}\right]$, where $\widetilde{V}_{\mathrm{NL}}^{\mathrm{KB}}=\mathrm{e}^{-\mathrm{i}\boldsymbol{A}(t)\cdot\boldsymbol{r}}\hat{V}_{\mathrm{NL}}^{\mathrm{KB}}\mathrm{e}^{\mathrm{i}\boldsymbol{A}(t)\cdot\boldsymbol{r}}$. $\boldsymbol{A}(t)$ is nonzero only for the velocity gauge (td_stype=1); otherwise $\boldsymbol{A}(t)=0$. Other nonlocal Hamiltonian terms, such as EXX, are not included explicitly. The total current is written to OUT.{suffix}/current_tot.txt. +* 2: Use the full Hamiltonian to construct the generalized velocity matrix in a nonorthogonal NAO basis, $\widetilde{v}_{\alpha}=\partial_{\alpha}H+\mathrm{i}HS^{-1}\mathcal{R}_{\alpha}-\mathrm{i}\mathcal{R}_{\alpha}S^{-1}H-HS^{-1}\partial_{\alpha}S$. This includes all contributions available in the real-space Hamiltonian matrix when enabled. This method is more general but more expensive. The total current is written to OUT.{suffix}/current_tot_comm.txt.)"; item.default_value = "0"; item.unit = ""; - item.availability = ""; + item.availability = "basis_type==lcao and esolver_type==tddft"; read_sync_int(input.out_current); this->add_item(item); } @@ -1644,11 +1645,12 @@ In molecular dynamics calculations, the output frequency is controlled by out_fr item.annotation = "output current for each k"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; item.type = "Boolean"; - item.description = R"(* True: Output current for each k-points separately. -* False: Output current in total.)"; + item.description = R"(Controls whether LCAO RT-TDDFT current density is also resolved by spin and k-point. The total-current file is always written when out_current is 1 or 2. +* True: In addition to the total, out_current=1 writes OUT.{suffix}/current_s[spin]k[kpoint].txt; out_current=2 writes OUT.{suffix}/current_s[spin]k[kpoint]_comm.txt. Both use one-based spin and k-point numbers, with k-points numbered independently within each spin channel. Each row contains the one-based electronic-step index followed by $J_x$, $J_y$, and $J_z$ in atomic units. +* False: Output only current_tot.txt for out_current=1 or current_tot_comm.txt for out_current=2.)"; item.default_value = "False"; item.unit = ""; - item.availability = ""; + item.availability = "basis_type==lcao and esolver_type==tddft and out_current>0"; read_sync_bool(input.out_current_k); this->add_item(item); } @@ -1657,12 +1659,12 @@ In molecular dynamics calculations, the output frequency is controlled by out_fr item.annotation = "output dipole or not"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; item.type = "Boolean"; - item.description = R"(Whether to output the electric field data to files. When enabled, writes real-time electric field values (unit: V/A) into files named efield_[num].txt, where [num] is the sequential index of the electric field ranges from 0 to N-1 for N configured fields. It is noteworthy that the field type sequence follows td_ttype, while the direction sequence follows td_vext_dire. -* True: Output electric field. -* False: Do not output electric field.)"; + item.description = R"(Controls time-dependent electric-field output. For each configured field, OUT.{suffix}/efield_[index].txt contains two columns: physical time in fs and the field value in V/Angstrom. The one-based field index follows the occurrence order shared by td_ttype and td_vext_dire, so fields assigned to the same direction remain in separate files. At initialization, a fresh calculation with md_restart=False truncates the files corresponding to the currently configured fields, whereas a calculation with md_restart=True preserves them and appends new samples. +* True: Output electric-field values on active electronic steps. +* False: Do not output electric-field values.)"; item.default_value = "False"; item.unit = ""; - item.availability = ""; + item.availability = "esolver_type==tddft and td_vext==true"; read_sync_bool(input.out_efield); this->add_item(item); } @@ -1671,12 +1673,12 @@ In molecular dynamics calculations, the output frequency is controlled by out_fr item.annotation = "output TDDFT vector potential or not"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; item.type = "Boolean"; - item.description = R"(Output vector potential or not (unit: a.u.). -* True: Output vector potential into file At.dat. -* False: Do not output vector potential.)"; + item.description = R"(Controls Cartesian vector-potential output for LCAO RT-TDDFT. OUT.{suffix}/vector_pot.txt contains four columns: the one-based electronic-step index followed by $A_x$, $A_y$, and $A_z$ in atomic units. At initialization, a fresh calculation with md_restart=False truncates the file and writes a new header, whereas a calculation with md_restart=True preserves a nonempty existing file and appends new samples. If the restart output file is missing or empty, a new file with a header is created. +* True: Write vector-potential samples on electronic propagation steps. +* False: Do not output the vector potential.)"; item.default_value = "False"; item.unit = ""; - item.availability = ""; + item.availability = "basis_type==lcao and esolver_type==tddft"; read_sync_bool(input.out_vecpot); this->add_item(item); } diff --git a/source/source_io/module_parameter/read_input_item_tddft.cpp b/source/source_io/module_parameter/read_input_item_tddft.cpp index 18e8ad3c00..50f10eccef 100644 --- a/source/source_io/module_parameter/read_input_item_tddft.cpp +++ b/source/source_io/module_parameter/read_input_item_tddft.cpp @@ -3,10 +3,154 @@ #include "read_input.h" #include "read_input_tool.h" +#include +#include + namespace ModuleIO { +namespace +{ + +std::vector parse_supersine_steps(const Input_Item& item, const int default_step) +{ + const std::vector tokens + = item.is_read() ? item.str_values : std::vector(1, "default"); + std::vector values; + for (const std::string& token: tokens) + { + if (token == "default") + { + values.push_back(default_step); + } + else + { + std::vector parsed; + parse_expression(std::vector(1, token), parsed); + values.insert(values.end(), parsed.begin(), parsed.end()); + } + } + return values; +} + +struct FieldParameterRule +{ + const char* name; + int field_type; + std::size_t size; +}; + +} // namespace + +void check_td_efield_parameters(const Input_para& input) +{ + if (input.td_ttype.size() != input.td_vext_dire.size()) + { + ModuleBase::WARNING_QUIT("ReadInput", + "td_ttype and td_vext_dire must contain the same number of fields."); + } + if (input.td_stype < 0 || input.td_stype > 2) + { + ModuleBase::WARNING_QUIT("ReadInput", "td_stype must be 0, 1, or 2."); + } + for (const int direction: input.td_vext_dire) + { + if (direction < 1 || direction > 3) + { + ModuleBase::WARNING_QUIT("ReadInput", "td_vext_dire must contain values from 1 to 3."); + } + } + + std::array field_counts = {{0, 0, 0, 0, 0}}; + for (const int field_type: input.td_ttype) + { + if (field_type < 0 || field_type > 4) + { + ModuleBase::WARNING_QUIT("ReadInput", "td_ttype must contain values from 0 to 4."); + } + ++field_counts[field_type]; + } + + const std::array rules = {{ + {"td_gauss_freq", 0, input.td_gauss_freq.size()}, + {"td_gauss_phase", 0, input.td_gauss_phase.size()}, + {"td_gauss_sigma", 0, input.td_gauss_sigma.size()}, + {"td_gauss_t0", 0, input.td_gauss_t0.size()}, + {"td_gauss_amp", 0, input.td_gauss_amp.size()}, + {"td_trape_freq", 1, input.td_trape_freq.size()}, + {"td_trape_phase", 1, input.td_trape_phase.size()}, + {"td_trape_t1", 1, input.td_trape_t1.size()}, + {"td_trape_t2", 1, input.td_trape_t2.size()}, + {"td_trape_t3", 1, input.td_trape_t3.size()}, + {"td_trape_amp", 1, input.td_trape_amp.size()}, + {"td_trigo_freq1", 2, input.td_trigo_freq1.size()}, + {"td_trigo_freq2", 2, input.td_trigo_freq2.size()}, + {"td_trigo_phase1", 2, input.td_trigo_phase1.size()}, + {"td_trigo_phase2", 2, input.td_trigo_phase2.size()}, + {"td_trigo_amp", 2, input.td_trigo_amp.size()}, + {"td_heavi_t0", 3, input.td_heavi_t0.size()}, + {"td_heavi_amp", 3, input.td_heavi_amp.size()}, + {"td_supsine_amp", 4, input.td_supsine_amp.size()}, + {"td_supsine_freq", 4, input.td_supsine_freq.size()}, + {"td_supsine_phase", 4, input.td_supsine_phase.size()}, + {"td_supsine_sigma", 4, input.td_supsine_sigma.size()}, + {"td_supsine_tstart", 4, input.td_supsine_tstart.size()}, + {"td_supsine_tend", 4, input.td_supsine_tend.size()}, + }}; + for (const FieldParameterRule& rule: rules) + { + const std::size_t field_count = field_counts[rule.field_type]; + if (field_count > 0 && rule.size != field_count) + { + ModuleBase::WARNING_QUIT("ReadInput", + std::string(rule.name) + + " must contain exactly one value for each td_ttype " + + std::to_string(rule.field_type) + " occurrence."); + } + } + + for (std::size_t index = 0; index < field_counts[0]; ++index) + { + if (input.td_gauss_sigma[index] == 0.0) + { + ModuleBase::WARNING_QUIT("ReadInput", "td_gauss_sigma must be nonzero."); + } + } + for (std::size_t index = 0; index < field_counts[1]; ++index) + { + if (input.td_trape_t1[index] > input.td_trape_t2[index] + || input.td_trape_t2[index] > input.td_trape_t3[index]) + { + ModuleBase::WARNING_QUIT( + "ReadInput", + "Each trapezoid field must satisfy td_trape_t1 <= td_trape_t2 <= td_trape_t3."); + } + } + for (std::size_t index = 0; index < field_counts[4]; ++index) + { + if (input.td_supsine_freq[index] == 0.0) + { + ModuleBase::WARNING_QUIT("ReadInput", "td_supsine_freq must be nonzero."); + } + if (input.td_supsine_sigma[index] <= 0.0 + || input.td_supsine_sigma[index] >= ModuleBase::PI / 2.0) + { + ModuleBase::WARNING_QUIT("ReadInput", + "td_supsine_sigma must be greater than 0 and less than pi/2."); + } + if (input.td_supsine_tstart[index] < input.td_tstart + || input.td_supsine_tstart[index] >= input.td_supsine_tend[index] + || input.td_supsine_tend[index] > input.td_tend) + { + ModuleBase::WARNING_QUIT( + "ReadInput", + "Each supersine pulse must satisfy td_tstart <= td_supsine_tstart < " + "td_supsine_tend <= td_tend."); + } + } +} + void ReadInput::item_rt_tddft() -{ +{ // NOTE: The order of add_item() calls below determines the parameter order // in the generated documentation (docs/advanced/input_files/input-main.md). // Please preserve this ordering when adding new parameters. @@ -28,7 +172,7 @@ void ReadInput::item_rt_tddft() item.annotation = "time step for evolving wavefunction"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; item.type = "Real"; - item.description = "The time step used in electronic propagation. Setting td_dt will reset the value of md_dt to td_dt * estep_per_md."; + item.description = R"(The time step used for electronic propagation. If td_dt is not specified, it is set to md_dt / estep_per_md. If td_dt is specified explicitly, md_dt is reset to td_dt * estep_per_md.)"; item.default_value = "md_dt / estep_per_md"; item.unit = "fs"; item.availability = ""; @@ -47,9 +191,9 @@ void ReadInput::item_rt_tddft() item.annotation = "the method to calculate the energy density matrix"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; item.type = "Integer"; - item.description = R"(Method to calculate the energy-density matrix, mainly affects the calculation of force and stress. -* 0: Using the original formula. -* 1: Using the formula for ground state (deprecated). Note that this usually does not hold if wave function is not the eigenstate of the Hamiltonian.)"; + item.description = R"(Method used to calculate the energy-density matrix for the overlap contribution to forces in LCAO RT-TDDFT. +* 0: Use $\mathrm{EDM}_{\boldsymbol{k}}=\frac{1}{2}\left(S_{\boldsymbol{k}}^{-1}H_{\boldsymbol{k}}\rho_{\boldsymbol{k}}+\rho_{\boldsymbol{k}}H_{\boldsymbol{k}}S_{\boldsymbol{k}}^{-1}\right)$. +* 1: Use the ground-state eigenvalue-weighted expression $\mathrm{EDM}_{\mu\nu,\boldsymbol{k}}=\sum_i w_{i\boldsymbol{k}}\epsilon_{i\boldsymbol{k}}C_{\mu i,\boldsymbol{k}}C_{\nu i,\boldsymbol{k}}^*$. This expression is deprecated for RT-TDDFT and is generally not valid when the propagated wave functions are not Hamiltonian eigenstates.)"; item.default_value = "0"; item.unit = ""; item.availability = ""; @@ -61,9 +205,9 @@ void ReadInput::item_rt_tddft() item.annotation = "print eij or not"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; item.type = "Real"; - item.description = R"(Controls the printing of Hamiltonian matrix elements. -* < 0: Suppress all output. -* >= 0: Print only elements with either i or j exceeding td_print_eij.)"; + item.description = R"(Controls output of the propagated-state Hamiltonian matrix elements $E_{ij}=\Braket{\psi_i | \hat{H} | \psi_j}$ to the running log. The printed band indices $i$ and $j$ are one-based global indices. Both the threshold and the printed matrix elements are in Ry. +* $\lt 0$: Disable the output. +* $\geqslant 0$: Print an element when either $\left|\operatorname{Re}E_{ij}\right|$ or $\left|\operatorname{Im}E_{ij}\right|$ is greater than or equal to td_print_eij.)"; item.default_value = "-1"; item.unit = "Ry"; item.availability = ""; @@ -75,11 +219,13 @@ void ReadInput::item_rt_tddft() item.annotation = "method of propagator"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; item.type = "Integer"; - item.description = R"(Methods of electronic propagation. -* 0: Crank-Nicolson, based on matrix inversion. -* 1: 4th-order Taylor expansion of exponential. -* 2: Enforced time-reversal symmetry (ETRS). -* 3: Crank-Nicolson, based on solving linear equation.)"; + item.description = R"(Method used to propagate the electronic states in a nonorthogonal LCAO basis. The formulas below use Hartree atomic units, with $S$, $H$, and $\Delta t=\mathtt{td\_dt}$ evaluated as required by each approximation. +* 0: Crank-Nicolson through an explicitly constructed evolution matrix, $U=\left[S+\mathrm{i}H\Delta t/2\right]^{-1}\left[S-\mathrm{i}H\Delta t/2\right]$. +* 1: Fourth-order Taylor approximation to the exponential. With $\mathcal{A}=-\mathrm{i}S^{-1}H\Delta t$, $U=I+\mathcal{A}+\mathcal{A}^2/2+\mathcal{A}^3/6+\mathcal{A}^4/24$. +* 2: Enforced time-reversal symmetry (ETRS), $U(t+\Delta t,t)=\exp\left[-\mathrm{i}S^{-1}H(t+\Delta t)\Delta t/2\right]\exp\left[-\mathrm{i}S^{-1}H(t)\Delta t/2\right]$. In the implementation, each exponential is replaced by the fourth-order Taylor polynomial from method 1 evaluated with a half time step. +* 3: Crank-Nicolson by directly solving $\left[S+\mathrm{i}H\Delta t/2\right]\psi(t+\Delta t)=\left[S-\mathrm{i}H\Delta t/2\right]\psi(t)$. + +[NOTE] GPU execution currently supports only method 0 in both single-GPU and multi-GPU solver configurations. CPU execution supports methods 0 through 3.)"; item.default_value = "0"; item.unit = ""; item.availability = ""; @@ -91,7 +237,8 @@ void ReadInput::item_rt_tddft() item.annotation = "add extern potential or not"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; item.type = "Boolean"; - item.description = R"(* True: Add a laser-material interaction (external electric field). + item.description = R"(Controls whether a time-dependent external electric field is applied. +* True: Add a laser-material interaction (external electric field). * False: No external electric field.)"; item.default_value = "False"; item.unit = ""; @@ -99,21 +246,12 @@ void ReadInput::item_rt_tddft() read_sync_bool(input.td_vext); this->add_item(item); } - // { - // Input_Item item("td_vext_dire"); - // item.annotation = "extern potential direction"; - // item.read_value = [](const Input_Item& item, Parameter& para) { - // para.input.td_vext_dire = longstring(item.str_values); - // }; - // sync_string(input.td_vext_dire); - // this->add_item(item); - // } { Input_Item item("td_vext_dire"); item.annotation = "extern potential direction"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; - item.type = "String"; - item.description = R"(Specifies the direction(s) of the external electric field when td_vext is enabled. For example, td_vext_dire 1 2 indicates that external electric fields are applied to both the x and y directions simultaneously. Electric field parameters can also be written as strings. For example, td_gauss_phase 0 1.5707963 indicates that the Gaussian type electric fields in the x and y directions have a phase delay of pi/2. + item.type = "Vector of Integer"; + item.description = R"(Specifies one absolute Cartesian direction for each external electric field when td_vext is enabled. Unlike the ground-state efield_dir parameter, these directions are not defined by lattice or reciprocal-lattice vectors. The number of values must equal that of td_ttype, and repeated directions are allowed; fields assigned to the same direction are added. For example, td_vext_dire 1 2 applies one field along Cartesian x and one along Cartesian y. * 1: The external field direction is along the x-axis. * 2: The external field direction is along the y-axis. * 3: The external field direction is along the z-axis.)"; @@ -123,13 +261,7 @@ void ReadInput::item_rt_tddft() item.read_value = [](const Input_Item& item, Parameter& para) { parse_expression(item.str_values, para.input.td_vext_dire); }; - item.get_final_value = [](Input_Item& item, const Parameter& para) { - if (item.is_read()) - { - item.final_value.str(longstring(item.str_values)); - } - }; - add_intvec_bcast(input.td_vext_dire, para.input.td_vext_dire.size(), 0); + sync_intvec(input.td_vext_dire, para.input.td_vext_dire.size(), 0); this->add_item(item); } { @@ -151,19 +283,25 @@ void ReadInput::item_rt_tddft() Input_Item item("td_ttype"); item.annotation = "type of electric field in time domain"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; - item.type = "String"; - item.description = R"(Type of electric field in the time domain. -* 0: Gaussian type function. -* 1: Trapezoid type function. -* 2: Trigonometric type function. -* 3: Heaviside type function.)"; + item.type = "Vector of Integer"; + item.description = R"(Specifies one time-domain type for each external electric field. Its number of values must equal that of td_vext_dire. Parameters belonging to each type must provide exactly one value for every occurrence of that type, in occurrence order; fields with a repeated direction are added. + +The formulas below use Hartree atomic units. For every ordinary input frequency $f$, $\omega=2\pi f$; $\Delta t=\mathtt{td\_dt}$; and $E_0$ denotes the corresponding amplitude parameter. A step-valued parameter $n_q$ represents the physical time $t_q=n_q\Delta t$. +* 0: Gaussian pulse, $E(t)=E_0\cos\left[\omega(t-t_0)+\varphi\right]\mathrm{e}^{-(t-t_0)^2/(2\sigma^2)}$, where $t_0=\mathtt{td\_gauss\_t0}\Delta t$. +* 1: Trapezoid pulse, $E(t)=E_0g(t)\cos(\omega t+\varphi)$. With $t_1=\mathtt{td\_trape\_t1}\Delta t$, $t_2=\mathtt{td\_trape\_t2}\Delta t$, and $t_3=\mathtt{td\_trape\_t3}\Delta t$, the envelope is $g(t)=t/t_1$ for $0\leqslant t\lt t_1$, $g(t)=1$ for $t_1\leqslant t\lt t_2$, $g(t)=(t_3-t)/(t_3-t_2)$ for $t_2\leqslant t\lt t_3$, and $g(t)=0$ otherwise. +* 2: Trigonometric pulse, $E(t)=E_0\cos(\omega_1t+\varphi_1)\sin^2(\omega_2t+\varphi_2)$. +* 3: Heaviside pulse defined on electronic steps. With $n_0=\mathtt{td\_heavi\_t0}$, $E(n)=E_0$ for $n\lt n_0$ and $E(n)=0$ for $n\geqslant n_0$. +* 4: Finite-support supersine pulse. For $t_{\mathrm{s}}\lt t\lt t_{\mathrm{e}}$, the envelope is $f(t)=\left\{\sin\left[\pi\frac{t-t_{\mathrm{s}}}{t_{\mathrm{e}}-t_{\mathrm{s}}}\right]\right\}^{\frac{\pi}{\sigma}\left|\frac{t-t_{\mathrm{s}}}{t_{\mathrm{e}}-t_{\mathrm{s}}}-\frac{1}{2}\right|}$ and the electric field is $E(t)=E_0\left\{f(t)\cos\left[\omega\left(t-\frac{t_{\mathrm{s}}+t_{\mathrm{e}}}{2}\right)+\varphi\right]+\frac{\dot{f}(t)}{\omega}\sin\left[\omega\left(t-\frac{t_{\mathrm{s}}+t_{\mathrm{e}}}{2}\right)+\varphi\right]\right\}$. The corresponding analytic vector potential is $\boldsymbol{A}(t)=-\frac{E_0}{\omega}f(t)\sin\left[\omega\left(t-\frac{t_{\mathrm{s}}+t_{\mathrm{e}}}{2}\right)+\varphi\right]\hat{\boldsymbol{e}}$, with $\boldsymbol{E}(t)=-\partial\boldsymbol{A}(t)/\partial t$. The envelope, electric field, and vector potential are zero at the pulse boundaries and outside the interval. + +In the velocity and hybrid gauges, ABACUS obtains the vector potential actually used in propagation by Simpson integration of the selected electric fields, including the supersine field, so a residual at the numerical-quadrature accuracy scale may remain.)"; item.default_value = "0"; item.unit = ""; item.availability = ""; item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_ttype = longstring(item.str_values); + parse_expression(item.str_values, para.input.td_ttype); }; - sync_string(input.td_ttype); + sync_intvec(input.td_ttype, para.input.td_ttype.size(), 0); + item.check_value = [](const Input_Item&, const Parameter& para) { check_td_efield_parameters(para.inp); }; this->add_item(item); } { @@ -171,7 +309,7 @@ void ReadInput::item_rt_tddft() item.annotation = " number of steps where electric field starts"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; item.type = "Integer"; - item.description = "The initial time step when the time-dependent electric field is activated."; + item.description = R"(First electronic step at which the time-dependent electric field is active. The interval from td_tstart through td_tend includes both endpoints. On each active step $n$, the velocity and hybrid gauges integrate the field over $[n\Delta t,(n+1)\Delta t]$, where $\Delta t=\mathtt{td\_dt}$.)"; item.default_value = "1"; item.unit = ""; item.availability = ""; @@ -183,7 +321,7 @@ void ReadInput::item_rt_tddft() item.annotation = "number of steps where electric field ends"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; item.type = "Integer"; - item.description = "The final time step when the time-dependent electric field is deactivated. The field remains active between td_tstart and td_tend."; + item.description = R"(Last electronic step at which the time-dependent electric field is active. The interval from td_tstart through td_tend includes both endpoints. On each active step $n$, the velocity and hybrid gauges integrate the field over $[n\Delta t,(n+1)\Delta t]$, where $\Delta t=\mathtt{td\_dt}$.)"; item.default_value = "1000"; item.unit = ""; item.availability = ""; @@ -195,7 +333,7 @@ void ReadInput::item_rt_tddft() item.annotation = "cut1 of interval in length gauge"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; item.type = "Real"; - item.description = "The lower bound of the interval in the length gauge RT-TDDFT, where the coordinate is the fractional coordinate."; + item.description = R"(Lower fractional-coordinate cutoff for the periodic spatial modulation used in the length gauge. Let $c_1=\mathtt{td\_lcut1}$, $c_2=\mathtt{td\_lcut2}$, $D=c_2-c_1$, and $G=c_1+1-c_2$. For a fractional coordinate $x$, the field factor is $\eta(x)=1$ when $c_1\leqslant x\lt c_2$ and $\eta(x)=-D/G$ elsewhere. The reversed outer interval makes the potential periodic and continuous and gives the field zero cell average.)"; item.default_value = "0.05"; item.unit = ""; item.availability = ""; @@ -207,7 +345,7 @@ void ReadInput::item_rt_tddft() item.annotation = "cut2 of interval in length gauge"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; item.type = "Real"; - item.description = "The upper bound of the interval in the length gauge RT-TDDFT, where the coordinate is the fractional coordinate."; + item.description = R"(Upper fractional-coordinate cutoff for the periodic spatial modulation used in the length gauge. Let $c_1=\mathtt{td\_lcut1}$, $c_2=\mathtt{td\_lcut2}$, $D=c_2-c_1$, and $G=c_1+1-c_2$. For a fractional coordinate $x$, the field factor is $\eta(x)=1$ when $c_1\leqslant x\lt c_2$ and $\eta(x)=-D/G$ elsewhere. The reversed outer interval makes the potential periodic and continuous and gives the field zero cell average.)"; item.default_value = "0.95"; item.unit = ""; item.availability = ""; @@ -218,270 +356,366 @@ void ReadInput::item_rt_tddft() Input_Item item("td_gauss_freq"); item.annotation = "frequency (freq) of Gauss type electric field"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; - item.type = "String"; - item.description = "Frequency of the Gaussian type electric field."; + item.type = "Vector of Real"; + item.description = R"(Ordinary frequency $f$ in the Gaussian-pulse formula, with $\omega=2\pi f$. Supply exactly one value for each td_ttype 0 occurrence, in occurrence order.)"; item.default_value = "22.13"; item.unit = "1/fs"; - item.availability = ""; + item.availability = "td_ttype contains 0"; item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_gauss_freq = longstring(item.str_values); + parse_expression(item.str_values, para.input.td_gauss_freq); }; - sync_string(input.td_gauss_freq); + sync_doublevec(input.td_gauss_freq, para.input.td_gauss_freq.size(), 0.0); this->add_item(item); } { Input_Item item("td_gauss_phase"); item.annotation = "phase of Gauss type electric field"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; - item.type = "String"; - item.description = "Phase of the Gaussian type electric field."; + item.type = "Vector of Real"; + item.description = R"(Carrier phase $\varphi$ in the Gaussian-pulse formula. Supply exactly one value for each td_ttype 0 occurrence, in occurrence order.)"; item.default_value = "0.0"; - item.unit = ""; - item.availability = ""; + item.unit = "rad"; + item.availability = "td_ttype contains 0"; item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_gauss_phase = longstring(item.str_values); + parse_expression(item.str_values, para.input.td_gauss_phase); }; - sync_string(input.td_gauss_phase); + sync_doublevec(input.td_gauss_phase, para.input.td_gauss_phase.size(), 0.0); this->add_item(item); } { Input_Item item("td_gauss_sigma"); item.annotation = "sigma of Gauss type electric field"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; - item.type = "String"; - item.description = "Pulse width (standard deviation) of the Gaussian type electric field."; + item.type = "Vector of Real"; + item.description = R"(Nonzero standard deviation $\sigma$ of the Gaussian envelope. Supply exactly one value for each td_ttype 0 occurrence, in occurrence order.)"; item.default_value = "30.0"; item.unit = "fs"; - item.availability = ""; + item.availability = "td_ttype contains 0"; item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_gauss_sigma = longstring(item.str_values); + parse_expression(item.str_values, para.input.td_gauss_sigma); }; - sync_string(input.td_gauss_sigma); + sync_doublevec(input.td_gauss_sigma, para.input.td_gauss_sigma.size(), 0.0); this->add_item(item); } { Input_Item item("td_gauss_t0"); item.annotation = "step number of time center (t0) of Gauss type electric field"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; - item.type = "String"; - item.description = "Step number of the time center of the Gaussian type electric field."; + item.type = "Vector of Real"; + item.description = R"(Electronic-step position of the Gaussian center, which defines $t_0=\mathtt{td\_gauss\_t0}\Delta t$. Supply exactly one value for each td_ttype 0 occurrence, in occurrence order.)"; item.default_value = "100"; item.unit = ""; - item.availability = ""; + item.availability = "td_ttype contains 0"; item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_gauss_t0 = longstring(item.str_values); + parse_expression(item.str_values, para.input.td_gauss_t0); }; - sync_string(input.td_gauss_t0); + sync_doublevec(input.td_gauss_t0, para.input.td_gauss_t0.size(), 0.0); this->add_item(item); } { Input_Item item("td_gauss_amp"); item.annotation = "amplitude of Gauss type electric field"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; - item.type = "String"; - item.description = "Amplitude of the Gaussian type electric field."; + item.type = "Vector of Real"; + item.description = R"(Electric-field scale $E_0$ in the Gaussian-pulse formula. Supply exactly one value for each td_ttype 0 occurrence, in occurrence order.)"; item.default_value = "0.25"; item.unit = "V/Angstrom"; - item.availability = ""; + item.availability = "td_ttype contains 0"; item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_gauss_amp = longstring(item.str_values); + parse_expression(item.str_values, para.input.td_gauss_amp); }; - sync_string(input.td_gauss_amp); + sync_doublevec(input.td_gauss_amp, para.input.td_gauss_amp.size(), 0.0); this->add_item(item); } { Input_Item item("td_trape_freq"); item.annotation = "frequency of Trapezoid type electric field"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; - item.type = "String"; - item.description = "Frequency of the trapezoid type electric field."; + item.type = "Vector of Real"; + item.description = R"(Ordinary carrier frequency $f$ in the trapezoid-pulse formula, with $\omega=2\pi f$. Supply exactly one value for each td_ttype 1 occurrence, in occurrence order.)"; item.default_value = "1.60"; item.unit = "1/fs"; - item.availability = ""; + item.availability = "td_ttype contains 1"; item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_trape_freq = longstring(item.str_values); + parse_expression(item.str_values, para.input.td_trape_freq); }; - sync_string(input.td_trape_freq); + sync_doublevec(input.td_trape_freq, para.input.td_trape_freq.size(), 0.0); this->add_item(item); } { Input_Item item("td_trape_phase"); item.annotation = "phase of Trapezoid type electric field"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; - item.type = "String"; - item.description = "Phase of the trapezoid type electric field."; + item.type = "Vector of Real"; + item.description = R"(Carrier phase $\varphi$ in the trapezoid-pulse formula. Supply exactly one value for each td_ttype 1 occurrence, in occurrence order.)"; item.default_value = "0.0"; - item.unit = ""; - item.availability = ""; + item.unit = "rad"; + item.availability = "td_ttype contains 1"; item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_trape_phase = longstring(item.str_values); + parse_expression(item.str_values, para.input.td_trape_phase); }; - sync_string(input.td_trape_phase); + sync_doublevec(input.td_trape_phase, para.input.td_trape_phase.size(), 0.0); this->add_item(item); } { Input_Item item("td_trape_t1"); item.annotation = "t1 of Trapezoid type electric field"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; - item.type = "String"; - item.description = "Step number of the time interval t1 of the trapezoid type electric field."; + item.type = "Vector of Real"; + item.description = R"(Electronic step defining the end of the linear rise, $t_1=\mathtt{td\_trape\_t1}\Delta t$. Each field must satisfy td_trape_t1 <= td_trape_t2 <= td_trape_t3. Supply exactly one value for each td_ttype 1 occurrence, in occurrence order.)"; item.default_value = "1875"; item.unit = ""; - item.availability = ""; + item.availability = "td_ttype contains 1"; item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_trape_t1 = longstring(item.str_values); + parse_expression(item.str_values, para.input.td_trape_t1); }; - sync_string(input.td_trape_t1); + sync_doublevec(input.td_trape_t1, para.input.td_trape_t1.size(), 0.0); this->add_item(item); } { Input_Item item("td_trape_t2"); item.annotation = "t2 of Trapezoid type electric field"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; - item.type = "String"; - item.description = "Step number of the time interval t2 of the trapezoid type electric field."; + item.type = "Vector of Real"; + item.description = R"(Electronic step defining the end of the plateau, $t_2=\mathtt{td\_trape\_t2}\Delta t$. Each field must satisfy td_trape_t1 <= td_trape_t2 <= td_trape_t3. Supply exactly one value for each td_ttype 1 occurrence, in occurrence order.)"; item.default_value = "5625"; item.unit = ""; - item.availability = ""; + item.availability = "td_ttype contains 1"; item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_trape_t2 = longstring(item.str_values); + parse_expression(item.str_values, para.input.td_trape_t2); }; - sync_string(input.td_trape_t2); + sync_doublevec(input.td_trape_t2, para.input.td_trape_t2.size(), 0.0); this->add_item(item); } { Input_Item item("td_trape_t3"); item.annotation = "t3 of Trapezoid type electric field"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; - item.type = "String"; - item.description = "Step number of the time interval t3 of the trapezoid type electric field."; + item.type = "Vector of Real"; + item.description = R"(Electronic step defining the end of the linear fall, $t_3=\mathtt{td\_trape\_t3}\Delta t$. Each field must satisfy td_trape_t1 <= td_trape_t2 <= td_trape_t3. Supply exactly one value for each td_ttype 1 occurrence, in occurrence order.)"; item.default_value = "7500"; item.unit = ""; - item.availability = ""; + item.availability = "td_ttype contains 1"; item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_trape_t3 = longstring(item.str_values); + parse_expression(item.str_values, para.input.td_trape_t3); }; - sync_string(input.td_trape_t3); + sync_doublevec(input.td_trape_t3, para.input.td_trape_t3.size(), 0.0); this->add_item(item); } { Input_Item item("td_trape_amp"); item.annotation = "amplitude of Trapezoid type electric field"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; - item.type = "String"; - item.description = "Amplitude of the trapezoid type electric field."; + item.type = "Vector of Real"; + item.description = R"(Electric-field scale $E_0$ in the trapezoid-pulse formula. Supply exactly one value for each td_ttype 1 occurrence, in occurrence order.)"; item.default_value = "2.74"; item.unit = "V/Angstrom"; - item.availability = ""; + item.availability = "td_ttype contains 1"; item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_trape_amp = longstring(item.str_values); + parse_expression(item.str_values, para.input.td_trape_amp); }; - sync_string(input.td_trape_amp); + sync_doublevec(input.td_trape_amp, para.input.td_trape_amp.size(), 0.0); this->add_item(item); } { Input_Item item("td_trigo_freq1"); item.annotation = "frequency 1 of Trigonometric type electric field"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; - item.type = "String"; - item.description = "Frequency 1 of the trigonometric type electric field."; + item.type = "Vector of Real"; + item.description = R"(First ordinary frequency $f_1$ in the trigonometric-pulse formula, with $\omega_1=2\pi f_1$. Supply exactly one value for each td_ttype 2 occurrence, in occurrence order.)"; item.default_value = "1.164656"; item.unit = "1/fs"; - item.availability = ""; + item.availability = "td_ttype contains 2"; item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_trigo_freq1 = longstring(item.str_values); + parse_expression(item.str_values, para.input.td_trigo_freq1); }; - sync_string(input.td_trigo_freq1); + sync_doublevec(input.td_trigo_freq1, para.input.td_trigo_freq1.size(), 0.0); this->add_item(item); } { Input_Item item("td_trigo_freq2"); item.annotation = "frequency 2 of Trigonometric type electric field"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; - item.type = "String"; - item.description = "Frequency 2 of the trigonometric type electric field."; + item.type = "Vector of Real"; + item.description = R"(Second ordinary frequency $f_2$ in the trigonometric-pulse formula, with $\omega_2=2\pi f_2$. Supply exactly one value for each td_ttype 2 occurrence, in occurrence order.)"; item.default_value = "0.029116"; item.unit = "1/fs"; - item.availability = ""; + item.availability = "td_ttype contains 2"; item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_trigo_freq2 = longstring(item.str_values); + parse_expression(item.str_values, para.input.td_trigo_freq2); }; - sync_string(input.td_trigo_freq2); + sync_doublevec(input.td_trigo_freq2, para.input.td_trigo_freq2.size(), 0.0); this->add_item(item); } { Input_Item item("td_trigo_phase1"); item.annotation = "phase 1 of Trigonometric type electric field"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; - item.type = "String"; - item.description = "Phase 1 of the trigonometric type electric field."; + item.type = "Vector of Real"; + item.description = R"(Carrier phase $\varphi_1$ in the cosine factor of the trigonometric-pulse formula. Supply exactly one value for each td_ttype 2 occurrence, in occurrence order.)"; item.default_value = "0.0"; - item.unit = ""; - item.availability = ""; + item.unit = "rad"; + item.availability = "td_ttype contains 2"; item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_trigo_phase1 = longstring(item.str_values); + parse_expression(item.str_values, para.input.td_trigo_phase1); }; - sync_string(input.td_trigo_phase1); + sync_doublevec(input.td_trigo_phase1, para.input.td_trigo_phase1.size(), 0.0); this->add_item(item); } { Input_Item item("td_trigo_phase2"); item.annotation = "phase 2 of Trigonometric type electric field"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; - item.type = "String"; - item.description = "Phase 2 of the trigonometric type electric field."; + item.type = "Vector of Real"; + item.description = R"(Envelope phase $\varphi_2$ in the sine-squared factor of the trigonometric-pulse formula. Supply exactly one value for each td_ttype 2 occurrence, in occurrence order.)"; item.default_value = "0.0"; - item.unit = ""; - item.availability = ""; + item.unit = "rad"; + item.availability = "td_ttype contains 2"; item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_trigo_phase2 = longstring(item.str_values); + parse_expression(item.str_values, para.input.td_trigo_phase2); }; - sync_string(input.td_trigo_phase2); + sync_doublevec(input.td_trigo_phase2, para.input.td_trigo_phase2.size(), 0.0); this->add_item(item); } { Input_Item item("td_trigo_amp"); item.annotation = "amplitude of Trigonometric type electric field"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; - item.type = "String"; - item.description = "Amplitude of the trigonometric type electric field."; + item.type = "Vector of Real"; + item.description = R"(Electric-field scale $E_0$ in the trigonometric-pulse formula. Supply exactly one value for each td_ttype 2 occurrence, in occurrence order.)"; item.default_value = "2.74"; item.unit = "V/Angstrom"; - item.availability = ""; + item.availability = "td_ttype contains 2"; item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_trigo_amp = longstring(item.str_values); + parse_expression(item.str_values, para.input.td_trigo_amp); }; - sync_string(input.td_trigo_amp); + sync_doublevec(input.td_trigo_amp, para.input.td_trigo_amp.size(), 0.0); this->add_item(item); } { Input_Item item("td_heavi_t0"); item.annotation = "t0 of Heaviside type electric field"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; - item.type = "String"; - item.description = "Step number of the switch time of the Heaviside type electric field."; + item.type = "Vector of Real"; + item.description = R"(Electronic switch step $n_0$ in the Heaviside-pulse definition. The field is $E_0$ for $n\lt n_0$ and zero for $n\geqslant n_0$. Supply exactly one value for each td_ttype 3 occurrence, in occurrence order.)"; item.default_value = "100"; item.unit = ""; - item.availability = ""; + item.availability = "td_ttype contains 3"; item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_heavi_t0 = longstring(item.str_values); + parse_expression(item.str_values, para.input.td_heavi_t0); }; - sync_string(input.td_heavi_t0); + sync_doublevec(input.td_heavi_t0, para.input.td_heavi_t0.size(), 0.0); this->add_item(item); } { Input_Item item("td_heavi_amp"); item.annotation = "amplitude of Heaviside type electric field"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; - item.type = "String"; - item.description = "Amplitude of the Heaviside type electric field."; + item.type = "Vector of Real"; + item.description = R"(Electric-field scale $E_0$ in the Heaviside-pulse definition. Supply exactly one value for each td_ttype 3 occurrence, in occurrence order.)"; item.default_value = "1.0"; item.unit = "V/Angstrom"; - item.availability = ""; + item.availability = "td_ttype contains 3"; item.read_value = [](const Input_Item& item, Parameter& para) { - para.input.td_heavi_amp = longstring(item.str_values); + parse_expression(item.str_values, para.input.td_heavi_amp); }; - sync_string(input.td_heavi_amp); + sync_doublevec(input.td_heavi_amp, para.input.td_heavi_amp.size(), 0.0); + this->add_item(item); + } + { + Input_Item item("td_supsine_amp"); + item.annotation = "carrier electric-field scale of the supersine pulse"; + item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; + item.type = "Vector of Real"; + item.description = R"(Carrier electric-field scale $E_0$ of each supersine pulse. This is not a normalization of the complete waveform maximum, because the envelope-derivative term also contributes. Supply exactly one value for each td_ttype 4 occurrence, in occurrence order.)"; + item.default_value = "0.27"; + item.unit = "V/Angstrom"; + item.availability = "td_ttype contains 4"; + item.read_value = [](const Input_Item& item, Parameter& para) { + parse_expression(item.str_values, para.input.td_supsine_amp); + }; + sync_doublevec(input.td_supsine_amp, para.input.td_supsine_amp.size(), 0.0); + this->add_item(item); + } + { + Input_Item item("td_supsine_freq"); + item.annotation = "carrier frequency of the supersine pulse"; + item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; + item.type = "Vector of Real"; + item.description = R"(Nonzero ordinary carrier frequency $f$ of each supersine pulse, with $\omega=2\pi f$. Supply exactly one value for each td_ttype 4 occurrence, in occurrence order.)"; + item.default_value = "0.18737028625"; + item.unit = "1/fs"; + item.availability = "td_ttype contains 4"; + item.read_value = [](const Input_Item& item, Parameter& para) { + parse_expression(item.str_values, para.input.td_supsine_freq); + }; + sync_doublevec(input.td_supsine_freq, para.input.td_supsine_freq.size(), 0.0); + this->add_item(item); + } + { + Input_Item item("td_supsine_phase"); + item.annotation = "carrier phase at the center of the supersine pulse"; + item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; + item.type = "Vector of Real"; + item.description = R"(Electric-field carrier phase $\varphi$ at the center of each supersine envelope. A value of 0 places a cosine carrier maximum at the envelope center. Supply exactly one value for each td_ttype 4 occurrence, in occurrence order.)"; + item.default_value = "0.0"; + item.unit = "rad"; + item.availability = "td_ttype contains 4"; + item.read_value = [](const Input_Item& item, Parameter& para) { + parse_expression(item.str_values, para.input.td_supsine_phase); + }; + sync_doublevec(input.td_supsine_phase, para.input.td_supsine_phase.size(), 0.0); + this->add_item(item); + } + { + Input_Item item("td_supsine_sigma"); + item.annotation = "shape parameter of the supersine envelope"; + item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; + item.type = "Vector of Real"; + item.description = R"(Dimensionless shape parameter $\sigma$ of each supersine envelope. It must satisfy $0\lt\sigma\lt\pi/2$ so that the electric field approaches zero at the pulse boundaries. Supply exactly one value for each td_ttype 4 occurrence, in occurrence order.)"; + item.default_value = "0.75"; + item.unit = ""; + item.availability = "td_ttype contains 4"; + item.read_value = [](const Input_Item& item, Parameter& para) { + parse_expression(item.str_values, para.input.td_supsine_sigma); + }; + sync_doublevec(input.td_supsine_sigma, para.input.td_supsine_sigma.size(), 0.0); + this->add_item(item); + } + { + Input_Item item("td_supsine_tstart"); + item.annotation = "start boundary step of the supersine pulse"; + item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; + item.type = "Vector of String"; + item.description = R"(Integer electronic step at the left, exactly zero boundary of each supersine pulse, defining $t_{\mathrm{s}}=\mathtt{td\_supsine\_tstart}\Delta t$. Supply exactly one integer or default token for each td_ttype 4 occurrence, in occurrence order; each default token inherits td_tstart. The complete pulse support must lie inside the inclusive global td_tstart to td_tend interval; hard truncation of a supersine pulse is rejected.)"; + item.default_value = "default"; + item.unit = ""; + item.availability = "td_ttype contains 4"; + item.reset_value = [](const Input_Item& item, Parameter& para) { + para.input.td_supsine_tstart = parse_supersine_steps(item, para.input.td_tstart); + }; + item.get_final_value = [](Input_Item& item, const Parameter&) { + item.final_value << (item.is_read() ? longstring(item.str_values) : "default"); + }; + add_intvec_bcast(input.td_supsine_tstart, para.input.td_supsine_tstart.size(), 0); + this->add_item(item); + } + { + Input_Item item("td_supsine_tend"); + item.annotation = "end boundary step of the supersine pulse"; + item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; + item.type = "Vector of String"; + item.description = R"(Integer electronic step at the right, exactly zero boundary of each supersine pulse, defining $t_{\mathrm{e}}=\mathtt{td\_supsine\_tend}\Delta t$. Supply exactly one integer or default token for each td_ttype 4 occurrence, in occurrence order; each default token inherits td_tend. The complete pulse support must lie inside the inclusive global td_tstart to td_tend interval; hard truncation of a supersine pulse is rejected.)"; + item.default_value = "default"; + item.unit = ""; + item.availability = "td_ttype contains 4"; + item.reset_value = [](const Input_Item& item, Parameter& para) { + para.input.td_supsine_tend = parse_supersine_steps(item, para.input.td_tend); + }; + item.get_final_value = [](Input_Item& item, const Parameter&) { + item.final_value << (item.is_read() ? longstring(item.str_values) : "default"); + }; + add_intvec_bcast(input.td_supsine_tend, para.input.td_supsine_tend.size(), 0); this->add_item(item); } { @@ -489,9 +723,9 @@ void ReadInput::item_rt_tddft() item.annotation = "init vector potential through file or not"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; item.type = "Boolean"; - item.description = R"(Initialize vector potential through file or not. -* True: Initialize vector potential from file At.dat (unit: a.u.). It consists of four columns, representing the step number and vector potential on each direction. -* False: Calculate vector potential by integrating the electric field.)"; + item.description = R"(Selects the source of the Cartesian vector potential used by LCAO RT-TDDFT. +* True: Read vector_pot.txt from the calculation working directory. Each non-comment line must contain four columns: a conventionally one-based electronic-step label followed by $A_x$, $A_y$, and $A_z$ in atomic units. Rows are consumed sequentially; the first column is read as a label and is not used for lookup. If propagation continues beyond the available rows, the last row is reused. +* False: Obtain the vector potential by integrating the configured electric field.)"; item.default_value = "False"; item.unit = ""; item.availability = ""; @@ -503,8 +737,9 @@ void ReadInput::item_rt_tddft() item.annotation = "change occupation or not"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; item.type = "Boolean"; - item.description = R"(* True: Fixes the band occupations based on the values specified in ocp_set. -* False: Does not fix the band occupations.)"; + item.description = R"(Controls fixed band occupations. In calculations other than LCAO RT-TDDFT, fixed values are applied during electronic-state setup. In LCAO RT-TDDFT, the initial ground-state SCF determines occupations normally, and fixed values from ocp_set are applied during the subsequent real-time propagation steps. +* True: Use the fixed occupations specified by ocp_set during propagation. +* False: Keep the occupations determined by the initial SCF.)"; item.default_value = "False"; item.unit = ""; item.availability = ""; @@ -516,11 +751,10 @@ void ReadInput::item_rt_tddft() item.annotation = "set occupation"; item.category = "RT-TDDFT: Real-Time Time-Dependent Density Functional Theory"; item.type = "String"; - item.description = R"(If ocp is set to 1, ocp_set must be provided as a string specifying the occupation numbers for each band across all k-points. The format follows a space-separated pattern, where occupations are assigned sequentially to bands for each k-point. A shorthand notation Nx can be used to repeat a value x for N bands. -* Example: - 1 10*1 0 1 represents occupations for 13 bands, where the 12th band is fully unoccupied (0), and all others are occupied (1). -* For a system with multiple k-points, the occupations must be specified for all k-points, following their order in the output file kpoints (may lead to fractional occupations). -* Incorrect specification of ocp_set could lead to inconsistencies in electron counting, causing the calculation to terminate with an error.)"; + item.description = R"(Fixed occupation weights used when ocp is true. Values are assigned band by band for each k-point, following k-point order. In LCAO RT-TDDFT, the initial ground-state SCF uses its normally determined occupations, and this array is applied only during subsequent real-time propagation steps. The repetition syntax N*x expands to N copies of x. +* Example: 1 10*1 0 1 expands to 13 values, with the 12th value equal to 0 and all other values equal to 1. +* After expansion, the array length must equal nks * nbands. +* The sum of all weights must equal nelec; otherwise the calculation terminates with an error.)"; item.default_value = "None"; item.unit = ""; item.availability = ""; @@ -536,8 +770,6 @@ void ReadInput::item_rt_tddft() add_doublevec_bcast(input.ocp_kb, para.input.ocp_kb.size(), 0.0); this->add_item(item); } - - } void ReadInput::item_tdofdft() { diff --git a/source/source_io/module_parameter/read_input_tool.h b/source/source_io/module_parameter/read_input_tool.h index d37967742f..9fe5109817 100644 --- a/source/source_io/module_parameter/read_input_tool.h +++ b/source/source_io/module_parameter/read_input_tool.h @@ -1,6 +1,8 @@ +#include +#include #include #include -#include + #ifdef __MPI #include "source_base/parallel_common.h" #endif @@ -179,7 +181,12 @@ void parse_expression(const std::vector& expressions, std::vector(std::stof(expr)); + std::istringstream value_stream(expr); + T T_value{}; + if (!(value_stream >> T_value) || !value_stream.eof()) + { + throw std::runtime_error("Invalid expression value: " + expr); + } result.push_back(T_value); } // e.g. "2*3", "2*3.5" @@ -189,14 +196,24 @@ void parse_expression(const std::vector& expressions, std::vector(std::stof(T_part)); - for(int i = 0 ; i < num; ++i) + std::istringstream count_stream(int_part); + int num = 0; + if (!(count_stream >> num) || !count_stream.eof() || num < 0) + { + throw std::runtime_error("Invalid expression count: " + int_part); + } + std::istringstream value_stream(T_part); + T T_value{}; + if (!(value_stream >> T_value) || !value_stream.eof()) + { + throw std::runtime_error("Invalid expression value: " + T_part); + } + for (int i = 0; i < num; ++i) { result.push_back(T_value); } } - // e.g. "2*3*3" + // e.g. "2*3*3" // If more than one '*' found, output an error message else { @@ -212,4 +229,4 @@ void reset_vector(std::vector& vec, int size, T default_value) { vec.resize(size, default_value); } -} \ No newline at end of file +} diff --git a/source/source_io/test/CMakeLists.txt b/source/source_io/test/CMakeLists.txt index 7044d7c8b9..e0c84b8468 100644 --- a/source/source_io/test/CMakeLists.txt +++ b/source/source_io/test/CMakeLists.txt @@ -34,6 +34,20 @@ AddTest( SOURCES output_test.cpp ) +AddTest( + TARGET MODULE_IO_td_efield_io_test + LIBS parameter base device + SOURCES td_efield_io_test.cpp ../module_efield/td_efield_io.cpp + ../../source_estate/module_pot/td_field.cpp ../../source_estate/module_pot/td_field_profiles.cpp + ../../source_estate/module_pot/td_field_manager.cpp +) + +AddTest( + TARGET MODULE_IO_td_vector_pot_io_test + LIBS parameter base device + SOURCES td_vector_pot_io_test.cpp ../module_efield/td_vector_pot_io.cpp +) + AddTest( TARGET MODULE_IO_binstream_test SOURCES binstream_test.cpp ../module_output/binstream.cpp diff --git a/source/source_io/test/for_testing_input_conv.h b/source/source_io/test/for_testing_input_conv.h index 5f34903207..4c04d48c56 100644 --- a/source/source_io/test/for_testing_input_conv.h +++ b/source/source_io/test/for_testing_input_conv.h @@ -1,23 +1,22 @@ #ifndef INPUT_CONV_TEST_H #define INPUT_CONV_TEST_H #define private public -#include "source_io/module_parameter/parameter.h" #include "source_cell/module_symmetry/symmetry.h" #include "source_cell/unitcell.h" #include "source_estate/elecstate_lcao.h" #include "source_estate/module_charge/charge_mixing.h" -#include "source_estate/occupy.h" -#include "source_estate/module_pot/H_TDDFT_pw.h" #include "source_estate/module_pot/efield.h" #include "source_estate/module_pot/gatefield.h" -#include "source_lcao/FORCE_STRESS.h" -#include "source_lcao/module_dftu/dftu.h" -#include "source_pw/module_pwdft/vnl_pw.h" -#include "source_pw/module_pwdft/structure_factor.h" +#include "source_estate/occupy.h" #include "source_hsolver/hsolver_lcao.h" -#include "source_io/module_unk/berryphase.h" +#include "source_io/module_parameter/parameter.h" #include "source_io/module_restart/restart.h" +#include "source_io/module_unk/berryphase.h" +#include "source_lcao/FORCE_STRESS.h" +#include "source_lcao/module_dftu/dftu.h" #include "source_md/md_func.h" +#include "source_pw/module_pwdft/structure_factor.h" +#include "source_pw/module_pwdft/vnl_pw.h" #include "source_relax/bfgs_basic.h" #include "source_relax/ions_move_basic.h" #include "source_relax/ions_move_cg.h" @@ -38,53 +37,6 @@ double elecstate::Efield::efield_pos_max; double elecstate::Efield::efield_pos_dec; double elecstate::Efield::efield_amp; -// Parameters of electric field for RT-TDDFT - -int elecstate::H_TDDFT_pw::stype; - -std::vector elecstate::H_TDDFT_pw::ttype; - -int elecstate::H_TDDFT_pw::tstart; -int elecstate::H_TDDFT_pw::tend; -double elecstate::H_TDDFT_pw::dt; -double elecstate::H_TDDFT_pw::dt_int; - -double elecstate::H_TDDFT_pw::lcut1; -double elecstate::H_TDDFT_pw::lcut2; - -// Gaussian -int elecstate::H_TDDFT_pw::gauss_count; -std::vector elecstate::H_TDDFT_pw::gauss_omega; // time(a.u.)^-1 -std::vector elecstate::H_TDDFT_pw::gauss_phase; -std::vector elecstate::H_TDDFT_pw::gauss_sigma; // time(a.u.) -std::vector elecstate::H_TDDFT_pw::gauss_t0; -std::vector elecstate::H_TDDFT_pw::gauss_amp; // Ry/bohr -std::vector elecstate::H_TDDFT_pw::gauss_ncut; - -// Trapezoid -int elecstate::H_TDDFT_pw::trape_count; -std::vector elecstate::H_TDDFT_pw::trape_omega; // time(a.u.)^-1 -std::vector elecstate::H_TDDFT_pw::trape_phase; -std::vector elecstate::H_TDDFT_pw::trape_t1; -std::vector elecstate::H_TDDFT_pw::trape_t2; -std::vector elecstate::H_TDDFT_pw::trape_t3; -std::vector elecstate::H_TDDFT_pw::trape_amp; // Ry/bohr -std::vector elecstate::H_TDDFT_pw::trape_ncut; - -// Trigonometric -int elecstate::H_TDDFT_pw::trigo_count; -std::vector elecstate::H_TDDFT_pw::trigo_omega1; // time(a.u.)^-1 -std::vector elecstate::H_TDDFT_pw::trigo_omega2; // time(a.u.)^-1 -std::vector elecstate::H_TDDFT_pw::trigo_phase1; -std::vector elecstate::H_TDDFT_pw::trigo_phase2; -std::vector elecstate::H_TDDFT_pw::trigo_amp; // Ry/bohr -std::vector elecstate::H_TDDFT_pw::trigo_ncut; - -// Heaviside -int elecstate::H_TDDFT_pw::heavi_count; -std::vector elecstate::H_TDDFT_pw::heavi_t0; -std::vector elecstate::H_TDDFT_pw::heavi_amp; // Ry/bohr - double BFGS_Basic::relax_bfgs_w1 = -1.0; double BFGS_Basic::relax_bfgs_w2 = -1.0; double Ions_Move_Basic::relax_bfgs_rmax = -1.0; @@ -141,70 +93,95 @@ UnitCell::UnitCell() { itia2iat.create(1, 1); } -UnitCell::~UnitCell() {} -Magnetism::Magnetism() {} -Magnetism::~Magnetism() {} -void Occupy::decision(const std::string& name, - const std::string& smearing_method, - const double& smearing_sigma) { +UnitCell::~UnitCell() +{ +} +Magnetism::Magnetism() +{ +} +Magnetism::~Magnetism() +{ +} +void Occupy::decision(const std::string& name, const std::string& smearing_method, const double& smearing_sigma) +{ return; } // void UnitCell::setup_from_input(const std::string&,const int&,const int&,const // bool&,const std::string&){return;} void UnitCell::setup_from_input(const std::string& latname_in, - const int& ntype_in, - const int& lmaxmax_in, - const bool& init_vel_in, - const std::string& fixed_axes_in) { + const int& ntype_in, + const int& lmaxmax_in, + const bool& init_vel_in, + const std::string& fixed_axes_in) +{ this->latName = latname_in; this->ntype = ntype_in; this->lmaxmax = lmaxmax_in; this->init_vel = init_vel_in; // pengfei Li add 2018-11-11 - if (fixed_axes_in == "None") { + if (fixed_axes_in == "None") + { this->lat_axis_free[0] = 1; this->lat_axis_free[1] = 1; this->lat_axis_free[2] = 1; - } else if (fixed_axes_in == "volume") { + } + else if (fixed_axes_in == "volume") + { this->lat_axis_free[0] = 1; this->lat_axis_free[1] = 1; this->lat_axis_free[2] = 1; - } else if (fixed_axes_in == "shape") { + } + else if (fixed_axes_in == "shape") + { this->lat_axis_free[0] = 1; this->lat_axis_free[1] = 1; this->lat_axis_free[2] = 1; - } else if (fixed_axes_in == "a") { + } + else if (fixed_axes_in == "a") + { this->lat_axis_free[0] = 0; this->lat_axis_free[1] = 1; this->lat_axis_free[2] = 1; - } else if (fixed_axes_in == "b") { + } + else if (fixed_axes_in == "b") + { this->lat_axis_free[0] = 1; this->lat_axis_free[1] = 0; this->lat_axis_free[2] = 1; - } else if (fixed_axes_in == "c") { + } + else if (fixed_axes_in == "c") + { this->lat_axis_free[0] = 1; this->lat_axis_free[1] = 1; this->lat_axis_free[2] = 0; - } else if (fixed_axes_in == "ab") { + } + else if (fixed_axes_in == "ab") + { this->lat_axis_free[0] = 0; this->lat_axis_free[1] = 0; this->lat_axis_free[2] = 1; - } else if (fixed_axes_in == "ac") { + } + else if (fixed_axes_in == "ac") + { this->lat_axis_free[0] = 0; this->lat_axis_free[1] = 1; this->lat_axis_free[2] = 0; - } else if (fixed_axes_in == "bc") { + } + else if (fixed_axes_in == "bc") + { this->lat_axis_free[0] = 1; this->lat_axis_free[1] = 0; this->lat_axis_free[2] = 0; - } else if (fixed_axes_in == "abc") { + } + else if (fixed_axes_in == "abc") + { this->lat_axis_free[0] = 0; this->lat_axis_free[1] = 0; this->lat_axis_free[2] = 0; - } else { - ModuleBase::WARNING_QUIT( - "Input", - "fixed_axes should be None,volume,shape,a,b,c,ab,ac,bc or abc!"); + } + else + { + ModuleBase::WARNING_QUIT("Input", "fixed_axes should be None,volume,shape,a,b,c,ab,ac,bc or abc!"); } return; } @@ -213,22 +190,23 @@ void UnitCell::setup_from_input(const std::string& latname_in, // return; // } -namespace MD_func { -void current_md_info(const int& my_rank, - const std::string& file_dir, - int& md_step, - double& temperature) { +namespace MD_func +{ +void current_md_info(const int& my_rank, const std::string& file_dir, int& md_step, double& temperature) +{ return; } } // namespace MD_func -namespace GlobalC { +namespace GlobalC +{ ModuleDFTU::DFTU dftu; Restart restart; } // namespace GlobalC #ifdef __PEXSI -namespace pexsi { +namespace pexsi +{ int PEXSI_Solver::pexsi_npole = 0; bool PEXSI_Solver::pexsi_inertia = 0; int PEXSI_Solver::pexsi_nmax = 0; diff --git a/source/source_io/test/read_input_ptest.cpp b/source/source_io/test/read_input_ptest.cpp index 8dc5a479d5..f278035afd 100644 --- a/source/source_io/test/read_input_ptest.cpp +++ b/source/source_io/test/read_input_ptest.cpp @@ -1,16 +1,17 @@ -#include -#include +#include "source_base/tool_quit.h" +#include "source_io/module_parameter/parameter.h" +#include "source_io/module_parameter/read_input.h" #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "source_base/tool_quit.h" -#include "source_io/module_parameter/read_input.h" -#include "source_io/module_parameter/parameter.h" +#include +#include +#include // #ifdef __MPI +#include "mpi.h" #include "source_base/parallel_global.h" #include "source_basis/module_pw/test/test_tool.h" -#include "mpi.h" // #endif /************************************************ * unit test of read_input_test.cpp @@ -26,7 +27,7 @@ class InputParaTest : public testing::Test { - protected: + protected: }; // #ifdef __MPI @@ -316,29 +317,31 @@ TEST_F(InputParaTest, ParaRead) EXPECT_EQ(param.inp.td_vext, 0); EXPECT_EQ(param.inp.propagator, 0); EXPECT_EQ(param.inp.td_stype, 0); - EXPECT_EQ(param.inp.td_ttype, "0"); + EXPECT_THAT(param.inp.td_ttype, testing::ElementsAre(0)); EXPECT_EQ(param.inp.td_tstart, 1); EXPECT_EQ(param.inp.td_tend, 1000); EXPECT_EQ(param.inp.td_lcut1, 0.05); EXPECT_EQ(param.inp.td_lcut2, 0.95); - EXPECT_EQ(param.inp.td_gauss_amp, "0.25"); - EXPECT_EQ(param.inp.td_gauss_freq, "22.13"); - EXPECT_EQ(param.inp.td_gauss_phase, "0.0"); - EXPECT_EQ(param.inp.td_gauss_t0, "100.0"); - EXPECT_EQ(param.inp.td_gauss_sigma, "30.0"); - EXPECT_EQ(param.inp.td_trape_amp, "2.74"); - EXPECT_EQ(param.inp.td_trape_freq, "1.60"); - EXPECT_EQ(param.inp.td_trape_phase, "0.0"); - EXPECT_EQ(param.inp.td_trape_t1, "1875"); - EXPECT_EQ(param.inp.td_trape_t2, "5625"); - EXPECT_EQ(param.inp.td_trape_t3, "7500"); - EXPECT_EQ(param.inp.td_trigo_freq1, "1.164656"); - EXPECT_EQ(param.inp.td_trigo_freq2, "0.029116"); - EXPECT_EQ(param.inp.td_trigo_phase1, "0.0"); - EXPECT_EQ(param.inp.td_trigo_phase2, "0.0"); - EXPECT_EQ(param.inp.td_trigo_amp, "2.74"); - EXPECT_EQ(param.inp.td_heavi_t0, "100"); - EXPECT_EQ(param.inp.td_heavi_amp, "1.0"); + EXPECT_THAT(param.inp.td_gauss_amp, testing::ElementsAre(0.25)); + EXPECT_THAT(param.inp.td_gauss_freq, testing::ElementsAre(22.13)); + EXPECT_THAT(param.inp.td_gauss_phase, testing::ElementsAre(0.0)); + EXPECT_THAT(param.inp.td_gauss_t0, testing::ElementsAre(100.0)); + EXPECT_THAT(param.inp.td_gauss_sigma, testing::ElementsAre(30.0)); + EXPECT_THAT(param.inp.td_trape_amp, testing::ElementsAre(2.74)); + EXPECT_THAT(param.inp.td_trape_freq, testing::ElementsAre(1.60)); + EXPECT_THAT(param.inp.td_trape_phase, testing::ElementsAre(0.0)); + EXPECT_THAT(param.inp.td_trape_t1, testing::ElementsAre(1875.0)); + EXPECT_THAT(param.inp.td_trape_t2, testing::ElementsAre(5625.0)); + EXPECT_THAT(param.inp.td_trape_t3, testing::ElementsAre(7500.0)); + EXPECT_THAT(param.inp.td_trigo_freq1, testing::ElementsAre(1.164656)); + EXPECT_THAT(param.inp.td_trigo_freq2, testing::ElementsAre(0.029116)); + EXPECT_THAT(param.inp.td_trigo_phase1, testing::ElementsAre(0.0)); + EXPECT_THAT(param.inp.td_trigo_phase2, testing::ElementsAre(0.0)); + EXPECT_THAT(param.inp.td_trigo_amp, testing::ElementsAre(2.74)); + EXPECT_THAT(param.inp.td_heavi_t0, testing::ElementsAre(100.0)); + EXPECT_THAT(param.inp.td_heavi_amp, testing::ElementsAre(1.0)); + EXPECT_THAT(param.inp.td_supsine_tstart, testing::ElementsAre(1)); + EXPECT_THAT(param.inp.td_supsine_tend, testing::ElementsAre(1000)); EXPECT_EQ(param.inp.out_dipole, 0); EXPECT_EQ(param.inp.out_efield, 0); @@ -456,6 +459,50 @@ TEST_F(InputParaTest, ParaRead) EXPECT_DOUBLE_EQ(param.inp.rdmft_power_alpha, 0.656); } +TEST_F(InputParaTest, TypedTDFieldLists) +{ + ModuleIO::ReadInput readinput(GlobalV::MY_RANK); + readinput.check_ntype_flag = false; + Parameter param; + readinput.read_parameters(param, "./support/INPUT.td_field_mixed"); + + EXPECT_THAT(param.inp.td_ttype, testing::ElementsAre(4, 0, 1, 2, 3, 0, 4, 1, 2, 3)); + EXPECT_THAT(param.inp.td_vext_dire, testing::ElementsAre(1, 2, 3, 1, 2, 3, 1, 2, 3, 1)); + EXPECT_THAT(param.inp.td_gauss_freq, testing::ElementsAre(0.7, -1.1)); + EXPECT_THAT(param.inp.td_gauss_amp, testing::ElementsAre(0.04, -0.03)); + EXPECT_THAT(param.inp.td_trape_t1, testing::ElementsAre(1.0, 2.0)); + EXPECT_THAT(param.inp.td_trape_amp, testing::ElementsAre(0.025, -0.02)); + EXPECT_THAT(param.inp.td_trigo_freq1, testing::ElementsAre(0.9, -1.4)); + EXPECT_THAT(param.inp.td_trigo_amp, testing::ElementsAre(0.015, -0.018)); + EXPECT_THAT(param.inp.td_heavi_t0, testing::ElementsAre(3.0, 5.0)); + EXPECT_THAT(param.inp.td_heavi_amp, testing::ElementsAre(0.01, -0.012)); + EXPECT_THAT(param.inp.td_supsine_freq, testing::ElementsAre(0.7, -1.0)); + EXPECT_THAT(param.inp.td_supsine_amp, testing::ElementsAre(0.05, -0.04)); + EXPECT_THAT(param.inp.td_supsine_tstart, testing::ElementsAre(0, 2)); + EXPECT_THAT(param.inp.td_supsine_tend, testing::ElementsAre(5, 6)); +} + +TEST_F(InputParaTest, TDFieldStructureErrors) +{ + struct InvalidCase + { + const char* name; + std::function mutate; + }; + + const std::vector cases = {{"missing", [](Input_para& input) { input.td_gauss_amp.clear(); }}, + {"extra", [](Input_para& input) { input.td_gauss_freq.push_back(1.0); }}, + {"field count", [](Input_para& input) { input.td_ttype.push_back(0); }}}; + + for (const InvalidCase& invalid_case: cases) + { + Input_para input; + input.td_vext = true; + invalid_case.mutate(input); + EXPECT_EXIT(ModuleIO::check_td_efield_parameters(input), testing::ExitedWithCode(1), "") << invalid_case.name; + } +} + TEST_F(InputParaTest, DiagoProc) { int rank = 0; diff --git a/source/source_io/test/support/INPUT.td_field_mixed b/source/source_io/test/support/INPUT.td_field_mixed new file mode 100644 index 0000000000..b60e6a7dc9 --- /dev/null +++ b/source/source_io/test/support/INPUT.td_field_mixed @@ -0,0 +1,37 @@ +INPUT_PARAMETERS + +td_vext 1 +td_vext_dire 1 2 3 1 2 3 1 2 3 1 +td_stype 2 +td_ttype 4 0 1 2 3 0 4 1 2 3 +td_tstart 0 +td_tend 6 + +td_gauss_freq 0.7 -1.1 +td_gauss_phase 0.1 -0.4 +td_gauss_sigma 0.12 -0.20 +td_gauss_t0 2 4 +td_gauss_amp 0.04 -0.03 + +td_trape_freq 0.8 -1.3 +td_trape_phase 0.2 -0.6 +td_trape_t1 1 2 +td_trape_t2 3 4 +td_trape_t3 5 6 +td_trape_amp 0.025 -0.02 + +td_trigo_freq1 0.9 -1.4 +td_trigo_freq2 1.8 -1.1 +td_trigo_phase1 0.3 -0.2 +td_trigo_phase2 0.1 0.7 +td_trigo_amp 0.015 -0.018 + +td_heavi_t0 3 5 +td_heavi_amp 0.01 -0.012 + +td_supsine_amp 0.05 -0.04 +td_supsine_freq 0.7 -1.0 +td_supsine_phase 0.2 -0.5 +td_supsine_sigma 0.75 1.20 +td_supsine_tstart default 2 +td_supsine_tend 5 default diff --git a/source/source_io/test/td_efield_io_test.cpp b/source/source_io/test/td_efield_io_test.cpp new file mode 100644 index 0000000000..c0fb6be358 --- /dev/null +++ b/source/source_io/test/td_efield_io_test.cpp @@ -0,0 +1,153 @@ +#include "source_io/module_efield/td_efield_io.h" + +#include "source_base/constants.h" +#include "source_estate/module_pot/td_field_manager.h" +#include "source_io/module_parameter/input_parameter.h" + +#include "gtest/gtest.h" +#include +#include +#include +#include +#include +#include + +namespace +{ + +class TDEFieldIOTest : public testing::Test +{ + protected: + void SetUp() override + { + clean_output_files(); + } + + void TearDown() override + { + clean_output_files(); + } + + std::string output_path(const int field_index) const + { + return output_prefix_ + "efield_" + std::to_string(field_index) + ".txt"; + } + + void clean_output_files() const + { + for (int field_index = 0; field_index <= 3; ++field_index) + { + std::remove(output_path(field_index).c_str()); + } + std::remove(blocking_path_.c_str()); + } + + std::shared_ptr create_manager(const std::vector& amplitudes) const + { + Input_para input; + input.td_vext = true; + input.td_stype = 0; + input.td_tstart = 0; + input.td_tend = 10; + input.td_dt = ModuleBase::AU_to_FS; + input.td_ttype.clear(); + input.td_vext_dire.clear(); + input.td_heavi_t0.clear(); + input.td_heavi_amp.clear(); + for (std::size_t field_index = 0; field_index < amplitudes.size(); ++field_index) + { + input.td_ttype.push_back(3); + input.td_vext_dire.push_back(static_cast(field_index % 3) + 1); + input.td_heavi_t0.push_back(10.0); + input.td_heavi_amp.push_back(amplitudes[field_index]); + } + return elecstate::create_td_field_manager(input); + } + + std::vector> read_samples(const int field_index) const + { + std::ifstream input(output_path(field_index).c_str()); + std::vector> samples; + double time = 0.0; + double field = 0.0; + while (input >> time >> field) + { + samples.push_back(std::make_pair(time, field)); + } + return samples; + } + + const std::string output_prefix_ = "td_efield_io_test_"; + const std::string blocking_path_ = "td_efield_io_test_blocking_path"; +}; + +TEST_F(TDEFieldIOTest, FreshCalculationTruncatesAndUsesOneBasedFiles) +{ + { + std::ofstream output_1(output_path(1).c_str()); + std::ofstream output_2(output_path(2).c_str()); + output_1 << "old data\n"; + output_2 << "old data\n"; + } + + std::shared_ptr manager = create_manager({2.0, 3.0}); + ModuleIO::prepare_td_field_output(output_prefix_, manager->fields().size(), false); + EXPECT_TRUE(read_samples(1).empty()); + EXPECT_TRUE(read_samples(2).empty()); + + manager->advance_length_gauge(); + ModuleIO::write_td_field_values(*manager, output_prefix_); + manager->advance_length_gauge(); + ModuleIO::write_td_field_values(*manager, output_prefix_); + + const std::vector> samples_1 = read_samples(1); + const std::vector> samples_2 = read_samples(2); + ASSERT_EQ(samples_1.size(), 2U); + ASSERT_EQ(samples_2.size(), 2U); + EXPECT_DOUBLE_EQ(samples_1[0].first, 0.0); + EXPECT_DOUBLE_EQ(samples_2[0].first, 0.0); + EXPECT_NEAR(samples_1[0].second, 2.0, 1.0e-12); + EXPECT_NEAR(samples_2[0].second, 3.0, 1.0e-12); + EXPECT_NEAR(samples_1[1].first, ModuleBase::AU_to_FS, 1.0e-7); + EXPECT_NEAR(samples_2[1].first, ModuleBase::AU_to_FS, 1.0e-7); + + std::ifstream zero_based_file(output_path(0).c_str()); + EXPECT_FALSE(zero_based_file.good()); +} + +TEST_F(TDEFieldIOTest, RestartPreservesExistingSamples) +{ + { + std::ofstream output(output_path(1).c_str()); + output << "7 8\n"; + } + + std::shared_ptr manager = create_manager({4.0}); + ModuleIO::prepare_td_field_output(output_prefix_, manager->fields().size(), true); + manager->advance_length_gauge(); + ModuleIO::write_td_field_values(*manager, output_prefix_); + + const std::vector> samples = read_samples(1); + ASSERT_EQ(samples.size(), 2U); + EXPECT_DOUBLE_EQ(samples[0].first, 7.0); + EXPECT_DOUBLE_EQ(samples[0].second, 8.0); + EXPECT_DOUBLE_EQ(samples[1].first, 0.0); + EXPECT_NEAR(samples[1].second, 4.0, 1.0e-12); +} + +TEST_F(TDEFieldIOTest, ReportsOutputOpenFailures) +{ + { + std::ofstream blocking_file(blocking_path_.c_str()); + blocking_file << "not a directory\n"; + } + const std::string invalid_directory = blocking_path_ + "/"; + std::shared_ptr manager = create_manager({4.0}); + + EXPECT_EXIT(ModuleIO::prepare_td_field_output(invalid_directory, manager->fields().size(), false), testing::ExitedWithCode(1), ""); + + manager->advance_length_gauge(); + EXPECT_EXIT(ModuleIO::write_td_field_values(*manager, invalid_directory), testing::ExitedWithCode(1), ""); +} + +} // namespace diff --git a/source/source_io/test/td_vector_pot_io_test.cpp b/source/source_io/test/td_vector_pot_io_test.cpp new file mode 100644 index 0000000000..1deb83ca13 --- /dev/null +++ b/source/source_io/test/td_vector_pot_io_test.cpp @@ -0,0 +1,148 @@ +#include "source_io/module_efield/td_vector_pot_io.h" + +#include "gtest/gtest.h" +#include +#include +#include +#include + +namespace +{ + +class TDVectorPotIOTest : public testing::Test +{ + protected: + void SetUp() override + { + clean_output_file(); + } + + void TearDown() override + { + clean_output_file(); + } + + void clean_output_file() const + { + std::remove(output_path_.c_str()); + } + + std::vector read_lines() const + { + std::ifstream input(output_path_.c_str()); + std::vector lines; + std::string line; + while (std::getline(input, line)) + { + lines.push_back(line); + } + return lines; + } + + const std::string output_prefix_ = "td_vector_pot_io_test_"; + const std::string output_path_ = output_prefix_ + "vector_pot.txt"; +}; + +TEST_F(TDVectorPotIOTest, FreshCalculationTruncatesAndWritesHeader) +{ + { + std::ofstream output(output_path_.c_str()); + output << "old data\n"; + } + + ModuleIO::prepare_td_vector_pot_output(output_prefix_, false); + ModuleIO::write_td_vector_pot(output_prefix_, 0, ModuleBase::Vector3(1.0, 2.0, 3.0)); + ModuleIO::write_td_vector_pot(output_prefix_, 1, ModuleBase::Vector3(4.0, 5.0, 6.0)); + + const std::vector lines = read_lines(); + ASSERT_EQ(lines.size(), 3U); + EXPECT_EQ(lines[0].find("#istep"), 0U); + EXPECT_EQ(lines[1].find("1"), 0U); + EXPECT_EQ(lines[2].find("2"), 0U); + + const std::vector> vector_potentials = ModuleIO::read_td_vector_pot(output_prefix_); + ASSERT_EQ(vector_potentials.size(), 2U); + EXPECT_DOUBLE_EQ(vector_potentials[0][0], 1.0); + EXPECT_DOUBLE_EQ(vector_potentials[0][1], 2.0); + EXPECT_DOUBLE_EQ(vector_potentials[0][2], 3.0); + EXPECT_DOUBLE_EQ(vector_potentials[1][0], 4.0); + EXPECT_DOUBLE_EQ(vector_potentials[1][1], 5.0); + EXPECT_DOUBLE_EQ(vector_potentials[1][2], 6.0); +} + +TEST_F(TDVectorPotIOTest, RestartPreservesExistingSamples) +{ + { + std::ofstream output(output_path_.c_str()); + output << "#istep A_x A_y A_z\n"; + output << "7 1.0 2.0 3.0\n"; + } + + ModuleIO::prepare_td_vector_pot_output(output_prefix_, true); + ModuleIO::write_td_vector_pot(output_prefix_, 7, ModuleBase::Vector3(4.0, 5.0, 6.0)); + + const std::vector lines = read_lines(); + ASSERT_EQ(lines.size(), 3U); + EXPECT_EQ(lines[0], "#istep A_x A_y A_z"); + EXPECT_EQ(lines[1], "7 1.0 2.0 3.0"); + EXPECT_EQ(lines[2].find("8"), 0U); +} + +TEST_F(TDVectorPotIOTest, RestartCreatesHeaderForMissingOrEmptyFile) +{ + ModuleIO::prepare_td_vector_pot_output(output_prefix_, true); + std::vector lines = read_lines(); + ASSERT_EQ(lines.size(), 1U); + EXPECT_EQ(lines[0].find("#istep"), 0U); + + { + std::ofstream output(output_path_.c_str(), std::ofstream::out); + } + ModuleIO::prepare_td_vector_pot_output(output_prefix_, true); + lines = read_lines(); + ASSERT_EQ(lines.size(), 1U); + EXPECT_EQ(lines[0].find("#istep"), 0U); +} + +TEST_F(TDVectorPotIOTest, ReaderSkipsBlankAndCommentLinesAndIgnoresLabels) +{ + { + std::ofstream output(output_path_.c_str()); + output << " # comment\n"; + output << "\n"; + output << "19 1.5 -2.5 3.5\n"; + output << "42 4.5 5.5 -6.5\n"; + } + + const std::vector> vector_potentials = ModuleIO::read_td_vector_pot(output_prefix_); + ASSERT_EQ(vector_potentials.size(), 2U); + EXPECT_DOUBLE_EQ(vector_potentials[0][0], 1.5); + EXPECT_DOUBLE_EQ(vector_potentials[0][1], -2.5); + EXPECT_DOUBLE_EQ(vector_potentials[0][2], 3.5); + EXPECT_DOUBLE_EQ(vector_potentials[1][0], 4.5); + EXPECT_DOUBLE_EQ(vector_potentials[1][1], 5.5); + EXPECT_DOUBLE_EQ(vector_potentials[1][2], -6.5); +} + +TEST_F(TDVectorPotIOTest, ReaderRejectsEmptyAndMalformedFiles) +{ + { + std::ofstream output(output_path_.c_str()); + output << "# no samples\n"; + } + EXPECT_EXIT(ModuleIO::read_td_vector_pot(output_prefix_), testing::ExitedWithCode(1), ""); + + { + std::ofstream output(output_path_.c_str(), std::ofstream::out); + output << "1 2.0 invalid 4.0\n"; + } + EXPECT_EXIT(ModuleIO::read_td_vector_pot(output_prefix_), testing::ExitedWithCode(1), ""); + + { + std::ofstream output(output_path_.c_str(), std::ofstream::out); + output << "1 2.0 3.0 4.0 extra\n"; + } + EXPECT_EXIT(ModuleIO::read_td_vector_pot(output_prefix_), testing::ExitedWithCode(1), ""); +} + +} // namespace diff --git a/source/source_io/test_serial/read_input_tool_test.cpp b/source/source_io/test_serial/read_input_tool_test.cpp index f66c5da958..5774dea777 100644 --- a/source/source_io/test_serial/read_input_tool_test.cpp +++ b/source/source_io/test_serial/read_input_tool_test.cpp @@ -1,4 +1,5 @@ #include "../module_parameter/read_input_tool.h" + #include // Test fixture for parse_expression tests @@ -40,16 +41,15 @@ TEST_F(ReadInputTool, parse_expression) } // Test case for expressions with one '*' { - std::vector expressions = {"3", "2*4.2", "1*7"}; - std::vector expected = {3.0, 4.2, 4.2, 7.0}; + std::vector expressions = {"3", "2*4.2", "1*7", "0.18737028625", "2*-0.125"}; + std::vector expected = {3.0, 4.2, 4.2, 7.0, 0.18737028625, -0.125, -0.125}; std::vector result; - parse_expression(expressions, result); EXPECT_EQ(expected.size(), result.size()); for (size_t i = 0; i < expected.size(); i++) { - EXPECT_NEAR(expected[i], result[i], 1e-5); + EXPECT_DOUBLE_EQ(expected[i], result[i]); } } // Test case for expressions with more than one '*' diff --git a/source/source_lcao/module_rt/band_energy.cpp b/source/source_lcao/module_rt/band_energy.cpp index 2c812a090d..142396dbba 100644 --- a/source/source_lcao/module_rt/band_energy.cpp +++ b/source/source_lcao/module_rt/band_energy.cpp @@ -40,57 +40,31 @@ void compute_ekb(const Parallel_Orbitals* pv, std::complex* eij = new std::complex[pv->nloc]; ModuleBase::GlobalFunc::ZEROS(eij, pv->nloc); - ScalapackConnector::gemm('N', - 'N', - nlocal, - nband, - nlocal, - 1.0, - Htmp, - 1, - 1, - pv->desc, - psi_k, - 1, - 1, - pv->desc_wfc, - 0.0, - tmp1, - 1, - 1, - pv->desc_wfc); - - ScalapackConnector::gemm('C', - 'N', - nband, - nband, - nlocal, - 1.0, - psi_k, - 1, - 1, - pv->desc_wfc, - tmp1, - 1, - 1, - pv->desc_wfc, - 0.0, - eij, - 1, - 1, - pv->desc_Eij); - - if (PARAM.inp.td_print_eij > 0.0) + ScalapackConnector:: + gemm('N', 'N', nlocal, nband, nlocal, 1.0, Htmp, 1, 1, pv->desc, psi_k, 1, 1, pv->desc_wfc, 0.0, tmp1, 1, 1, pv->desc_wfc); + + ScalapackConnector:: + gemm('C', 'N', nband, nband, nlocal, 1.0, psi_k, 1, 1, pv->desc_wfc, tmp1, 1, 1, pv->desc_wfc, 0.0, eij, 1, 1, pv->desc_Eij); + + if (PARAM.inp.td_print_eij >= 0.0) { - ofs_running - << "------------------------------------------------------------------------------------------------" - << std::endl; + ofs_running << "------------------------------------------------------------------------------------------------" << std::endl; ofs_running << " Eij:" << std::endl; for (int i = 0; i < pv->nrow_bands; i++) { + const int global_i = globalIndex(i, pv->nb, pv->dim0, pv->get_coord_row()); + if (global_i >= nband) + { + continue; + } const int in = i * pv->ncol; for (int j = 0; j < pv->ncol_bands; j++) { + const int global_j = globalIndex(j, pv->nb, pv->dim1, pv->get_coord_col()); + if (global_j >= nband) + { + continue; + } double aa = eij[in + j].real(); double bb = eij[in + j].imag(); if (std::abs(aa) < PARAM.inp.td_print_eij) @@ -105,18 +79,15 @@ void compute_ekb(const Parallel_Orbitals* pv, { std::streamsize original_precision = ofs_running.precision(); ofs_running << std::fixed << std::setprecision(8); - ofs_running << "i = " << std::setw(2) << i << ", j = " << std::setw(2) << j - << ", Eij = " << std::setw(12) << aa << " + " << std::setw(12) << bb << " i" - << std::endl; + ofs_running << "i = " << std::setw(2) << global_i + 1 << ", j = " << std::setw(2) << global_j + 1 + << ", Eij = " << std::setw(12) << aa << " + " << std::setw(12) << bb << " i" << std::endl; ofs_running.unsetf(std::ios_base::fixed); ofs_running.precision(original_precision); } } } ofs_running << std::endl; - ofs_running - << "------------------------------------------------------------------------------------------------" - << std::endl; + ofs_running << "------------------------------------------------------------------------------------------------" << std::endl; } int info = 0; @@ -407,14 +378,10 @@ void compute_ekb_tensor_lapack(const Parallel_Orbitals* pv, using ct_Device = typename ct::PsiToContainer::type; // Create Tensor objects for temporary data - ct::Tensor tmp1(ct::DataType::DT_COMPLEX_DOUBLE, - ct_device_type, - ct::TensorShape({nlocal * nband})); // tmp1 shape: nlocal * nband + ct::Tensor tmp1(ct::DataType::DT_COMPLEX_DOUBLE, ct_device_type, ct::TensorShape({nlocal * nband})); // tmp1 shape: nlocal * nband tmp1.zero(); - ct::Tensor eij(ct::DataType::DT_COMPLEX_DOUBLE, - ct_device_type, - ct::TensorShape({nlocal * nlocal})); // eij shape: nlocal * nlocal + ct::Tensor eij(ct::DataType::DT_COMPLEX_DOUBLE, ct_device_type, ct::TensorShape({nlocal * nlocal})); // eij shape: nlocal * nlocal // Why not use nband * nband ????? eij.zero(); @@ -455,9 +422,7 @@ void compute_ekb_tensor_lapack(const Parallel_Orbitals* pv, { ct::Tensor eij_cpu = eij.to_device(); - ofs_running - << "------------------------------------------------------------------------------------------------" - << std::endl; + ofs_running << "------------------------------------------------------------------------------------------------" << std::endl; ofs_running << " Eij:" << std::endl; for (int i = 0; i < nband; i++) { @@ -478,18 +443,15 @@ void compute_ekb_tensor_lapack(const Parallel_Orbitals* pv, { std::streamsize original_precision = ofs_running.precision(); ofs_running << std::fixed << std::setprecision(8); - ofs_running << "i = " << std::setw(2) << i << ", j = " << std::setw(2) << j - << ", Eij = " << std::setw(12) << aa << " + " << std::setw(12) << bb << " i" - << std::endl; + ofs_running << "i = " << std::setw(2) << i + 1 << ", j = " << std::setw(2) << j + 1 << ", Eij = " << std::setw(12) << aa + << " + " << std::setw(12) << bb << " i" << std::endl; ofs_running.unsetf(std::ios_base::fixed); ofs_running.precision(original_precision); } } } ofs_running << std::endl; - ofs_running - << "------------------------------------------------------------------------------------------------" - << std::endl; + ofs_running << "------------------------------------------------------------------------------------------------" << std::endl; } // Extract diagonal elements of eij into ekb diff --git a/source/source_lcao/module_rt/force_rt_overlap.cpp b/source/source_lcao/module_rt/force_rt_overlap.cpp index a224e5d24b..4744467643 100644 --- a/source/source_lcao/module_rt/force_rt_overlap.cpp +++ b/source/source_lcao/module_rt/force_rt_overlap.cpp @@ -1,10 +1,12 @@ #include "force_rt_overlap.h" -#include "td_info.h" -#include "td_folding.h" + #include "source_base/module_external/lapack_connector.h" #include "source_base/module_external/scalapack_connector.h" -#include "source_estate/module_pot/H_TDDFT_pw.h" #include "source_base/parallel_reduce.h" +#include "source_estate/module_pot/H_TDDFT_pw.h" +#include "source_io/module_parameter/parameter.h" +#include "td_folding.h" +#include "td_info.h" template <> void cal_foverlap_rt(ModuleBase::matrix& foverlap, const LCAO_domain::Setup_DM>& dmat, @@ -18,7 +20,7 @@ void cal_foverlap_rt(ModuleBase::matrix& foverlap, assert(nlocal >= 0); TD_info* td_info = TD_info::td_vel_op; - //get dS/dR_{x,y,z} + // get dS/dR_{x,y,z} std::vector*> dsxr = td_info->get_grad_overlap(); // allocate matrix const long nloc = pv.nloc; @@ -32,7 +34,7 @@ void cal_foverlap_rt(ModuleBase::matrix& foverlap, std::complex* tmp3 = new std::complex[nloc]; std::complex* Hybridtmp = new std::complex[nloc]; std::vector*> tmp_out = {nullptr, nullptr, nullptr}; - for(int dir = 0; dir<3; dir++) + for (int dir = 0; dir < 3; dir++) { tmp_out[dir] = new std::complex[nloc]; ModuleBase::GlobalFunc::ZEROS(tmp_out[dir], nloc); @@ -48,12 +50,12 @@ void cal_foverlap_rt(ModuleBase::matrix& foverlap, ModuleBase::GlobalFunc::ZEROS(tmp1, nloc); ModuleBase::GlobalFunc::ZEROS(tmp2, nloc); ModuleBase::GlobalFunc::ZEROS(Hybridtmp, nloc); - + const int inc = 1; hamilt::MatrixBlock> h_mat; hamilt::MatrixBlock> s_mat; - //get Hk Sk + // get Hk Sk p_hamilt->matrix(h_mat, s_mat); BlasConnector::copy(nloc, h_mat.p, inc, Htmp, inc); BlasConnector::copy(nloc, s_mat.p, inc, Sinv, inc); @@ -77,39 +79,18 @@ void cal_foverlap_rt(ModuleBase::matrix& foverlap, // if liwork = -1, then the size of iwork is (at least) of length 1. std::vector iwork(1, 0); - ScalapackConnector::getri(nlocal, - Sinv, - one_int, - one_int, - pv.desc, - ipiv.data(), - work.data(), - &lwork, - iwork.data(), - &liwork, - &info); + ScalapackConnector::getri(nlocal, Sinv, one_int, one_int, pv.desc, ipiv.data(), work.data(), &lwork, iwork.data(), &liwork, &info); lwork = work[0].real(); work.resize(lwork, 0); liwork = iwork[0]; iwork.resize(liwork, 0); - ScalapackConnector::getri(nlocal, - Sinv, - one_int, - one_int, - pv.desc, - ipiv.data(), - work.data(), - &lwork, - iwork.data(), - &liwork, - &info); + ScalapackConnector::getri(nlocal, Sinv, one_int, one_int, pv.desc, ipiv.data(), work.data(), &lwork, iwork.data(), &liwork, &info); const char N_char = 'N'; const char T_char = 'T'; const char C_char = 'C'; - ScalapackConnector::gemm(T_char, C_char, @@ -150,13 +131,21 @@ void cal_foverlap_rt(ModuleBase::matrix& foverlap, one_int, one_int, pv.desc); - for(int dir = 0; dir<3; dir++) + for (int dir = 0; dir < 3; dir++) { ModuleBase::GlobalFunc::ZEROS(dsxk, nloc); ModuleBase::GlobalFunc::ZEROS(pdsxk, nloc); ModuleBase::GlobalFunc::ZEROS(tmp3, nloc); module_rt::folding_HR_td(*dsxr[dir], dsxk, kv.kvec_d[ik], TD_info::cart_At, TD_info::td_vel_op->get_phase_hybrid(), nrow, 1); - module_rt::folding_partial_dot(*dsxr[dir], pdsxk, kv.kvec_d[ik], nrow, 1, &ucell, TD_info::td_vel_op->get_phase_hybrid(), TD_info::cart_At, elecstate::H_TDDFT_pw::Et); + module_rt::folding_partial_dot(*dsxr[dir], + pdsxk, + kv.kvec_d[ik], + nrow, + 1, + &ucell, + TD_info::td_vel_op->get_phase_hybrid(), + TD_info::cart_At, + elecstate::H_TDDFT_pw::Et); ScalapackConnector::gemm(N_char, N_char, nlocal, @@ -196,7 +185,7 @@ void cal_foverlap_rt(ModuleBase::matrix& foverlap, one_int, one_int, pv.desc); - + ScalapackConnector::geadd(N_char, nlocal, nlocal, @@ -239,34 +228,33 @@ void cal_foverlap_rt(ModuleBase::matrix& foverlap, // } auto row_indexes = pv.get_indexes_row(); auto col_indexes = pv.get_indexes_col(); - #pragma omp parallel for - for(int iat = 0; iat < ucell.nat; iat++) +#pragma omp parallel for + for (int iat = 0; iat < ucell.nat; iat++) { - + double* force_tmp1 = &foverlap(iat, 0); int row0 = pv.atom_begin_row[iat]; int col0 = pv.atom_begin_col[iat]; const int row_size = pv.get_row_size(); std::vector*> p_diag = {tmp_out[0], tmp_out[1], tmp_out[2]}; - for(int mu = 0; mu < pv.get_nrow_atom(iat); ++mu) + for (int mu = 0; mu < pv.get_nrow_atom(iat); ++mu) { - for(int nu = 0; nu < pv.get_ncol_atom(iat); ++nu) + for (int nu = 0; nu < pv.get_ncol_atom(iat); ++nu) { - if(row_indexes[row0+mu]==col_indexes[col0+nu]) + if (row_indexes[row0 + mu] == col_indexes[col0 + nu]) { - const long index = (row0 + mu) + (col0 + nu)*row_size; - for(int dir = 0; dir<3; dir++) + const long index = (row0 + mu) + (col0 + nu) * row_size; + for (int dir = 0; dir < 3; dir++) { // p_diag[dir] = tmp_out[dir] + (row0 + mu) + (col0 + nu) * row_size; force_tmp1[dir] += (tmp_out[dir][index]).real(); } } } - } } Parallel_Reduce::reduce_all(foverlap.c, foverlap.nr * foverlap.nc); - for(int dir = 0; dir<3; dir++) + for (int dir = 0; dir < 3; dir++) { delete[] tmp_out[dir]; } @@ -282,4 +270,4 @@ void cal_foverlap_rt(ModuleBase::matrix& foverlap, UnitCell& ucell) { return; -} \ No newline at end of file +} diff --git a/source/source_lcao/module_rt/td_info.cpp b/source/source_lcao/module_rt/td_info.cpp index 916dc3b081..555adabb01 100644 --- a/source/source_lcao/module_rt/td_info.cpp +++ b/source/source_lcao/module_rt/td_info.cpp @@ -1,7 +1,9 @@ #include "td_info.h" +#include "source_base/global_variable.h" #include "source_base/libm/libm.h" #include "source_estate/module_pot/H_TDDFT_pw.h" +#include "source_io/module_efield/td_vector_pot_io.h" #include "source_io/module_parameter/parameter.h" bool TD_info::out_mat_R = false; @@ -19,14 +21,15 @@ int TD_info::max_istep = -1; ModuleBase::Vector3 TD_info::cart_At; std::vector> TD_info::At_from_file; -TD_info::TD_info(const UnitCell* ucell_in,const Parallel_Orbitals& pv, const LCAO_Orbitals& orb) +TD_info::TD_info(const UnitCell* ucell_in, const Parallel_Orbitals& pv, const LCAO_Orbitals& orb) { if (init_vecpot_file && istep == -1) { - this->read_cart_At(); + At_from_file = ModuleIO::read_td_vector_pot(""); + max_istep = At_from_file.size() - 1; } - //read in restart step - if(PARAM.inp.mdp.md_restart) + // read in restart step + if (PARAM.inp.mdp.md_restart) { std::stringstream ssc; ssc << PARAM.globalv.global_readin_dir << "Restart_td.txt"; @@ -36,10 +39,10 @@ TD_info::TD_info(const UnitCell* ucell_in,const Parallel_Orbitals& pv, const LCA ModuleBase::WARNING_QUIT("TD_info::TD_info", "No Restart_td.txt!"); } file >> estep_shift; - //std::cout<<"estep_shift"<istep += estep_shift; - if(out_current==2||elecstate::H_TDDFT_pw::stype == 2) + if (out_current == 2 || elecstate::H_TDDFT_pw::stype == 2) { r_calculator.init(*ucell_in, pv, orb); } @@ -47,7 +50,7 @@ TD_info::TD_info(const UnitCell* ucell_in,const Parallel_Orbitals& pv, const LCA } TD_info::~TD_info() { - if(elecstate::H_TDDFT_pw::stype == 1) + if (elecstate::H_TDDFT_pw::stype == 1) { this->destroy_HS_R_td_sparse(); } @@ -67,38 +70,6 @@ TD_info::~TD_info() } } -void TD_info::output_cart_At(const std::string& out_dir) -{ - if (GlobalV::MY_RANK == 0) - { - std::string out_file; - // generate the output file name - out_file = out_dir + "At.dat"; - std::ofstream ofs; - // output title - if (istep == estep_shift) - { - ofs.open(out_file.c_str(), std::ofstream::out); - ofs << std::left << std::setw(8) << "#istep" << std::setw(15) << "A_x" << std::setw(15) << "A_y" - << std::setw(15) << "A_z" << std::endl; - } - else - { - ofs.open(out_file.c_str(), std::ofstream::app); - } - // output the vector potential - ofs << std::left << std::setw(8) << istep; - // divide by 2.0 to get the atomic unit - for (int i = 0; i < 3; i++) - { - ofs << std::scientific << std::setprecision(4) << std::setw(15) << cart_At[i]; - } - ofs << std::endl; - ofs.close(); - } - return; -} - void TD_info::cal_cart_At(const ModuleBase::Vector3& At) { istep++; @@ -112,14 +83,14 @@ void TD_info::cal_cart_At(const ModuleBase::Vector3& At) cart_At = At / 2.0; } // output the vector potential if needed - if (out_vecpot == true) + if (out_vecpot && GlobalV::MY_RANK == 0) { - this->output_cart_At(PARAM.globalv.global_out_dir); + ModuleIO::write_td_vector_pot(PARAM.globalv.global_out_dir, istep, cart_At); } // update hybrid gauge phase - if(elecstate::H_TDDFT_pw::stype == 2) + if (elecstate::H_TDDFT_pw::stype == 2) { - for(const auto& phase_pair : phase_hybrid) + for (const auto& phase_pair: phase_hybrid) { const ModuleBase::Vector3& r_index = phase_pair.first; ModuleBase::Vector3 dR = double(r_index.x) * a1 + double(r_index.y) * a2 + double(r_index.z) * a3; @@ -131,67 +102,17 @@ void TD_info::cal_cart_At(const ModuleBase::Vector3& At) } } -void TD_info::read_cart_At(void) -{ - std::string in_file; - // generate the input file name - in_file = "At.dat"; - std::ifstream ifs(in_file.c_str()); - // check if the file is exist - if (!ifs) - { - ModuleBase::WARNING_QUIT("TD_info::read_cart_At", "Cannot open Vector potential file!"); - } - std::string line; - std::vector str_vec; - // use tmp to skip the istep number - int tmp = 0; - while (std::getline(ifs, line)) - { - // A tmporary vector3 to store the data of this line - ModuleBase::Vector3 At; - if (line[0] == '#') - { - continue; - } - std::istringstream iss(line); - // skip the istep number - if (!(iss >> tmp)) - { - ModuleBase::WARNING_QUIT("TD_info::read_cart_At", "Error reading istep!"); - } - // read the vector potential - double component = 0; - // Read three components - for (int i = 0; i < 3; i++) - { - if (!(iss >> component)) - { - ModuleBase::WARNING_QUIT("TD_info::read_cart_At", - "Error reading component " + std::to_string(i + 1) + " for istep " - + std::to_string(tmp) + "!"); - } - At[i] = component; - } - // add the tmporary vector3 to the vector potential vector - At_from_file.push_back(At); - } - // set the max_istep - max_istep = At_from_file.size() - 1; - ifs.close(); - - return; -} -void TD_info::out_restart_info(const int nstep, - const ModuleBase::Vector3& At_current, - const ModuleBase::Vector3& At_laststep) +void TD_info::out_restart_info(const int nstep, + const ModuleBase::Vector3& At_current, + const ModuleBase::Vector3& At_laststep) { if (GlobalV::MY_RANK == 0) { // open file std::string outdir = PARAM.globalv.global_out_dir + "Restart_td.txt"; std::ofstream outFile(outdir); - if (!outFile) { + if (!outFile) + { ModuleBase::WARNING_QUIT("out_restart_info", "no Restart_td.txt!"); } // write data @@ -200,7 +121,6 @@ void TD_info::out_restart_info(const int nstep, outFile << At_laststep[0] << " " << At_laststep[1] << " " << At_laststep[2] << std::endl; outFile.close(); } - return; } @@ -214,11 +134,11 @@ void TD_info::initialize_phase_hybrid(const UnitCell& ucell, const hamilt::HCont for (int i = 0; i < hR->size_atom_pairs(); ++i) { hamilt::AtomPair& tmp = hR->get_atom_pair(i); - for(int ir = 0;ir < tmp.get_R_size(); ++ir ) + for (int ir = 0; ir < tmp.get_R_size(); ++ir) { const ModuleBase::Vector3 r_index = tmp.get_R_index(ir); - if(phase_hybrid.count(r_index))continue; - + if (phase_hybrid.count(r_index)) + continue; ModuleBase::Vector3 dR = double(r_index.x) * a1 + double(r_index.y) * a2 + double(r_index.z) * a3; const double arg_td = cart_At * dR * lat0; @@ -228,8 +148,7 @@ void TD_info::initialize_phase_hybrid(const UnitCell& ucell, const hamilt::HCont } } } -void TD_info::initialize_current_term(const hamilt::HContainer>* HR, - const Parallel_Orbitals* paraV) +void TD_info::initialize_current_term(const hamilt::HContainer>* HR, const Parallel_Orbitals* paraV) { ModuleBase::TITLE("TD_info", "initialize_current_term"); ModuleBase::timer::start("TD_info", "initialize_current_term"); @@ -266,10 +185,8 @@ void TD_info::initialize_current_term(const hamilt::HContainer, std::map>>> - empty_HR_sparse_td_vel_up; - std::map, std::map>>> - empty_HR_sparse_td_vel_down; + std::map, std::map>>> empty_HR_sparse_td_vel_up; + std::map, std::map>>> empty_HR_sparse_td_vel_down; HR_sparse_td_vel[0].swap(empty_HR_sparse_td_vel_up); HR_sparse_td_vel[1].swap(empty_HR_sparse_td_vel_down); } @@ -282,7 +199,7 @@ void TD_info::calculate_grad_overlap(const Parallel_Orbitals& paraV, { ModuleBase::TITLE("TD_info", "calculate_grad_overlap"); ModuleBase::timer::start("TD_info", "calculate_grad_overlap"); - for (int dir=0;dir<3;dir++) + for (int dir = 0; dir < 3; dir++) { if (this->grad_overlap[dir] != nullptr) { @@ -293,8 +210,8 @@ void TD_info::calculate_grad_overlap(const Parallel_Orbitals& paraV, for (int iat1 = 0; iat1 < ucell.nat; iat1++) { auto tau1 = ucell.get_tau(iat1); - int T1=0; - int I1=0; + int T1 = 0; + int I1 = 0; ucell.iat2iait(iat1, &I1, &T1); AdjacentAtomInfo adjs; GridD.Find_atom(ucell, tau1, T1, I1, &adjs); @@ -312,20 +229,19 @@ void TD_info::calculate_grad_overlap(const Parallel_Orbitals& paraV, // Note: the distance of atoms should less than the cutoff radius, // When equal, the theoretical value of matrix element is zero, // but the calculated value is not zero due to the numerical error, which would lead to result changes. - if (ucell.cal_dtau(iat1, iat2, R_index).norm() * ucell.lat0 - >= orb_cutoff[T1] + orb_cutoff[T2]) + if (ucell.cal_dtau(iat1, iat2, R_index).norm() * ucell.lat0 >= orb_cutoff[T1] + orb_cutoff[T2]) { continue; } hamilt::AtomPair tmp(iat1, iat2, R_index, ¶V); - for (int dir=0;dir<3;dir++) + for (int dir = 0; dir < 3; dir++) { this->grad_overlap[dir]->insert_pair(tmp); } } } // allocate the memory of BaseMatrix in grad_overlap, and set the new values to zero - for (int dir=0;dir<3;dir++) + for (int dir = 0; dir < 3; dir++) { this->grad_overlap[dir]->allocate(nullptr, true); } @@ -352,11 +268,11 @@ void TD_info::calculate_grad_overlap(const Parallel_Orbitals& paraV, // --------------------------------------------- // get info of orbitals of atom1 and atom2 from ucell // --------------------------------------------- - int T1=0; - int I1=0; + int T1 = 0; + int I1 = 0; ucell.iat2iait(iat1, &I1, &T1); - int T2=0; - int I2=0; + int T2 = 0; + int I2 = 0; ucell.iat2iait(iat2, &I2, &T2); Atom& atom1 = ucell.atoms[T1]; Atom& atom2 = ucell.atoms[T2]; @@ -413,13 +329,10 @@ void TD_info::calculate_grad_overlap(const Parallel_Orbitals& paraV, { p_data[dir] += (npol - 1) * col_indexes.size(); } - } } } ModuleBase::timer::end("TD_info", "calculate_grad_overlap"); } -template -void TD_info::initialize_phase_hybrid>(const UnitCell& ucell, const hamilt::HContainer>* hR); -template -void TD_info::initialize_phase_hybrid(const UnitCell& ucell, const hamilt::HContainer* hR); \ No newline at end of file +template void TD_info::initialize_phase_hybrid>(const UnitCell& ucell, const hamilt::HContainer>* hR); +template void TD_info::initialize_phase_hybrid(const UnitCell& ucell, const hamilt::HContainer* hR); diff --git a/source/source_lcao/module_rt/td_info.h b/source/source_lcao/module_rt/td_info.h index 7aa07e5d78..65175ce868 100644 --- a/source/source_lcao/module_rt/td_info.h +++ b/source/source_lcao/module_rt/td_info.h @@ -102,12 +102,6 @@ class TD_info /// @brief store time-dependent phase for hybrid gauge std::map, std::complex> phase_hybrid; - /// @brief read At from output file - void read_cart_At(); - - /// @brief output cart_At to output file - void output_cart_At(const std::string& out_dir); - /// @brief store isteps now static int istep; diff --git a/source/source_lcao/module_rt/test/band_energy_test.cpp b/source/source_lcao/module_rt/test/band_energy_test.cpp index 01db6f3709..6de2836154 100644 --- a/source/source_lcao/module_rt/test/band_energy_test.cpp +++ b/source/source_lcao/module_rt/test/band_energy_test.cpp @@ -1,11 +1,24 @@ #include "source_lcao/module_rt/band_energy.h" +#include "source_basis/module_ao/parallel_orbitals.h" +#include "source_io/module_parameter/parameter.h" +#include "tddft_test.h" + +#include +#include #include -#include +#include #include +#include -#include "source_basis/module_ao/parallel_orbitals.h" -#include "tddft_test.h" +class TestParameters +{ + public: + static void set_td_print_eij(const double threshold) + { + PARAM.input.td_print_eij = threshold; + } +}; /************************************************ * unit test of functions in band_energy.h @@ -26,7 +39,6 @@ TEST(BandEnergyTest, testBandEnergy) double* ekb; int nband = 3; int nlocal = 4; - bool print_matrix = false; Parallel_Orbitals* pv; pv = new Parallel_Orbitals(); pv->nloc = nlocal * nlocal; @@ -34,6 +46,7 @@ TEST(BandEnergyTest, testBandEnergy) pv->ncol = nlocal; pv->nrow = nlocal; pv->ncol_bands = nband; + pv->nrow_bands = nband; pv->dim0 = 1; pv->dim1 = 1; pv->nb = 1; @@ -47,8 +60,7 @@ TEST(BandEnergyTest, testBandEnergy) // Initialize input matrices int info; int mb = 1, nb = 1, lda = nband, ldc = nlocal; - int irsrc = 0, icsrc = 0, lld = numroc_(&nlocal, &mb, &myprow, &irsrc, &nprow), - lld1 = numroc_(&nband, &mb, &myprow, &irsrc, &nprow); + int irsrc = 0, icsrc = 0, lld = numroc_(&nlocal, &mb, &myprow, &irsrc, &nprow), lld1 = numroc_(&nband, &mb, &myprow, &irsrc, &nprow); descinit_(pv->desc, &nlocal, &nlocal, &mb, &nb, &irsrc, &icsrc, &ictxt, &lld, &info); descinit_(pv->desc_wfc, &nlocal, &nband, &mb, &nb, &irsrc, &icsrc, &ictxt, &lld, &info); descinit_(pv->desc_Eij, &nband, &nband, &mb, &nb, &irsrc, &icsrc, &ictxt, &lld, &info); @@ -85,14 +97,29 @@ TEST(BandEnergyTest, testBandEnergy) psi_k[11] = 1.0; // Call the function with local ofstream - std::ofstream ofs("/dev/null"); + int rank = 0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + const std::string output_path = "band_energy_test_output_" + std::to_string(rank) + ".txt"; + std::ofstream ofs(output_path.c_str()); + TestParameters::set_td_print_eij(0.0); module_rt::compute_ekb(pv, nband, nlocal, Htmp, psi_k, ekb, ofs); + ofs.close(); + TestParameters::set_td_print_eij(-1.0); // Check the results EXPECT_NEAR(ekb[0], 3.0, doublethreshold); EXPECT_NEAR(ekb[1], 8.0, doublethreshold); EXPECT_NEAR(ekb[2], 10.0, doublethreshold); + std::ifstream output(output_path.c_str()); + const std::string output_text((std::istreambuf_iterator(output)), std::istreambuf_iterator()); + EXPECT_NE(output_text.find("i = 1"), std::string::npos); + EXPECT_NE(output_text.find("j = 1"), std::string::npos); + EXPECT_EQ(output_text.find("i = 0"), std::string::npos); + EXPECT_EQ(output_text.find("j = 0"), std::string::npos); + output.close(); + std::remove(output_path.c_str()); + delete[] psi_k; delete[] Htmp; delete[] ekb; diff --git a/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/INPUT b/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/INPUT new file mode 100644 index 0000000000..a01547e839 --- /dev/null +++ b/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/INPUT @@ -0,0 +1,40 @@ +INPUT_PARAMETERS + +suffix autotest +pseudo_dir ../../PP_ORB +orbital_dir ../../PP_ORB + +calculation md +esolver_type tddft +basis_type lcao +gamma_only 0 +ecutwfc 20 + +scf_nmax 50 +scf_thr 1.0e-6 +ks_solver scalapack_gvx +mixing_type broyden +mixing_beta 0.7 +mixing_gg0 0.0 + +md_type nve +md_nstep 1 +estep_per_md 7 +td_dt 0.05 +md_tfirst 0 + +td_vext 1 +td_vext_dire 1 2 +td_stype 1 +td_ttype 4 4 +td_tstart 0 +td_tend 6 +td_supsine_amp 0.27 0.18 +td_supsine_freq 0.18737028625 0.35 +td_supsine_phase 0.0 1.5707963267948966 +td_supsine_sigma 0.75 0.60 +td_supsine_tstart 1 1 +td_supsine_tend 5 5 + +out_efield 1 +out_vecpot 1 diff --git a/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/KPT b/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/KPT new file mode 100644 index 0000000000..c289c0158a --- /dev/null +++ b/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/KPT @@ -0,0 +1,4 @@ +K_POINTS +0 +Gamma +1 1 1 0 0 0 diff --git a/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/STRU b/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/STRU new file mode 100644 index 0000000000..7593d35795 --- /dev/null +++ b/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/STRU @@ -0,0 +1,22 @@ +ATOMIC_SPECIES +Si 28.085 Si_ONCV_PBE-1.0.upf + +NUMERICAL_ORBITAL +Si_gga_6au_100Ry_2s2p1d.orb + +LATTICE_CONSTANT +10.2 + +LATTICE_VECTORS +0.0 0.5 0.5 +0.5 0.0 0.5 +0.5 0.5 0.0 + +ATOMIC_POSITIONS +Cartesian + +Si +0.0 +2 +0.00 0.00 0.00 0 0 0 +0.25 0.25 0.25 1 1 1 diff --git a/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/efield_1.txt.ref b/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/efield_1.txt.ref new file mode 100644 index 0000000000..3824b279fe --- /dev/null +++ b/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/efield_1.txt.ref @@ -0,0 +1,7 @@ +0 0 +0.05 0 +0.1 -0.0350185 +0.15 0.27 +0.2 -0.0350185 +0.25 0 +0.3 0 diff --git a/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/efield_2.txt.ref b/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/efield_2.txt.ref new file mode 100644 index 0000000000..0ec5febc62 --- /dev/null +++ b/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/efield_2.txt.ref @@ -0,0 +1,7 @@ +0 0 +0.05 0 +0.1 1.54425 +0.15 1.10218e-17 +0.2 -1.54425 +0.25 0 +0.3 0 diff --git a/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/result.ref b/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/result.ref new file mode 100644 index 0000000000..b700497be8 --- /dev/null +++ b/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/result.ref @@ -0,0 +1,6 @@ +etotref -194.2795836013149 +etotperatomref -97.1397918007 +Compareefield_1_txt_pass 0 +Compareefield_2_txt_pass 0 +CompareVectorPot_pass 0 +totaltimeref 5.46 diff --git a/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/vector_pot.txt.ref b/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/vector_pot.txt.ref new file mode 100644 index 0000000000..90a39b97af --- /dev/null +++ b/tests/05_rtTDDFT/19_NO_SUPERSINE_TDDFT/vector_pot.txt.ref @@ -0,0 +1,9 @@ +#istep A_x A_y A_z +1 0.000000e+00 0.000000e+00 0.000000e+00 +2 4.096138e-03 -2.078722e-02 0.000000e+00 +3 4.375154e-03 -5.368872e-02 0.000000e+00 +4 -3.259088e-03 -5.368872e-02 0.000000e+00 +5 -2.980072e-03 -2.078722e-02 0.000000e+00 +6 1.116066e-03 -6.938894e-18 0.000000e+00 +7 1.116066e-03 -6.938894e-18 0.000000e+00 +8 1.116066e-03 -6.938894e-18 0.000000e+00 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/INPUT b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/INPUT new file mode 100644 index 0000000000..eb7dda811d --- /dev/null +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/INPUT @@ -0,0 +1,63 @@ +INPUT_PARAMETERS + +suffix autotest +pseudo_dir ../../PP_ORB +orbital_dir ../../PP_ORB + +calculation md +esolver_type tddft +basis_type lcao +gamma_only 0 +ecutwfc 20 + +scf_nmax 50 +scf_thr 1.0e-6 +ks_solver scalapack_gvx +mixing_type broyden +mixing_beta 0.7 +mixing_gg0 0.0 + +md_type nve +md_nstep 1 +estep_per_md 7 +td_dt 0.05 +md_tfirst 0 + +td_vext 1 +td_vext_dire 1 2 3 1 2 3 1 2 3 1 +td_stype 2 +td_ttype 0 4 1 0 3 2 4 1 3 2 +td_tstart 0 +td_tend 6 + +td_gauss_freq 0.7 1.1 +td_gauss_phase 0.1 -0.4 +td_gauss_sigma 0.12 0.20 +td_gauss_t0 2 4 +td_gauss_amp 0.04 -0.03 + +td_trape_freq 0.8 1.3 +td_trape_phase 0.2 -0.6 +td_trape_t1 1 2 +td_trape_t2 3 4 +td_trape_t3 5 6 +td_trape_amp 0.025 -0.02 + +td_trigo_freq1 0.9 1.4 +td_trigo_freq2 1.8 1.1 +td_trigo_phase1 0.3 -0.2 +td_trigo_phase2 0.1 0.7 +td_trigo_amp 0.015 -0.018 + +td_heavi_t0 3 5 +td_heavi_amp 0.01 -0.012 + +td_supsine_amp 0.05 -0.04 +td_supsine_freq 0.7 1.0 +td_supsine_phase 0.2 -0.5 +td_supsine_sigma 0.75 1.20 +td_supsine_tstart 1 2 +td_supsine_tend 5 6 + +out_efield 1 +out_vecpot 1 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/KPT b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/KPT new file mode 100644 index 0000000000..c289c0158a --- /dev/null +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/KPT @@ -0,0 +1,4 @@ +K_POINTS +0 +Gamma +1 1 1 0 0 0 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/README b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/README new file mode 100644 index 0000000000..e736e706bc --- /dev/null +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/README @@ -0,0 +1 @@ +Hybrid-gauge RT-TDDFT with ten interleaved fields, two instances of every time profile, repeated directions, per-field waveform checks, and the summed vector potential. diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/STRU b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/STRU new file mode 100644 index 0000000000..7593d35795 --- /dev/null +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/STRU @@ -0,0 +1,22 @@ +ATOMIC_SPECIES +Si 28.085 Si_ONCV_PBE-1.0.upf + +NUMERICAL_ORBITAL +Si_gga_6au_100Ry_2s2p1d.orb + +LATTICE_CONSTANT +10.2 + +LATTICE_VECTORS +0.0 0.5 0.5 +0.5 0.0 0.5 +0.5 0.5 0.0 + +ATOMIC_POSITIONS +Cartesian + +Si +0.0 +2 +0.00 0.00 0.00 0 0 0 +0.25 0.25 0.25 1 1 1 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_1.txt.ref b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_1.txt.ref new file mode 100644 index 0000000000..f7029d49cf --- /dev/null +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_1.txt.ref @@ -0,0 +1,7 @@ +0 0.0266495 +0.05 0.0364109 +0.1 0.0398002 +0.15 0.0348135 +0.2 0.0242465 +0.25 0.0132775 +0.3 0.00555872 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_10.txt.ref b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_10.txt.ref new file mode 100644 index 0000000000..c9c6191732 --- /dev/null +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_10.txt.ref @@ -0,0 +1,7 @@ +0 -0.00732139 +0.05 -0.013089 +0.1 -0.0135533 +0.15 -0.00763671 +0.2 -0.00015746 +0.25 0.00320401 +0.3 0.00177906 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_2.txt.ref b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_2.txt.ref new file mode 100644 index 0000000000..d5fd25849f --- /dev/null +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_2.txt.ref @@ -0,0 +1,7 @@ +0 0 +0.05 0 +0.1 0.031042 +0.15 0.0490033 +0.2 -0.0446739 +0.25 0 +0.3 0 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_3.txt.ref b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_3.txt.ref new file mode 100644 index 0000000000..989ccf3f7e --- /dev/null +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_3.txt.ref @@ -0,0 +1,7 @@ +0 0 +0.05 0.0224967 +0.1 0.0190782 +0.15 0.014461 +0.2 0.00446755 +0.25 0 +0.3 -0 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_4.txt.ref b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_4.txt.ref new file mode 100644 index 0000000000..47ba81f3d5 --- /dev/null +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_4.txt.ref @@ -0,0 +1,7 @@ +0 0.00381989 +0.05 -0.00302697 +0.1 -0.0122172 +0.15 -0.0213628 +0.2 -0.0276318 +0.25 -0.0290339 +0.3 -0.0253607 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_5.txt.ref b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_5.txt.ref new file mode 100644 index 0000000000..bc3b37422d --- /dev/null +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_5.txt.ref @@ -0,0 +1,7 @@ +0 0.01 +0.05 0.01 +0.1 0.01 +0.15 0 +0.2 0 +0.25 0 +0.3 0 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_6.txt.ref b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_6.txt.ref new file mode 100644 index 0000000000..d29952a4c9 --- /dev/null +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_6.txt.ref @@ -0,0 +1,7 @@ +0 0.000142823 +0.05 0.00477471 +0.1 0.00864368 +0.15 0.00584356 +0.2 0.00103323 +0.25 -9.65006e-05 +0.3 -0.000733575 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_7.txt.ref b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_7.txt.ref new file mode 100644 index 0000000000..a316aa5db4 --- /dev/null +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_7.txt.ref @@ -0,0 +1,7 @@ +0 0 +0.05 -0 +0.1 -0 +0.15 0.0327856 +0.2 -0.0351033 +0.25 -0.0452257 +0.3 -0 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_8.txt.ref b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_8.txt.ref new file mode 100644 index 0000000000..558f86c14b --- /dev/null +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_8.txt.ref @@ -0,0 +1,7 @@ +0 -0 +0.05 -0.00981702 +0.1 -0.0195318 +0.15 -0.0162167 +0.2 -0.0102341 +0.25 -0.00128406 +0.3 0 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_9.txt.ref b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_9.txt.ref new file mode 100644 index 0000000000..a58d4028d6 --- /dev/null +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/efield_9.txt.ref @@ -0,0 +1,7 @@ +0 -0.012 +0.05 -0.012 +0.1 -0.012 +0.15 -0.012 +0.2 -0.012 +0.25 0 +0.3 0 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/result.ref b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/result.ref new file mode 100644 index 0000000000..806dccec0a --- /dev/null +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/result.ref @@ -0,0 +1,14 @@ +etotref -194.7659294665949 +etotperatomref -97.3829647333 +Compareefield_1_txt_pass 0 +Compareefield_2_txt_pass 0 +Compareefield_3_txt_pass 0 +Compareefield_4_txt_pass 0 +Compareefield_5_txt_pass 0 +Compareefield_6_txt_pass 0 +Compareefield_7_txt_pass 0 +Compareefield_8_txt_pass 0 +Compareefield_9_txt_pass 0 +Compareefield_10_txt_pass 0 +CompareVectorPot_pass 0 +totaltimeref 3.59 diff --git a/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/vector_pot.txt.ref b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/vector_pot.txt.ref new file mode 100644 index 0000000000..c863f6c0a6 --- /dev/null +++ b/tests/05_rtTDDFT/20_NO_MIXED_EFIELD_TDDFT/vector_pot.txt.ref @@ -0,0 +1,9 @@ +#istep A_x A_y A_z +1 -4.438787e-04 -1.062812e-04 -3.472515e-05 +2 -1.236916e-03 -5.069838e-05 -3.925135e-04 +3 -3.258834e-03 -6.966863e-04 -9.687700e-04 +4 -4.699256e-03 -1.623112e-03 -1.228331e-03 +5 -3.452404e-03 -3.408082e-04 -1.031516e-03 +6 -1.742707e-03 1.059340e-03 -8.252427e-04 +7 -6.868887e-04 1.058611e-03 -7.611232e-04 +8 -3.262524e-04 1.058611e-03 -7.000927e-04 diff --git a/tests/05_rtTDDFT/CASES_CPU.txt b/tests/05_rtTDDFT/CASES_CPU.txt index b8743ffd81..f01c2b2353 100644 --- a/tests/05_rtTDDFT/CASES_CPU.txt +++ b/tests/05_rtTDDFT/CASES_CPU.txt @@ -16,3 +16,5 @@ 16_NO_vel_TDDFT 17_NO_vel_TDDFT 18_NO_hyb_TDDFT +19_NO_SUPERSINE_TDDFT +20_NO_MIXED_EFIELD_TDDFT diff --git a/tests/15_rtTDDFT_GPU/19_NO_SUPERSINE_TDDFT_GPU/INPUT b/tests/15_rtTDDFT_GPU/19_NO_SUPERSINE_TDDFT_GPU/INPUT new file mode 100644 index 0000000000..23e0cf0492 --- /dev/null +++ b/tests/15_rtTDDFT_GPU/19_NO_SUPERSINE_TDDFT_GPU/INPUT @@ -0,0 +1,41 @@ +INPUT_PARAMETERS + +suffix autotest +pseudo_dir ../../PP_ORB +orbital_dir ../../PP_ORB + +calculation md +esolver_type tddft +basis_type lcao +gamma_only 0 +ecutwfc 20 + +scf_nmax 50 +scf_thr 1.0e-6 +device gpu +ks_solver cusolver +mixing_type broyden +mixing_beta 0.7 +mixing_gg0 0.0 + +md_type nve +md_nstep 1 +estep_per_md 7 +td_dt 0.05 +md_tfirst 0 + +td_vext 1 +td_vext_dire 1 2 +td_stype 1 +td_ttype 4 4 +td_tstart 0 +td_tend 6 +td_supsine_amp 0.27 0.18 +td_supsine_freq 0.18737028625 0.35 +td_supsine_phase 0.0 1.5707963267948966 +td_supsine_sigma 0.75 0.60 +td_supsine_tstart 1 1 +td_supsine_tend 5 5 + +out_efield 1 +out_vecpot 1 diff --git a/tests/15_rtTDDFT_GPU/19_NO_SUPERSINE_TDDFT_GPU/KPT b/tests/15_rtTDDFT_GPU/19_NO_SUPERSINE_TDDFT_GPU/KPT new file mode 100644 index 0000000000..c289c0158a --- /dev/null +++ b/tests/15_rtTDDFT_GPU/19_NO_SUPERSINE_TDDFT_GPU/KPT @@ -0,0 +1,4 @@ +K_POINTS +0 +Gamma +1 1 1 0 0 0 diff --git a/tests/15_rtTDDFT_GPU/19_NO_SUPERSINE_TDDFT_GPU/STRU b/tests/15_rtTDDFT_GPU/19_NO_SUPERSINE_TDDFT_GPU/STRU new file mode 100644 index 0000000000..7593d35795 --- /dev/null +++ b/tests/15_rtTDDFT_GPU/19_NO_SUPERSINE_TDDFT_GPU/STRU @@ -0,0 +1,22 @@ +ATOMIC_SPECIES +Si 28.085 Si_ONCV_PBE-1.0.upf + +NUMERICAL_ORBITAL +Si_gga_6au_100Ry_2s2p1d.orb + +LATTICE_CONSTANT +10.2 + +LATTICE_VECTORS +0.0 0.5 0.5 +0.5 0.0 0.5 +0.5 0.5 0.0 + +ATOMIC_POSITIONS +Cartesian + +Si +0.0 +2 +0.00 0.00 0.00 0 0 0 +0.25 0.25 0.25 1 1 1 diff --git a/tests/15_rtTDDFT_GPU/19_NO_SUPERSINE_TDDFT_GPU/efield_1.txt.ref b/tests/15_rtTDDFT_GPU/19_NO_SUPERSINE_TDDFT_GPU/efield_1.txt.ref new file mode 100644 index 0000000000..3824b279fe --- /dev/null +++ b/tests/15_rtTDDFT_GPU/19_NO_SUPERSINE_TDDFT_GPU/efield_1.txt.ref @@ -0,0 +1,7 @@ +0 0 +0.05 0 +0.1 -0.0350185 +0.15 0.27 +0.2 -0.0350185 +0.25 0 +0.3 0 diff --git a/tests/15_rtTDDFT_GPU/19_NO_SUPERSINE_TDDFT_GPU/efield_2.txt.ref b/tests/15_rtTDDFT_GPU/19_NO_SUPERSINE_TDDFT_GPU/efield_2.txt.ref new file mode 100644 index 0000000000..0ec5febc62 --- /dev/null +++ b/tests/15_rtTDDFT_GPU/19_NO_SUPERSINE_TDDFT_GPU/efield_2.txt.ref @@ -0,0 +1,7 @@ +0 0 +0.05 0 +0.1 1.54425 +0.15 1.10218e-17 +0.2 -1.54425 +0.25 0 +0.3 0 diff --git a/tests/15_rtTDDFT_GPU/19_NO_SUPERSINE_TDDFT_GPU/result.ref b/tests/15_rtTDDFT_GPU/19_NO_SUPERSINE_TDDFT_GPU/result.ref new file mode 100644 index 0000000000..b700497be8 --- /dev/null +++ b/tests/15_rtTDDFT_GPU/19_NO_SUPERSINE_TDDFT_GPU/result.ref @@ -0,0 +1,6 @@ +etotref -194.2795836013149 +etotperatomref -97.1397918007 +Compareefield_1_txt_pass 0 +Compareefield_2_txt_pass 0 +CompareVectorPot_pass 0 +totaltimeref 5.46 diff --git a/tests/15_rtTDDFT_GPU/19_NO_SUPERSINE_TDDFT_GPU/vector_pot.txt.ref b/tests/15_rtTDDFT_GPU/19_NO_SUPERSINE_TDDFT_GPU/vector_pot.txt.ref new file mode 100644 index 0000000000..90a39b97af --- /dev/null +++ b/tests/15_rtTDDFT_GPU/19_NO_SUPERSINE_TDDFT_GPU/vector_pot.txt.ref @@ -0,0 +1,9 @@ +#istep A_x A_y A_z +1 0.000000e+00 0.000000e+00 0.000000e+00 +2 4.096138e-03 -2.078722e-02 0.000000e+00 +3 4.375154e-03 -5.368872e-02 0.000000e+00 +4 -3.259088e-03 -5.368872e-02 0.000000e+00 +5 -2.980072e-03 -2.078722e-02 0.000000e+00 +6 1.116066e-03 -6.938894e-18 0.000000e+00 +7 1.116066e-03 -6.938894e-18 0.000000e+00 +8 1.116066e-03 -6.938894e-18 0.000000e+00 diff --git a/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/INPUT b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/INPUT new file mode 100644 index 0000000000..860e186c97 --- /dev/null +++ b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/INPUT @@ -0,0 +1,64 @@ +INPUT_PARAMETERS + +suffix autotest +pseudo_dir ../../PP_ORB +orbital_dir ../../PP_ORB + +calculation md +esolver_type tddft +basis_type lcao +gamma_only 0 +ecutwfc 20 + +scf_nmax 50 +scf_thr 1.0e-6 +device gpu +ks_solver scalapack_gvx +mixing_type broyden +mixing_beta 0.7 +mixing_gg0 0.0 + +md_type nve +md_nstep 1 +estep_per_md 7 +td_dt 0.05 +md_tfirst 0 + +td_vext 1 +td_vext_dire 1 2 3 1 2 3 1 2 3 1 +td_stype 2 +td_ttype 0 4 1 0 3 2 4 1 3 2 +td_tstart 0 +td_tend 6 + +td_gauss_freq 0.7 1.1 +td_gauss_phase 0.1 -0.4 +td_gauss_sigma 0.12 0.20 +td_gauss_t0 2 4 +td_gauss_amp 0.04 -0.03 + +td_trape_freq 0.8 1.3 +td_trape_phase 0.2 -0.6 +td_trape_t1 1 2 +td_trape_t2 3 4 +td_trape_t3 5 6 +td_trape_amp 0.025 -0.02 + +td_trigo_freq1 0.9 1.4 +td_trigo_freq2 1.8 1.1 +td_trigo_phase1 0.3 -0.2 +td_trigo_phase2 0.1 0.7 +td_trigo_amp 0.015 -0.018 + +td_heavi_t0 3 5 +td_heavi_amp 0.01 -0.012 + +td_supsine_amp 0.05 -0.04 +td_supsine_freq 0.7 1.0 +td_supsine_phase 0.2 -0.5 +td_supsine_sigma 0.75 1.20 +td_supsine_tstart 1 2 +td_supsine_tend 5 6 + +out_efield 1 +out_vecpot 1 diff --git a/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/KPT b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/KPT new file mode 100644 index 0000000000..c289c0158a --- /dev/null +++ b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/KPT @@ -0,0 +1,4 @@ +K_POINTS +0 +Gamma +1 1 1 0 0 0 diff --git a/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/STRU b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/STRU new file mode 100644 index 0000000000..7593d35795 --- /dev/null +++ b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/STRU @@ -0,0 +1,22 @@ +ATOMIC_SPECIES +Si 28.085 Si_ONCV_PBE-1.0.upf + +NUMERICAL_ORBITAL +Si_gga_6au_100Ry_2s2p1d.orb + +LATTICE_CONSTANT +10.2 + +LATTICE_VECTORS +0.0 0.5 0.5 +0.5 0.0 0.5 +0.5 0.5 0.0 + +ATOMIC_POSITIONS +Cartesian + +Si +0.0 +2 +0.00 0.00 0.00 0 0 0 +0.25 0.25 0.25 1 1 1 diff --git a/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_1.txt.ref b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_1.txt.ref new file mode 100644 index 0000000000..f7029d49cf --- /dev/null +++ b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_1.txt.ref @@ -0,0 +1,7 @@ +0 0.0266495 +0.05 0.0364109 +0.1 0.0398002 +0.15 0.0348135 +0.2 0.0242465 +0.25 0.0132775 +0.3 0.00555872 diff --git a/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_10.txt.ref b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_10.txt.ref new file mode 100644 index 0000000000..c9c6191732 --- /dev/null +++ b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_10.txt.ref @@ -0,0 +1,7 @@ +0 -0.00732139 +0.05 -0.013089 +0.1 -0.0135533 +0.15 -0.00763671 +0.2 -0.00015746 +0.25 0.00320401 +0.3 0.00177906 diff --git a/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_2.txt.ref b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_2.txt.ref new file mode 100644 index 0000000000..d5fd25849f --- /dev/null +++ b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_2.txt.ref @@ -0,0 +1,7 @@ +0 0 +0.05 0 +0.1 0.031042 +0.15 0.0490033 +0.2 -0.0446739 +0.25 0 +0.3 0 diff --git a/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_3.txt.ref b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_3.txt.ref new file mode 100644 index 0000000000..989ccf3f7e --- /dev/null +++ b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_3.txt.ref @@ -0,0 +1,7 @@ +0 0 +0.05 0.0224967 +0.1 0.0190782 +0.15 0.014461 +0.2 0.00446755 +0.25 0 +0.3 -0 diff --git a/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_4.txt.ref b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_4.txt.ref new file mode 100644 index 0000000000..47ba81f3d5 --- /dev/null +++ b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_4.txt.ref @@ -0,0 +1,7 @@ +0 0.00381989 +0.05 -0.00302697 +0.1 -0.0122172 +0.15 -0.0213628 +0.2 -0.0276318 +0.25 -0.0290339 +0.3 -0.0253607 diff --git a/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_5.txt.ref b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_5.txt.ref new file mode 100644 index 0000000000..bc3b37422d --- /dev/null +++ b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_5.txt.ref @@ -0,0 +1,7 @@ +0 0.01 +0.05 0.01 +0.1 0.01 +0.15 0 +0.2 0 +0.25 0 +0.3 0 diff --git a/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_6.txt.ref b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_6.txt.ref new file mode 100644 index 0000000000..d29952a4c9 --- /dev/null +++ b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_6.txt.ref @@ -0,0 +1,7 @@ +0 0.000142823 +0.05 0.00477471 +0.1 0.00864368 +0.15 0.00584356 +0.2 0.00103323 +0.25 -9.65006e-05 +0.3 -0.000733575 diff --git a/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_7.txt.ref b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_7.txt.ref new file mode 100644 index 0000000000..a316aa5db4 --- /dev/null +++ b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_7.txt.ref @@ -0,0 +1,7 @@ +0 0 +0.05 -0 +0.1 -0 +0.15 0.0327856 +0.2 -0.0351033 +0.25 -0.0452257 +0.3 -0 diff --git a/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_8.txt.ref b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_8.txt.ref new file mode 100644 index 0000000000..558f86c14b --- /dev/null +++ b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_8.txt.ref @@ -0,0 +1,7 @@ +0 -0 +0.05 -0.00981702 +0.1 -0.0195318 +0.15 -0.0162167 +0.2 -0.0102341 +0.25 -0.00128406 +0.3 0 diff --git a/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_9.txt.ref b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_9.txt.ref new file mode 100644 index 0000000000..a58d4028d6 --- /dev/null +++ b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/efield_9.txt.ref @@ -0,0 +1,7 @@ +0 -0.012 +0.05 -0.012 +0.1 -0.012 +0.15 -0.012 +0.2 -0.012 +0.25 0 +0.3 0 diff --git a/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/result.ref b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/result.ref new file mode 100644 index 0000000000..806dccec0a --- /dev/null +++ b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/result.ref @@ -0,0 +1,14 @@ +etotref -194.7659294665949 +etotperatomref -97.3829647333 +Compareefield_1_txt_pass 0 +Compareefield_2_txt_pass 0 +Compareefield_3_txt_pass 0 +Compareefield_4_txt_pass 0 +Compareefield_5_txt_pass 0 +Compareefield_6_txt_pass 0 +Compareefield_7_txt_pass 0 +Compareefield_8_txt_pass 0 +Compareefield_9_txt_pass 0 +Compareefield_10_txt_pass 0 +CompareVectorPot_pass 0 +totaltimeref 3.59 diff --git a/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/vector_pot.txt.ref b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/vector_pot.txt.ref new file mode 100644 index 0000000000..c863f6c0a6 --- /dev/null +++ b/tests/15_rtTDDFT_GPU/20_NO_MIXED_EFIELD_TDDFT_GPU/vector_pot.txt.ref @@ -0,0 +1,9 @@ +#istep A_x A_y A_z +1 -4.438787e-04 -1.062812e-04 -3.472515e-05 +2 -1.236916e-03 -5.069838e-05 -3.925135e-04 +3 -3.258834e-03 -6.966863e-04 -9.687700e-04 +4 -4.699256e-03 -1.623112e-03 -1.228331e-03 +5 -3.452404e-03 -3.408082e-04 -1.031516e-03 +6 -1.742707e-03 1.059340e-03 -8.252427e-04 +7 -6.868887e-04 1.058611e-03 -7.611232e-04 +8 -3.262524e-04 1.058611e-03 -7.000927e-04 diff --git a/tests/15_rtTDDFT_GPU/CASES_GPU.txt b/tests/15_rtTDDFT_GPU/CASES_GPU.txt index 53bce2d505..44b6b4e89e 100644 --- a/tests/15_rtTDDFT_GPU/CASES_GPU.txt +++ b/tests/15_rtTDDFT_GPU/CASES_GPU.txt @@ -14,3 +14,5 @@ 16_NO_vel_TDDFT_GPU 17_NO_vel_TDDFT_GPU 18_NO_hyb_TDDFT_GPU +19_NO_SUPERSINE_TDDFT_GPU +20_NO_MIXED_EFIELD_TDDFT_GPU diff --git a/tests/integrate/tools/catch_properties.sh b/tests/integrate/tools/catch_properties.sh index 56b78632b3..e860f1fae4 100755 --- a/tests/integrate/tools/catch_properties.sh +++ b/tests/integrate/tools/catch_properties.sh @@ -124,6 +124,8 @@ base=$(get_input_key_value "basis_type" "INPUT") word_total_time="atomic_world" symmetry=$(get_input_key_value "symmetry" "INPUT") out_current=$(get_input_key_value "out_current" "INPUT") +out_efield=$(get_input_key_value "out_efield" "INPUT") +out_vecpot=$(get_input_key_value "out_vecpot" "INPUT") nspin=$(get_input_key_value "nspin" "INPUT") test -e $1 && rm $1 @@ -773,6 +775,29 @@ if ! test -z "$out_current" && [ $out_current ]; then echo "CompareCurrent_pass $?" >>$1 fi +#-------------------------------------------- +# Check electric fields in rt-TDDFT +#-------------------------------------------- +if ! test -z "$out_efield" && [ "$out_efield" == 1 ]; then + efield_refs=(efield_*.txt.ref) + if [ ! -e "${efield_refs[0]}" ]; then + echo "CompareEfieldReference_pass 1" >>$1 + else + for efield_ref in "${efield_refs[@]}"; do + efield_name=${efield_ref%.ref} + efield_key=$(sanitize_result_key "$efield_name") + record_compare_result "$1" "Compare${efield_key}_pass" "$efield_ref" "OUT.autotest/$efield_name" 8 + done + fi +fi + +#-------------------------------------------- +# Check vector potential in rt-TDDFT +#-------------------------------------------- +if ! test -z "$out_vecpot" && [ "$out_vecpot" == 1 ]; then + record_compare_result "$1" "CompareVectorPot_pass" "vector_pot.txt.ref" "OUT.autotest/vector_pot.txt" 8 +fi + #-------------------------------------------- # Linear response function #--------------------------------------------