qprop.f

qprop.f

C***********************************************************************
C    Module:  qprop.f
C 
C    Copyright (C) 2005 Mark Drela 
C 
C    This program is free software; you can redistribute it and/or modify
C    it under the terms of the GNU General Public License as published by
C    the Free Software Foundation; either version 2 of the License, or
C    (at your option) any later version.
C
C    This program is distributed in the hope that it will be useful,
C    but WITHOUT ANY WARRANTY; without even the implied warranty of
C    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
C    GNU General Public License for more details.
C
C    You should have received a copy of the GNU General Public License
C    along with this program; if not, write to the Free Software
C    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
C***********************************************************************

      PROGRAM QPROP
C--------------------------------------------------------
C     Propeller/motor performance program
C     Version 1.20   12 Mar 06
C
C     Usage:
C
C % qprop propfile motorfile Vel Rpm Volt dBeta    (single-point)
C
C % qprop propfile motorfile Vel1,Vel2,dVel Rpm Volt dBeta  (1-param multi-point)
C
C % qprop propfile motorfile Vel1,Vel2/NVel Rpm Volt dBeta  (1-param multi-point)
C
C % qprop propfile motorfile Vel1,Vel2/NVel Rpm Volt1,Volt2,dVolt dBeta  
C                                                           (2-param multi-point)
C
C % qprop propfile motorfile runfile               (multi-point)
C
C
C--------------------------------------------------------
      IMPLICIT REAL (A-H,M,O-Z)
C
C---- input radial quantities (from propfile)
      PARAMETER (IRDIM=81)
      REAL WORK(IRDIM)
      REAL RB(IRDIM), CB(IRDIM), BB(IRDIM)
      REAL CL0B(IRDIM), DCLDAB(IRDIM), CLMINB(IRDIM), CLMAXB(IRDIM)
      REAL CD0B(IRDIM), CD2UB(IRDIM), CD2LB(IRDIM), CLCD0B(IRDIM)
      REAL REREFB(IRDIM), REEXPB(IRDIM), MCRITB(IRDIM)
C
C---- radial quantities interpolated to computational stations
      PARAMETER (IDIM=25)
      REAL R(IDIM), C(IDIM), B(IDIM), DR(IDIM)
      REAL CL0(IDIM), DCLDA(IDIM), CLMIN(IDIM), CLMAX(IDIM)
      REAL CD0(IDIM), CD2U(IDIM), CD2L(IDIM), CLCD0(IDIM)
      REAL REREF(IDIM), REEXP(IDIM), MCRIT(IDIM)
      REAL VA(IDIM), VT(IDIM), CL(IDIM), CD(IDIM)
      LOGICAL STALL(IDIM)
C
      REAL TP_C(IDIM), TP_B(IDIM),
     &     QP_C(IDIM), QP_B(IDIM)
C
C---- motor parameters
      PARAMETER (NMPDIM=10)
      REAL PARMOT(NMPDIM)
      CHARACTER*32 PMLAB(NMPDIM)
C
C---- various character variables
      CHARACTER*1 CHARF, ANS
      CHARACTER*80 PNAME, MNAME
      CHARACTER*80 ARGP1, ARGP2, ARGP3, ARGP4, ARGP5,
     &             ARGP6, ARGP7, ARGP8, ARGP9, ARGP10
      CHARACTER*80 FILNAM
      CHARACTER*128 LINE
C
      LOGICAL LRDUMP
      LOGICAL LRPMSET,
     &        LVOLTSET,
     &        LTHRUSET,
     &        LTORQSET,
     &        LAMPSSET,
     &        LPELESET
      LOGICAL ERROR
C
      INCLUDE 'QDEF.INC'
C
C---- input receiving arrays
      REAL RVAL(15)
      INTEGER IVAL(15)
C
      DATA PI / 3.14159265 /
ccc      DATA EPS / 1.0E-6 /
      DATA EPS / 1.0E-8 /
C
      DATA VERSION / 1.22 /

C---- default Mcrit
      MCRIT0 = 0.70
C
C---- get Unix command-line arguments, if any
      CALL GETARG0(1,ARGP1)
      CALL GETARG0(2,ARGP2)
      CALL GETARG0(3,ARGP3)
      CALL GETARG0(4,ARGP4)
      CALL GETARG0(5,ARGP5)
      CALL GETARG0(6,ARGP6)
      CALL GETARG0(7,ARGP7)
      CALL GETARG0(8,ARGP8)
      CALL GETARG0(9,ARGP9)
      CALL GETARG0(10,ARGP10)
C
      IF(ARGP1.EQ.' ') THEN
       WRITE(*,1005)
 1005  FORMAT(
     & /' QPROP usage:'
     &//' % qprop propfile motorfile Vel Rpm ',
     &             '[ Volt dBeta Thrust Torque Amps Pele ]',
     &  '   (single-point)'
     &//' % qprop propfile motorfile Vel1,Vel2,dVel Rpm ["]           ',
     &  '   (multi-point 1-parameter sweep over Vel, Rpm set)'
     &//' % qprop propfile motorfile Vel1,Vel2,dVel 0 Volt ["]        ',
     &  '   (multi-point 1-parameter sweep over Vel, Volt set)'
     &//' % qprop propfile motorfile Vel1,Vel2,dVel Rpm1,Rpm2,dRpm ["]',
     &  '   (multi-point 2-parameter sweep over Vel and Rpm)'
     &//' % qprop propfile motorfile runfile                          ',
     &  '   (multi-point, via file specification)'
     &       )
       WRITE(*,*)
       WRITE(*,*) 'Run with default inputs?  Y'
       READ(*,1000) ANS
       IF(INDEX('Nn',ANS) .NE. 0) STOP
       WRITE(*,*) 
      ENDIF
C
C---- default fluid properties from QDEF.INC
      RHO = RHO1   ! density
      RMU = RMU1   ! viscosity
      VSO = VSO1   ! speed of sound
C
      CALL QCGET(RHO,RMU,VSO)
C
C==========================================================
C---- set default prop
      PNAME = 'Graupner CAM 6x3 folder'
      BLDS = 2.0
C
C---- number of radial stations
      NR = 7
