LCOV - code coverage report
Current view: top level - src/motion - cell_opt_utils.F (source / functions) Coverage Total Hit
Test: CP2K Regtests (git:123285f) Lines: 97.3 % 261 254
Test Date: 2026-11-04 00:58:17 Functions: 100.0 % 13 13

            Line data    Source code
       1              : !--------------------------------------------------------------------------------------------------!
       2              : !   CP2K: A general program to perform molecular dynamics simulations                              !
       3              : !   Copyright 2000-2026 CP2K developers group <https://cp2k.org>                                   !
       4              : !                                                                                                  !
       5              : !   SPDX-License-Identifier: GPL-2.0-or-later                                                      !
       6              : !--------------------------------------------------------------------------------------------------!
       7              : 
       8              : ! **************************************************************************************************
       9              : !> \brief contains a functional that calculates the energy and its derivatives
      10              : !>      for the geometry optimizer
      11              : !> \par History
      12              : !>      03.2008 - Teodoro Laino [tlaino] - University of Zurich - Cell Optimization
      13              : ! **************************************************************************************************
      14              : MODULE cell_opt_utils
      15              :    USE cell_types,                      ONLY: &
      16              :         cell_sym_cubic, cell_sym_hexagonal_gamma_120, cell_sym_hexagonal_gamma_60, &
      17              :         cell_sym_monoclinic, cell_sym_monoclinic_gamma_ab, cell_sym_orthorhombic, &
      18              :         cell_sym_rhombohedral, cell_sym_tetragonal_ab, cell_sym_tetragonal_ac, &
      19              :         cell_sym_tetragonal_bc, cell_sym_triclinic, cell_type
      20              :    USE input_constants,                 ONLY: fix_none
      21              :    USE input_section_types,             ONLY: section_vals_type,&
      22              :                                               section_vals_val_get
      23              :    USE kinds,                           ONLY: default_string_length,&
      24              :                                               dp
      25              :    USE mathconstants,                   ONLY: sqrt3
      26              :    USE mathlib,                         ONLY: angle,&
      27              :                                               det_3x3
      28              :    USE string_utilities,                ONLY: uppercase
      29              : #include "../base/base_uses.f90"
      30              : 
      31              :    IMPLICIT NONE
      32              :    PRIVATE
      33              : 
      34              :    CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'cell_opt_utils'
      35              : 
      36              :    PUBLIC :: get_dg_dh, keep_angles_conflict, &
      37              :              keep_symmetry_coupled_set, keep_symmetry_conflict, &
      38              :              pres_crit_shear_pinned, parse_token, &
      39              :              read_external_press_tensor, &
      40              :              apply_cell_constraints, cell_opt_constraint_label, &
      41              :              parse_cell_constraint, &
      42              :              rescale_new_cell_volume
      43              : 
      44              :    ! Masks of the six independent cell variables in the canonical frame
      45              :    ! (a along x, b in the xy plane): AX, BX, BY, CX, CY, CZ.
      46              :    ! A set bit means that a CONSTRAINT fixes the component.
      47              :    ! constraint_id = 0 (fix_none) leaves every component relaxable.
      48              :    INTEGER, PARAMETER, PUBLIC :: comp_ax = 2**0, & ! a(x)
      49              :                                  comp_bx = 2**1, & ! b(x)
      50              :                                  comp_by = 2**2, & ! b(y)
      51              :                                  comp_cx = 2**3, & ! c(x)
      52              :                                  comp_cy = 2**4, & ! c(y)
      53              :                                  comp_cz = 2**5  ! c(z)
      54              :    ! Shorthands for complete cell vectors, Cartesian directions and coupled
      55              :    ! component sets (unions of disjoint bits, hence representable as sums)
      56              :    INTEGER, PARAMETER, PUBLIC :: mask_a = comp_ax, &
      57              :                                  mask_b = comp_bx + comp_by, &
      58              :                                  mask_c = comp_cx + comp_cy + comp_cz, &
      59              :                                  mask_x = comp_ax + comp_bx + comp_cx, &
      60              :                                  mask_y = comp_bx + comp_by + comp_cy, &
      61              :                                  mask_z = mask_c, &
      62              :                                  mask_xy = comp_ax + comp_bx + comp_by + comp_cx + comp_cy, &
      63              :                                  mask_xz = comp_ax + comp_bx + comp_cx + comp_cy + comp_cz, &
      64              :                                  mask_yz = comp_bx + comp_by + comp_cx + comp_cy + comp_cz, &
      65              :                                  mask_diag = comp_ax + comp_by + comp_cz, &
      66              :                                  mask_diag_ab = comp_ax + comp_by, &
      67              :                                  mask_diag_ac = comp_ax + comp_cz, &
      68              :                                  mask_diag_bc = comp_by + comp_cz, &
      69              :                                  mask_ab_plane = comp_ax + comp_bx + comp_by, &
      70              :                                  mask_shear = comp_bx + comp_cx + comp_cy, &
      71              :                                  mask_all = comp_ax + comp_bx + comp_by + comp_cx + comp_cy + comp_cz
      72              : 
      73              : CONTAINS
      74              : 
      75              : ! **************************************************************************************************
      76              : !> \brief Reads the external pressure tensor
      77              : !> \param geo_section ...
      78              : !> \param cell ...
      79              : !> \param pres_ext ...
      80              : !> \param mtrx ...
      81              : !> \param input_to_opt rotation from the input Cartesian frame to the optimization frame
      82              : !> \author Teodoro Laino [tlaino] - University of Zurich - 03.2008
      83              : ! **************************************************************************************************
      84          234 :    SUBROUTINE read_external_press_tensor(geo_section, cell, pres_ext, mtrx, input_to_opt)
      85              :       TYPE(section_vals_type), POINTER                   :: geo_section
      86              :       TYPE(cell_type), POINTER                           :: cell
      87              :       REAL(KIND=dp), INTENT(OUT)                         :: pres_ext
      88              :       REAL(KIND=dp), DIMENSION(3, 3), INTENT(OUT)        :: mtrx
      89              :       REAL(KIND=dp), DIMENSION(3, 3), INTENT(IN)         :: input_to_opt
      90              : 
      91              :       INTEGER                                            :: i, ind, j
      92              :       LOGICAL                                            :: check
      93              :       REAL(KIND=dp), DIMENSION(3, 3)                     :: pres_ext_tens
      94          234 :       REAL(KIND=dp), DIMENSION(:), POINTER               :: pvals
      95              : 
      96          234 :       NULLIFY (pvals)
      97          234 :       mtrx = 0.0_dp
      98          234 :       pres_ext_tens = 0.0_dp
      99          234 :       pres_ext = 0.0_dp
     100          234 :       CALL section_vals_val_get(geo_section, "EXTERNAL_PRESSURE", r_vals=pvals)
     101          234 :       check = (SIZE(pvals) == 1) .OR. (SIZE(pvals) == 9)
     102          234 :       IF (.NOT. check) THEN
     103            0 :          CPABORT("EXTERNAL_PRESSURE can have 1 or 9 components only!")
     104              :       END IF
     105              : 
     106          234 :       IF (SIZE(pvals) == 9) THEN
     107              :          ind = 0
     108          472 :          DO i = 1, 3
     109         1534 :             DO j = 1, 3
     110         1062 :                ind = ind + 1
     111         1416 :                pres_ext_tens(j, i) = pvals(ind)
     112              :             END DO
     113              :          END DO
     114              :          ! Express the pressure tensor in the canonical optimization frame.
     115              :          pres_ext_tens = MATMUL(input_to_opt, &
     116         9322 :                                 MATMUL(pres_ext_tens, TRANSPOSE(input_to_opt)))
     117          472 :          DO i = 1, 3
     118          472 :             pres_ext = pres_ext + pres_ext_tens(i, i)
     119              :          END DO
     120          118 :          pres_ext = pres_ext/3.0_dp
     121          472 :          DO i = 1, 3
     122          472 :             pres_ext_tens(i, i) = pres_ext_tens(i, i) - pres_ext
     123              :          END DO
     124              :       ELSE
     125          116 :          pres_ext = pvals(1)
     126              :       END IF
     127              : 
     128         3042 :       IF (ANY(ABS(pres_ext_tens) > 1.0E-5_dp)) THEN
     129            0 :          mtrx = cell%deth*MATMUL(cell%h_inv, MATMUL(pres_ext_tens, TRANSPOSE(cell%h_inv)))
     130              :       END IF
     131              : 
     132          234 :    END SUBROUTINE read_external_press_tensor
     133              : 
     134              : ! **************************************************************************************************
     135              : !> \brief Parses the tokens of the CONSTRAINT keyword into a cell component mask
     136              : !> \param constraint_tokens value tokens of the CONSTRAINT keyword (char_t list)
     137              : !> \param cell simulation cell (only the periodicity is used)
     138              : !> \param explicit whether the CONSTRAINT keyword was given explicitly
     139              : !> \param mask union of the fixed cell components (comp_* and mask_* bits)
     140              : ! **************************************************************************************************
     141          422 :    SUBROUTINE parse_cell_constraint(constraint_tokens, cell, explicit, mask)
     142              :       CHARACTER(LEN=*), DIMENSION(:), INTENT(IN)         :: constraint_tokens
     143              :       TYPE(cell_type), POINTER                           :: cell
     144              :       LOGICAL, INTENT(IN)                                :: explicit
     145              :       INTEGER, INTENT(OUT)                               :: mask
     146              : 
     147              :       CHARACTER(LEN=default_string_length)               :: reject_reason, token
     148              :       INTEGER                                            :: axis_index, nonperiodic_axis, overlap, &
     149              :                                                             required_mask, tok_index, tok_mask
     150              :       LOGICAL                                            :: accepted
     151              : 
     152              :       ! The input tokenizer delivers a bare keyword as one blank token.
     153          422 :       IF (SIZE(constraint_tokens) == 0 .OR. &
     154          422 :           (SIZE(constraint_tokens) == 1 .AND. LEN_TRIM(constraint_tokens(1)) == 0)) THEN
     155              :          CALL cp_abort(__LOCATION__, &
     156            0 :                        "CELL_OPT CONSTRAINT requires a value.")
     157              :       END IF
     158              : 
     159          422 :       mask = 0
     160         1094 :       DO tok_index = 1, SIZE(constraint_tokens)
     161          672 :          token = ADJUSTL(constraint_tokens(tok_index))
     162          672 :          CALL uppercase(token)
     163          672 :          IF (token == "NONE") THEN
     164              :             ! NONE must be the first and only token: leftover tokens on the
     165              :             ! line or an already-accumulated mask mean it is being combined.
     166          224 :             IF (tok_index < SIZE(constraint_tokens) .OR. mask /= 0) THEN
     167              :                CALL cp_abort(__LOCATION__, &
     168              :                              "CELL_OPT CONSTRAINT NONE cannot be combined "// &
     169            0 :                              "with other tokens.")
     170              :             END IF
     171          224 :             mask = 0
     172          224 :             CYCLE
     173              :          END IF
     174          448 :          CALL parse_token(token, accepted, tok_mask, reject_reason)
     175          448 :          IF (.NOT. accepted) THEN
     176            0 :             CALL cp_abort(__LOCATION__, TRIM(reject_reason))
     177              :          END IF
     178          448 :          overlap = IAND(tok_mask, mask)
     179          448 :          IF (overlap == tok_mask) THEN
     180              :             CALL cp_warn(__LOCATION__, &
     181              :                          "Redundant CELL_OPT CONSTRAINT token: "//TRIM(token)// &
     182              :                          " (already constrained: "// &
     183            2 :                          TRIM(ADJUSTL(cell_opt_constraint_label(overlap)))//").")
     184          446 :          ELSE IF (overlap /= 0) THEN
     185              :             CALL cp_warn(__LOCATION__, &
     186              :                          "CELL_OPT CONSTRAINT token "//TRIM(token)//" partially overlaps: "// &
     187              :                          "already constrained "// &
     188              :                          TRIM(ADJUSTL(cell_opt_constraint_label(overlap)))// &
     189              :                          ", newly fixed "// &
     190            2 :                          TRIM(ADJUSTL(cell_opt_constraint_label(IAND(NOT(mask), tok_mask))))//".")
     191              :          END IF
     192         1318 :          mask = IOR(mask, tok_mask)
     193              :       END DO
     194              : 
     195              :       ! For 2D periodicity the non-periodic cell direction must be constrained.
     196              :       ! Complete the constraint automatically if the keyword was not given.
     197         1688 :       IF (COUNT(cell%perd /= 0) == 2) THEN
     198              :          nonperiodic_axis = 0
     199           80 :          DO axis_index = 1, 3
     200           80 :             IF (cell%perd(axis_index) == 0) nonperiodic_axis = axis_index
     201              :          END DO
     202           20 :          required_mask = 0
     203            6 :          SELECT CASE (nonperiodic_axis)
     204              :          CASE (1)
     205            6 :             required_mask = mask_x
     206              :          CASE (2)
     207            6 :             required_mask = mask_y
     208              :          CASE (3)
     209           20 :             required_mask = mask_z
     210              :          END SELECT
     211           20 :          IF (.NOT. explicit .AND. mask == 0) THEN
     212            8 :             mask = required_mask
     213              :             CALL cp_warn(__LOCATION__, &
     214            8 :                          "2D CELL_OPT: constraining non-periodic cell direction.")
     215           12 :          ELSE IF (IAND(mask, required_mask) /= required_mask) THEN
     216              :             CALL cp_abort(__LOCATION__, &
     217            0 :                           "2D CELL_OPT needs constrained non-periodic cell direction.")
     218              :          END IF
     219              :       END IF
     220              : 
     221          422 :    END SUBROUTINE parse_cell_constraint
     222              : 
     223              : ! **************************************************************************************************
     224              : !> \brief Returns the label of the consolidated cell constraint mask
     225              : !> \param mask bit mask of fixed cell components (comp_* / mask_* bits)
     226              : !> \return the historical four-character labels ("   X", "  XY", "NONE") for
     227              : !>         the legacy constraint values (padded for backward compatible
     228              : !>         printing), the fixed components spelled out ("AX BX BY") otherwise
     229              : ! **************************************************************************************************
     230          503 :    PURE FUNCTION cell_opt_constraint_label(mask) RESULT(label)
     231              :       INTEGER, INTENT(IN)                                :: mask
     232              :       CHARACTER(LEN=default_string_length)               :: label
     233              : 
     234          822 :       SELECT CASE (mask)
     235              :       CASE (fix_none)
     236          319 :          label = "NONE"
     237              :       CASE (mask_x)
     238            4 :          label = "   X"
     239              :       CASE (mask_y)
     240            4 :          label = "   Y"
     241              :       CASE (mask_z)
     242           18 :          label = "   Z"
     243              :       CASE (mask_xy)
     244            7 :          label = "  XY"
     245              :       CASE (mask_xz)
     246            4 :          label = "  XZ"
     247              :       CASE (mask_yz)
     248            4 :          label = "  YZ"
     249              :       CASE DEFAULT
     250              :          ! Composite constraint: spell out the fixed components
     251          143 :          label = ""
     252          143 :          IF (IAND(mask, comp_ax) /= 0) label = TRIM(label)//" AX"
     253          143 :          IF (IAND(mask, comp_bx) /= 0) label = TRIM(label)//" BX"
     254          143 :          IF (IAND(mask, comp_by) /= 0) label = TRIM(label)//" BY"
     255          143 :          IF (IAND(mask, comp_cx) /= 0) label = TRIM(label)//" CX"
     256          143 :          IF (IAND(mask, comp_cy) /= 0) label = TRIM(label)//" CY"
     257          143 :          IF (IAND(mask, comp_cz) /= 0) label = TRIM(label)//" CZ"
     258          646 :          label = ADJUSTL(label)
     259              :       END SELECT
     260              : 
     261          503 :    END FUNCTION cell_opt_constraint_label
     262              : 
     263              : ! **************************************************************************************************
     264              : !> \brief Whether the mask fixes some but not all components of a coupled slot set
     265              : !> \param mask bit mask of fixed cell components (comp_* / mask_* bits)
     266              : !> \param slots the coupled set of components (a mask_* union)
     267              : !> \return ...
     268              : !> \note How it compares: IAND(mask, slots) is a set intersection. It holds
     269              : !>       the components that the mask fixes inside the coupled set. The
     270              : !>       predicate is true when the intersection is non-empty and is not the
     271              : !>       full set. In words: the constraint fixes at least one component of
     272              : !>       the set, and it leaves at least one component free.
     273              : !>       Why the guards need it: a complete freeze and no freeze both keep
     274              : !>       the KEEP_ANGLES and KEEP_SYMMETRY projections consistent. A partial
     275              : !>       fixation breaks them, so the guards reject exactly this case.
     276              : ! **************************************************************************************************
     277          110 :    PURE FUNCTION fixes_partially(mask, slots) RESULT(partial)
     278              :       INTEGER, INTENT(IN)                                :: mask, slots
     279              :       LOGICAL                                            :: partial
     280              : 
     281          110 :       partial = IAND(mask, slots) /= 0 .AND. IAND(mask, slots) /= slots
     282          110 :    END FUNCTION fixes_partially
     283              : 
     284              : ! **************************************************************************************************
     285              : !> \brief Whether the constraint mask fixes the given component slot
     286              : !> \param mask bit mask of fixed cell components
     287              : !> \param slot component index in the canonical order AX..CZ (1..6)
     288              : !> \return ...
     289              : !> \note How it compares: BTEST(mask, slot - 1) reads one bit of the mask.
     290              : !>       Each bit position corresponds to one component slot, in the order
     291              : !>       comp_ax ... comp_cz = 2**0 ... 2**5. The slot argument counts from
     292              : !>       one so that it indexes the gradient components directly:
     293              : !>       gradient(1) = AX ... gradient(6) = CZ. BTEST counts bits from zero.
     294              : ! **************************************************************************************************
     295        36030 :    PURE FUNCTION slot_is_fixed(mask, slot) RESULT(fixed)
     296              :       INTEGER, INTENT(IN)                                :: mask, slot
     297              :       LOGICAL                                            :: fixed
     298              : 
     299        36030 :       fixed = BTEST(mask, slot - 1)
     300        36030 :    END FUNCTION slot_is_fixed
     301              : 
     302              : ! **************************************************************************************************
     303              : !> \brief Whether a CONSTRAINT mask conflicts with the KEEP_ANGLES projection
     304              : !> \param mask bit mask of fixed cell components
     305              : !> \param orthorhombic whether the cell is orthorhombic
     306              : !> \return conflict flag; the caller aborts when it is true
     307              : !> \note KEEP_ANGLES projects the gradient slots {BX,BY} onto b and {CX,CY,CZ}
     308              : !>       onto c. A conflict arises only on a non-orthorhombic cell, and only
     309              : !>       when the mask fixes part of the b vector or part of the c vector.
     310              : ! **************************************************************************************************
     311           74 :    PURE FUNCTION keep_angles_conflict(mask, orthorhombic) RESULT(conflict)
     312              :       INTEGER, INTENT(IN)                                :: mask
     313              :       LOGICAL, INTENT(IN)                                :: orthorhombic
     314              :       LOGICAL                                            :: conflict
     315              : 
     316              :       conflict = .NOT. orthorhombic .AND. &
     317           74 :                  (fixes_partially(mask, mask_b) .OR. fixes_partially(mask, mask_c))
     318           74 :    END FUNCTION keep_angles_conflict
     319              : 
     320              : ! **************************************************************************************************
     321              : !> \brief Whether a CONSTRAINT mask conflicts with the KEEP_SYMMETRY projection
     322              : !> \param mask bit mask of fixed cell components
     323              : !> \param symmetry_id crystal system of the cell (cell_sym_* constants)
     324              : !> \return conflict flag; the caller aborts when it is true
     325              : !> \note Each crystal system couples a set of cell components. A conflict
     326              : !>       arises when the mask fixes part of such a set. Systems without
     327              : !>       coupling never conflict.
     328              : ! **************************************************************************************************
     329           74 :    PURE FUNCTION keep_symmetry_conflict(mask, symmetry_id) RESULT(conflict)
     330              :       INTEGER, INTENT(IN)                                :: mask, symmetry_id
     331              :       LOGICAL                                            :: conflict
     332              : 
     333              :       INTEGER                                            :: coupled_set
     334              : 
     335           74 :       coupled_set = keep_symmetry_coupled_set(symmetry_id)
     336           74 :       conflict = coupled_set /= 0 .AND. fixes_partially(mask, coupled_set)
     337           74 :    END FUNCTION keep_symmetry_conflict
     338              : 
     339              : ! **************************************************************************************************
     340              : !> \brief The set of cell components coupled by a crystal system (for diagnostics)
     341              : !> \param symmetry_id crystal system of the cell (cell_sym_* constants)
     342              : !> \return mask of the coupled set; zero for systems without coupling
     343              : !> \note Mirrors the SELECT CASE in keep_symmetry_conflict. The abort messages
     344              : !>       print this label so the user can see which components the symmetry
     345              : !>       couples against their constraint.
     346              : ! **************************************************************************************************
     347           74 :    PURE FUNCTION keep_symmetry_coupled_set(symmetry_id) RESULT(coupled_set)
     348              :       INTEGER, INTENT(IN)                                :: symmetry_id
     349              :       INTEGER                                            :: coupled_set
     350              : 
     351           74 :       SELECT CASE (symmetry_id)
     352              :       CASE (cell_sym_cubic)
     353            6 :          coupled_set = mask_diag
     354              :       CASE (cell_sym_rhombohedral)
     355            6 :          coupled_set = mask_all
     356              :       CASE (cell_sym_hexagonal_gamma_60, &
     357              :             cell_sym_hexagonal_gamma_120, &
     358              :             cell_sym_monoclinic_gamma_ab)
     359           18 :          coupled_set = mask_ab_plane
     360              :       CASE (cell_sym_tetragonal_ab)
     361            8 :          coupled_set = mask_diag_ab
     362              :       CASE (cell_sym_tetragonal_ac)
     363            6 :          coupled_set = mask_diag_ac
     364              :       CASE (cell_sym_tetragonal_bc)
     365            6 :          coupled_set = mask_diag_bc
     366              :       CASE DEFAULT
     367           74 :          coupled_set = 0
     368              :       END SELECT
     369           74 :    END FUNCTION keep_symmetry_coupled_set
     370              : 
     371              : ! **************************************************************************************************
     372              : !> \brief Whether the masked pressure criterion is structurally off target
     373              : !> \param mask bit mask of fixed cell components (comp_* / mask_* bits)
     374              : !> \param orthorhombic whether the cell is orthorhombic
     375              : !> \param keep_angles whether the KEEP_ANGLES projection is active
     376              : !> \return pinned flag; the caller warns, the run stays legal
     377              : !> \note On a non-orthorhombic cell the masked pressure criterion (the
     378              : !>       average of the diagonal stresses over the free diagonal slots)
     379              : !>       equals the external pressure at the constrained minimum only when
     380              : !>       the shear stresses conjugate to the pinned shear components vanish.
     381              : !>       That needs the shear components BX, CX, CY to stay free. The
     382              : !>       criterion is therefore not guaranteed to converge when the mask
     383              : !>       pins a shear component, or when KEEP_ANGLES pins the cell angles
     384              : !>       while a diagonal slot is fixed. Zero or all diagonal slots fixed
     385              : !>       stay fine (the hydrostatic trace, resp. the external-pressure
     386              : !>       bypass). Measured on a UO2 cell: the deviation stays at -117 bar
     387              : !>       for CONSTRAINT Z on a triclinic cell without KEEP_ANGLES and at
     388              : !>       +110 bar for CONSTRAINT A with KEEP_ANGLES, static under forced
     389              : !>       continuation, while a symmetric-angle setup converges to -0.003
     390              : !>       bar. The projected-stress criterion planned in #6031 removes the
     391              : !>       limitation.
     392              : !>       The orthorhombic branch is evaluated at setup: a run that starts
     393              : !>       orthorhombic can grow oblique cell components when a shear slot is
     394              : !>       pinned while its partners stay free. That residual is second order
     395              : !>       (two evolved shears must couple into a checked diagonal) and the
     396              : !>       legacy direction masks X, Y, Z are immune to it because their
     397              : !>       pinned slots stay zero, where the coupling vanishes.
     398              : ! **************************************************************************************************
     399          270 :    PURE LOGICAL FUNCTION pres_crit_shear_pinned(mask, orthorhombic, keep_angles)
     400              :       INTEGER, INTENT(IN)                                :: mask
     401              :       LOGICAL, INTENT(IN)                                :: orthorhombic, keep_angles
     402              : 
     403          270 :       pres_crit_shear_pinned = .FALSE.
     404          270 :       IF (.NOT. orthorhombic) THEN
     405           94 :          IF (IAND(mask, mask_diag) /= mask_diag) THEN
     406           90 :             pres_crit_shear_pinned = IAND(mask, mask_shear) /= 0
     407           90 :             IF (.NOT. pres_crit_shear_pinned) THEN
     408           82 :                pres_crit_shear_pinned = keep_angles .AND. IAND(mask, mask_diag) /= 0
     409              :             END IF
     410              :          END IF
     411              :       END IF
     412          270 :    END FUNCTION pres_crit_shear_pinned
     413              : 
     414              : ! **************************************************************************************************
     415              : !> \brief Classify one CONSTRAINT token without aborting (decision half of the parser)
     416              : !> \param token one upper-case token of the CONSTRAINT value line
     417              : !> \param accepted .TRUE. when the token contributes a mask
     418              : !> \param token_mask the component mask contributed by an accepted token
     419              : !> \param reject_reason empty for accepted tokens; the abort message otherwise
     420              : !> \note parse_cell_constraint applies this classification and turns a rejection
     421              : !>       into a cp_abort. Keeping the decision here keeps it unit-testable:
     422              : !>       the unit test checks the classification table without triggering aborts.
     423              : ! **************************************************************************************************
     424          458 :    PURE SUBROUTINE parse_token(token, accepted, token_mask, reject_reason)
     425              :       CHARACTER(LEN=*), INTENT(IN)                       :: token
     426              :       LOGICAL, INTENT(OUT)                               :: accepted
     427              :       INTEGER, INTENT(OUT)                               :: token_mask
     428              :       CHARACTER(LEN=default_string_length), INTENT(OUT)  :: reject_reason
     429              : 
     430          458 :       accepted = .TRUE.
     431          458 :       token_mask = 0
     432          458 :       reject_reason = ""
     433           82 :       SELECT CASE (token)
     434              :       CASE ("A", "AX")
     435           82 :          token_mask = mask_a
     436              :       CASE ("B")
     437           20 :          token_mask = mask_b
     438              :       CASE ("BX")
     439           56 :          token_mask = comp_bx
     440              :       CASE ("BY")
     441           64 :          token_mask = comp_by
     442              :       CASE ("C", "Z")
     443           22 :          token_mask = mask_c
     444              :       CASE ("CX")
     445           56 :          token_mask = comp_cx
     446              :       CASE ("CY")
     447           56 :          token_mask = comp_cy
     448              :       CASE ("CZ")
     449           64 :          token_mask = comp_cz
     450              :       CASE ("X")
     451            8 :          token_mask = mask_x
     452              :       CASE ("Y")
     453            6 :          token_mask = mask_y
     454              :       CASE ("XY")
     455            6 :          token_mask = mask_xy
     456              :       CASE ("XZ")
     457            4 :          token_mask = mask_xz
     458              :       CASE ("YZ")
     459            4 :          token_mask = mask_yz
     460              :       CASE ("ALPHA", "BETA", "GAMMA")
     461            2 :          accepted = .FALSE.
     462            2 :          reject_reason = "CELL_OPT CONSTRAINT on angles (ALPHA, BETA, GAMMA) is not supported yet."
     463              :       CASE ("AY", "AZ", "BZ")
     464            6 :          accepted = .FALSE.
     465              :          reject_reason = "CELL_OPT CONSTRAINT token "//TRIM(token)//" is always zero in "// &
     466              :                          "the cell convention (a lies along x, b lies in the xy plane) "// &
     467            6 :                          "and cannot be constrained."
     468              :       CASE DEFAULT
     469            2 :          accepted = .FALSE.
     470              :          reject_reason = "Unknown CELL_OPT CONSTRAINT token: "//TRIM(token)// &
     471              :                          ". Accepted tokens: NONE, the directions X, Y, Z,"// &
     472              :                          " XY, XZ, YZ, the vectors A, B, C, and the components"// &
     473          458 :                          " AX, BX, BY, CX, CY, CZ. Several tokens may be combined."
     474              :       END SELECT
     475          458 :    END SUBROUTINE parse_token
     476              : 
     477              : ! **************************************************************************************************
     478              : !> \brief Computes the derivatives for the cell
     479              : !> \param gradient ...
     480              : !> \param av_ptens ...
     481              : !> \param pres_ext ...
     482              : !> \param cell ...
     483              : !> \param mtrx ...
     484              : !> \param keep_angles ...
     485              : !> \param keep_symmetry ...
     486              : !> \param pres_int ...
     487              : !> \param pres_constr ...
     488              : !> \param constraint_id ...
     489              : !> \author Teodoro Laino [tlaino] - University of Zurich - 03.2008
     490              : ! **************************************************************************************************
     491         5333 :    SUBROUTINE get_dg_dh(gradient, av_ptens, pres_ext, cell, mtrx, keep_angles, &
     492              :                         keep_symmetry, pres_int, pres_constr, constraint_id)
     493              : 
     494              :       REAL(KIND=dp), DIMENSION(:), POINTER               :: gradient
     495              :       REAL(KIND=dp), DIMENSION(3, 3), INTENT(IN)         :: av_ptens
     496              :       REAL(KIND=dp), INTENT(IN)                          :: pres_ext
     497              :       TYPE(cell_type), POINTER                           :: cell
     498              :       REAL(KIND=dp), DIMENSION(3, 3), INTENT(IN)         :: mtrx
     499              :       LOGICAL, INTENT(IN), OPTIONAL                      :: keep_angles, keep_symmetry
     500              :       REAL(KIND=dp), INTENT(OUT)                         :: pres_int, pres_constr
     501              :       INTEGER, INTENT(IN), OPTIONAL                      :: constraint_id
     502              : 
     503              :       INTEGER                                            :: i, my_constraint_id
     504              :       LOGICAL                                            :: my_keep_angles, my_keep_symmetry
     505              :       LOGICAL, DIMENSION(3)                              :: diag_free
     506              :       REAL(KIND=dp), DIMENSION(3)                        :: diag_ptens
     507              :       REAL(KIND=dp), DIMENSION(3, 3)                     :: correction, pten_hinv_old, ptens
     508              : 
     509         5333 :       my_keep_angles = .FALSE.
     510         5333 :       IF (PRESENT(keep_angles)) my_keep_angles = keep_angles
     511         5333 :       my_keep_symmetry = .FALSE.
     512         5333 :       IF (PRESENT(keep_symmetry)) my_keep_symmetry = keep_symmetry
     513        37331 :       gradient = 0.0_dp
     514         5333 :       IF (PRESENT(constraint_id)) THEN
     515         5333 :          my_constraint_id = constraint_id
     516              :       ELSE
     517            0 :          my_constraint_id = fix_none
     518              :       END IF
     519              : 
     520        37331 :       gradient = 0.0_dp
     521              : 
     522         5333 :       ptens = av_ptens
     523              : 
     524              :       ! Evaluating the internal pressure
     525         5333 :       pres_int = 0.0_dp
     526        21332 :       DO i = 1, 3
     527        21332 :          pres_int = pres_int + ptens(i, i)
     528              :       END DO
     529         5333 :       pres_int = pres_int/3.0_dp
     530              : 
     531              :       ! Nominal pressure averaged over the relaxed diagonal cell components
     532              :       ! (AX, BY, CZ couple to the diagonal of the pressure tensor). If all
     533              :       ! diagonal components are fixed, no pressure criterion can be relaxed
     534              :       ! and the external pressure trivially satisfies the tolerance.
     535              :       diag_free = [IAND(my_constraint_id, comp_ax) == 0, &
     536              :                    IAND(my_constraint_id, comp_by) == 0, &
     537        21332 :                    IAND(my_constraint_id, comp_cz) == 0]
     538        21332 :       diag_ptens = [ptens(1, 1), ptens(2, 2), ptens(3, 3)]
     539         5395 :       IF (ANY(diag_free)) THEN
     540        42640 :          pres_constr = SUM(diag_ptens, MASK=diag_free)/COUNT(diag_free)
     541              :       ELSE
     542            3 :          pres_constr = pres_ext
     543              :       END IF
     544              : 
     545         5333 :       ptens(1, 1) = av_ptens(1, 1) - pres_ext
     546         5333 :       ptens(2, 2) = av_ptens(2, 2) - pres_ext
     547         5333 :       ptens(3, 3) = av_ptens(3, 3) - pres_ext
     548              : 
     549       277316 :       pten_hinv_old = cell%deth*MATMUL(cell%h_inv, ptens)
     550       213320 :       correction = MATMUL(mtrx, cell%hmat)
     551              : 
     552         5333 :       gradient(1) = pten_hinv_old(1, 1) - correction(1, 1)
     553         5333 :       gradient(2) = pten_hinv_old(2, 1) - correction(2, 1)
     554         5333 :       gradient(3) = pten_hinv_old(2, 2) - correction(2, 2)
     555         5333 :       gradient(4) = pten_hinv_old(3, 1) - correction(3, 1)
     556         5333 :       gradient(5) = pten_hinv_old(3, 2) - correction(3, 2)
     557         5333 :       gradient(6) = pten_hinv_old(3, 3) - correction(3, 3)
     558              : 
     559         5333 :       CALL apply_cell_constraints(gradient, cell, my_keep_angles, my_keep_symmetry, my_constraint_id)
     560              : 
     561        37331 :       gradient = -gradient
     562              : 
     563         5333 :    END SUBROUTINE get_dg_dh
     564              : 
     565              : ! **************************************************************************************************
     566              : !> \brief Apply cell constraints
     567              : !> \param gradient ...
     568              : !> \param cell ...
     569              : !> \param keep_angles ...
     570              : !> \param keep_symmetry ...
     571              : !> \param constraint_id ...
     572              : !> \author Matthias Krack (October 26, 2017, MK)
     573              : ! **************************************************************************************************
     574         6005 :    SUBROUTINE apply_cell_constraints(gradient, cell, keep_angles, keep_symmetry, constraint_id)
     575              : 
     576              :       REAL(KIND=dp), DIMENSION(:), POINTER               :: gradient
     577              :       TYPE(cell_type), POINTER                           :: cell
     578              :       LOGICAL, INTENT(IN)                                :: keep_angles, keep_symmetry
     579              :       INTEGER, INTENT(IN)                                :: constraint_id
     580              : 
     581              :       INTEGER                                            :: slot
     582              :       REAL(KIND=dp)                                      :: a, a_length, ab_length, b_length, cosa, &
     583              :                                                             cosah, cosg, deriv_gamma, g, gamma, &
     584              :                                                             norm, norm_b, norm_c, sina, sinah, sing
     585              : 
     586         6005 :       IF (keep_angles) THEN
     587              :          ! If we want to keep the angles constant we have to project out the
     588              :          ! components of the cell angles
     589         3568 :          norm_b = DOT_PRODUCT(cell%hmat(:, 2), cell%hmat(:, 2))
     590         2676 :          norm = DOT_PRODUCT(cell%hmat(1:2, 2), gradient(2:3))
     591         4460 :          gradient(2:3) = cell%hmat(1:2, 2)/norm_b*norm
     592         3568 :          norm_c = DOT_PRODUCT(cell%hmat(:, 3), cell%hmat(:, 3))
     593         3568 :          norm = DOT_PRODUCT(cell%hmat(1:3, 3), gradient(4:6))
     594         6244 :          gradient(4:6) = cell%hmat(1:3, 3)/norm_c*norm
     595              :          ! Retain an exact orthorhombic cell
     596              :          ! (off-diagonal elements must remain zero identically to keep QS fast)
     597          892 :          IF (cell%orthorhombic) THEN
     598          238 :             gradient(2) = 0.0_dp
     599          238 :             gradient(4) = 0.0_dp
     600          238 :             gradient(5) = 0.0_dp
     601              :          END IF
     602              :       END IF
     603              : 
     604         6005 :       IF (keep_symmetry) THEN
     605         3184 :          SELECT CASE (cell%symmetry_id)
     606              :          CASE (cell_sym_cubic, &
     607              :                cell_sym_tetragonal_ab, &
     608              :                cell_sym_tetragonal_ac, &
     609              :                cell_sym_tetragonal_bc, &
     610              :                cell_sym_orthorhombic)
     611          104 :             SELECT CASE (cell%symmetry_id)
     612              :             CASE (cell_sym_cubic)
     613          104 :                g = (gradient(1) + gradient(3) + gradient(6))/3.0_dp
     614          104 :                gradient(1) = g
     615          104 :                gradient(3) = g
     616          104 :                gradient(6) = g
     617              :             CASE (cell_sym_tetragonal_ab, &
     618              :                   cell_sym_tetragonal_ac, &
     619              :                   cell_sym_tetragonal_bc)
     620          588 :                SELECT CASE (cell%symmetry_id)
     621              :                CASE (cell_sym_tetragonal_ab)
     622          190 :                   g = 0.5_dp*(gradient(1) + gradient(3))
     623          190 :                   gradient(1) = g
     624          190 :                   gradient(3) = g
     625              :                CASE (cell_sym_tetragonal_ac)
     626           97 :                   g = 0.5_dp*(gradient(1) + gradient(6))
     627           97 :                   gradient(1) = g
     628           97 :                   gradient(6) = g
     629              :                CASE (cell_sym_tetragonal_bc)
     630           90 :                   g = 0.5_dp*(gradient(3) + gradient(6))
     631           90 :                   gradient(3) = g
     632          377 :                   gradient(6) = g
     633              :                END SELECT
     634              :             CASE (cell_sym_orthorhombic)
     635              :                ! Nothing else to do
     636              :             END SELECT
     637          588 :             gradient(2) = 0.0_dp
     638          588 :             gradient(4) = 0.0_dp
     639          588 :             gradient(5) = 0.0_dp
     640              :          CASE (cell_sym_hexagonal_gamma_60)
     641          240 :             g = 0.5_dp*(gradient(1) + 0.5_dp*(gradient(2) + sqrt3*gradient(3)))
     642          240 :             gradient(1) = g
     643          240 :             gradient(2) = 0.5_dp*g
     644          240 :             gradient(3) = sqrt3*gradient(2)
     645          240 :             gradient(4) = 0.0_dp
     646          240 :             gradient(5) = 0.0_dp
     647              :          CASE (cell_sym_hexagonal_gamma_120)
     648          122 :             g = 0.5_dp*(gradient(1) - 0.5_dp*(gradient(2) - sqrt3*gradient(3)))
     649          122 :             gradient(1) = g
     650          122 :             gradient(2) = -0.5_dp*g
     651          122 :             gradient(3) = -sqrt3*gradient(2)
     652          122 :             gradient(4) = 0.0_dp
     653          122 :             gradient(5) = 0.0_dp
     654              :          CASE (cell_sym_rhombohedral)
     655              :             a = (angle(cell%hmat(:, 3), cell%hmat(:, 2)) + &
     656              :                  angle(cell%hmat(:, 1), cell%hmat(:, 3)) + &
     657          105 :                  angle(cell%hmat(:, 1), cell%hmat(:, 2)))/3.0_dp
     658          105 :             cosa = COS(a)
     659          105 :             sina = SIN(a)
     660          105 :             cosah = COS(0.5_dp*a)
     661          105 :             sinah = SIN(0.5_dp*a)
     662          105 :             norm = cosa/cosah
     663          105 :             norm_c = SQRT(1.0_dp - norm*norm)
     664              :             g = (gradient(1) + gradient(2)*cosa + gradient(3)*sina + &
     665          105 :                  gradient(4)*cosah*norm + gradient(5)*sinah*norm + gradient(6)*norm_c)/3.0_dp
     666          105 :             gradient(1) = g
     667          105 :             gradient(2) = g*cosa
     668          105 :             gradient(3) = g*sina
     669          105 :             gradient(4) = g*cosah*norm
     670          105 :             gradient(5) = g*sinah*norm
     671          105 :             gradient(6) = g*norm_c
     672              :          CASE (cell_sym_monoclinic)
     673         1191 :             gradient(2) = 0.0_dp
     674         1191 :             gradient(5) = 0.0_dp
     675              :          CASE (cell_sym_monoclinic_gamma_ab)
     676              :             ! Cell symmetry with a = b, alpha = beta = 90 degree and gammma not equal 90 degree
     677              :             a_length = SQRT(cell%hmat(1, 1)*cell%hmat(1, 1) + &
     678              :                             cell%hmat(2, 1)*cell%hmat(2, 1) + &
     679          112 :                             cell%hmat(3, 1)*cell%hmat(3, 1))
     680              :             b_length = SQRT(cell%hmat(1, 2)*cell%hmat(1, 2) + &
     681              :                             cell%hmat(2, 2)*cell%hmat(2, 2) + &
     682          112 :                             cell%hmat(3, 2)*cell%hmat(3, 2))
     683          112 :             ab_length = 0.5_dp*(a_length + b_length)
     684          112 :             gamma = angle(cell%hmat(:, 1), cell%hmat(:, 2))
     685          112 :             cosg = COS(gamma)
     686          112 :             sing = SIN(gamma)
     687              :             ! Here, g is the average derivative of the cell vector length ab_length,
     688              :             ! and deriv_gamma is the derivative of the angle gamma
     689          112 :             g = 0.5_dp*(gradient(1) + cosg*gradient(2) + sing*gradient(3))
     690          112 :             deriv_gamma = (gradient(3)*cosg - gradient(2)*sing)/b_length
     691          112 :             gradient(1) = g
     692          112 :             gradient(2) = g*cosg - ab_length*sing*deriv_gamma
     693          112 :             gradient(3) = g*sing + ab_length*cosg*deriv_gamma
     694          112 :             gradient(4) = 0.0_dp
     695         2596 :             gradient(5) = 0.0_dp
     696              :          CASE (cell_sym_triclinic)
     697              :             ! Nothing to do
     698              :          END SELECT
     699              :       END IF
     700              : 
     701              :       ! Invariant: the projections above run first, the hard constraint
     702              :       ! zeroing runs last. KEEP_VOLUME is handled separately by
     703              :       ! rescale_new_cell_volume. No projected gradient survives into a fixed
     704              :       ! slot. The cell block is always the last six components of the
     705              :       ! optimization vector.
     706        42035 :       DO slot = 1, 6
     707        42035 :          IF (slot_is_fixed(constraint_id, slot)) gradient(slot) = 0.0_dp
     708              :       END DO
     709              : 
     710         6005 :    END SUBROUTINE apply_cell_constraints
     711              : 
     712              : ! **************************************************************************************************
     713              : !> \brief Rescale x(idg+1:idg+6) according to vol
     714              : !> \param vol ...
     715              : !> \param x ...
     716              : !> \param idg ...
     717              : ! **************************************************************************************************
     718         2284 :    SUBROUTINE rescale_new_cell_volume(vol, x, idg)
     719              : 
     720              :       REAL(KIND=dp), INTENT(IN)                          :: vol
     721              :       REAL(KIND=dp), DIMENSION(:), POINTER               :: x
     722              :       INTEGER, INTENT(IN)                                :: idg
     723              : 
     724              :       INTEGER                                            :: i, j, my_idg
     725              :       REAL(KIND=dp)                                      :: ratio
     726              :       REAL(KIND=dp), DIMENSION(3, 3)                     :: hmat_tmp
     727              : 
     728         2284 :       CPASSERT((SIZE(x) == idg + 6))
     729         2284 :       my_idg = idg
     730              : 
     731         2284 :       hmat_tmp(:, :) = 0.0_dp
     732         9136 :       DO i = 1, 3
     733        22840 :          DO j = 1, i
     734        13704 :             my_idg = my_idg + 1
     735        20556 :             hmat_tmp(j, i) = x(my_idg)
     736              :          END DO
     737              :       END DO
     738              : 
     739         2284 :       ratio = vol/ABS(det_3x3(hmat_tmp))
     740         2284 :       ratio = ratio**(1.0_dp/3.0_dp) ! no need to use EXP(LOG(ratio)/3) for cube root
     741              : 
     742         2284 :       my_idg = idg
     743         9136 :       DO i = 1, 3
     744        22840 :          DO j = 1, i
     745        13704 :             my_idg = my_idg + 1
     746        20556 :             x(my_idg) = x(my_idg)*ratio
     747              :          END DO
     748              :       END DO
     749              : 
     750         2284 :    END SUBROUTINE rescale_new_cell_volume
     751              : 
     752              : END MODULE cell_opt_utils
        

Generated by: LCOV version 2.0-1