51 USE ieee_arithmetic,
ONLY: ieee_is_finite
60 INTEGER,
PARAMETER,
PUBLIC ::
slot_ax = 1, &
66 INTEGER,
PARAMETER,
PUBLIC ::
geom_a = 1, &
75 INTEGER,
PARAMETER,
PUBLIC ::
comp_ax = 2**0, &
96 INTEGER,
PARAMETER,
PUBLIC ::
gfix_a = 2**6, &
110 REAL(kind=
dp),
PARAMETER,
PRIVATE :: max_cell_coord = 1.0e90_dp
111 REAL(kind=
dp),
PARAMETER,
PRIVATE :: collinear_cos_margin = 1.0e-8_dp
112 REAL(kind=
dp),
PARAMETER,
PRIVATE :: licq_pivot_tol = 1.0e-6_dp
113 REAL(kind=
dp),
PARAMETER,
PRIVATE :: basis_tie_rel_tol = 1.0e-8_dp
114 REAL(kind=
dp),
PARAMETER,
PRIVATE :: default_chart_tol = 1.0e-12_dp
115 INTEGER,
PARAMETER,
PRIVATE :: default_chart_max_iter = 100
116 REAL(kind=
dp),
PARAMETER,
PRIVATE :: proj_pressure_rel_tol = 1.0e-12_dp
117 REAL(kind=
dp),
PARAMETER,
PRIVATE :: redundancy_probe_frac = 5.0e-2_dp
118 REAL(kind=
dp),
PARAMETER,
PRIVATE :: redundancy_chart_tol = 1.0e-13_dp
119 REAL(kind=
dp),
PARAMETER,
PRIVATE :: redundancy_rel_tol = 1.0e-9_dp
120 REAL(kind=
dp),
PARAMETER,
PRIVATE :: redundancy_drift_rel_tol = 1.0e-10_dp
121 REAL(kind=
dp),
PARAMETER,
PRIVATE :: redundancy_curv_rel_tol = 5.0e-12_dp
122 REAL(kind=
dp),
PARAMETER,
PRIVATE :: semidef_fd_step = 1.0e-3_dp
123 REAL(kind=
dp),
PARAMETER,
PRIVATE :: semidef_fd_shrink = 0.25_dp
124 INTEGER,
PARAMETER,
PRIVATE :: semidef_fd_max_tries = 6
125 REAL(kind=
dp),
PARAMETER,
PRIVATE :: semidef_lam_rel_tol = 1.0e-11_dp
126 REAL(kind=
dp),
PARAMETER,
PRIVATE :: semidef_min_curv = 1.0e-8_dp
127 REAL(kind=
dp),
PARAMETER,
PRIVATE :: semidef_eig_rel_tol = 1.0e-5_dp
128 REAL(kind=
dp),
PARAMETER,
PRIVATE :: semidef_vertex_rel_tol = 1.0e-3_dp
129 REAL(kind=
dp),
PARAMETER,
PRIVATE :: semidef_fd_noise_floor = 1.0e-11_dp
130 REAL(kind=
dp),
PARAMETER,
PRIVATE :: semidef_vec_zero_tol = 1.0e-9_dp
131 REAL(kind=
dp),
PARAMETER,
PRIVATE :: semidef_max_vec_tol = 1.0e-4_dp
151 INTEGER :: n_slot_rows = 0
153 INTEGER :: n_geom_rows = 0
156 INTEGER :: n_lin_rows = 0
159 INTEGER :: n_eq_rows = 0
165 REAL(kind=
dp) :: tol = default_chart_tol
166 INTEGER :: max_iter = default_chart_max_iter
167 INTEGER :: last_iter = 0
168 REAL(kind=
dp) :: last_resid = 0.0_dp
186 EXTERNAL ::
dgeqrf, dgetrf, dgetrs, dorgqr, dpotrf, dpotrs, dsyev
201 REAL(kind=
dp),
INTENT(IN) :: q0(
n_slot)
202 INTEGER,
INTENT(IN) :: mask
203 INTEGER,
INTENT(OUT) :: info
204 REAL(kind=
dp),
INTENT(IN),
OPTIONAL :: lin_w(:, :)
205 INTEGER,
INTENT(IN),
OPTIONAL :: eq_pair(:, :)
207 INTEGER,
PARAMETER :: &
210 INTEGER :: i, n_eq, n_lin
211 REAL(kind=
dp) :: weight_norm
225 IF (iand(mask, comp_flags(i)) /= 0)
THEN
226 m%n_slot_rows = m%n_slot_rows + 1
227 m%slot_rows(m%n_slot_rows) = i
231 IF (iand(mask, gfix_flags(i)) /= 0)
THEN
232 m%n_geom_rows = m%n_geom_rows + 1
233 m%geom_rows(m%n_geom_rows) = i
237 IF (
PRESENT(lin_w))
THEN
238 n_lin =
SIZE(lin_w, 2)
245 weight_norm = norm2(lin_w(:, i))
246 IF (weight_norm <= tiny(1.0_dp))
THEN
250 m%lin_w(:, i) = lin_w(:, i)/weight_norm
255 IF (
PRESENT(eq_pair))
THEN
256 n_eq =
SIZE(eq_pair, 2)
257 IF (
SIZE(eq_pair, 1) /= 2 .OR. n_eq >
n_geom - 1)
THEN
263 eq_pair(1, i) == eq_pair(2, i))
THEN
267 m%eq_pair(:, i) = eq_pair(:, i)
272 m%k = m%n_slot_rows + m%n_geom_rows + m%n_lin_rows + m%n_eq_rows
279 CALL build_chart_basis(m, info)
290 REAL(kind=
dp),
INTENT(IN) :: q(
n_slot)
291 REAL(kind=
dp),
INTENT(OUT) :: s(
n_geom)
292 INTEGER,
INTENT(OUT) :: info
294 REAL(kind=
dp) :: ax, bx, by, cos_ang(3), cx, cy, cz, &
295 vec_a(3), vec_b(3), vec_c(3)
298 IF (.NOT. all(ieee_is_finite(q)))
THEN
302 IF (any(abs(q) > max_cell_coord))
THEN
320 vec_a = [ax, 0.0_dp, 0.0_dp]
321 vec_b = [bx, by, 0.0_dp]
326 cos_ang(1) = dot_product(vec_b, vec_c)/(s(
geom_b)*s(
geom_c))
327 cos_ang(2) = dot_product(vec_a, vec_c)/(s(
geom_a)*s(
geom_c))
328 cos_ang(3) = dot_product(vec_a, vec_b)/(s(
geom_a)*s(
geom_b))
329 IF (any(abs(cos_ang) > 1.0_dp - collinear_cos_margin))
THEN
347 REAL(kind=
dp),
INTENT(IN) :: q(
n_slot)
349 INTEGER,
INTENT(OUT) :: info
351 REAL(kind=
dp) :: ax, bx, by, cos_ang(3), cx, cy, cz, db_dq(
n_slot), dc_dq(
n_slot), &
352 ddot_dq(
n_slot), len_a, len_b, len_c, s(
n_geom), sin_ang(3)
370 db_dq = [0.0_dp, bx/len_b, by/len_b, 0.0_dp, 0.0_dp, 0.0_dp]
373 dc_dq = [0.0_dp, 0.0_dp, 0.0_dp, cx/len_c, cy/len_c, cz/len_c]
376 cos_ang = [dot_product([bx, by, 0.0_dp], [cx, cy, cz])/(len_b*len_c), &
377 (ax*cx)/(len_a*len_c), &
378 (ax*bx)/(len_a*len_b)]
379 cos_ang = min(1.0_dp, max(-1.0_dp, cos_ang))
383 ddot_dq = [0.0_dp, cx, cy, bx, by, 0.0_dp]
384 j(
geom_alpha, :) = angle_grad_row(cos_ang(1), sin_ang(1), ddot_dq, len_b, len_c, db_dq, dc_dq)
388 ddot_dq = [cx, 0.0_dp, 0.0_dp, ax, 0.0_dp, 0.0_dp]
389 j(
geom_beta, :) = angle_grad_row(cos_ang(2), sin_ang(2), ddot_dq, len_a, len_c, &
394 ddot_dq = [bx, ax, 0.0_dp, 0.0_dp, 0.0_dp, 0.0_dp]
395 j(
geom_gamma, :) = angle_grad_row(cos_ang(3), sin_ang(3), ddot_dq, len_a, len_b, &
399 j(
geom_v, :) = [by*cz, 0.0_dp, ax*cz, 0.0_dp, 0.0_dp, ax*by]
412 REAL(kind=
dp),
INTENT(IN) :: q(
n_slot)
413 REAL(kind=
dp),
INTENT(OUT) :: r(m%k)
414 INTEGER,
INTENT(OUT) :: info
421 IF (.NOT. all(ieee_is_finite(q)))
THEN
432 DO i = 1, m%n_slot_rows
433 r(i) = q(m%slot_rows(i)) - m%q0(m%slot_rows(i))
435 DO i = 1, m%n_geom_rows
436 row = m%n_slot_rows + i
437 r(row) = s(m%geom_rows(i)) - m%s0(m%geom_rows(i))
439 DO i = 1, m%n_lin_rows
440 row = m%n_slot_rows + m%n_geom_rows + i
441 r(row) = dot_product(m%lin_w(:, i), q) - dot_product(m%lin_w(:, i), m%q0)
443 DO i = 1, m%n_eq_rows
444 row = m%n_slot_rows + m%n_geom_rows + m%n_lin_rows + i
445 r(row) = s(m%eq_pair(1, i)) - s(m%eq_pair(2, i))
459 REAL(kind=
dp),
INTENT(IN) :: q(
n_slot)
460 REAL(kind=
dp),
INTENT(OUT) :: a(m%k,
n_slot)
461 INTEGER,
INTENT(OUT) :: info
473 DO i = 1, m%n_slot_rows
474 a(i, m%slot_rows(i)) = 1.0_dp
476 DO i = 1, m%n_geom_rows
477 row = m%n_slot_rows + i
478 a(row, :) = j(m%geom_rows(i), :)
480 DO i = 1, m%n_lin_rows
481 row = m%n_slot_rows + m%n_geom_rows + i
482 a(row, :) = m%lin_w(:, i)
484 DO i = 1, m%n_eq_rows
485 row = m%n_slot_rows + m%n_geom_rows + m%n_lin_rows + i
486 a(row, :) = j(m%eq_pair(1, i), :) - j(m%eq_pair(2, i), :)
503 REAL(kind=
dp),
INTENT(IN) :: u(m%ndof)
504 REAL(kind=
dp),
INTENT(OUT) :: q(
n_slot)
505 INTEGER,
INTENT(OUT) :: info
507 INTEGER :: it, lapack_info
509 row_scales(m%k), row_norms(m%k), &
510 r(m%k), dw(m%k), b(m%k, m%k), &
515 IF (.NOT. all(ieee_is_finite(u)))
THEN
518 m%last_resid = max_cell_coord
522 IF (any(abs(u) > max_cell_coord))
THEN
525 m%last_resid = max_cell_coord
531 m%last_resid = 0.0_dp
534 IF (m%ndof == 0)
THEN
545 qt = m%q0 + matmul(m%Z(:, 1:m%ndof), u - m%u0(1:m%ndof))
546 row_scales = active_row_scales(m)
551 m%last_resid = max_cell_coord
554 m%last_resid = norm2(r)
555 rn_rel = norm2(r/row_scales)
557 DO it = 1, m%max_iter
558 IF (rn_rel <= m%tol)
EXIT
559 CALL normalized_active_jacobian(m, qt, a, row_norms, info)
561 CALL factor_normal_block(m%k, a, m%Y(:, 1:m%k), b, ipiv, info)
564 CALL dgetrs(
'N', m%k, 1, b, m%k, ipiv, dw, m%k, lapack_info)
565 IF (lapack_info /= 0)
THEN
569 qt = qt - matmul(m%Y(:, 1:m%k), dw)
573 m%last_resid = max_cell_coord
576 m%last_resid = norm2(r)
577 rn_rel = norm2(r/row_scales)
578 IF (.NOT. ieee_is_finite(m%last_resid) .OR. .NOT. ieee_is_finite(rn_rel))
THEN
598 REAL(kind=
dp),
INTENT(IN) :: q(
n_slot)
599 REAL(kind=
dp),
INTENT(OUT) :: u(m%ndof)
601 IF (m%ndof == 0)
RETURN
606 u = m%u0(1:m%ndof) + matmul(transpose(m%Z(:, 1:m%ndof)), q - m%q0)
623 REAL(kind=
dp),
INTENT(OUT) :: g_u(m%ndof)
624 INTEGER,
INTENT(OUT) :: info
626 INTEGER :: lapack_info
627 REAL(kind=
dp) :: row_norms(m%k), lam(m%k), b(m%k, m%k), &
633 IF (m%ndof == 0)
RETURN
640 CALL normalized_active_jacobian(m, q, a, row_norms, info)
643 CALL factor_normal_block(m%k, a, m%Y(:, 1:m%k), b, ipiv, info)
646 lam = matmul(transpose(m%Y(:, 1:m%k)), g_q)
647 CALL dgetrs(
'T', m%k, 1, b, m%k, ipiv, lam, m%k, lapack_info)
648 IF (lapack_info /= 0)
THEN
652 g_u = matmul(transpose(m%Z(:, 1:m%ndof)), g_q - matmul(transpose(a), lam))
672 REAL(kind=
dp),
INTENT(OUT) :: pdiff
673 LOGICAL,
INTENT(OUT) :: projected
674 INTEGER,
INTENT(OUT) :: info
676 INTEGER :: lapack_info
678 row_norms(m%k), mu(m%k), chol(m%k, m%k), a(m%k,
n_slot)
683 IF (m%ndof == 0)
RETURN
691 dv_dq = [by*cz, 0.0_dp, ax*cz, 0.0_dp, 0.0_dp, ax*by]
692 dv_norm2 = dot_product(dv_dq, dv_dq)
696 CALL normalized_active_jacobian(m, q, a, row_norms, info)
698 chol = matmul(a, transpose(a))
699 CALL dpotrf(
'U', m%k, chol, m%k, lapack_info)
700 IF (lapack_info /= 0)
THEN
704 mu = matmul(a, dv_dq)
705 CALL dpotrs(
'U', m%k, 1, chol, m%k, mu, m%k, lapack_info)
706 IF (lapack_info /= 0)
THEN
710 vt = dv_dq - matmul(transpose(a), mu)
713 denom = dot_product(vt, vt)
714 IF (denom <= proj_pressure_rel_tol*dv_norm2)
RETURN
719 pdiff = -dot_product(g_phys, vt)/denom
729 REAL(kind=
dp),
DIMENSION(3, 3),
INTENT(IN) :: hmat
730 REAL(kind=
dp),
DIMENSION(n_slot) :: q
732 q = [hmat(1, 1), hmat(1, 2), hmat(2, 2), hmat(1, 3), hmat(2, 3), hmat(3, 3)]
742 REAL(kind=
dp),
DIMENSION(n_slot),
INTENT(IN) :: q
743 REAL(kind=
dp),
DIMENSION(3, 3),
INTENT(OUT) :: hmat
761 INTEGER,
INTENT(IN) :: info
762 CHARACTER(LEN=default_string_length) :: label
768 label =
"degenerate or non-positive cell"
770 label =
"cell constraints are linearly dependent or ill-conditioned"
772 label =
"nonlinear cell constraint solver did not converge"
774 label =
"invalid constraint mask or row specification"
776 label =
"unknown cell-constraint status"
791 INTEGER,
INTENT(IN) :: mask
792 REAL(kind=
dp),
INTENT(IN),
OPTIONAL :: lin_w(:, :)
793 INTEGER,
INTENT(IN),
OPTIONAL :: eq_pair(:, :)
795 REAL(kind=
dp),
PARAMETER :: inv_sqrt2 = sqrt(0.5_dp)
797 INTEGER :: i, i_probe, info, j, n_probes
798 REAL(kind=
dp) :: anchor_limit, cand_scales(1), curv_limit, drift_limit, q_probe(
n_slot), &
799 r0(1), r_neg(1), r_pos(1), r_snap(1), rel_norm, step_u, z_dir(
n_slot)
801 REAL(kind=
dp) :: u_probe(m%ndof), &
802 u_dirs(m%ndof, m%ndof*m%ndof)
804 is_redundant = .false.
806 IF (info /=
info_ok .OR. m_cand%k /= 1)
RETURN
808 cand_scales = active_row_scales(m_cand)
809 anchor_limit = redundancy_rel_tol*cand_scales(1)
810 drift_limit = redundancy_drift_rel_tol*cand_scales(1)
811 curv_limit = redundancy_curv_rel_tol*cand_scales(1)
814 IF (info /=
info_ok .OR. abs(r0(1)) > anchor_limit)
RETURN
816 IF (m%ndof == 0)
THEN
817 is_redundant = .true.
822 m_work%tol = min(m%tol, redundancy_chart_tol)
829 IF (info /=
info_ok .OR. abs(r_snap(1)) > anchor_limit)
RETURN
831 n_probes = m%ndof*m%ndof
835 i_probe = i_probe + 1
836 u_dirs(i, i_probe) = 1.0_dp
838 i_probe = i_probe + 1
839 u_dirs(i, i_probe) = inv_sqrt2
840 u_dirs(j, i_probe) = inv_sqrt2
841 i_probe = i_probe + 1
842 u_dirs(i, i_probe) = inv_sqrt2
843 u_dirs(j, i_probe) = -inv_sqrt2
847 DO i_probe = 1, n_probes
848 z_dir = matmul(m%Z(:, 1:m%ndof), u_dirs(:, i_probe))
855 step_u = redundancy_probe_frac/rel_norm
856 u_probe = m%u0(1:m%ndof) + step_u*u_dirs(:, i_probe)
861 u_probe = m%u0(1:m%ndof) - step_u*u_dirs(:, i_probe)
866 IF (max(abs(r_pos(1) - r_snap(1)), abs(r_neg(1) - r_snap(1))) > drift_limit)
RETURN
867 IF (abs(r_pos(1) + r_neg(1) - 2.0_dp*r_snap(1)) > curv_limit)
RETURN
870 is_redundant = .true.
888 INTEGER,
INTENT(IN) :: mask
889 LOGICAL,
INTENT(OUT) :: is_pinned
890 REAL(kind=
dp),
INTENT(IN),
OPTIONAL :: lin_w(:, :)
891 INTEGER,
INTENT(IN),
OPTIONAL :: eq_pair(:, :)
893 INTEGER :: col, i, info, j, lapack_info, lwork, &
894 mask_new, max_slot, n_lin_basis, &
895 n_lin_new, n_pin, ndof, slot
896 LOGICAL :: ok, slot_fixed(
n_slot)
897 REAL(kind=
dp) :: cand_grad(
n_slot), cand_norm, cand_scales(1), eig_thresh, g_tilde(
n_slot), &
900 pin_norm, r0(1), slot_scales(
n_slot), vec_tol, w_col(
n_slot), w_norm, work_query(1), &
902 REAL(kind=
dp),
ALLOCATABLE :: work(:)
904 REAL(kind=
dp) :: z_tilde(
n_slot, m%ndof), w_pin(
n_slot, m%ndof), h_red(m%ndof, m%ndof), &
905 eigvecs(m%ndof, m%ndof), eigvals(m%ndof)
909 IF (ndof <= 0)
RETURN
910 IF (mask /= 0 .AND. iand(m%mask, mask) /= 0)
RETURN
913 IF (info /=
info_ok .OR. m_cand%k /= 1)
RETURN
915 cand_scales = active_row_scales(m_cand)
917 IF (info /=
info_ok .OR. abs(r0(1)) > redundancy_rel_tol*cand_scales(1))
RETURN
921 CALL lagrangian_hessian_at_q0(m, m_cand, cand_scales(1), slot_scales, &
922 cand_grad, lag_grad, g_tilde, h_tilde, ok)
924 cand_norm = norm2(cand_grad)
927 z_scaled = m%Z(:, j)/slot_scales
928 z_tilde(:, j) = z_scaled/norm2(z_scaled)
930 h_red = matmul(transpose(z_tilde), matmul(h_tilde, z_tilde))
931 h_red = 0.5_dp*(h_red + transpose(h_red))
932 IF (.NOT. all(ieee_is_finite(h_red)))
RETURN
935 CALL dsyev(
'V',
'U', ndof, eigvecs, ndof, eigvals, work_query, -1, lapack_info)
936 IF (lapack_info /= 0)
RETURN
937 lwork = int(work_query(1))
938 ALLOCATE (work(max(1, lwork)))
939 CALL dsyev(
'V',
'U', ndof, eigvecs, ndof, eigvals, work,
SIZE(work), lapack_info)
941 IF (lapack_info /= 0)
RETURN
943 max_abs_eig = maxval(abs(eigvals))
944 IF (max_abs_eig <= semidef_min_curv)
RETURN
945 eig_thresh = semidef_eig_rel_tol*max_abs_eig
946 IF (maxval(eigvals) > eig_thresh .AND. minval(eigvals) < -eig_thresh)
RETURN
949 DO i = 1, m%n_slot_rows
950 slot_fixed(m%slot_rows(i)) = .true.
954 DO i = 1, m%n_lin_rows
955 lin_col = m%lin_w(:, i)*slot_scales
956 WHERE (slot_fixed) lin_col = 0.0_dp
957 DO j = 1, n_lin_basis
958 lin_col = lin_col - dot_product(lin_basis(:, j), lin_col)*lin_basis(:, j)
960 w_norm = norm2(lin_col)
961 IF (w_norm > semidef_vec_zero_tol)
THEN
962 n_lin_basis = n_lin_basis + 1
963 lin_basis(:, n_lin_basis) = lin_col/w_norm
970 min_pin_norm = huge(1.0_dp)
972 IF (abs(eigvals(j)) > eig_thresh)
THEN
974 mode_vec = matmul(z_tilde, eigvecs(:, j))
975 IF (abs(dot_product(mode_vec, g_tilde)) > semidef_vertex_rel_tol*abs(eigvals(j)))
RETURN
976 pin_col = matmul(h_tilde, mode_vec)
977 WHERE (slot_fixed) pin_col = 0.0_dp
978 DO i = 1, n_lin_basis
979 pin_col = pin_col - dot_product(lin_basis(:, i), pin_col)*lin_basis(:, i)
981 pin_norm = norm2(pin_col)
982 IF (pin_norm <= tiny(1.0_dp))
RETURN
983 min_pin_norm = min(min_pin_norm, pin_norm)
984 w_pin(:, n_pin) = pin_col/pin_norm
987 IF (n_pin == 0)
RETURN
988 vec_tol = max(semidef_vec_zero_tol, semidef_fd_noise_floor/min_pin_norm)
989 IF (vec_tol > semidef_max_vec_tol)
RETURN
991 CALL echelon_pin_columns(w_pin(:, 1:n_pin), n_pin, vec_tol, ok)
996 n_lin_new = m%n_lin_rows
998 pin_col = w_pin(:, col)
999 WHERE (abs(pin_col) <= vec_tol*maxval(abs(pin_col))) pin_col = 0.0_dp
1000 w_col = pin_col/slot_scales
1001 w_norm = norm2(w_col)
1002 IF (w_norm <= tiny(1.0_dp))
RETURN
1003 w_col = w_col/w_norm
1004 IF (count(w_col /= 0.0_dp) == 1)
THEN
1005 slot = maxloc(abs(w_col), dim=1)
1006 mask_new = ior(mask_new, comp_flags(slot))
1008 max_slot = maxloc(abs(w_col), dim=1)
1009 IF (w_col(max_slot) < 0.0_dp) w_col = -w_col
1010 n_lin_new = n_lin_new + 1
1011 IF (n_lin_new >
n_slot)
RETURN
1012 lin_w_new(:, n_lin_new) = w_col
1017 lin_w=lin_w_new(:, 1:n_lin_new), &
1018 eq_pair=m%eq_pair(:, 1:m%n_eq_rows))
1019 IF (info /=
info_ok .OR. m_pinned%ndof /= ndof - n_pin)
RETURN
1021 IF (m_pinned%ndof > 0)
THEN
1022 IF (norm2(matmul(transpose(m_pinned%Z(:, 1:m_pinned%ndof)), lag_grad)) > &
1023 redundancy_chart_tol*cand_norm)
RETURN
1038 PURE FUNCTION active_row_scales(m)
RESULT(scales)
1040 REAL(kind=
dp) :: scales(m%k)
1042 INTEGER :: g, i, p1, p2, row
1043 REAL(kind=
dp) :: coord_scale
1045 coord_scale = maxval(abs(m%q0))
1046 DO i = 1, m%n_slot_rows
1047 scales(i) = coord_scale
1049 DO i = 1, m%n_geom_rows
1050 row = m%n_slot_rows + i
1053 scales(row) = 1.0_dp
1055 scales(row) = abs(m%s0(g))
1058 DO i = 1, m%n_lin_rows
1059 row = m%n_slot_rows + m%n_geom_rows + i
1060 scales(row) = coord_scale
1062 DO i = 1, m%n_eq_rows
1063 row = m%n_slot_rows + m%n_geom_rows + m%n_lin_rows + i
1064 p1 = m%eq_pair(1, i)
1065 p2 = m%eq_pair(2, i)
1067 scales(row) = 1.0_dp
1069 scales(row) = max(abs(m%s0(p1)), abs(m%s0(p2)))
1073 END FUNCTION active_row_scales
1087 SUBROUTINE lagrangian_hessian_at_q0(m, m_cand, cand_scale, slot_scales, &
1088 cand_grad, lag_grad, g_tilde, H_tilde, ok)
1090 REAL(kind=
dp),
INTENT(IN) :: cand_scale, slot_scales(
n_slot)
1091 REAL(kind=
dp),
INTENT(OUT) :: cand_grad(
n_slot), lag_grad(
n_slot), &
1094 LOGICAL,
INTENT(OUT) :: ok
1096 REAL(kind=
dp),
PARAMETER :: richardson_w1 = 8.0_dp/12.0_dp, richardson_w2 = 1.0_dp/12.0_dp, &
1097 stencil_offsets(4) = [1.0_dp, -1.0_dp, 2.0_dp, -2.0_dp]
1099 INTEGER :: i_pt, i_try, info, lapack_info, slot
1100 LOGICAL :: stencil_ok
1102 stencil_grad(
n_slot, 4), step_q, row_norms(m%k), lam_raw(m%k), lam(m%k), b(m%k, m%k), &
1104 INTEGER :: ipiv(m%k)
1114 cand_grad = a_cand(1, :)
1115 cand_norm = norm2(cand_grad)
1116 IF (cand_norm <= tiny(1.0_dp))
RETURN
1117 IF (norm2(matmul(transpose(m%Z(:, 1:m%ndof)), cand_grad)) > licq_pivot_tol*cand_norm)
RETURN
1119 lag_grad = cand_grad
1121 CALL normalized_active_jacobian(m, m%q0, a_norm, row_norms, info)
1123 CALL factor_normal_block(m%k, a_norm, m%Y(:, 1:m%k), b, ipiv, info)
1125 lam = matmul(transpose(m%Y(:, 1:m%k)), cand_grad)
1126 CALL dgetrs(
'T', m%k, 1, b, m%k, ipiv, lam, m%k, lapack_info)
1127 IF (lapack_info /= 0)
RETURN
1128 WHERE (abs(lam) <= semidef_lam_rel_tol*cand_norm) lam = 0.0_dp
1129 lam_raw = lam/row_norms
1130 lag_grad = cand_grad - matmul(transpose(a_norm), lam)
1133 g_tilde = (slot_scales/cand_scale)*lag_grad
1136 step_q = semidef_fd_step*slot_scales(slot)
1137 stencil_ok = .false.
1138 DO i_try = 1, semidef_fd_max_tries
1140 DO i_pt = 1,
SIZE(stencil_offsets)
1142 q_pert(slot) = q_pert(slot) + stencil_offsets(i_pt)*step_q
1145 stencil_ok = .false.
1148 stencil_grad(:, i_pt) = a_cand(1, :)
1152 stencil_ok = .false.
1155 stencil_grad(:, i_pt) = stencil_grad(:, i_pt) - matmul(transpose(a_raw), lam_raw)
1158 IF (stencil_ok)
EXIT
1159 step_q = semidef_fd_shrink*step_q
1161 IF (.NOT. stencil_ok)
RETURN
1162 dg_dq = (richardson_w1*(stencil_grad(:, 1) - stencil_grad(:, 2)) - &
1163 richardson_w2*(stencil_grad(:, 3) - stencil_grad(:, 4)))/step_q
1164 h_tilde(:, slot) = (slot_scales*slot_scales(slot)/cand_scale)*dg_dq
1167 h_tilde = 0.5_dp*(h_tilde + transpose(h_tilde))
1168 IF (.NOT. all(ieee_is_finite(h_tilde)))
RETURN
1171 END SUBROUTINE lagrangian_hessian_at_q0
1182 PURE SUBROUTINE echelon_pin_columns(W_pin, n_pin, vec_tol, ok)
1183 INTEGER,
INTENT(IN) :: n_pin
1184 REAL(kind=
dp),
INTENT(INOUT) :: w_pin(
n_slot, n_pin)
1185 REAL(kind=
dp),
INTENT(IN) :: vec_tol
1186 LOGICAL,
INTENT(OUT) :: ok
1188 INTEGER :: col, i, j, piv_col, piv_row, tmp_row, &
1190 LOGICAL :: row_used(
n_slot)
1191 REAL(kind=
dp) :: piv_val, tmp_col(
n_slot)
1200 IF (row_used(i)) cycle
1202 IF (abs(w_pin(i, j)) > piv_val)
THEN
1203 piv_val = abs(w_pin(i, j))
1209 IF (piv_row == 0 .OR. piv_val <= vec_tol)
RETURN
1210 row_used(piv_row) = .true.
1211 piv_rows(col) = piv_row
1212 IF (piv_col /= col)
THEN
1213 tmp_col = w_pin(:, col)
1214 w_pin(:, col) = w_pin(:, piv_col)
1215 w_pin(:, piv_col) = tmp_col
1217 w_pin(:, col) = w_pin(:, col)/w_pin(piv_row, col)
1219 IF (j /= col) w_pin(:, j) = w_pin(:, j) - w_pin(piv_row, j)*w_pin(:, col)
1223 DO col = 1, n_pin - 1
1224 DO j = col + 1, n_pin
1225 IF (piv_rows(j) < piv_rows(col))
THEN
1226 tmp_row = piv_rows(col)
1227 piv_rows(col) = piv_rows(j)
1228 piv_rows(j) = tmp_row
1229 tmp_col = w_pin(:, col)
1230 w_pin(:, col) = w_pin(:, j)
1231 w_pin(:, j) = tmp_col
1237 END SUBROUTINE echelon_pin_columns
1248 SUBROUTINE build_chart_basis(m, info)
1250 INTEGER,
INTENT(OUT) :: info
1252 INTEGER :: cs, i, info_lapack, j, lwork, piv, pmax
1253 LOGICAL :: slot_used(
n_slot)
1256 REAL(kind=
dp),
ALLOCATABLE :: work(:)
1257 REAL(kind=
dp) :: tau(m%k), row_norms(m%k), &
1258 gram(m%k, m%k), gdiag(m%k), &
1273 CALL normalized_active_jacobian(m, m%q0, a, row_norms, info)
1276 gram = matmul(a, transpose(a))
1277 CALL dpotrf(
'U', m%k, gram, m%k, info_lapack)
1278 IF (info_lapack /= 0)
THEN
1282 gdiag = [(gram(i, i), i=1, m%k)]
1283 IF (minval(gdiag) <= licq_pivot_tol*maxval(gdiag))
THEN
1290 lwork = int(work_query(1))
1294 lwork = max(lwork, int(work_query(1)))
1295 ALLOCATE (work(max(1, lwork)))
1298 IF (info_lapack /= 0)
THEN
1307 IF (info_lapack /= 0)
THEN
1314 pmax = maxloc(abs(qfull(:, j)), dim=1)
1315 IF (qfull(pmax, j) < 0.0_dp) qfull(:, j) = -qfull(:, j)
1318 m%Y(:, 1:m%k) = qfull(:, 1:m%k)
1325 IF (m%ndof > 0)
THEN
1328 z_rows(:, 1:m%ndof) = qfull(:, m%k + 1:
n_slot)
1331 row_norms_slot = 0.0_dp
1333 IF (.NOT. slot_used(i)) row_norms_slot(i) = norm2(z_rows(i, 1:m%ndof))
1337 piv = findloc(row_norms_slot >= (1.0_dp - basis_tie_rel_tol)*maxval(row_norms_slot), &
1339 c_rot(1:m%ndof, cs) = z_rows(piv, 1:m%ndof)/row_norms_slot(piv)
1340 slot_used(piv) = .true.
1342 IF (.NOT. slot_used(i))
THEN
1343 z_rows(i, 1:m%ndof) = z_rows(i, 1:m%ndof) - &
1344 dot_product(z_rows(i, 1:m%ndof), c_rot(1:m%ndof, cs))*c_rot(1:m%ndof, cs)
1348 m%Z(:, 1:m%ndof) = matmul(qfull(:, m%k + 1:
n_slot), c_rot(1:m%ndof, 1:m%ndof))
1351 m%u0(1:m%ndof) = matmul(transpose(m%Z(:, 1:m%ndof)), m%q0)
1353 END SUBROUTINE build_chart_basis
1365 SUBROUTINE normalized_active_jacobian(m, q, A, row_norms, info)
1367 REAL(kind=
dp),
INTENT(IN) :: q(
n_slot)
1368 REAL(kind=
dp),
INTENT(OUT) :: a(m%k,
n_slot), row_norms(m%k)
1369 INTEGER,
INTENT(OUT) :: info
1378 row_norms(i) = norm2(a(i, :))
1379 IF (row_norms(i) <= tiny(1.0_dp))
THEN
1383 a(i, :) = a(i, :)/row_norms(i)
1386 END SUBROUTINE normalized_active_jacobian
1400 SUBROUTINE factor_normal_block(k, A, Y, B, ipiv, info)
1401 INTEGER,
INTENT(IN) :: k
1403 REAL(kind=
dp),
INTENT(OUT) :: b(k, k)
1404 INTEGER,
INTENT(OUT) :: ipiv(k), info
1406 INTEGER :: i, lapack_info
1407 REAL(kind=
dp) :: u_pivots(k)
1410 CALL dgetrf(k, k, b, k, ipiv, lapack_info)
1411 u_pivots = [(abs(b(i, i)), i=1, k)]
1412 IF (lapack_info /= 0 .OR. minval(u_pivots) <= licq_pivot_tol*maxval(u_pivots))
THEN
1418 END SUBROUTINE factor_normal_block
1431 PURE FUNCTION angle_grad_row(cos_ang, sin_ang, ddot_dq, len_u, len_v, dlen_u_dq, dlen_v_dq) &
1433 REAL(kind=
dp),
INTENT(IN) :: cos_ang, sin_ang, ddot_dq(
n_slot), &
1434 len_u, len_v, dlen_u_dq(
n_slot), &
1440 dcos_dq = ddot_dq/(len_u*len_v) - cos_ang*(dlen_u_dq/len_u + dlen_v_dq/len_v)
1441 d_ang_dq = -dcos_dq/sin_ang
1443 END FUNCTION angle_grad_row
Reduced-coordinate parameterization of cell constraints for CELL_OPT.
integer, parameter, public info_no_converge
integer, parameter, public comp_ax
subroutine, public cell_manifold_jacobian(q, j, info)
Compute the analytic 7 x 6 Jacobian J = ds/dq of the geometric quantities.
integer, parameter, public comp_cx
integer, parameter, public geom_gamma
integer, parameter, public geom_beta
integer, parameter, public gfix_gamma
subroutine, public cell_manifold_pin_semidefinite(m, mask, is_pinned, lin_w, eq_pair)
Pin implied directions when a candidate constraint is semidefinite at q0.
integer, parameter, public slot_cx
integer, parameter, public n_constraint_bits
integer, parameter, public mask_a
integer, parameter, public slot_by
integer, parameter, public gfix_c
integer, parameter, public gfix_v
integer, parameter, public info_degenerate
subroutine, public cell_manifold_chart_pullback_grad(m, q, g_q, g_u, info)
Transform a 6-component cell gradient g_q into the reduced gradient g_u = (dq/du)^T g_q.
integer, parameter, public info_bad_input
integer, parameter, public n_geom
integer, parameter, public n_slot
integer, parameter, public mask_yz
real(kind=dp), parameter, public min_cell_length
integer, parameter, public gfix_all
integer, parameter, public slot_ax
integer, parameter, public gfix_a
subroutine, public cell_manifold_chart_inv(m, q, u)
Map a canonical cell q back to reduced coordinates u = u0 + Z^T (q - q0).
integer, parameter, public gfix_b
subroutine, public cell_manifold_residual(m, q, r, info)
Evaluate the k active constraint residuals r(q).
subroutine, public cell_manifold_init(m, q0, mask, info, lin_w, eq_pair)
Record constraint targets at q0, check row independence, and build [Y, Z].
integer, parameter, public comp_by
integer, parameter, public mask_y
subroutine, public cell_manifold_proj_pressure(m, q, g_phys, pdiff, projected, info)
Compute the pressure residual P_constr - pres_ext along unconstrained cell directions.
integer, parameter, public geom_a
integer, parameter, public mask_x
subroutine, public cell_manifold_chart_eval(m, u, q, info)
Map reduced coordinates u to a canonical cell q(u) that satisfies r(q) = 0.
integer, parameter, public info_licq
integer, parameter, public slot_cz
integer, parameter, public gfix_beta
integer, parameter, public mask_xy
pure subroutine, public cell_manifold_unpack_hmat(q, hmat)
Write q(1:6) into the upper-triangular canonical cell matrix.
integer, parameter, public mask_c
logical function, public cell_manifold_is_redundant(m, mask, lin_w, eq_pair)
Test whether one candidate constraint stays zero on manifold m near q0.
integer, parameter, public slot_bx
integer, parameter, public mask_b
subroutine, public cell_manifold_geometry(q, s, info)
Compute cell lengths, angles in radians, and volume s(q).
integer, parameter, public mask_all
integer, parameter, public mask_xz
pure character(len=default_string_length) function, public cell_manifold_status_label(info)
Return a short description of a cell_manifold_utils status code.
integer, parameter, public comp_cz
pure real(kind=dp) function, dimension(n_slot), public cell_manifold_pack_hmat(hmat)
Pack the upper-triangular canonical cell matrix into q(1:6).
integer, parameter, public comp_bx
integer, parameter, public slot_cy
integer, parameter, public comp_cy
integer, parameter, public mask_z
integer, parameter, public geom_c
integer, parameter, public geom_v
integer, parameter, public geom_alpha
integer, parameter, public gfix_alpha
integer, parameter, public geom_b
subroutine, public cell_manifold_active_jacobian(m, q, a, info)
Assemble the k x 6 Jacobian A(q) = dr/dq of the active constraints.
integer, parameter, public info_ok
Defines the basic variable types.
integer, parameter, public dp
integer, parameter, public default_string_length
Active constraint set and fixed coordinate bases anchored at q0.