C
C---- linear CL(alpha) function
C     CL  =  CL0 + DCLCD*alpha  ,  clipped if outside range  CLMIN..CLMAX
      DO IR = 1, NR
        CL0B(IR) = 0.5
        DCLDAB(IR) = 5.8
        CLMINB(IR) = -0.4
        CLMAXB(IR) = 1.2
      ENDDO
C
C---- quadratic CD(CL,Re) function
C     CD  =  [ CD0 + CD2*(CL-CLCD0)**2 ] * [Re/REREF]^REEXP
      DO IR = 1, NR
        CD0B(IR) = 0.028
        CD2UB(IR) = 0.050
        CD2LB(IR) = 0.050
        CLCD0B(IR) = 0.5
        REREFB(IR) = 70000.0
        REEXPB(IR) = -0.7
        MCRITB(IR) = MCRIT0
      ENDDO
C
C---- radii
      RFAC = 0.0254
      RADD = 0.
      RB(1) = 0.75  
      RB(2) = 1.00  
      RB(3) = 1.50  
      RB(4) = 2.00  
      RB(5) = 2.50  
      RB(6) = 2.875 
      RB(7) = 3.00  
C
C---- chords
      CFAC = 0.0254
      CADD = 0.
      CB(1) = 0.66 
      CB(2) = 0.69 
      CB(3) = 0.63 
      CB(4) = 0.55 
      CB(5) = 0.44 
      CB(6) = 0.30 
      CB(7) = 0.19 
C
C---- blade angles
      BFAC = 1.0
      BADD = 0.
      BB(1) = 27.5
      BB(2) = 22.0
      BB(3) = 15.2
      BB(4) = 10.2
      BB(5) =  6.5
      BB(6) =  4.6
      BB(7) =  4.2
C
      RAD = RB(NR)
C
C----------------------------------------------------
C---- default motor/gear combo
      MNAME = "Speed-400 3321 (6V) direct drive"
      IMOTYPE = 1
      PARMOT(1) = 0.31    ! Rmotor  (Ohms)
      PARMOT(2) = 0.77    ! Io      (Amps)
      PARMOT(3) = 2760.0  ! Kv      (rpm/Volt)
      PMLAB(1) = 'R  (Ohm)'
      PMLAB(2) = 'Io (Amp)'
      PMLAB(3) = 'Kv (rpm/Volt)'
      NMPAR = 3
C
C----------------------------------------------------
C---- default parameter sweeps
      VEL1 =  0.0
      VEL2 = 10.0
      NVEL = 6
C
      RPM1 = 10000.0
      RPM2 = 16000.0
      NRPM = 7
C
      VOLT1 = 6.0
      VOLT2 = 9.0
      NVOLT = 4
C
      DBET1 = -2.0
      DBET2 =  2.0
      NDBET = 5
C
      THRU1 = 0.
      THRU2 = 0.
      NTHRU = 1
C
      AMPS1 = 0.
      AMPS2 = 0.
      NAMPS = 1
C
      PELE1 = 0.
      PELE2 = 0.
      NPELE = 1
C
C---- do not dump radial distributions
      LRDUMP = .FALSE.
C
C==========================================================
 1000 FORMAT(A)
C
C----------------------------------------------------
C---- read prop data file
      FILNAM = ARGP1
      IF(FILNAM.EQ.' ') GO TO 18
C
      LU = 1
      OPEN(LU,FILE=FILNAM,STATUS='OLD',ERR=18)
C
      ILINE = 0
C
C---- prop name
      CALL FREAD(LU,LINE,ILINE,IERR,PNAME)
      IF(IERR.EQ.+1) GO TO 900
      IF(IERR.EQ.-1) GO TO 950
C
C---- extract parameters on data lines
      NVAL = 2
      CALL RREAD(LU,LINE,ILINE,IERR,NVAL,RVAL)
      IF(IERR.EQ.+1) GO TO 900
      IF(IERR.EQ.-1) GO TO 950
      IF(NVAL.LT. 1) GO TO 980
      BLDS = RVAL(1)
      IF(NVAL.GE.2) THEN
       RAD = RVAL(2)
      ELSE
       RAD = 0.
      ENDIF
C
      NVAL = 2
      CALL RREAD(LU,LINE,ILINE,IERR,NVAL,RVAL)
      IF(IERR.EQ.+1) GO TO 900
      IF(IERR.EQ.-1) GO TO 950
      IF(NVAL.LT. 2) GO TO 980
      DO IR = 1, IRDIM
        CL0B(IR)   = RVAL(1)
        DCLDAB(IR) = RVAL(2)
      ENDDO
C
      NVAL = 2
      CALL RREAD(LU,LINE,ILINE,IERR,NVAL,RVAL)
      IF(IERR.EQ.+1) GO TO 900
      IF(IERR.EQ.-1) GO TO 950
      IF(NVAL.LT. 2) GO TO 980
      DO IR = 1, IRDIM
        CLMINB(IR) = RVAL(1)
        CLMAXB(IR) = RVAL(2)
      ENDDO
C
      NVAL = 4
      CALL RREAD(LU,LINE,ILINE,IERR,NVAL,RVAL)
      IF(IERR.EQ.+1) GO TO 900
      IF(IERR.EQ.-1) GO TO 950
      IF(NVAL.LT. 3) GO TO 980
      DO IR = 1, IRDIM
        CD0B(IR)   = RVAL(1)
        CD2UB(IR)  = RVAL(2)
        CD2LB(IR)  = RVAL(3)
        CLCD0B(IR) = RVAL(4)
      ENDDO
C
      NVAL = 2
      CALL RREAD(LU,LINE,ILINE,IERR,NVAL,RVAL)
      IF(IERR.EQ.+1) GO TO 900
      IF(IERR.EQ.-1) GO TO 950
      IF(NVAL.LT. 2) GO TO 980
      DO IR = 1, IRDIM
        REREFB(IR) = RVAL(1)
        REEXPB(IR) = RVAL(2)
      ENDDO
C
C
      NVAL = 3
      CALL RREAD(LU,LINE,ILINE,IERR,NVAL,RVAL)
      IF(IERR.EQ.+1) GO TO 900
      IF(IERR.EQ.-1) GO TO 950
      IF(NVAL.LT. 3) GO TO 980
      RFAC = RVAL(1)
      CFAC = RVAL(2)
      BFAC = RVAL(3)
