// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced // at the Lawrence Livermore National Laboratory. All Rights reserved. See files // LICENSE and NOTICE for details. LLNL-CODE-806117. // // This file is part of the MFEM library. For more information and source code // availability visit https://mfem.org. // // MFEM is free software; you can redistribute it and/or modify it under the // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. #include "mma.hpp" #include "vector.hpp" #include "../general/communication.hpp" #include "../general/error.hpp" #include #include namespace { // check if C++ 14 or beyond #if __cplusplus >= 201402L inline ::std::unique_ptr<::mfem::real_t[]> allocArray(int size) { return ::std::make_unique<::mfem::real_t[]>(size); } #else inline ::std::unique_ptr<::mfem::real_t[]> allocArray(int size) { return ::std::unique_ptr<::mfem::real_t[]>(new ::mfem::real_t[size]); } #endif } #ifdef MFEM_USE_LAPACK extern "C" void dgesv_(int* nLAP, int* nrhs, double* AA, int* lda, int* ipiv, double* bb, int* ldb, int* info); extern "C" void sgesv_(int* nLAP, int* nrhs, float* AA, int* lda, int* ipiv, float* bb, int* ldb, int* info); #endif namespace mfem { void solveLU(int nCon, real_t* AA1, real_t* bb1) { // Solve linear system with LU decomposition ifndef LAPACK int nLAP = nCon + 1; // Convert AA1 to matrix A and bb1 to vector B ::std::unique_ptr<::std::unique_ptr[]> A( new ::std::unique_ptr[nLAP]); for (int i = 0; i < nLAP; ++i) { A[i] = allocArray(nLAP); } ::std::unique_ptr B = allocArray(nLAP); for (int i = 0; i < nLAP; ++i) { for (int j = 0; j < nLAP; ++j) { A[i][j] = AA1[j * nLAP + i]; } B[i] = bb1[i]; } // Perform LU decomposition ::std::unique_ptr<::std::unique_ptr[]> L( new ::std::unique_ptr[nLAP]), U(new ::std::unique_ptr[nLAP]); for (int i = 0; i < nLAP; ++i) { L[i] = allocArray(nLAP); U[i] = allocArray(nLAP); for (int j = 0; j < nLAP; ++j) { L[i][j] = 0.0; U[i][j] = 0.0; } } for (int i = 0; i < nLAP; ++i) { for (int k = i; k < nLAP; ++k) { real_t sum = 0.0; for (int j = 0; j < i; ++j) { sum += (L[i][j] * U[j][k]); } U[i][k] = A[i][k] - sum; } for (int k = i; k < nLAP; ++k) { if (i == k) { L[i][i] = 1.0; } else { real_t sum = 0.0; for (int j = 0; j < i; ++j) { sum += (L[k][j] * U[j][i]); } L[k][i] = (A[k][i] - sum) / U[i][i]; } } } // Check for singular matrix for (int i = 0; i < nLAP; ++i) { if (U[i][i] == 0.0) { MFEM_ABORT("Error: matrix in MMA LU Solve is singular."); } } // Forward substitution to solve L * Y = B ::std::unique_ptr Y=allocArray(nLAP); for (int i = 0; i < nLAP; ++i) { real_t sum = 0.0; for (int j = 0; j < i; ++j) { sum += L[i][j] * Y[j]; } Y[i] = (B[i] - sum) / L[i][i]; } // Backward substitution to solve U * X = Y ::std::unique_ptr X=allocArray(nLAP); for (int i = nLAP - 1; i >= 0; --i) { real_t sum = 0.0; for (int j = i + 1; j < nLAP; ++j) { sum += U[i][j] * X[j]; } X[i] = (Y[i] - sum) / U[i][i]; } // Copy results back to bb1 for (int i = 0; i < (nCon + 1); i++) { bb1[i] = X[i]; } } void MMA::MMASubSvanberg::AllocSubData(int nvar, int ncon) { epsi = 1.0; ittt = itto = itera = 0; raa0 = 0.00001; move = 0.5; albefa = 0.1; xmamieps = 1e-5; ux1 = allocArray(nvar); // ini xl1 = allocArray(nvar); // ini plam = allocArray(nvar); // ini qlam = allocArray(nvar); // ini gvec = allocArray(ncon); // ini residu = allocArray(3 * nvar + 4 * ncon + 2); // ini GG = allocArray(nvar * ncon); // ini delx = allocArray(nvar); // ini dely = allocArray(ncon); // ini dellam = allocArray(ncon); // ini dellamyi = allocArray(ncon); diagx = allocArray(nvar); // ini diagy = allocArray(ncon); // ini diaglamyi = allocArray(ncon); // ini bb = allocArray(nvar + 1); bb1 = allocArray(ncon + 1); // ini Alam = allocArray(ncon * ncon); // ini AA = allocArray((nvar + 1) * (nvar + 1)); AA1 = allocArray((ncon + 1) * (ncon + 1)); // ini dlam = allocArray(ncon); // ini dx = allocArray(nvar); // ini dy = allocArray(ncon); // ini dxsi = allocArray(nvar); // ini deta = allocArray(nvar); // ini dmu = allocArray(ncon); // ini Axx = allocArray(nvar * ncon); // ini axz = allocArray(nvar); // ini ds = allocArray(ncon); // ini xx = allocArray(4 * ncon + 2 * nvar + 2); // ini dxx = allocArray(4 * ncon + 2 * nvar + 2); // ini stepxx = allocArray(4 * ncon + 2 * nvar + 2); // ini sum = 0; sum1 = allocArray(nvar); stepalfa = allocArray(nvar); // ini stepbeta = allocArray(nvar); // ini xold = allocArray(nvar); // ini yold = allocArray(ncon); // ini lamold = allocArray(ncon); // ini xsiold = allocArray(nvar); // ini etaold = allocArray(nvar); // ini muold = allocArray(ncon); // ini sold = allocArray(ncon); // ini q0 = allocArray(nvar); // ini p0 = allocArray(nvar); // ini P = allocArray(ncon * nvar); // ini Q = allocArray(ncon * nvar); // ini alfa = allocArray(nvar); // ini beta = allocArray(nvar); // ini xmami = allocArray(nvar); b = allocArray(ncon); // ini b_local = allocArray(ncon); gvec_local = allocArray(ncon); Alam_local = allocArray(ncon * ncon); sum_local = allocArray(ncon); sum_global = allocArray(ncon); for (int i=0; i<(3 * nvar + 4 * ncon + 2); i++) { residu[i]=0.0; } } void MMA::MMASubSvanberg::Update(const real_t* dfdx, const real_t* gx, const real_t* dgdx, const real_t* xmin, const real_t* xmax, const real_t* xval) { MMA& mma = this->mma_ref; int rank = 0; #ifdef MFEM_USE_MPI MPI_Comm_rank(mma.comm, &rank); #endif int ncon = mma.nCon; int nvar = mma.nVar; real_t zero = 0.0; ittt = 0; itto = 0; epsi = 1.0; itera = 0; mma.z = 1.0; mma.zet = 1.0; for (int i = 0; i < ncon; i++) { b[i] = 0.0; b_local[i] = 0.0; } for (int i = 0; i < nvar; i++) { // Calculation of bounds alfa and beta according to: // alfa = max{xmin, low + 0.1(xval-low), xval-0.5(xmax-xmin)} // beta = min{xmax, upp - 0.1(upp-xval), xval+0.5(xmax-xmin)} alfa[i] = std::max(std::max(mma.low[i] + albefa * (xval[i] - mma.low[i]), xval[i] - move * (xmax[i] - xmin[i])), xmin[i]); beta[i] = std::min(std::min(mma.upp[i] - albefa * (mma.upp[i] - xval[i]), xval[i] + move * (xmax[i] - xmin[i])), xmax[i]); xmami[i] = std::max(xmax[i] - xmin[i], xmamieps); // Calculations of p0, q0, P, Q, and b ux1[i] = mma.upp[i] - xval[i]; if (std::fabs(ux1[i]) <= mma.machineEpsilon) { ux1[i] = mma.machineEpsilon; } xl1[i] = xval[i] - mma.low[i]; if (std::fabs(xl1[i]) <= mma.machineEpsilon) { xl1[i] = mma.machineEpsilon; } p0[i] = ( std::max(dfdx[i], zero) + 0.001 * (std::max(dfdx[i], zero) + std::max(-dfdx[i], zero)) + raa0 / xmami[i]) * ux1[i] * ux1[i]; q0[i] = ( std::max(-dfdx[i], zero) + 0.001 * (std::max(dfdx[i], zero) + std::max(-dfdx[i], zero)) + raa0 / xmami[i]) * xl1[i] * xl1[i]; } // P = max(dgdx,0) // Q = max(-dgdx,0) // P = P + 0.001(P+Q) + raa0/xmami // Q = Q + 0.001(P+Q) + raa0/xmami for (int i = 0; i < ncon; i++) { for (int j = 0; j < nvar; j++) { // P = P * spdiags(ux2,0,n,n) // Q = Q * spdiags(xl2,0,n,n) P[i * nvar + j] = (std::max(dgdx[i * nvar + j], zero) + 0.001 * (std::max(dgdx[i * nvar + j], zero) + std::max(-1*dgdx[i * nvar + j], zero)) + raa0 / xmami[j]) * ux1[j] * ux1[j]; Q[i * nvar + j] = (std::max(-1*dgdx[i * nvar + j], zero) + 0.001 * (std::max(dgdx[i * nvar + j], zero) + std::max(-1*dgdx[i * nvar + j], zero)) + raa0 / xmami[j]) * xl1[j] * xl1[j]; // b = P/ux1 + Q/xl1 - gx b_local[i] = b_local[i] + P[i * nvar + j] / ux1[j] + Q[i * nvar + j] / xl1[j]; } } std::copy(b_local.get(), b_local.get() + ncon, b.get()); #ifdef MFEM_USE_MPI MPI_Allreduce(b_local.get(), b.get(), ncon, MPITypeMap::mpi_type, MPI_SUM, mma.comm); #endif for (int i = 0; i < ncon; i++) { b[i] = b[i] - gx[i]; } for (int i = 0; i < nvar; i++) { mma.x[i] = 0.5 * (alfa[i] + beta[i]); mma.xsi[i] = 1.0/(mma.x[i] - alfa[i]); mma.xsi[i] = std::max(mma.xsi[i], static_cast(1.0)); mma.eta[i] = 1.0/(beta[i] - mma.x[i]); mma.eta[i] = std::max(mma.eta[i], static_cast(1.0)); ux1[i] = 0.0; xl1[i] = 0.0; } for (int i = 0; i < ncon; i++) { mma.y[i] = 1.0; mma.lam[i] = 1.0; mma.mu[i] = std::max(1.0, 0.5 * mma.c[i]); mma.s[i] = 1.0; } while (epsi > mma.epsimin) { residu[nvar + ncon] = mma.a0 - mma.zet; // rez for (int i = 0; i < nvar; i++) { ux1[i] = mma.upp[i] - mma.x[i]; if (std::fabs(ux1[i]) < mma.machineEpsilon) { ux1[i] = mma.machineEpsilon; } xl1[i] = mma.x[i] - mma.low[i]; if (std::fabs(xl1[i]) < mma.machineEpsilon) { xl1[i] = mma.machineEpsilon; } // plam = P' * lam, qlam = Q' * lam plam[i] = p0[i]; qlam[i] = q0[i]; for (int j = 0; j < ncon; j++) { plam[i] += P[j * nvar + i] * mma.lam[j]; qlam[i] += Q[j * nvar + i] * mma.lam[j]; residu[nvar + ncon] -= mma.a[j] * mma.lam[j]; // rez } residu[i] = plam[i] / (ux1[i] * ux1[i]) - qlam[i] / (xl1[i] * xl1[i]) - mma.xsi[i] + mma.eta[i]; // rex // residu[nvar + ncon] -= mma.a[i] * mma.lam[i]; // rez residu[nvar + ncon + 1 + ncon + i] = mma.xsi[i] * (mma.x[i] - alfa[i]) - epsi; // rexsi if (std::fabs(mma.x[i]-alfa[i]) < mma.machineEpsilon) { residu[nvar + ncon + 1 + ncon + i] = mma.xsi[i] * mma.machineEpsilon - epsi; } residu[nvar + ncon + 1 + ncon + nvar + i] = mma.eta[i] * (beta[i] - mma.x[i]) - epsi; // reeta if (std::fabs(beta[i] - mma.x[i]) < mma.machineEpsilon) { residu[nvar + ncon + 1 + ncon + nvar + i] = mma.eta[i] * mma.machineEpsilon - epsi; } } for (int i = 0; i < ncon; i++) { gvec_local[i] = 0.0; // gvec = P/ux + Q/xl for (int j = 0; j < nvar; j++) { gvec_local[i] = gvec_local[i] + P[i * nvar + j] / ux1[j] + Q[i * nvar + j] / xl1[j]; } } std::copy(gvec_local.get(), gvec_local.get() + ncon, gvec.get()); #ifdef MFEM_USE_MPI MPI_Allreduce(gvec_local.get(), gvec.get(), ncon, MPITypeMap::mpi_type, MPI_SUM, mma.comm); #endif if ( rank == 0) { for (int i = 0; i < ncon; i++) { residu[nvar + i] = mma.c[i] + mma.d[i] * mma.y[i] - mma.mu[i] - mma.lam[i]; // rey residu[nvar + ncon + 1 + i] = gvec[i] - mma.a[i] * mma.z - mma.y[i] + mma.s[i] - b[i]; // relam residu[nvar + ncon + 1 + ncon + 2 * nvar + i] = mma.mu[i] * mma.y[i] - epsi; // remu residu[nvar + ncon + 1 + 2 * nvar + 2 * ncon + 1 + i] = mma.lam[i] * mma.s[i] - epsi; // res } residu[nvar + ncon + 1 + 2 * nvar + 2 * ncon] = mma.zet * mma.z - epsi; } // Get vector product and maximum absolute value residunorm = 0.0; residumax = 0.0; for (int i = 0; i < (3 * nvar + 4 * ncon + 2); i++) { residunorm += residu[i] * residu[i]; residumax = std::max(residumax, std::abs(residu[i])); } global_norm = residunorm; global_max = residumax; #ifdef MFEM_USE_MPI MPI_Allreduce(&residunorm, &global_norm, 1 , MPITypeMap::mpi_type, MPI_SUM, mma.comm); MPI_Allreduce(&residumax, &global_max, 1 , MPITypeMap::mpi_type, MPI_MAX, mma.comm); #endif // Norm of the residual residunorm = std::sqrt(global_norm); residumax = global_max; ittt = 0; while (residumax > 0.9 * epsi && ittt < 200) { ittt++; for (int i = 0; i < nvar; i++) { ux1[i] = mma.upp[i] - mma.x[i]; if (std::fabs(ux1[i]) < mma.machineEpsilon) { ux1[i] = mma.machineEpsilon; } xl1[i] = mma.x[i] - mma.low[i]; if (std::fabs(xl1[i]) <= mma.machineEpsilon) { xl1[i] = mma.machineEpsilon; } // plam = P' * lam, qlam = Q' * lam plam[i] = p0[i]; qlam[i] = q0[i]; for (int j = 0; j < ncon; j++) { plam[i] += P[j * nvar + i] * mma.lam[j]; qlam[i] += Q[j * nvar + i] * mma.lam[j]; } // NaN-Avoidance if (std::fabs(mma.x[i] - alfa[i]) < mma.machineEpsilon) { if (std::fabs(beta[i] - mma.x[i]) < mma.machineEpsilon) { delx[i] = plam[i] / (ux1[i] * ux1[i]) - qlam[i] / (xl1[i] * xl1[i]); diagx[i] = 2 * (plam[i] / (ux1[i] * ux1[i] * ux1[i]) + qlam[i] / (xl1[i] * xl1[i] * xl1[i])) + mma.xsi[i] / mma.machineEpsilon + mma.eta[i] / mma.machineEpsilon; } else { delx[i] = plam[i] / (ux1[i] * ux1[i]) - qlam[i] / (xl1[i] * xl1[i]) - epsi / mma.machineEpsilon + epsi / (beta[i] - mma.x[i]); diagx[i] = 2 * (plam[i] / (ux1[i] * ux1[i] * ux1[i]) + qlam[i] / (xl1[i] * xl1[i] * xl1[i])) + mma.xsi[i] / (mma.x[i] - alfa[i]) + mma.eta[i] / (beta[i] - mma.x[i]); } } else if (std::fabs(beta[i] - mma.x[i]) < mma.machineEpsilon) { delx[i] = plam[i] / (ux1[i] * ux1[i]) - qlam[i] / (xl1[i] * xl1[i]) - epsi / (mma.x[i] - alfa[i]) + epsi / mma.machineEpsilon; diagx[i] = 2 * (plam[i] / (ux1[i] * ux1[i] * ux1[i]) + qlam[i] / (xl1[i] * xl1[i] * xl1[i])) + mma.xsi[i] / (mma.x[i] - alfa[i]) + mma.eta[i] / mma.machineEpsilon; } else { delx[i] = plam[i] / (ux1[i] * ux1[i]) - qlam[i] / (xl1[i] * xl1[i]) - epsi / (mma.x[i] - alfa[i]) + epsi / (beta[i] - mma.x[i]); diagx[i] = 2 * (plam[i] / (ux1[i] * ux1[i] * ux1[i]) + qlam[i] / (xl1[i] * xl1[i] * xl1[i])) + mma.xsi[i] / (mma.x[i] - alfa[i]) + mma.eta[i] / (beta[i] - mma.x[i]); } } for (int i = 0; i < ncon; i++) { gvec_local[i] = 0.0; // gvec = P/ux + Q/xl for (int j = 0; j < nvar; j++) { gvec_local[i] = gvec_local[i] + P[i * nvar + j] / ux1[j] + Q[i * nvar + j] / xl1[j]; GG[i * nvar + j] = P[i * nvar + j] / (ux1[j] * ux1[j]) - Q[i * nvar + j] / (xl1[j] * xl1[j]); } } std::copy(gvec_local.get(), gvec_local.get() + ncon, gvec.get()); #ifdef MFEM_USE_MPI MPI_Allreduce(gvec_local.get(), gvec.get(), ncon, MPITypeMap::mpi_type, MPI_SUM, mma.comm); #endif delz = mma.a0 - epsi / mma.z; for (int i = 0; i < ncon; i++) { dely[i] = mma.c[i] + mma.d[i] * mma.y[i] - mma.lam[i] - epsi / mma.y[i]; delz -= mma.a[i] * mma.lam[i]; dellam[i] = gvec[i] - mma.a[i] * mma.z - mma.y[i] - b[i] + epsi / mma.lam[i]; diagy[i] = mma.d[i] + mma.mu[i] / mma.y[i]; diaglamyi[i] = mma.s[i] / mma.lam[i] + 1.0 / diagy[i]; } if (ncon < nVar_global) { // bb1 = dellam + dely./diagy - GG*(delx./diagx); // bb1 = [bb1; delz]; for (int j = 0; j < ncon; j++) { sum_local[j] = 0.0; for (int i = 0; i < nvar; i++) { sum_local[j] = sum_local[j] + GG[j * nvar + i] * (delx[i] / diagx[i]); } } std::copy(sum_local.get(), sum_local.get() + ncon, sum_global.get()); #ifdef MFEM_USE_MPI MPI_Allreduce(sum_local.get(), sum_global.get(), ncon, MPITypeMap::mpi_type, MPI_SUM, mma.comm); #endif for (int j = 0; j < ncon; j++) { bb1[j] = - sum_global[j] + dellam[j] + dely[j] / diagy[j]; } bb1[ncon] = delz; // Alam = spdiags(diaglamyi,0,m,m) + GG*spdiags(diagxinv,0,n,n)*GG'; for (int i = 0; i < ncon; i++) { // Axx = GG*spdiags(diagxinv,0,n,n); for (int k = 0; k < nvar; k++) { Axx[i * nvar + k] = GG[k * ncon + i] / diagx[k]; } } // Alam = spdiags(diaglamyi,0,m,m) + Axx*GG'; for (int i = 0; i < ncon; i++) { for (int j = 0; j < ncon; j++) { Alam_local[i * ncon + j] = 0.0; for (int k = 0; k < nvar; k++) { Alam_local[i * ncon + j] += Axx[i * nvar + k] * GG[j * nvar + k]; } } } std::copy(Alam_local.get(), Alam_local.get() + ncon * ncon, Alam.get()); #ifdef MFEM_USE_MPI MPI_Reduce(Alam_local.get(), Alam.get(), ncon * ncon, MPITypeMap::mpi_type, MPI_SUM, 0, mma.comm); #endif if (0 == rank) { for (int i = 0; i < ncon; i++) { for (int j = 0; j < ncon; j++) { if (i == j) { Alam[i * ncon + j] += diaglamyi[i]; } } } // AA1 = [Alam a ] // [ a' -zet/z] for (int i = 0; i < ncon; i++) { for (int j = 0; j < ncon; j++) { AA1[i * (ncon + 1) + j] = Alam[i * ncon + j]; } AA1[i * (ncon + 1) + ncon] = mma.a[i]; } for (int i = 0; i < ncon; i++) { AA1[ncon * (ncon + 1) + i] = mma.a[i]; } AA1[(ncon + 1) * (ncon + 1) - 1] = -mma.zet / mma.z; #ifdef MFEM_USE_LAPACK // bb1 = AA1\bb1 --> solve linear system of equations using LAPACK int info; int nLAP = ncon + 1; int nrhs = 1; int lda = nLAP; int ldb = nLAP; int* ipiv = new int[nLAP]; #if defined(MFEM_USE_DOUBLE) dgesv_(&nLAP, &nrhs, AA1.get(), &lda, ipiv, bb1.get(), &ldb, &info); #elif defined(MFEM_USE_SINGLE) sgesv_(&nLAP, &nrhs, AA1.get(), &lda, ipiv, bb1.get(), &ldb, &info); #else #error "Only single and double precision are supported!" #endif if (info == 0) { delete[] ipiv; } else if (info > 0) { MFEM_ABORT("MMA: matrix is singular."); } else { MFEM_ABORT("MMA: Argument " << info << " in linear system solve has illegal value."); } #else solveLU(ncon, AA1.get(), bb1.get()); #endif } #ifdef MFEM_USE_MPI MPI_Bcast(bb1.get(), ncon + 1, MPITypeMap::mpi_type, 0, mma.comm); #endif // Reassign results for (int i = 0; i < ncon; i++) { dlam[i] = bb1[i]; } dz = bb1[ncon]; // dx = -(GG'*dlam)./diagx - delx./diagx; for (int i = 0; i < nvar; i++) { sum = 0.0; for (int j = 0; j < ncon; j++) { sum = sum + GG[j * nvar + i] * dlam[j]; } dx[i] = -sum / diagx[i] - delx[i] / diagx[i]; } } else { MFEM_ABORT("MMA: Optimization problem case which has more constraints than design variables is not implemented!"); } for (int i = 0; i < ncon; i++) { dy[i] = -dely[i] / diagy[i] + dlam[i] / diagy[i]; dmu[i] = -mma.mu[i] + epsi / mma.y[i] - (mma.mu[i] * dy[i]) / mma.y[i]; ds[i] = -mma.s[i] + epsi / mma.lam[i] - (mma.s[i] * dlam[i]) / mma.lam[i]; // xx = [y z lam xsi eta mu zet s] // dxx = [dy dz dlam dxsi deta dmu dzet ds] xx[i] = mma.y[i]; xx[ncon + 1 + i] = mma.lam[i]; xx[2 * ncon + 1 + 2 * nvar + i] = mma.mu[i]; xx[3 * ncon + 2 * nvar + 2 + i] = mma.s[i]; dxx[i] = dy[i]; dxx[ncon + 1 + i] = dlam[i]; dxx[2 * ncon + 1 + 2 * nvar + i] = dmu[i]; dxx[3 * ncon + 2 * nvar + 2 + i] = ds[i]; } xx[ncon] = mma.z; xx[3 * ncon + 2 * nvar + 1] = mma.zet; dxx[ncon] = dz; for (int i = 0; i < nvar; i++) { // NaN-Avoidance if (std::fabs(mma.x[i] - alfa[i]) < mma.machineEpsilon) { if (std::fabs(beta[i] - mma.x[i]) < mma.machineEpsilon) { dxsi[i] = -mma.xsi[i] + epsi / mma.machineEpsilon - (mma.xsi[i] * dx[i]) / mma.machineEpsilon; deta[i] = -mma.eta[i] + epsi / mma.machineEpsilon + (mma.eta[i] * dx[i]) / mma.machineEpsilon; } else { dxsi[i] = -mma.xsi[i] + epsi / mma.machineEpsilon - (mma.xsi[i] * dx[i]) / mma.machineEpsilon; deta[i] = -mma.eta[i] + epsi / (beta[i] - mma.x[i]) + (mma.eta[i] * dx[i]) / (beta[i] - mma.x[i]); } } else if (std::fabs(beta[i] - mma.x[i]) < mma.machineEpsilon) { dxsi[i] = -mma.xsi[i] + epsi / (mma.x[i] - alfa[i]) - (mma.xsi[i] * dx[i]) / (mma.x[i] - alfa[i]); deta[i] = -mma.eta[i] + epsi / mma.machineEpsilon + (mma.eta[i] * dx[i]) / mma.machineEpsilon; } else { dxsi[i] = -mma.xsi[i] + epsi / (mma.x[i] - alfa[i]) - (mma.xsi[i] * dx[i]) / (mma.x[i] - alfa[i]); deta[i] = -mma.eta[i] + epsi / (beta[i] - mma.x[i]) + (mma.eta[i] * dx[i]) / (beta[i] - mma.x[i]); } xx[ncon + 1 + ncon + i] = mma.xsi[i]; xx[ncon + 1 + ncon + nvar + i] = mma.eta[i]; dxx[ncon + 1 + ncon + i] = dxsi[i]; dxx[ncon + 1 + ncon + nvar + i] = deta[i]; } dzet = -mma.zet + epsi / mma.z - mma.zet * dz / mma.z; dxx[3 * ncon + 2 * nvar + 1] = dzet; stmxx = 0.0; for (int i = 0; i < (4 * ncon + 2 * nvar + 2); i++) { stepxx[i] = -1.01*dxx[i] / xx[i]; stmxx = std::max(stepxx[i], stmxx); } stmxx_global = stmxx; #ifdef MFEM_USE_MPI MPI_Allreduce(&stmxx, &stmxx_global, 1, MPITypeMap::mpi_type, MPI_MAX, mma.comm); #endif stmalfa = 0.0; stmbeta = 0.0; for (int i = 0; i < nvar; i++) { // NaN-Avoidance if (std::fabs(mma.x[i] - alfa[i]) < mma.machineEpsilon) { stepalfa[i] = -1.01*dx[i] / mma.machineEpsilon; } else { stepalfa[i] = -1.01*dx[i] / (mma.x[i] - alfa[i]); } if (std::fabs(beta[i] - mma.x[i]) < mma.machineEpsilon) { stepbeta[i] = 1.01*dx[i] / mma.machineEpsilon; } else { stepbeta[i] = 1.01*dx[i] / (beta[i] - mma.x[i]); } stmalfa = std::max(stepalfa[i], stmalfa); stmbeta = std::max(stepbeta[i], stmbeta); } stmalfa_global = stmalfa; stmbeta_global = stmbeta; #ifdef MFEM_USE_MPI MPI_Allreduce(&stmalfa, &stmalfa_global, 1, MPITypeMap::mpi_type, MPI_MAX, mma.comm); MPI_Allreduce(&stmbeta, &stmbeta_global, 1, MPITypeMap::mpi_type, MPI_MAX, mma.comm); #endif stminv = std::max(std::max(std::max(stmalfa_global, stmbeta_global), stmxx_global), static_cast(1.0)); steg = 1.0 / stminv; for (int i = 0; i < nvar; i++) { xold[i] = mma.x[i]; xsiold[i] = mma.xsi[i]; etaold[i] = mma.eta[i]; } for (int i = 0; i < ncon; i++) { yold[i] = mma.y[i]; lamold[i] = mma.lam[i]; muold[i] = mma.mu[i]; sold[i] = mma.s[i]; } zold = mma.z; zetold = mma.zet; itto = 0; resinew = 2.0 * residunorm; while (resinew > residunorm && itto < 50) { itto++; for (int i = 0; i < ncon; ++i) { mma.y[i] = yold[i] + steg * dy[i]; if (std::fabs(mma.y[i])< mma.machineEpsilon) { mma.y[i] = mma.machineEpsilon; } mma.lam[i] = lamold[i] + steg * dlam[i]; if (std::fabs(mma.lam[i])< mma.machineEpsilon ) { mma.lam[i] = mma.machineEpsilon; } mma.mu[i] = muold[i] + steg * dmu[i]; mma.s[i] = sold[i] + steg * ds[i]; } residu[nvar + ncon] = mma.a0 - mma.zet; // rez for (int i = 0; i < nvar; ++i) { mma.x[i] = xold[i] + steg * dx[i]; mma.xsi[i] = xsiold[i] + steg * dxsi[i]; mma.eta[i] = etaold[i] + steg * deta[i]; ux1[i] = mma.upp[i] - mma.x[i]; if (std::fabs(ux1[i]) < mma.machineEpsilon) { ux1[i] = mma.machineEpsilon; } xl1[i] = mma.x[i] - mma.low[i]; if (std::fabs(xl1[i]) < mma.machineEpsilon ) { xl1[i] = mma.machineEpsilon; } // plam & qlam plam[i] = p0[i]; qlam[i] = q0[i]; for (int j = 0; j < ncon; j++) { plam[i] += P[j * nvar + i] * mma.lam[j]; qlam[i] += Q[j * nvar + i] * mma.lam[j]; residu[nvar + ncon] -= mma.a[j] * mma.lam[j]; // rez } // Assembly starts here residu[i] = plam[i] / (ux1[i] * ux1[i]) - qlam[i] / (xl1[i] * xl1[i]) - mma.xsi[i] + mma.eta[i]; // rex // residu[nvar + ncon] -= mma.a[i] * mma.lam[i]; // rez residu[nvar + ncon + 1 + ncon + i] = mma.xsi[i] * (mma.x[i] - alfa[i]) - epsi; // rexsi if (std::fabs(mma.x[i] - alfa[i]) < mma.machineEpsilon) { residu[nvar + ncon + 1 + ncon + i] = mma.xsi[i] * mma.machineEpsilon - epsi; } residu[nvar + ncon + 1 + ncon + nvar + i] = mma.eta[i] * (beta[i] - mma.x[i]) - epsi; // reeta if (std::fabs(beta[i] - mma.x[i]) < mma.machineEpsilon) { residu[nvar + ncon + 1 + ncon + nvar + i] = mma.eta[i] * mma.machineEpsilon - epsi; } } mma.z = zold + steg * dz; if (std::fabs(mma.z) < mma.machineEpsilon) { mma.z = mma.machineEpsilon; } mma.zet = zetold + steg * dzet; // gvec = P/ux + Q/xl for (int i = 0; i < ncon; i++) { gvec_local[i] = 0.0; for (int j = 0; j < nvar; j++) { gvec_local[i] = gvec_local[i] + P[i * nvar + j] / ux1[j] + Q[i * nvar + j] / xl1[j]; } } std::copy(gvec_local.get(), gvec_local.get() + ncon, gvec.get()); #ifdef MFEM_USE_MPI MPI_Allreduce(gvec_local.get(), gvec.get(), ncon, MPITypeMap::mpi_type, MPI_SUM, mma.comm); #endif if (rank == 0) { for (int i = 0; i < ncon; i++) { residu[nvar + i] = mma.c[i] + mma.d[i] * mma.y[i] - mma.mu[i] - mma.lam[i]; // rey residu[nvar + ncon + 1 + i] = gvec[i] - mma.a[i] * mma.z - mma.y[i] + mma.s[i] - b[i]; // relam residu[nvar + ncon + 1 + ncon + 2 * nvar + i] = mma.mu[i] * mma.y[i] - epsi; // remu residu[nvar + ncon + 1 + 2 * nvar + 2 * ncon + 1 + i] = mma.lam[i] * mma.s[i] - epsi; // res } residu[nvar + ncon + 1 + 2 * nvar + 2 * ncon] = mma.zet * mma.z - epsi; // rezet } // Get vector product and maximum absolute value resinew = 0.0; for (int i = 0; i < (3 * nvar + 4 * ncon + 2); i++) { resinew = resinew + residu[i] * residu[i]; } global_norm = resinew; #ifdef MFEM_USE_MPI MPI_Allreduce(&resinew, &global_norm, 1, MPITypeMap::mpi_type, MPI_SUM, mma.comm); #endif // Norm of the residual resinew = std::sqrt(global_norm); steg = steg / 2.0; } residunorm = resinew; residumax = 0.0; for (int i = 0; i < (3 * nvar + 4 * ncon + 2); i++) { residumax = std::max(residumax, std::abs(residu[i])); } global_max = residumax; #ifdef MFEM_USE_MPI MPI_Allreduce(&residumax, &global_max, 1, MPITypeMap::mpi_type, MPI_MAX, mma.comm); #endif residumax = global_max; steg = steg * 2.0; } if (ittt > 198 && mma.print_level>=2) { out << "Warning: Max number of iterations reached in MMA subsolve.\n"; } epsi = 0.1 * epsi; } // returns x, y, z, lam, xsi, eta, mu, zet, s } void MMA::InitData(real_t *xval) { for (int i = 0; i < nVar; i++) { x[i]=xval[i]; xo1[i] = 0.0; xo2[i] = 0.0; } for (int i = 0; i < nCon; i++) { a[i] = 0.0; c[i] = 1000.0; d[i] = 1.0; } a0 = 1.0; } /// Serial MMA MMA::MMA(int nVar, int nCon, real_t *xval, int iter) { #ifdef MFEM_USE_MPI comm=MPI_COMM_SELF; #endif AllocData(nVar,nCon); InitData(xval); // allocate the serial subproblem #if __cplusplus >= 201402L mSubProblem = ::std::make_unique(*this, nVar, nCon); #else mSubProblem.reset(new MMA::MMASubSvanberg(*this, nVar, nCon)); #endif } MMA::MMA(const int nVar, int nCon, Vector &xval, int iter) : MMA(nVar, nCon, xval.GetData(), iter) {} #ifdef MFEM_USE_MPI MMA::MMA(MPI_Comm comm_, int nVar, int nCon, real_t *xval, int iter) { int rank = 0; MPI_Comm_rank(comm_, &rank); // create new communicator int colour; if ( 0 != nVar) { colour = 0; } else { colour = MPI_UNDEFINED; } // Split the global communicator MPI_Comm_split(comm_, colour, rank, &comm); AllocData(nVar,nCon); InitData(xval); // allocate the serial subproblem mSubProblem.reset(new MMA::MMASubSvanberg(*this, nVar, nCon)); } MMA::MMA(MPI_Comm comm_, const int nVar, const int nCon, const Vector & xval, int iter) : MMA(comm_, nVar, nCon, xval.GetData(), iter) {} #endif MMA::~MMA() { } void MMA::AllocData(int nVariables,int nConstr) { // accessed by the subproblems nVar = nVariables; nCon = nConstr; x= allocArray(nVar); // ini xo1 = allocArray(nVar); // ini xo2 = allocArray(nVar); // ini y = allocArray(nCon); // ini c = allocArray(nCon); // ini d = allocArray(nCon); // ini a = allocArray(nCon); // ini lam = allocArray(nCon); // ini xsi = allocArray(nVar); // ini eta = allocArray(nVar); // ini mu = allocArray(nCon); // ini s = allocArray(nCon); // ini z = zet = 1.0; kktnorm = 10; machineEpsilon = 1e-10; // accessed by MMA epsimin = 1e-7; asyinit = 0.5; asyincr = 1.1; asydecr = 0.7; low = allocArray(nVar); // ini upp = allocArray(nVar); // ini factor = allocArray(nVar); // ini lowmin = lowmax = uppmin = uppmax = zz = 0.0; } void MMA::Update( const Vector& dfdx, const Vector& gx, const Vector& dgdx, const Vector& xmin, const Vector& xmax, Vector& xval) { this->Update(dfdx.GetData(), gx.GetData(),dgdx.GetData(), xmin.GetData(), xmax.GetData(), xval.GetData()); } void MMA::Update( const Vector& dfdx, const Vector& xmin, const Vector& xmax, Vector& xval) { MFEM_ASSERT(0 == nCon, "MMA nCon != 0. Provide constraint values and gradients"); this->Update(dfdx.GetData(), nullptr,nullptr, xmin.GetData(), xmax.GetData(), xval.GetData()); } void MMA::Update(const real_t* dfdx, const real_t* gx,const real_t* dgdx, const real_t* xmin, const real_t* xmax, real_t* xval) { // Calculation of the asymptotes low and upp if (iter < 3) { for (int i = 0; i < nVar; i++) { low[i] = xval[i] - asyinit * (xmax[i] - xmin[i]); upp[i] = xval[i] + asyinit * (xmax[i] - xmin[i]); } } else { for (int i = 0; i < nVar; i++) { // Determine sign zz = (xval[i] - xo1[i]) * (xo1[i] - xo2[i]); if ( zz > 0.0) { factor[i] = asyincr; } else if ( zz < 0.0) { factor[i] = asydecr; } else { factor[i] = 1.0; } // Find new asymptote low[i] = xval[i] - factor[i] * (xo1[i] - low[i]); upp[i] = xval[i] + factor[i] * (upp[i] - xo1[i]); lowmin = xval[i] - 10.0 * (xmax[i] - xmin[i]); lowmax = xval[i] - 0.01 * (xmax[i] - xmin[i]); uppmin = xval[i] + 0.01 * (xmax[i] - xmin[i]); uppmax = xval[i] + 10.0 * (xmax[i] - xmin[i]); low[i] = std::max(low[i], lowmin); low[i] = std::min(low[i], lowmax); upp[i] = std::max(upp[i], uppmin); upp[i] = std::min(upp[i], uppmax); } } mSubProblem->Update(dfdx,gx,dgdx,xmin,xmax,xval); // Update design variables for (int i = 0; i < nVar; i++) { xo2[i] = xo1[i]; xo1[i] = xval[i]; xval[i] = x[i]; } iter++; } }