lu1fac Subroutine

public subroutine lu1fac(m, n, nelem, lena, luparm, parmlu, a, indc, indr, p, q, lenc, lenr, locc, locr, iploc, iqloc, ipinv, iqinv, w, inform)

! nelem = numnz !!! Don't change nelem. ! nelem is now numnz below (it might be less than the input value).

Arguments

Type IntentOptional Attributes Name
integer(kind=ip), intent(in) :: m
integer(kind=ip), intent(in) :: n
integer(kind=ip), intent(in) :: nelem
integer(kind=ip), intent(in) :: lena
integer(kind=ip), intent(inout) :: luparm(30)
real(kind=rp), intent(inout) :: parmlu(30)
real(kind=rp), intent(inout) :: a(lena)
integer(kind=ip), intent(inout) :: indc(lena)
integer(kind=ip), intent(inout) :: indr(lena)
integer(kind=ip), intent(inout) :: p(m)
integer(kind=ip), intent(inout) :: q(n)
integer(kind=ip), intent(inout) :: lenc(n)
integer(kind=ip), intent(inout) :: lenr(m)
integer(kind=ip), intent(inout) :: locc(n)
integer(kind=ip), intent(inout) :: locr(m)
integer(kind=ip), intent(inout) :: iploc(n)
integer(kind=ip), intent(inout) :: iqloc(m)
integer(kind=ip), intent(inout) :: ipinv(m)
integer(kind=ip), intent(inout) :: iqinv(n)
real(kind=rp), intent(inout) :: w(n)
integer(kind=ip), intent(out) :: inform

Calls

proc~~lu1fac~~CallsGraph proc~lu1fac lusol::lu1fac proc~lu1fad lusol::lu1fad proc~lu1fac->proc~lu1fad proc~lu1or1 lusol::lu1or1 proc~lu1fac->proc~lu1or1 proc~lu1or2 lusol::lu1or2 proc~lu1fac->proc~lu1or2 proc~lu1or3 lusol::lu1or3 proc~lu1fac->proc~lu1or3 proc~lu1or4 lusol::lu1or4 proc~lu1fac->proc~lu1or4 proc~lu1pq1 lusol::lu1pq1 proc~lu1fac->proc~lu1pq1 proc~lu1slk lusol::lu1slk proc~lu1fac->proc~lu1slk proc~lu6chk lusol::lu6chk proc~lu1fac->proc~lu6chk proc~hbuild lusol::Hbuild proc~lu1fad->proc~hbuild proc~hchange lusol::Hchange proc~lu1fad->proc~hchange proc~hdelete lusol::Hdelete proc~lu1fad->proc~hdelete proc~lu1ful lusol::lu1ful proc~lu1fad->proc~lu1ful proc~lu1gau lusol::lu1gau proc~lu1fad->proc~lu1gau proc~lu1mar lusol::lu1mar proc~lu1fad->proc~lu1mar proc~lu1mrp lusol::lu1mRP proc~lu1fad->proc~lu1mrp proc~lu1msp lusol::lu1mSP proc~lu1fad->proc~lu1msp proc~lu1mxc lusol::lu1mxc proc~lu1fad->proc~lu1mxc proc~lu1mxr lusol::lu1mxr proc~lu1fad->proc~lu1mxr proc~lu1pen lusol::lu1pen proc~lu1fad->proc~lu1pen proc~lu1pq2 lusol::lu1pq2 proc~lu1fad->proc~lu1pq2 proc~lu1pq3 lusol::lu1pq3 proc~lu1fad->proc~lu1pq3 proc~lu1rec lusol::lu1rec proc~lu1fad->proc~lu1rec proc~hinsert lusol::Hinsert proc~hbuild->proc~hinsert proc~hdown lusol::Hdown proc~hchange->proc~hdown proc~hup lusol::Hup proc~hchange->proc~hup proc~hdelete->proc~hchange proc~lu1dcp lusol::lu1DCP proc~lu1ful->proc~lu1dcp proc~lu1dpp lusol::lu1DPP proc~lu1ful->proc~lu1dpp proc~hinsert->proc~hup proc~jdamax lusol::jdamax proc~lu1dcp->proc~jdamax proc~lu1dpp->proc~jdamax

Called by

proc~~lu1fac~~CalledByGraph proc~lu1fac lusol::lu1fac proc~solve lusol_ez_module::solve proc~solve->proc~lu1fac proc~test_1 main::test_1 proc~test_1->proc~solve proc~test_2 main::test_2 proc~test_2->proc~solve program~main~2 main program~main~2->proc~test_1 program~main~2->proc~test_2