C
      NVAL = 3
      CALL RREAD(LU,LINE,ILINE,IERR,NVAL,RVAL)
      IF(IERR.EQ.+1) GO TO 900
      IF(IERR.EQ.-1) GO TO 950
      IF(NVAL.LT. 3) GO TO 980
      RADD = RVAL(1)
      CADD = RVAL(2)
      BADD = RVAL(3)
C
      KR = 0
C
 14   CONTINUE
C
        NVAL = 13
        CALL RREAD(LU,LINE,ILINE,IERR,NVAL,RVAL)
        IF(IERR.EQ.+1) GO TO 900
        IF(IERR.EQ.-1) GO TO 16
        IF(NVAL.LT. 3) GO TO 980
C
        KR = KR + 1
        IR = MIN( KR , IRDIM )
        RB(IR) = RVAL(1)
        CB(IR) = RVAL(2)
        BB(IR) = RVAL(3)
C
        MCRITB(IR) = MCRIT0
C
        IF(NVAL.GE. 4) CL0B(IR)   = RVAL( 4)
        IF(NVAL.GE. 5) DCLDAB(IR) = RVAL( 5)
        IF(NVAL.GE. 6) CLMINB(IR) = RVAL( 6)
        IF(NVAL.GE. 7) CLMAXB(IR) = RVAL( 7)
        IF(NVAL.GE. 8) CD0B(IR)   = RVAL( 8)
        IF(NVAL.GE. 9) CD2UB(IR)  = RVAL( 9)
        IF(NVAL.GE.10) CD2LB(IR)  = RVAL(10)
        IF(NVAL.GE.11) CLCD0B(IR) = RVAL(11)
        IF(NVAL.GE.12) REREFB(IR) = RVAL(12)
        IF(NVAL.GE.13) REEXPB(IR) = RVAL(13)
C
        GO TO 14
C
 16   CONTINUE
      CLOSE(LU)
C
      IF(KR.GT.0) THEN
       NR = IR
       IF(KR.GT.NR) THEN
        WRITE(*,*) 'Array overflow.  Increase IRDIM to', KR
        STOP
       ENDIF
      ENDIF
      GO TO 19
C
 18   CONTINUE
      WRITE(*,*)
      WRITE(*,*) 'Prop file not found:  ', FILNAM(1:48)
      WRITE(*,*) 'Default prop used  :  ', PNAME
C
C
 19   CONTINUE
C
      IF(NR.LE.1) THEN
       WRITE(*,*)
       WRITE(*,*) '*** Must define at least two radial stations'
       STOP
      ENDIF
C
C---- apply scaling factors
      DO IR = 1, NR
        RB(IR) =  RB(IR)*RFAC + RADD
        CB(IR) =  CB(IR)*CFAC + CADD
        BB(IR) = (BB(IR)*BFAC + BADD)* PI / 180.0
      ENDDO
C
      IF(RAD .EQ. 0.0) THEN
       RAD = RB(NR)
      ENDIF
C
      DO IR = 1, NR-1
        IF(CB(IR) .LE. 0.0) 
     &     STOP 'Chords must be positive'
        IF(RB(IR) .LT. 0.0)
     &     STOP 'Radii must be nonnegative'
        IF(RB(IR) .GE. RB(IR+1))
     &     STOP 'Radii must increase monotonically'
      ENDDO
C
      IF(RAD .LT. RB(NR)) THEN
       WRITE(*,1050) RAD, RB(NR)
 1050  FORMAT(/' Given on line 2:  R =', G12.4,
     &        /' Last r station :  r =', G12.4,
     &       //' Must have  R > r' / )
       STOP
      ENDIF
C
C==========================================================
C---- read motor data file
      FILNAM = ARGP2
      IF(FILNAM.EQ.' ') GO TO 28
C
      LU = 2
      OPEN(LU,FILE=FILNAM,STATUS='OLD',ERR=28)
C
C---- clear motor data in case it's not all in the file
      DO IMPAR = 1, NMPDIM
        PARMOT(IMPAR) = 0.0
        PMLAB(IMPAR) = ' '
      ENDDO
C
      ILINE = 0
C
C---- motor name
      CALL FREAD(LU,LINE,ILINE,IERR,MNAME)
      IF(IERR.EQ.+1) GO TO 900
      IF(IERR.EQ.-1) GO TO 950
C
C---- motor model index
      NVAL = 1
      CALL IREAD(LU,LINE,ILINE,IERR,NVAL,IVAL)
      IF(IERR.EQ.+1) GO TO 900
      IF(IERR.EQ.-1) GO TO 950
      IF(NVAL.LT. 1) GO TO 980
      IMOTYPE = IVAL(1)
C
C---- extract parameters on data lines
      DO IMPAR = 1, NMPDIM+1
        NVAL = 1
        CALL RREAD(LU,LINE,ILINE,IERR,NVAL,RVAL)
        IF(IERR.EQ.+1) GO TO 900
        IF(IERR.EQ.-1) GO TO 25
        IF(NVAL.LT. 1) GO TO 980
        IF(IMPAR.EQ.NMPDIM+1) THEN
         WRITE(*,*) '* Motor parameter array overflow. Increase NMPDIM'
         STOP
        ENDIF
        PARMOT(IMPAR) = RVAL(1)
        KEX = INDEX(LINE,'!')
        IF(KEX.GE.1) THEN
         PMLAB(IMPAR) = LINE(KEX+1:80)
        ELSE
         PMLAB(IMPAR) = ' '
        ENDIF
      ENDDO
C
 25   CONTINUE
      NMPAR = IMPAR-1
C
      CLOSE(LU)
      GO TO 29
C
 28   CONTINUE
      WRITE(*,*)
      WRITE(*,*) 'Motor file not found:  ', FILNAM(1:48)
      WRITE(*,*) 'Default motor used  :  ', MNAME
C
 29   CONTINUE
C
C==========================================================
C---- operating parameter data file, or single-point parameters
      FILNAM = ARGP3
      IF(FILNAM.EQ.' ') GO TO 80
C
C---- first assume that 3rd Unix argument is parameter data filename
      LU = 4
      OPEN(LU,FILE=FILNAM,STATUS='OLD',ERR=31)
C
C---- file open successful... read parameter data
      ILINE = 0
C
C---- extract parameters on data lines
      NVAL = 3
      CALL RREAD(LU,LINE,ILINE,IERR,NVAL,RVAL)
      IF(IERR.EQ.+1) GO TO 900
      IF(IERR.EQ.-1) GO TO 950
      IF(NVAL.LT. 3) GO TO 980
      VEL1 = RVAL(1)
      VEL2 = RVAL(2)
      NVEL = INT( RVAL(3) + 0.01 )
C
C---- extract parameters on data lines
      NVAL = 3
      CALL RREAD(LU,LINE,ILINE,IERR,NVAL,RVAL)
      IF(IERR.EQ.+1) GO TO 900
      IF(IERR.EQ.-1) GO TO 950
      IF(NVAL.LT. 3) GO TO 980
      RPM1 = RVAL(1)
      RPM2 = RVAL(2)
      NRPM = INT( RVAL(3) + 0.01 )
C
C---- extract parameters on data lines
      NVAL = 3
      CALL RREAD(LU,LINE,ILINE,IERR,NVAL,RVAL)
      IF(IERR.EQ.+1) GO TO 900
      IF(IERR.EQ.-1) GO TO 950
      IF(NVAL.LT. 3) GO TO 980
      VOLT1 = RVAL(1)
      VOLT2 = RVAL(2)
      NVOLT = INT( RVAL(3) + 0.01 )
C
C---- extract parameters on data lines
      NVAL = 3
      CALL RREAD(LU,LINE,ILINE,IERR,NVAL,RVAL)
      IF(IERR.EQ.+1) GO TO 900
      IF(IERR.EQ.-1 .OR. NVAL.LT. 3) THEN
       DBET1 = 0.0
       DBET2 = 0.0
       NDBET = 0
      ELSE
       DBET1 = RVAL(1)
       DBET2 = RVAL(2)
       NDBET = INT( RVAL(3) + 0.01 )
      ENDIF
      NDBET = MAX( 1 , NDBET )
C
      NVAL = 3
      CALL RREAD(LU,LINE,ILINE,IERR,NVAL,RVAL)
      IF(IERR.EQ.+1) GO TO 900
      IF(IERR.EQ.-1 .OR. NVAL.LT. 3) THEN
       THRU1 = 0.0
       THRU2 = 0.0
       NTHRU = 0
      ELSE
       THRU1 = RVAL(1)
       THRU2 = RVAL(2)
       NTHRU = INT( RVAL(3) + 0.01 )
      ENDIF
      NTHRU = MAX( 1 , NTHRU )
C
      CLOSE(LU)
      GO TO 82
C
C-------------------------------------------------------
C---- pick up here if 3rd Unix argument is not a filename
 31   CONTINUE
C
C---- try reading velocity 3rd Unix argument
      CALL PPARSE(ARGP3,VEL1,VEL2,NVEL,IERR)
      IF(IERR.EQ.+1) GO TO 80
      IF(IERR.EQ.-1) GO TO 80
C
C---- set new default single-point RPM or VOLT
      RPM1 = 0.
      RPM2 = 0.
      NRPM = 0
C
      VOLT1 = 0.
      VOLT2 = 0.
      NVOLT = 0
C
      DBET1 = 0.
      DBET2 = 0.
      NDBET = 1
C
      THRU1 = 0.
      THRU2 = 0.
      NTHRU = 1
C
      TORQ1 = 0.
      TORQ2 = 0.
      NTORQ = 1
C
      AMPS1 = 0.
      AMPS2 = 0.
      NAMPS = 1
C
      PELE1 = 0.
      PELE2 = 0.
      NPELE = 1
C
C---- try reading Rpm from 4th Unix argument
      CALL PPARSE(ARGP4,RPM1,RPM2,NRPM,IERR)
      IF(IERR.EQ.+1 .OR.
     &   IERR.EQ.-1     ) THEN
       RPM = 0.0
       NRPM = 0
      ENDIF
C
C---- try reading Voltage from 5th Unix argument
      CALL PPARSE(ARGP5,VOLT1,VOLT2,NVOLT,IERR)
      IF(IERR.EQ.+1 .OR.
     &   IERR.EQ.-1     ) THEN
       VOLT = 0.0
       NVOLT = 0
      ENDIF
C
C---- try reading pitch change from 6th Unix argument
      CALL PPARSE(ARGP6,DBET1,DBET2,NDBET,IERR)
      IF(IERR.EQ.+1 .OR.
     &   IERR.EQ.-1     ) THEN
       DBET = 0.0
       NDBET = 1
      ENDIF
C
C---- try reading thrust from 7th Unix argument
      CALL PPARSE(ARGP7,THRU1,THRU2,NTHRU,IERR)
      IF(IERR.EQ.+1 .OR.
     &   IERR.EQ.-1     ) THEN
       THRU = 0.0
       NTHRU = 1
      ENDIF
C
C---- try reading torque from 8th Unix argument
      CALL PPARSE(ARGP8,TORQ1,TORQ2,NTORQ,IERR)
      IF(IERR.EQ.+1 .OR.
     &   IERR.EQ.-1     ) THEN
       TORQ = 0.0
       NTHRU = 1
      ENDIF
C
C---- try reading current from 9th Unix argument
      CALL PPARSE(ARGP9,AMPS1,AMPS2,NAMPS,IERR)
      IF(IERR.EQ.+1 .OR.
     &   IERR.EQ.-1     ) THEN
       AMPS = 0.0
       NAMPS = 1
      ENDIF
C
C---- try reading Pele from 10th Unix argument
      CALL PPARSE(ARGP10,PELE1,PELE2,NPELE,IERR)
      IF(IERR.EQ.+1 .OR.
     &   IERR.EQ.-1     ) THEN
       PELE = 0.0
       NPELE = 1
      ENDIF
C
C---- if this is a single-point case... will dump radial distributions
      LRDUMP = NVEL .LE.1
     &   .AND. NRPM .LE.1
     &   .AND. NVOLT.LE.1
     &   .AND. NDBET.LE.1
     &   .AND. NTHRU.LE.1
     &   .AND. NTORQ.LE.1
     &   .AND. NAMPS.LE.1
     &   .AND. NPELE.LE.1
C
      GO TO 82
C
C
 80   CONTINUE
      WRITE(*,*)
      WRITE(*,*) 'Run parameter file not found: ', FILNAM(1:48)
      WRITE(*,*) 'Default velocities, voltages, pitch used'
C
 82   CONTINUE
C
      IF(NRPM .EQ.0 .AND. 
     &   NVOLT.EQ.0 .AND. 
     &   NTHRU.EQ.0 .AND.
     &   NAMPS.EQ.0 .AND.
     &   NPELE.EQ.0       ) THEN
       WRITE(*,*) 
     &  'Must specify either Rpm or Volts or Thrust or Amps or Pele'
       STOP
      ENDIF
C
      LRPMSET  = NRPM  .GT. 0 .AND. (RPM1  .NE. 0.0 .OR. RPM2  .NE. 0.0)
      LVOLTSET = NVOLT .GT. 0 .AND. (VOLT1 .NE. 0.0 .OR. VOLT2 .NE. 0.0)
      LTHRUSET = NTHRU .GT. 0 .AND. (THRU1 .NE. 0.0 .OR. THRU2 .NE. 0.0)
      LTORQSET = NTORQ .GT. 0 .AND. (TORQ1 .NE. 0.0 .OR. TORQ2 .NE. 0.0)
      LAMPSSET = NAMPS .GT. 0 .AND. (AMPS1 .NE. 0.0 .OR. AMPS2 .NE. 0.0)
      LPELESET = NPELE .GT. 0 .AND. (PELE1 .NE. 0.0 .OR. PELE2 .NE. 0.0)
C
cc    write(*,*)
cc   &  lrpmset, lvoltset, lthruset, ltorqset, lampsset, lpeleset

C==========================================================
C
C---- set up finely-spaced radial arrays
      R0 = RB(1)
      R1 = RB(NR)
C
      N = IDIM
      DO I = 1, N
        FRAC = (FLOAT(I)-0.5)/FLOAT(N)
        R(I) = R0*(1.0-FRAC) + R1*FRAC
        DR(I) = (R1-R0)/FLOAT(N)
      ENDDO
C
      CALL SPLINE(CB,WORK,RB,NR)
      DO I = 1, N
        C(I) = SEVAL(R(I),CB,WORK,RB,NR)
      ENDDO
C
      CALL SPLINE(BB,WORK,RB,NR)
      DO I = 1, N
        B(I) = SEVAL(R(I),BB,WORK,RB,NR)
      ENDDO
C
      CALL SPLINE(CL0B,WORK,RB,NR)
      DO I = 1, N
        CL0(I) = SEVAL(R(I),CL0B,WORK,RB,NR)
      ENDDO
C
      CALL SPLINE(DCLDAB,WORK,RB,NR)
      DO I = 1, N
        DCLDA(I) = SEVAL(R(I),DCLDAB,WORK,RB,NR)
      ENDDO
C
      CALL SPLINE(CLMINB,WORK,RB,NR)
      DO I = 1, N
        CLMIN(I) = SEVAL(R(I),CLMINB,WORK,RB,NR)
      ENDDO
C
      CALL SPLINE(CLMAXB,WORK,RB,NR)
      DO I = 1, N
        CLMAX(I) = SEVAL(R(I),CLMAXB,WORK,RB,NR)
      ENDDO
C
      CALL SPLINE(CD0B,WORK,RB,NR)
      DO I = 1, N
        CD0(I) = SEVAL(R(I),CD0B,WORK,RB,NR)
      ENDDO
C
      CALL SPLINE(CD2UB,WORK,RB,NR)
      DO I = 1, N 
        CD2U(I) = SEVAL(R(I),CD2UB,WORK,RB,NR)
      ENDDO
C
      CALL SPLINE(CD2LB,WORK,RB,NR)
      DO I = 1, N
        CD2L(I) = SEVAL(R(I),CD2LB,WORK,RB,NR)
      ENDDO
C
      CALL SPLINE(CLCD0B,WORK,RB,NR)
      DO I = 1, N
        CLCD0(I) = SEVAL(R(I),CLCD0B,WORK,RB,NR)
      ENDDO
C
      CALL SPLINE(REREFB,WORK,RB,NR)
      DO I = 1, N
        REREF(I) = SEVAL(R(I),REREFB,WORK,RB,NR)
      ENDDO
C
      CALL SPLINE(REEXPB,WORK,RB,NR)
      DO I = 1, N
        REEXP(I) = SEVAL(R(I),REEXPB,WORK,RB,NR)
      ENDDO
C
      CALL SPLINE(MCRITB,WORK,RB,NR)
      DO I = 1, N
        MCRIT(I) = SEVAL(R(I),MCRITB,WORK,RB,NR)
      ENDDO
C
C---- reality checks
      ERROR = .FALSE.
      DO I = 1, N
        IF(C(I) .LE. 0.0) THEN
         WRITE(*,*) 'Negative chord at i =', I
         ERROR = .TRUE.
        ENDIF
        IF(REREF(I) .LE. 0.0) THEN
         WRITE(*,*) 'Negative Re_ref at i =', I
         ERROR = .TRUE.
        ENDIF
        IF(MCRIT(I) .LE. 0.0) THEN
         WRITE(*,*) 'Negative Mcrit at i =', I
         ERROR = .TRUE.
        ENDIF
      ENDDO
C
      IF(ERROR) THEN
        WRITE(*,*)
       WRITE(*,1100)
     & ' i   radius   chord     beta    Re_ref'
        DO I = 1, N
          IRE = INT( REREF(I) )
          WRITE(*,1070) I, R(I), C(I), B(I)*180.0/PI, IRE
 1070     FORMAT(1X,I3, F9.4, F9.4, F9.3, I9)
        ENDDO
        WRITE(*,*)
        STOP
      ENDIF

C
C----------------------------------------------------
C---- perform calculations and dump output
C
      LU = 6
      WRITE(*,*)
C
 1105 FORMAT('# QPROP Version', F5.2)
 1100 FORMAT('# ', A,A,A,A)
 1110 FORMAT('#  ', G12.5, 1X, A)
 1120 FORMAT('#   rho =', G12.5,' kg/m^3'
     &      /'#   mu  =', G12.5,' kg/m-s'
     &      /'#   a   =', G12.5,' m/s   ' )
C
      WRITE(LU,1105) VERSION
      WRITE(LU,1100)
      WRITE(LU,1100) PNAME
      WRITE(LU,1100)
      WRITE(LU,1100) MNAME
      DO IMPAR=1, NMPAR
        WRITE(LU,1110) PARMOT(IMPAR), PMLAB(IMPAR)
      ENDDO
      WRITE(LU,1100)
      WRITE(LU,1120) RHO, RMU, VSO
      WRITE(LU,1100)
      WRITE(LU,1100)
     & ' 1         2        3          4          5        ',
     & ' 6            7         8       9        10        11    ',
     & '    12          13        14        15      16          17   ',
     & '        18      19'
      WRITE(LU,1100)
      WRITE(LU,1100)
     & ' V(m/s)    rpm      Dbeta      T(N)       Q(N-m)   ',
     & ' Pshaft(W)    Volts     Amps    effmot   effprop   adv   ',
     & '    CT          CP        DV(m/s)   eff     Pelec       Pprop',
     & '        cl_avg  cd_avg'
C
      IF(LRDUMP) THEN
       CHARF = '#'
      ELSE
       CHARF = ' '
      ENDIF
C
      NVELM = MAX( 1 , NVEL-1 )
      DVEL = (VEL2-VEL1)/FLOAT(NVELM)
C
      NDBETM = MAX( 1 , NDBET-1 )
      DDBET = (DBET2-DBET1)/FLOAT(NDBETM)
C
      IF(LRPMSET) THEN
       NRPMM = MAX( 1 , NRPM-1 )
       PAR1 = RPM1
       PAR2 = RPM2
       DPAR = (RPM2-RPM1)/FLOAT(NRPMM)
       NPAR = NRPM
      ELSEIF(LVOLTSET) THEN
       NVOLTM = MAX( 1 , NVOLT-1 )
       PAR1 = VOLT1
       PAR2 = VOLT2
       DPAR = (VOLT2-VOLT1)/FLOAT(NVOLTM)
       NPAR = NVOLT
      ELSEIF(LTHRUSET) THEN
       NTHRUM = MAX( 1 , NTHRU-1 )
       PAR1 = THRU1
       PAR2 = THRU2
       DPAR = (THRU2-THRU1)/FLOAT(NTHRUM)
       NPAR = NTHRU
      ELSEIF(LTORQSET) THEN
       NTORQM = MAX( 1 , NTORQ-1 )
       PAR1 = TORQ1
       PAR2 = TORQ2
       DPAR = (TORQ2-TORQ1)/FLOAT(NTORQM)
       NPAR = NTORQ
      ELSEIF(LAMPSSET) THEN
       NAMPSM = MAX( 1 , NAMPS-1 )
       PAR1 = AMPS1
       PAR2 = AMPS2
       DPAR = (AMPS2-AMPS1)/FLOAT(NAMPSM)
       NPAR = NAMPS
      ELSEIF(LPELESET) THEN
       NPELEM = MAX( 1 , NPELE-1 )
       PAR1 = PELE1
       PAR2 = PELE2
       DPAR = (PELE2-PELE1)/FLOAT(NPELEM)
       NPAR = NPELE
      ELSE
       WRITE(*,*) 'Additional parameter not specified'
       WRITE(*,*) ' RPM   :',  lrpmset
       WRITE(*,*) ' Volt  :',  lvoltset
       WRITE(*,*) ' Thrust:',  lthruset
       WRITE(*,*) ' Torque:',  ltorqset
       WRITE(*,*) ' Amps  :',  lampsset
       WRITE(*,*) ' Pelec :',  lpeleset
       STOP
      ENDIF
C
      LRDUMP = NVEL .LE.1
     &   .AND. NRPM .LE.1
     &   .AND. NVOLT.LE.1
     &   .AND. NDBET.LE.1
     &   .AND. NTHRU.LE.1
     &   .AND. NTORQ.LE.1
     &   .AND. NAMPS.LE.1
     &   .AND. NPELE.LE.1

C
      DO IDBET = 1, NDBET
        DBET = DBET1 + DDBET*FLOAT(IDBET-1)
        DBE = DBET * PI/180.0
C
        DO IPAR = 1, NPAR
          PAR = PAR1 + DPAR*FLOAT(IPAR-1)
C
          IF    (LRPMSET ) THEN
           RPM = PAR
          ELSEIF(LVOLTSET) THEN
           VOLT = PAR
          ELSEIF(LTHRUSET) THEN
           THRU = PAR
          ELSEIF(LTORQSET) THEN
           TORQ = PAR
          ELSEIF(LAMPSSET ) THEN
           AMPS = PAR
          ELSEIF(LPELESET) THEN
           PELE = PAR
          ENDIF
C
          DO IVEL = 1, NVEL
            VEL = VEL1 + DVEL*FLOAT(IVEL-1)
C
C---------- set initial omega
            IF    (LRPMSET) THEN
             OMG = RPM * PI/30.0
C
            ELSEIF(LVOLTSET) THEN
C----------- guess using 80% radius effective pitch angle
             I = MAX( 1 , (8*N)/10 )
             RT = R(I)
             BT = B(I) - CL0(I)/DCLDA(I) + DBE
             BT = MAX( 0.02 , MIN( 0.45*PI , BT ) )
             IF(VEL.EQ.0.0) THEN
              OMG = 1.0
             ELSE
              OMG = VEL/(RT*TAN(BT))
             ENDIF
C
            ELSE
C----------- guess using 80% radius effective pitch angle
             I = MAX( 1 , (8*N)/10 )
             RT = R(I)
             BT = B(I) - CL0(I)/DCLDA(I) + DBE
             BT = MAX( 0.02 , MIN( 0.45*PI , BT ) )
             IF(VEL.EQ.0.0) THEN
              OMG = 1.0
             ELSE
              OMG = VEL/(RT*TAN(BT))
             ENDIF
C
C----------- set voltage to get zero torque
             QP = 0.
             CALL VOLTM(OMG,QP, IMOTYPE, PARMOT,NMPAR,
     &                  VM,VM_OMG,VM_QP,
     &                  AM,AM_OMG,AM_QP )
             VOLT = VM
            ENDIF
C
C---------- Newton iteration to converge on trimmed omega
            DO 100 ITER = 1, 25
              CALL TQCALC(N,C,B,R,DR,
     &              VA,VT,CL,CD,STALL,
     &              BLDS,RAD,VEL,OMG,DBE,
     &              RHO,RMU,VSO,
     &              CL0,DCLDA,CLMIN,CLMAX,MCRIT,
     &              CD0,CD2U,CD2L,CLCD0,REREF,REEXP,
     &              TP, TP_VEL, TP_OMG, TP_DBE, TP_C, TP_B,
     &              QP, QP_VEL, QP_OMG, QP_DBE, QP_C, QP_B )
C
              IF(LRPMSET) THEN
C------------- Residual  =  prop omega  -  prescribed omega
               RES = 0.
               RES_OMG = 1.0
C
C------------- Newton change
               DOMG = -RES/RES_OMG
               DVOLT = 0.
C
C------------- set voltage = f(w,Q) by inverting MOTORQ's Q(w,voltage) function
               CALL VOLTM(OMG,QP, IMOTYPE, PARMOT,NMPAR,
     &                  VM,VM_OMG,VM_QP,
     &                  AM,AM_OMG,AM_QP )
               VOLT = VM
               AMPS = AM
C
              ELSEIF(LVOLTSET) THEN
               CALL MOTORQ(OMG,VOLT, IMOTYPE, PARMOT,NMPAR, 
     &                     QM,QM_OMG,QM_VOLT,
     &                     AM,AM_OMG,AM_VOLT )
C
C------------- Residual  =  prop torque - motor torque  at current omega
               RES     = QP     - QM
               RES_OMG = QP_OMG - QM_OMG
C
C------------- Newton change
               DOMG = -RES/RES_OMG
               DVOLT = 0.
C
               AMPS = AM
C
              ELSEIF(LTHRUSET) THEN
               CALL MOTORQ(OMG,VOLT, IMOTYPE, PARMOT,NMPAR, 
     &                     QM,QM_OMG,QM_VOLT,
     &                     AM,AM_OMG,AM_VOLT )
C
C------------- Residual  =  prop torque - motor torque  at current omega
               RES1      = QP     - QM
               RES1_OMG  = QP_OMG - QM_OMG
               RES1_VOLT =        - QM_VOLT
C
C------------- Residual  =  prop thrust - specified thrust
               RES2      = TP     - THRU
               RES2_OMG  = TP_OMG
               RES2_VOLT = 0.
C
               A11 = RES1_OMG
               A12 = RES1_VOLT
               A21 = RES2_OMG
               A22 = RES2_VOLT
C
               DET   =   A11 *A22  - A12 *A21
               DOMG  = -(RES1*A22  - A12 *RES2) / DET
               DVOLT = -(A11 *RES2 - RES1*A21 ) / DET
C
               AMPS = AM
C
              ELSEIF(LTORQSET) THEN
               CALL MOTORQ(OMG,VOLT, IMOTYPE, PARMOT,NMPAR, 
     &                     QM,QM_OMG,QM_VOLT,
     &                     AM,AM_OMG,AM_VOLT )
C
C------------- Residual  =  prop torque - motor torque  at current omega
               RES1      = QP     - QM
               RES1_OMG  = QP_OMG - QM_OMG
               RES1_VOLT =        - QM_VOLT
C
C------------- Residual  =  prop torque - specified torque
               RES2      = QP     - TORQ
               RES2_OMG  = QP_OMG
               RES2_VOLT = 0.
C
               A11 = RES1_OMG
               A12 = RES1_VOLT
               A21 = RES2_OMG
               A22 = RES2_VOLT
C
               DET   =   A11 *A22  - A12 *A21
               DOMG  = -(RES1*A22  - A12 *RES2) / DET
               DVOLT = -(A11 *RES2 - RES1*A21 ) / DET
C
               AMPS = AM
C
              ELSEIF(LAMPSSET) THEN
               CALL MOTORQ(OMG,VOLT, IMOTYPE, PARMOT,NMPAR, 
     &                     QM,QM_OMG,QM_VOLT, 
     &                     AM,AM_OMG,AM_VOLT )
C
C------------- Residual  =  prop torque - motor torque  at current omega
               RES1      = QP     - QM
               RES1_OMG  = QP_OMG - QM_OMG
               RES1_VOLT =        - QM_VOLT
C
C------------- Residual  = amps - specified amps
               RES2      = AM   - AMPS
               RES2_OMG  = AM_OMG
               RES2_VOLT = AM_VOLT
C
               A11 = RES1_OMG
               A12 = RES1_VOLT
               A21 = RES2_OMG
               A22 = RES2_VOLT
C
               DET   =   A11 *A22  - A12 *A21
               DOMG  = -(RES1*A22  - A12 *RES2) / DET
               DVOLT = -(A11 *RES2 - RES1*A21 ) / DET
C
              ELSEIF(LPELESET) THEN
               CALL MOTORQ(OMG,VOLT, IMOTYPE, PARMOT,NMPAR, 
     &                     QM,QM_OMG,QM_VOLT, 
     &                     AM,AM_OMG,AM_VOLT )
C
C------------- Residual  =  prop torque - motor torque  at current omega
               RES1      = QP     - QM
               RES1_OMG  = QP_OMG - QM_OMG
               RES1_VOLT =        - QM_VOLT
C
C------------- Residual  =  Pele - specified Pele
               RES2      = VOLT*AM   - PELE
               RES2_OMG  = VOLT*AM_OMG
               RES2_VOLT = VOLT*AM_VOLT + AM
C
               A11 = RES1_OMG
               A12 = RES1_VOLT
               A21 = RES2_OMG
               A22 = RES2_VOLT
C
               DET   =   A11 *A22  - A12 *A21
               DOMG  = -(RES1*A22  - A12 *RES2) / DET
               DVOLT = -(A11 *RES2 - RES1*A21 ) / DET
C
               AMPS = AM
C
              ENDIF
C
              RLX = 1.0
              IF(RLX*DOMG  .GT.  1.0*OMG) RLX =  1.0*OMG/DOMG
              IF(RLX*DOMG  .LT. -0.5*OMG) RLX = -0.5*OMG/DOMG
C
              IF(RLX*DVOLT .GT.  2.0*VOLT) RLX =  2.0*VOLT/DVOLT
              IF(RLX*DVOLT .LT. -0.5*VOLT) RLX = -0.5*VOLT/DVOLT

c           write(*,'(1x,i3,2(f12.3,e12.4),f7.3)') 
c    &            iter, omg, domg, volt, dvolt, rlx

C------------ convergence check
              IF(ABS(DOMG) .LT. EPS*ABS(OMG)) GO TO 110
C
C------------ Newton update
              OMG  = OMG  + RLX*DOMG
              VOLT = VOLT + RLX*DVOLT
 100        CONTINUE
cc            WRITE(*,*) 'QPROP: Convergence failed. Res =', RES
C
 110        CONTINUE

c        Q = 1.0 / (Kv*pi/30.0) * (I-Io)
c        I = Io + Q*(Kv*pi/30.0)
c        P = (V-I*R) * (I-Io)
c        eff = P / (I*V)
c        rpm = Kv * (V-I*R)
C
C---------- compute thrust-average blade cl and cd
            DTSUM = 0.
            CLAVG = 0.
            CDAVG = 0.
            DO I = 1, N
              WA = VEL + VA(I)
              WT = OMG*R(I) - VT(I)
              WSQ = WA**2 + WT**2
              DTSUM = DTSUM + WSQ*C(I)*DR(I)
              CLAVG = CLAVG + WSQ*C(I)*DR(I)*CL(I)
              CDAVG = CDAVG + WSQ*C(I)*DR(I)*CD(I)
            ENDDO
            CLAVG = CLAVG / DTSUM
            CDAVG = CDAVG / DTSUM
C
C---------- print output
            RPM = OMG*30.0/PI
            PPROP = TP*VEL
            POWER = QP*OMG
C
            PINPUT = VOLT*AMPS
C
            IF(POWER .NE. 0.0) THEN
             EFFP = PPROP/POWER
            ELSE
             EFFP = 0.
            ENDIF
C
            IF(PINPUT .NE. 0.0) THEN
             EFFM = POWER/PINPUT
            ELSE
             EFFM = 0.0
            ENDIF
C
            EFF = EFFM*EFFP
C
            IF(ABS(OMG).GT.0.0) THEN
             ADV = VEL/(OMG*RAD)
            ELSE
             ADV = 0.
            ENDIF
C
            IF(OMG .EQ. 0.0) THEN
             WRI = 0.
            ELSE
             WRI = 1.0 / (OMG*RAD)
            ENDIF
C
            CT = TP * WRI**2 * 2.0 / (RHO * PI * RAD**2)
            CP = QP * WRI**2 * 2.0 / (RHO * PI * RAD**3)
            DV = SQRT(VEL**2 + TP * 2.0/(RHO*PI*RAD**2)) - VEL
C
            WRITE(LU,2100) CHARF,
     &        VEL,   RPM,  DBET,   TP,     QP, POWER, 
     &       VOLT,AMPS,  EFFM,  EFFP, ADV, CT, CP, DV, 
     &       EFF, PINPUT, PPROP, CLAVG, CDAVG
 2100       FORMAT(A,
     &       F8.3,  G12.4,  F7.3, G12.4, G12.4, G12.4,
     &       F8.3, F10.4,  F9.4, F9.4, F10.5, G12.4, G12.4, F9.4,
     &       F9.4, G12.4, G12.4, F9.4, G12.4)
          ENDDO
          IF(.NOT.LRDUMP .AND. NVEL.GT.1) WRITE(LU,1000)
        ENDDO
      ENDDO
C
      IF(LRDUMP) THEN
C----- dump radial distributions
       WRITE(LU,1100)
       WRITE(LU,1100)
     & ' radius   chord   beta      Cl       Cd       Re    Mach',
     & '     effi     effp    Wa(m/s)     Aswirl      adv_wake'
C                              123456789012123456789012123456789012
       DO I = 1, N
         WA = VEL + VA(I)
         WT = OMG*R(I) - VT(I)
         WSQ = WA**2 + WT**2
         W = SQRT(WSQ)
C
C------- local Mach and Re
         AMA = W/VSO
         IRE = INT( RHO*W*C(I)/RMU + 0.5 )
C
C------- local wake advance ratio, induced and profile efficiencies
         IF(WA.NE.0.0 .AND. WT.NE.0.0) THEN
          ADW = (WA/WT) * (R(I)/RAD)
          EFFI = (VEL/(OMG*R(I))) * (WT/WA)
          EFFP = (CL(I) - CD(I)*WA/WT)
     &         / (CL(I) + CD(I)*WT/WA)
         ELSE
          ADW = 0.
          EFFI = 0.
          EFFP = 0.
         ENDIF
C
         EFFI = MAX( -99.0 , MIN( 99.0 , EFFI ) )
         EFFP = MAX( -99.0 , MIN( 99.0 , EFFP ) )
C
         RU = R(I)
         CU = C(I)
         BU = B(I) * 180.0/PI
C
C------- swirl flow angle in non-rotating frame
         ASWIRL = ATAN2( VT(I) , WA ) * 180.0/PI
C
         WRITE(LU,3100) 
     &               RU,   CU,   BU, CL(I), CD(I),
     &              IRE,  AMA, EFFI, EFFP, WA, ASWIRL, ADW
 3100    FORMAT(1X,F8.4, F8.4, F8.3, F9.4,  F9.5,
     &               I9, F7.3, F9.4, F9.4, G12.4, G12.4, G12.4)
       ENDDO
      ENDIF
C
      STOP
C
 900  CONTINUE
      WRITE(*,9000) FILNAM(1:64), ILINE, LINE
 9000 FORMAT(/' Read error'
     &       /'   in file:  ', A
     &       /'   line',I3,':  ', A)
      STOP
C
 950  CONTINUE
      WRITE(*,9500) FILNAM(1:64), ILINE
 9500 FORMAT(/' Unexpected end-of-file reached'
     &       /'   in file:  ', A
     &       /'   line',I3)
      STOP
C
C
 980  CONTINUE
      WRITE(*,9500) FILNAM(1:64), ILINE
 9800 FORMAT(/' Fewer parameters than required'
     &       /'   in file:  ', A
     &       /'   line',I3)
      STOP
C
      END ! QPROP