Source Code

  subroutine lu1fac( m    , n    , nelem, lena , luparm, parmlu,       &
                     a    , indc , indr , p    , q     ,               &
                     lenc , lenr , locc , locr ,                       &
                     iploc, iqloc, ipinv, iqinv, w     , inform )

    integer(ip),   intent(in)    :: m, n, nelem, lena

    integer(ip),   intent(inout) :: luparm(30)
    integer(ip),   intent(inout) :: indc(lena), indr(lena),            &
                                    p(m)      , q(n)      ,            &
                                    lenc(n)   , lenr(m)   ,            &
                                    iploc(n)  , iqloc(m)  ,            &
                                    ipinv(m)  , iqinv(n)  ,            &
                                    locc(n)   , locr(m)
    real(rp),      intent(inout) :: parmlu(30), a(lena), w(n)

    integer(ip),   intent(out)   :: inform

    !------------------------------------------------------------------
    ! lu1fac computes a factorization A = L*U, where A is a sparse
    ! matrix with m rows and n columns, P*L*P' is lower triangular
    ! and P*U*Q is upper triangular for certain permutations P, Q
    ! (which are returned in the arrays p, q).
    ! Stability is ensured by limiting the size of the elements of L.
    !
    ! The nonzeros of A are input via the parallel arrays a, indc, indr,
    ! which should contain nelem entries of the form    aij,    i,    j
    ! in any order.  There should be no duplicate pairs         i,    j.
    !
    ! ******************************************************************
    ! *        Beware !!!   The row indices i must be in indc,         *
    ! *              and the column indices j must be in indr.         *
    ! *              (Not the other way round!)                        *
    ! ******************************************************************
    !
    ! It does not matter if some of the entries in a(*) are zero.
    ! Entries satisfying  abs( a(i) ) .le. parmlu(3)  are ignored.
    ! Other parameters in luparm and parmlu are described below.
    !
    ! The matrix A may be singular.  On exit, nsing = luparm(11) gives
    ! the number of apparent singularities.  This is the number of
    ! "small" diagonals of the permuted factor U, as judged by
    ! the input tolerances Utol1 = parmlu(4) and  Utol2 = parmlu(5).
    ! The diagonal element diagj associated with column j of A is
    ! "small" if
    !                 abs( diagj ) .le. Utol1
    ! or
    !                 abs( diagj ) .le. Utol2 * max( uj ),
    !
    ! where max( uj ) is the maximum element in the j-th column of U.
    ! The position of such elements is returned in w(*).  In general,
    ! w(j) = + max( uj ),  but if column j is a singularity,
    ! w(j) = - max( uj ).  Thus, w(j) .le. 0 if column j appears to be
    ! dependent on the other columns of A.
    !
    ! NOTE: lu1fac (like certain other sparse LU packages) does not
    ! treat dense columns efficiently.  This means it will be slow
    ! on "arrow matrices" of the form
    !                  A = (x       a)
    !                      (  x     b)
    !                      (    x   c)
    !                      (      x d)
    !                      (x x x x e)
    ! if the numerical values in the dense column allow it to be
    ! chosen LATE in the pivot order.
    !
    ! With TPP (Threshold Partial Pivoting), the dense column is
    ! likely to be chosen late.
    !
    ! With TCP (Threshold Complete Pivoting), if any of a,b,c,d
    ! is significantly larger than other elements of A, it will
    ! be chosen as the first pivot and the dense column will be
    ! eliminated, giving reasonably sparse factors.
    ! However, if element e is so big that TCP chooses it, the factors
    ! will become dense.  (It's hard to win on these examples!)
    !==================================================================
    !
    !
    ! Notes on the array names
    ! ------------------------
    !
    ! During the LU factorization, the sparsity pattern of the matrix
    ! being factored is stored twice: in a column list and a row list.
    !
    ! The column list is ( a, indc, locc, lenc )
    ! where
    !       a(*)    holds the nonzeros,
    !       indc(*) holds the indices for the column list,
    !       locc(j) points to the start of column j in a(*) and indc(*),
    !       lenc(j) is the number of nonzeros in column j.
    !
    ! The row list is    (    indr, locr, lenr )
    ! where
    !       indr(*) holds the indices for the row list,
    !       locr(i) points to the start of row i in indr(*),
    !       lenr(i) is the number of nonzeros in row i.
    !
    !
    ! At all stages of the LU factorization, p contains a complete
    ! row permutation.  At the start of stage k,  p(1), ..., p(k-1)
    ! are the first k-1 rows of the final row permutation P.
    ! The remaining rows are stored in an ordered list
    !                    ( p, iploc, ipinv )
    ! where
    !       iploc(nz) points to the start in p(*) of the set of rows
    !                 that currently contain nz nonzeros,
    !       ipinv(i)  points to the position of row i in p(*).
    !
    ! For example,
    !       iploc(1) = k   (and this is where rows of length 1 begin),
    !       iploc(2) = k+p  if there are p rows of length 1
    !                      (and this is where rows of length 2 begin).
    !
    ! Similarly for q, iqloc, iqinv.
    !==================================================================
    !
    !
    ! 00 Jun 1983  Original version.
    ! 00 Jul 1987  nrank  saved in luparm(16).
    ! 12 Apr 1989  ipinv, iqinv added as workspace.
    ! 26 Apr 1989  maxtie replaced by maxcol in Markowitz search.
    ! 16 Mar 1992  jumin  saved in luparm(19).
    ! 10 Jun 1992  lu1fad has to move empty rows and cols to the bottom
    !              (via lu1pq3) before doing the dense LU.
    ! 12 Jun 1992  Deleted dense LU (lu1ful, lu1vlu).
    ! 25 Oct 1993  keepLU implemented.
    ! 07 Feb 1994  Added new dense LU (lu1ful, lu1den).
    ! 21 Dec 1994  Bugs fixed in lu1fad (nrank) and lu1ful (ipvt).
    ! 08 Aug 1995  Use p instead of w as parameter to lu1or3 (for F90).
    ! 13 Sep 2000  TPP and TCP options implemented.
    ! 17 Oct 2000  Fixed troubles due to A = empty matrix (Todd Munson).
    ! 01 Dec 2000  Save Lmax, Umax, etc. after both lu1fad and lu6chk.
    !              lu1fad sets them when keepLU = false.
    !              lu6chk sets them otherwise, and includes items
    !              from the dense LU.
    ! 11 Mar 2001  lu6chk now looks at diag(U) when keepLU = false.
    ! 26 Apr 2002  New TCP implementation using heap routines to
    !              store largest element in each column.
    !              New workspace arrays Ha, Hj, Hk required.
    !              For compatibility, borrow space from a, indc, indr
    !              rather than adding new input parameters.
    ! 01 May 2002  lu1den changed to lu1DPP (dense partial  pivoting).
    !              lu1DCP implemented       (dense complete pivoting).
    !              Both TPP and TCP now switch to dense mode and end.
    !
    ! 10 Jan 2010: First f90 version.
    !---------------------------------------------------------------------
    !
    !
    !  INPUT PARAMETERS
    !
    !  m      (not altered) is the number of rows in A.
    !  n      (not altered) is the number of columns in A.
    !  nelem  (not altered) is the number of matrix entries given in
    !         the arrays a, indc, indr.
    !  lena   (not altered) is the dimension of  a, indc, indr.
    !         This should be significantly larger than nelem.
    !         Typically one should have
    !            lena > max( 2*nelem, 10*m, 10*n, 10000 )
    !         but some applications may need more.
    !         On machines with virtual memory it is safe to have
    !         lena "far bigger than necessary", since not all of the
    !         arrays will be used.
    !  a      (overwritten) contains entries   Aij  in   a(1:nelem).
    !  indc   (overwritten) contains the indices i in indc(1:nelem).
    !  indr   (overwritten) contains the indices j in indr(1:nelem).
    !
    !  luparm input parameters:                                Typical value
    !
    !  luparm( 1) = nout     File number for printed messages.         6
    !
    !  luparm( 2) = lprint   Print level.                              0
    !                   <  0 suppresses output.
    !                   =  0 gives error messages.
    !                  >= 10 gives statistics about the LU factors.
    !                  >= 50 gives debug output from lu1fac
    !                        (the pivot row and column and the
    !                        no. of rows and columns involved at
    !                        each elimination step).
    !
    !  luparm( 3) = maxcol   lu1fac: maximum number of columns         5
    !                        searched allowed in a Markowitz-type
    !                        search for the next pivot element.
    !                        For some of the factorization, the
    !                        number of rows searched is
    !                        maxrow = maxcol - 1.
    !
    !  luparm( 6) = 0    =>  TPP: Threshold Partial   Pivoting.        0
    !             = 1    =>  TRP: Threshold Rook      Pivoting.
    !             = 2    =>  TCP: Threshold Complete  Pivoting.
    !             = 3    =>  TSP: Threshold Symmetric Pivoting.
    !             = 4    =>  TDP: Threshold Diagonal  Pivoting.
    !                             (TDP not yet implemented).
    !                        TRP and TCP are more expensive than TPP but
    !                        more stable and better at revealing rank.
    !                        Take care with setting parmlu(1), especially
    !                        with TCP.
    !                        NOTE: TSP and TDP are for symmetric matrices
    !                        that are either definite or quasi-definite.
    !                        TSP is effectively TRP for symmetric matrices.
    !                        TDP is effectively TCP for symmetric matrices.
    !
    !  luparm( 8) = keepLU   lu1fac: keepLU = 1 means the numerical    1
    !                        factors will be computed if possible.
    !                        keepLU = 0 means L and U will be discarded
    !                        but other information such as the row and
    !                        column permutations will be returned.
    !                        The latter option requires less storage.
    !
    !  parmlu input parameters:                                Typical value
    !
    !  parmlu( 1) = Ltol1    Max Lij allowed during Factor.
    !                                                  TPP     10.0 or 100.0
    !                                                  TRP      4.0 or  10.0
    !                                                  TCP      5.0 or  10.0
    !                                                  TSP      4.0 or  10.0
    !                        With TRP and TCP (Rook and Complete Pivoting),
    !                        values less than 25.0 may be expensive
    !                        on badly scaled data.  However,
    !                        values less than 10.0 may be needed
    !                        to obtain a reliable rank-revealing
    !                        factorization.
    !  parmlu( 2) = Ltol2    Max Lij allowed during Updates.            10.0
    !                        during updates.
    !  parmlu( 3) = small    Absolute tolerance for       eps**0.8 = 3.0d-13
    !                        treating reals as zero.
    !  parmlu( 4) = Utol1    Absolute tol for flagging    eps**0.67= 3.7d-11
    !                        small diagonals of U.
    !  parmlu( 5) = Utol2    Relative tol for flagging    eps**0.67= 3.7d-11
    !                        small diagonals of U.
    !                        (eps = machine precision)
    !  parmlu( 6) = Uspace   Factor limiting waste space in  U.      3.0
    !                        In lu1fac, the row or column lists
    !                        are compressed if their length
    !                        exceeds Uspace times the length of
    !                        either file after the last compression.
    !  parmlu( 7) = dens1    The density at which the Markowitz      0.3
    !                        pivot strategy should search maxcol
    !                        columns and no rows.
    !                        (Use 0.3 unless you are experimenting
    !                        with the pivot strategy.)
    !  parmlu( 8) = dens2    the density at which the Markowitz      0.5
    !                        strategy should search only 1 column,
    !                        or (if storage is available)
    !                        the density at which all remaining
    !                        rows and columns will be processed
    !                        by a dense LU code.
    !                        For example, if dens2 = 0.1 and lena is
    !                        large enough, a dense LU will be used
    !                        once more than 10 per cent of the
    !                        remaining matrix is nonzero.
    !
    !
    !  OUTPUT PARAMETERS
    !
    !  a, indc, indr     contain the nonzero entries in the LU factors of A.
    !         If keepLU = 1, they are in a form suitable for use
    !         by other parts of the LUSOL package, such as lu6sol.
    !         U is stored by rows at the start of a, indr.
    !         L is stored by cols at the end   of a, indc.
    !         If keepLU = 0, only the diagonals of U are stored, at the
    !         end of a.
    !  p, q   are the row and column permutations defining the
    !         pivot order.  For example, row p(1) and column q(1)
    !         defines the first diagonal of U.
    !  lenc(1:numl0) contains the number of entries in nontrivial
    !         columns of L (in pivot order).
    !  lenr(1:m) contains the number of entries in each row of U
    !         (in original order).
    !  locc(1:n) = 0 (ready for the LU update routines).
    !  locr(1:m) points to the beginning of the rows of U in a, indr.
    !  iploc, iqloc, ipinv, iqinv  are undefined.
    !  w      indicates singularity as described above.
    !  inform = 0 if the LU factors were obtained successfully.
    !         = 1 if U appears to be singular, as judged by lu6chk.
    !         = 3 if some index pair indc(l), indr(l) lies outside
    !             the matrix dimensions 1:m , 1:n.
    !         = 4 if some index pair indc(l), indr(l) duplicates
    !             another such pair.
    !         = 7 if the arrays a, indc, indr were not large enough.
    !             Their length "lena" should be increase to at least
    !             the value "minlen" given in luparm(13).
    !         = 8 if there was some other fatal error.  (Shouldn't happen!)
    !         = 9 if no diagonal pivot could be found with TSP or TDP.
    !             The matrix must not be sufficiently definite
    !             or quasi-definite.
    !         =10 if there was some other fatal error.
    !
    !  luparm output parameters:
    !
    !  luparm(10) = inform   Return code from last call to any LU routine.
    !  luparm(11) = nsing    No. of singularities marked in the
    !                        output array w(*).
    !  luparm(12) = jsing    Column index of last singularity.
    !  luparm(13) = minlen   Minimum recommended value for  lena.
    !  luparm(14) = maxlen   ?
    !  luparm(15) = nupdat   No. of updates performed by the lu8 routines.
    !  luparm(16) = nrank    No. of nonempty rows of U.
    !  luparm(17) = ndens1   No. of columns remaining when the density of
    !                        the matrix being factorized reached dens1.
    !  luparm(18) = ndens2   No. of columns remaining when the density of
    !                        the matrix being factorized reached dens2.
    !  luparm(19) = jumin    The column index associated with DUmin.
    !  luparm(20) = numL0    No. of columns in initial  L.
    !  luparm(21) = lenL0    Size of initial  L  (no. of nonzeros).
    !  luparm(22) = lenU0    Size of initial  U.
    !  luparm(23) = lenL     Size of current  L.
    !  luparm(24) = lenU     Size of current  U.
    !  luparm(25) = lrow     Length of row file.
    !  luparm(26) = ncp      No. of compressions of LU data structures.
    !  luparm(27) = mersum   lu1fac: sum of Markowitz merit counts.
    !  luparm(28) = nUtri    lu1fac: triangular rows in U.
    !  luparm(29) = nLtri    lu1fac: triangular rows in L.
    !  luparm(30) = nslack   lu1fac: no. of unit vectors at start of U. (info only)
    !
    !
    !
    !  parmlu output parameters:
    !
    !  parmlu(10) = Amax     Maximum element in  A.
    !  parmlu(11) = Lmax     Maximum multiplier in current  L.
    !  parmlu(12) = Umax     Maximum element in current  U.
    !  parmlu(13) = DUmax    Maximum diagonal in  U.
    !  parmlu(14) = DUmin    Minimum diagonal in  U.
    !  parmlu(15) = Akmax    Maximum element generated at any stage
    !                        during TCP factorization.
    !  parmlu(16) = growth   TPP: Umax/Amax    TRP, TCP, TSP: Akmax/Amax
    !  parmlu(17) =
    !  parmlu(18) =
    !  parmlu(19) =
    !  parmlu(20) = resid    lu6sol: residual after solve with U or U'.
    !  ...
    !  parmlu(30) =
    !---------------------------------------------------------------------

    character(1)           :: mnkey
    character(2)           :: kPiv(0:3)
    integer(ip)            :: i, idummy, j, jsing, jumin,              &
                              k, l, l2, lena2, lenH, lenL,             &
                              lenLk, lenU, lenUk, lerr,                &
                              ll, llsave, lm, lmaxr, locH,             &
                              lprint, lPiv, lrow, ltopl,               &
                              lu, mersum, minlen, nbump,               &
                              ncp, ndens1, ndens2,                     &
                              nLtri, nmove, nout, nrank,               &
                              nsing, numl0, numnz, nslack, nUtri
    logical                :: keepLU, TCP, TPP, TRP, TSP
    real(rp)               :: Agrwth, Akmax, Amax, avgmer,             &
                              condU, delem, densty, dincr,             &
                              dm, dn, DUmax, DUmin, growth,            &
                              Lmax, Ltol, small, Ugrwth,               &
                              Umax

    ! Grab relevant input parameters.

    nout   = luparm(1)
    lprint = luparm(2)
    lPiv   = luparm(6)
    keepLU = luparm(8) /= 0

    Ltol   = parmlu(1)  ! Limit on size of Lij
    small  = parmlu(3)  ! Drop tolerance

    TPP    = lPiv == 0  ! Threshold Partial   Pivoting (normal).
    TRP    = lPiv == 1  ! Threshold Rook      Pivoting
    TCP    = lPiv == 2  ! Threshold Complete  Pivoting.
    TSP    = lPiv == 3  ! Threshold Symmetric Pivoting.
    kPiv(0)= 'PP'
    kPiv(1)= 'RP'
    kPiv(2)= 'CP'
    kPiv(3)= 'SP'

    ! Initialize output parameters.

    inform = 0
    minlen = nelem + 2*(m + n)
    numl0  = 0
    lenL   = 0
    lenU   = 0
    lrow   = 0
    mersum = 0
    nUtri  = m
    nLtri  = 0
    ndens1 = 0
    ndens2 = 0
    nrank  = 0
    nsing  = 0
    jsing  = 0
    jumin  = 0
    nslack = 0

    Amax   = zero
    Lmax   = zero
    Umax   = zero
    DUmax  = zero
    DUmin  = zero
    Akmax  = zero

    if (m > n) then
        mnkey  = '>'
    else if (m == n) then
        mnkey  = '='
    else
        mnkey  = '<'
    end if

    ! Float version of dimensions.

    dm     = m
    dn     = n
    delem  = nelem

    ! Initialize workspace parameters.

    luparm(26) = 0             ! ncp
    if (lena < minlen) go to 970

    !-------------------------------------------------------------------
    ! Organize the  aij's  in  a, indc, indr.
    ! lu1or1  deletes small entries, tests for illegal  i,j's,
    !         and counts the nonzeros in each row and column.
    ! lu1or2  reorders the elements of  A  by columns.
    ! lu1or3  uses the column list to test for duplicate entries
    !         (same indices  i,j).
    ! lu1or4  constructs a row list from the column list.
    !-------------------------------------------------------------------
    call lu1or1( m   , n    , nelem, lena , small,                     &
                 a   , indc , indr , lenc , lenr,                      &
                 Amax, numnz, lerr , inform )

    if (nout > 0  .and.  lprint >= 10) then
       densty = 100.0_rp * delem / (dm * dn)
       write(nout, 1000) m, mnkey, n, numnz, Amax, densty
    end if
    if (inform /= 0) go to 930

!!! nelem  = numnz     !!! Don't change nelem.
!!! nelem is now numnz below (it might be less than the input value).

    call lu1or2( n, numnz, lena, a, indc, indr, lenc, locc )
    call lu1or3( m, n, lena, indc, lenc, locc, p, lerr, inform )

    if (inform /= 0) go to 940

    call lu1or4( m, n, numnz, lena, indc, indr, lenc, lenr, locc, locr )

    !------------------------------------------------------------------
    ! Set up lists of rows and columns with equal numbers of nonzeros,
    ! using  indc(*)  as workspace.
    ! 12 Dec 2015: Always call lu1slk here now.
    ! This sets nslack and w(j) = 1.0 for slacks, else 0.0.
    !------------------------------------------------------------------
    call lu1pq1( m, n, lenr, p, iploc, ipinv, indc(numnz + 1) )
    call lu1pq1( n, m, lenc, q, iqloc, iqinv, indc(numnz + 1) )
    call lu1slk( m, n, lena, q, iqloc, a, indc, locc, nslack, w )
    luparm(30) = nslack

    !------------------------------------------------------------------
    ! For TCP, allocate Ha, Hj, Hk at the end of a, indc, indr.
    ! Then compute the factorization  A = L*U.
    !------------------------------------------------------------------
    lenH   = 0                ! Keep -Wmaybe-uninitialized happy.
    lena2  = 0                !
    locH   = 0                !
    lmaxr  = 0                !
    if (TPP .or. TSP) then
       lenH   = 1
       lena2  = lena
       locH   = lena
       lmaxr  = 1
    else if (TRP) then
       lenH   = 1             ! Dummy
       lena2  = lena  - m     ! Reduced length of      a
       locH   = lena          ! Dummy
       lmaxr  = lena2 + 1     ! Start of Amaxr      in a
    else if (TCP) then
       lenH   = n             ! Length of heap
       lena2  = lena  - lenH  ! Reduced length of      a, indc, indr
       locH   = lena2 + 1     ! Start of Ha, Hj, Hk in a, indc, indr
       lmaxr  = 1             ! Dummy
    end if

    call lu1fad( m     , n     , numnz , lena2 , luparm, parmlu,       &
                 a     , indc  , indr  , p     , q     ,               &
                 lenc  , lenr  , locc  , locr  ,                       &
                 iploc , iqloc , ipinv , iqinv , w     ,               &
                 lenH  ,a(locH), indc(locH), indr(locH), a(lmaxr),     &
                 inform, lenL  , lenU  , minlen, mersum,               &
                 nUtri , nLtri , ndens1, ndens2, nrank , nslack,       &
                 Lmax  , Umax  , DUmax , DUmin , Akmax )

    luparm(16) = nrank
    luparm(23) = lenL
    if (inform == 7) go to 970
    if (inform == 9) go to 985
    if (inform ==10) go to 981
    if (inform >  0) go to 980

    if ( keepLU ) then
       !---------------------------------------------------------------
       ! The LU factors are at the top of  a, indc, indr,
       ! with the columns of  L  and the rows of  U  in the order
       !
       ! ( free )   ... ( u3 ) ( l3 ) ( u2 ) ( l2 ) ( u1 ) ( l1 ).
       !
       ! Starting with ( l1 ) and ( u1 ), move the rows of  U  to the
       ! left and the columns of  L  to the right, giving
       !
       ! ( u1 ) ( u2 ) ( u3 ) ...   ( free )   ... ( l3 ) ( l2 ) ( l1 ).
       !
       ! Also, set  numl0 = the number of nonempty columns of L.
       !---------------------------------------------------------------
       lu     = 0
       ll     = lena  + 1
       lm     = lena2 + 1
       ltopl  = ll - lenL - lenU
       lrow   = lenU

       do k = 1, nrank
          i       =   p(k)
          lenUk   = - lenr(i)
          lenr(i) =   lenUk
          j       =   q(k)
          lenLk   = - lenc(j) - 1
          if (lenLk > 0) then
             numl0        = numl0 + 1
             iqloc(numl0) = lenLk
          end if

          if (lu + lenUk < ltopl) then
             !=========================================================
             ! There is room to move ( uk ).  Just right-shift ( lk ).
             !=========================================================
             do idummy = 1, lenLk
                ll       = ll - 1
                lm       = lm - 1
                a(ll)    = a(lm)
                indc(ll) = indc(lm)
                indr(ll) = indr(lm)
             end do
          else
             !=========================================================
             ! There is no room for ( uk ) yet.  We have to
             ! right-shift the whole of the remaining LU file.
             ! Note that ( lk ) ends up in the correct place.
             !=========================================================
             llsave = ll - lenLk
             nmove  = lm - ltopl

             do idummy = 1, nmove
                ll       = ll - 1
                lm       = lm - 1
                a(ll)    = a(lm)
                indc(ll) = indc(lm)
                indr(ll) = indr(lm)
             end do

             ltopl  = ll
             ll     = llsave
             lm     = ll
          end if

          !======================================================
          ! Left-shift ( uk ).
          !======================================================
          locr(i) = lu + 1
          l2      = lm - 1
          lm      = lm - lenUk

          do l = lm, l2
             lu       = lu + 1
             a(lu)    = a(l)
             indr(lu) = indr(l)
          end do
       end do

       !---------------------------------------------------------------
       ! Save the lengths of the nonempty columns of  L,
       ! and initialize  locc(j)  for the LU update routines.
       !---------------------------------------------------------------
       lenc(1:numl0) = iqloc(1:numl0)
       locc(1:n)     = 0

       !---------------------------------------------------------------
       ! Test for singularity.
       ! lu6chk  sets  nsing, jsing, jumin, Lmax, Umax, DUmax, DUmin
       ! (including entries from the dense LU).
       ! input      i1 = 1 means we're calling lu6chk from LUSOL.
       ! output inform = 1 if there are singularities (nsing > 0).
       ! 12 Dec 2015: nslack is now an input.
       !---------------------------------------------------------------
       call lu6chk( i1, m, n, nslack, w, lena, luparm, parmlu,         &
                    a, indc, indr, p, q,                               &
                    lenc, lenr, locc, locr, inform )
       nsing  = luparm(11)
       jsing  = luparm(12)
       jumin  = luparm(19)
       Lmax   = parmlu(11)
       Umax   = parmlu(12)
       DUmax  = parmlu(13)
       DUmin  = parmlu(14)

    else
       !---------------------------------------------------------------
       ! keepLU = 0.  L and U were not kept, just the diagonals of U.
       ! lu1fac will probably be called again soon with keepLU = .true.
       ! 11 Mar 2001: lu6chk revised.  We can call it with keepLU = 0,
       !              but we want to keep Lmax, Umax from lu1fad.
       ! 05 May 2002: Allow for TCP with new lu1DCP.  Diag(U) starts
       !              below lena2, not lena.  Need lena2 in next line.
       ! 12 Dec 2015: nslack is now an input.
       !---------------------------------------------------------------
       call lu6chk( i1, m, n, nslack, w, lena2, luparm, parmlu,        &
                    a, indc, indr, p, q,                               &
                    lenc, lenr, locc, locr, inform )
       nsing  = luparm(11)
       jsing  = luparm(12)
       jumin  = luparm(19)
       DUmax  = parmlu(13)
       DUmin  = parmlu(14)
    end if

    go to 990

    !------------
    ! Error exits.
    !------------
930 inform = 3
    if (lprint >= 0) write(nout, 1300) lerr, indc(lerr), indr(lerr)
    go to 990

940 inform = 4
    if (lprint >= 0) write(nout, 1400) lerr, indc(lerr), indr(lerr)
    go to 990

970 inform = 7
    if (lprint >= 0) write(nout, 1700) lena, minlen
    go to 990

980 inform = 8
    if (lprint >= 0) write(nout, 1800)
    go to 990

981 inform = 10
    go to 990

985 inform = 9
    if (lprint >= 0) write(nout, 1900)

    ! Store output parameters.

990 luparm(10) = inform
    luparm(11) = nsing
    luparm(12) = jsing
    luparm(13) = minlen
    luparm(15) = 0
    luparm(16) = nrank
    luparm(17) = ndens1
    luparm(18) = ndens2
    luparm(19) = jumin
    luparm(20) = numl0
    luparm(21) = lenL
    luparm(22) = lenU
    luparm(23) = lenL
    luparm(24) = lenU
    luparm(25) = lrow
    luparm(27) = mersum
    luparm(28) = nUtri
    luparm(29) = nLtri

    parmlu(10) = Amax
    parmlu(11) = Lmax
    parmlu(12) = Umax
    parmlu(13) = DUmax
    parmlu(14) = DUmin
    parmlu(15) = Akmax

    Agrwth = Akmax  / (Amax + 1.0e-20_rp)
    Ugrwth = Umax   / (Amax + 1.0e-20_rp)
    if ( TPP ) then
        growth = Ugrwth
    else ! TRP or TCP or TSP
        growth = Agrwth
    end if
    parmlu(16) = growth

    !------------------------------------------------------------------
    ! Print statistics for the LU factors.
    !------------------------------------------------------------------
    ncp    = luparm(26)
    condU  = DUmax / max( DUmin, 1.0e-20_rp )
    dincr  = lenL + lenU - nelem
    dincr  = dincr * 100.0_rp / max( delem, one )
    avgmer = mersum
    avgmer = avgmer / dm
    nbump  = m - nUtri - nLtri

    if (nout > 0  .and.  lprint >= 10) then
       if ( TPP ) then
          write(nout, 1100) avgmer, lenL, lenL+lenU, ncp, dincr,       &
                            nUtri, lenU, Ltol, Umax, Ugrwth,           &
                            nLtri, ndens1, Lmax

       else
          write(nout, 1120) kPiv(lPiv), avgmer,                        &
                            lenL, lenL+lenU, ncp, dincr,               &
                            nUtri, lenU, Ltol, Umax, Ugrwth,           &
                            nLtri, ndens1, Lmax, Akmax, Agrwth
       end if

       write(nout, 1200) nbump, ndens2, DUmax, DUmin, condU
    end if

    return

1000 format(' m', i12, ' ', a, 'n', i12, '  Elems', i9,                &
            '  Amax', es10.1, '  Density', f7.2)
1100 format(' Merit', f8.1, '  lenL', i9, '  L+U', i11,                &
            '  Cmpressns', i5, '  Incres', f8.2                        &
      /     ' Utri', i9, '  lenU', i9, '  Ltol', es10.2,               &
            '  Umax', es10.1, '  Ugrwth', es8.1                        &
      /     ' Ltri', i9, '  dense1', i7, '  Lmax', es10.2)
1120 format(' Mer', a2, f8.1, '  lenL', i9, '  L+U', i11,              &
            '  Cmpressns', i5, '  Incres', f8.2                        &
      /     ' Utri', i9, '  lenU', i9, '  Ltol', es10.2,               &
            '  Umax', es10.1, '  Ugrwth', es8.1                        &
      /     ' Ltri', i9, '  dense1', i7, '  Lmax', es10.2,             &
            '  Akmax', es9.1, '  Agrwth', es8.1)
1200 format(' bump', i9, '  dense2', i7, '  DUmax', es9.1,             &
            '  DUmin', es9.1, '  condU', es9.1)
1300 format(/ ' lu1fac  error...  entry  a(', i8, ')  has an illegal', &
              ' row or column index'                                   &
            //' indc, indr =', 2i8)
1400 format(/ ' lu1fac  error...  entry  a(', i8, ')  has the same',   &
              ' indices as an earlier entry'                           &
            //' indc, indr =', 2i8)
1700 format(/ ' lu1fac  error...  insufficient storage'                &
            //' Increase  lena  from', i10, '  to at least', i10)
1800 format(/ ' lu1fac  error...  fatal bug',                          &
              '   (sorry --- this should never happen)')
1900 format(/ ' lu1fac  error...  TSP used but',                       &
              ' diagonal pivot could not be found')

  end subroutine lu1fac