SUBROUTINE econ_analysis(TIME,delt,tfinal,time0,Par,XIN,OUT) 
!**********************************************************************************************

! This component is Type 197, but is just a tweaked version of Type 582 - so '582' is used freq

!**********************************************************************************************
! This component generates a P1 P2 style economic analysis. In Mode 1 of this component, P1 and
!  P2 are supplied by the user as PARAMETERS. In Mode 2, P1 and P2 are caclulated from a long
!  series of PARAMETERS that the user must specify.
!
! WRITTEN BY: D. Bradley for Thermal Energy System Specialists - Summer 2003
!
! REVISION HISTORY:
!   05.10.2004 - DEB - Updated to TRNSYS 16 coding standard.
!**********************************************************************************************
! Copyright © 2004 Thermal Energy SYstem Specialists, LLC, All rights reserved.

IMPLICIT NONE !force explicit declaration of local variables

INTEGER, PARAMETER :: iknd=SELECTED_INT_KIND(8)  ! nobody does this
INTEGER, PARAMETER :: DP=SELECTED_REAL_KIND(14)  ! this is double precision
INTEGER, PARAMETER :: SP=SELECTED_REAL_KIND(6)  ! this is single precision
      
!    TRNSYS DECLARATIONS
real(DP),intent(in) :: XIN,PAR
real(DP),intent(in) :: TIME,delt
real(DP),intent(out) :: OUT

!    SET THE MAXIMUM NUMBER OF PARAMETERS(NP),INPUTS(NI),OUTPUTS(NOUT),AND DERIVATIVES(ND)
!    THAT MAY BE SUPPLIED FOR THIS TYPE
INTEGER, PARAMETER :: MaxAlternatives=10
INTEGER, PARAMETER :: NPMAX=11+8*MaxAlternatives,NIMAX=MaxAlternatives,NOMAX=7*MaxAlternatives

!    REQUIRED TRNSYS DIMENSIONS
DIMENSION XIN(2*NIMAX),OUT(NOMAX),PAR(NPMAX)

!    LOCAL VARIABLE DECLARATIONS
CHARACTER*100 MSG582_1,MSG582_2
real(DP) :: stored(MaxAlternatives), & !array used to send values to and recieve values from global storage
AllowableCost(MaxAlternatives), &           !the maximum amount that the equipment may cost (results in 0 life cycle savings)
IntFuelCost(MaxAlternatives), & !integrated fuel cost at this point in the simulation.
FuelCost(MaxAlternatives), &    !fuel cost rate for the current timestep                     (INP) [currency/hr]
InstCost(MaxAlternatives), &    !fuel cost for the current timestep                          (INP) [currency]
AnnualFuelCost(MaxAlternatives), &          !fuel cost for a year
PWF, &                          !present worth factor function
SCPWF, &                        !present worth factor of a one time cost function
i, &                            !interest rate argument to the PWF function
FuelInflationRate, &            !rate at which fuel costs are expected to increase           (PAR) [0..1]
InterestRate, &                 !interest rate anticipated for alternative investment        (PAR) [0..1]
TaxRate, &                      !combined (federal and state) tax rate                       (PAR) [0..1] 
SalvageFrac(MaxAlternatives), & !salvage price as a fraction of equipment purchase price     (PAR) [0..1]
RebateFrac(MaxAlternatives), &  !rebate or refund as a fraction of equipment purchase price  (PAR) [0..1]
P1, &
P2Term1(MaxAlternatives), &     !individual term used to build P2
P2Term2(MaxAlternatives), &     !individual term used to build P2
P2Term3(MaxAlternatives), &     !individual term used to build P2
P2Term4(MaxAlternatives), &     !individual term used to build P2
P2Term5(MaxAlternatives), &     !individual term used to build P2
P2Term6(MaxAlternatives), &     !individual term used to build P2
P2(MaxAlternatives), &
DownPayFrac(MaxAlternatives), & !the fraction of the purchase price paid up front                 (PAR) [0..1]
InstallFrac(MaxAlternatives), & !the fraction of the purchase price to install the equipment      (PAR) [0..1] 
GeneralInflation, &             !general inflation rate                                           (PAR) [0..1] 
PropertyTaxRate, &              !property tax rate                                                (PAR) [0..1] 
Payback(MaxAlternatives), &     !simple payback period of this alternative as compared to another (OUT) [years]
ROI(MaxAlternatives), &         !return on investment of this alternative as compared to another  (OUT) [0..1] 
MiscExpFrac(MaxAlternatives), & !miscelaneous annual expenses                                     (PAR) [currency] 
LoanRate, &                     !the interest rate on the loan for equipment purchase             (PAR) [0..1]
EquipCost(MaxAlternatives), &   !equipment first cost                                             (PAR) [currency]
LCC(MaxAlternatives), &         !life cycle cost of the investment                                (OUT) [currency]
LCS(MaxAlternatives), &         !life cycle savings of this alternative as compared to another    (OUT) [currency]
TIME0, &                        !the hour of the year on which the simulation starts
!DELT, &                         !the simulation time step 
TFINAL, &                       !the hour of the year on which the simulation ends
jjd, &                          !counter; double precision 
TADJ                            !time adjustment factor

INTEGER :: nYears, &               !number of years in the analysis                                  (PAR) [years]
ComparisonSys(MaxAlternatives), &    !the system alternative number to which a system is compared (PAR) [-]
nMortgage, &                    !number of years over which equipment is financed                 (PAR) [years]
nAlternatives, &                !number of system alternatives to be evaluated                    (PAR) [-]
DepreciationLife(MaxAlternatives), & !the deprication lifetime for the equipment                  (PAR) [years]
n, &                            !number of years argument to the PWF function
cFlag, &                        !flag telling whehther the installation is commercial or not      (PAR) [0 or 1]
jj, &                           !do loop counter
NI, &                           !the number of inputs in this instance of type582
NP, &                           !the number of parameters in this instance of type582
NO, &                           !the number of outputs in this instance of type582
Mode                            !1:P1 and P2 are parameters, 2:P1 and P2 are calculated           (PAR) [1 or 2] 

!    DATA STATEMENTS
DATA MSG582_1/'an annual fuel cost was computed based on a simulation length of less than one year.'/
DATA MSG582_2/'an average annual fuel cost was computed based on a simulation length of greater than one year.'/
logical first_run
!save first_run
save    !stored
!    FUNCTIONS USED BY THIS COMPONENT
!present worth factor of a single cost
SCPWF(n,i) = 1.d0/((1.d0+i)**n)

!--------------------------------------------------------------------------------------------------
!    PERFORM INITIAL TIMESTEP MANIPULATIONS
!IF(.not.first_run)THEN
if (time < delt) then
    first_run = .true.
    !read parameter values
    Mode                = jfix(PAR(1)+0.01)
    nAlternatives       = jfix(PAR(2)+0.01)
    NO = 8*nAlternatives

    IF((Mode < 1).OR.(Mode > 2)) write(*,510) "mode is out of range"
    IF((nAlternatives < 0).OR.(nAlternatives > MaxAlternatives)) write(*,510) "nAlternatives is out of range"
    510 format(a)

    !call the type check subroutine to compare what this type requires with what is in the input file.
    NI = 2*nAlternatives
    IF(Mode == 1) THEN
    NP = 3+3*nAlternatives
    ELSEIF(Mode == 2) THEN
    NP = 11+8*nAlternatives
    ENDIF 
  
    IF (Mode == 1) THEN
        P1                = PAR(3)
        DO jj=1,nAlternatives
            ComparisonSys(jj)     = PAR(1+(3*jj))
            EquipCost(jj)         = PAR(2+(3*jj))
            P2(jj)                = PAR(3+(3*jj))
        ENDDO
    ELSEIF (Mode == 2) THEN
        nYears            = PAR(3)
        nMortgage         = PAR(4)
        cFlag             = JFIX(PAR(5)+0.01)
        FuelInflationRate = PAR(6)
        InterestRate      = PAR(7)
        TaxRate           = PAR(8)
        GeneralInflation  = PAR(9)
        PropertyTaxRate   = PAR(10)
        LoanRate          = PAR(11)
        DO jj=1,nAlternatives
            ComparisonSys(jj)    = PAR(4+(8*jj))
            EquipCost(jj)        = PAR(5+(8*jj))
            DepreciationLife(jj) = PAR(6+(8*jj))
            DownPayFrac(jj)      = PAR(7+(8*jj))
            InstallFrac(jj)      = PAR(8+(8*jj))
            RebateFrac(jj)       = PAR(9+(8*jj))
            SalvageFrac(jj)      = PAR(10+(8*jj))
            MiscExpFrac(jj)      = PAR(11+(8*jj)) 
        ENDDO
    ENDIF
        !check the parameters for problems and RETURN if any are found
        IF (Mode == 1) THEN
            IF(P1 <= 0) write(*,510) "P1 <= 0"
            DO jj=1,nAlternatives
                IF((ComparisonSys(jj) < 0).OR.(ComparisonSys(jj) > nAlternatives))    write(*,510) "ComparisonSys is out of range"
                IF(EquipCost(jj) < 0)then
                                                                 write(*,510) "EquipCost < 0"
                endif
                IF(P2(jj) <= 0)                                                       write(*,510) "P2 <= 0"
            ENDDO
        ELSEIF(Mode == 2) THEN
            IF(nYears < 0)                                                  write(*,510) "nYears < 0"
            IF(nMortgage < 0)                                               write(*,510) "nMortgage < 0"
            IF((cFlag < 0.d0).OR.(cFlag > 1.d0))                            write(*,510) "cFlag is out of range"
            IF((FuelInflationRate < 0.d0).OR.(FuelInflationRate > 1.d0))    write(*,510) "FuelInflationRate is out of range"
            IF((InterestRate < 0.d0).OR.(InterestRate > 1.d0))              write(*,510) "InterestRate is out of range"
            IF((TaxRate < 0.d0).OR.(TaxRate > 1.d0))                        write(*,510) "TaxRate is out of range"
            IF((GeneralInflation < 0.d0).OR.(GeneralInflation > 1.d0))      write(*,510) "GeneralInflation is out of range"
            IF((PropertyTaxRate < 0.d0).OR.(PropertyTaxRate > 1.d0))        write(*,510) "PropertyTaxRate is out of range"
            IF((LoanRate < 0.d0).OR.(LoanRate > 1.d0))                      write(*,510) "LoanRate is out of range"
            DO jj=1,nAlternatives
                IF((ComparisonSys(jj) < 0).OR.(ComparisonSys(jj) > nAlternatives))    write(*,510) "ComparisonSys is out of range" 
                IF(EquipCost(jj) < 0)then
                                                            write(*,510) "EquipCost < 0"
                endif
                IF(DepreciationLife(jj) < 0)                                          write(*,510) "DepreciationLife < 0"
                IF((DownPayFrac(jj) < 0.d0).OR.(DownPayFrac(jj) > 1.d0))              write(*,510) "DownPayFrac is out of range"
                IF((InstallFrac(jj) < 0.d0).OR.(InstallFrac(jj) > 1.d0))              write(*,510) "InstallFrac is out of range"
                IF((RebateFrac(jj) < 0.d0).OR.(RebateFrac(jj) > 1.d0))                write(*,510) "RebateFrac is out of range"
                IF((SalvageFrac(jj) < 0.d0).OR.(SalvageFrac(jj) > 1.d0))              write(*,510) "SalvageFrac is out of range"
                IF((MiscExpFrac(jj) < 0.d0).OR.(MiscExpFrac(jj) > 1.d0))              write(*,510) "MiscExpFrac is out of range"
            ENDDO
        ENDIF
    !initialize storage
    stored(1:nAlternatives) = 0.d0
    !set the outputs to 0
    OUT(1:NO) = 0.d0 !zero outputs
else
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!    RETRIEVE VALUES OF STORED VARIABLES
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!    Mode                = jfix(PAR(1)+0.01)
!    nAlternatives       = jfix(PAR(2)+0.01)
!    NO = 8*nAlternatives
!    IF (Mode == 1) THEN
!        P1                = PAR(3)
!        DO jj=1,nAlternatives
!          ComparisonSys(jj)     = PAR(1+(3*jj))
!          EquipCost(jj)         = PAR(2+(3*jj))
!          P2(jj)                = PAR(3+(3*jj))
!        ENDDO
!    ELSEIF (Mode == 2) THEN
!        nYears            = PAR(3)
!        nMortgage         = PAR(4)
!        cFlag             = JFIX(PAR(5)+0.01)
!        FuelInflationRate = PAR(6)
!        InterestRate      = PAR(7)
!        TaxRate           = PAR(8)
!        GeneralInflation  = PAR(9)
!        PropertyTaxRate   = PAR(10)
!        LoanRate          = PAR(11)
!        DO jj=1,nAlternatives
!          ComparisonSys(jj)    = PAR(4+(8*jj))
!          EquipCost(jj)        = PAR(5+(8*jj))
!          DepreciationLife(jj) = PAR(6+(8*jj))
!          DownPayFrac(jj)      = PAR(7+(8*jj))
!          InstallFrac(jj)      = PAR(8+(8*jj))
!          RebateFrac(jj)       = PAR(9+(8*jj))
!          SalvageFrac(jj)      = PAR(10+(8*jj))
!          MiscExpFrac(jj)      = PAR(11+(8*jj)) 
!        ENDDO
!    ENDIF
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    IntFuelCost(1:nAlternatives) = stored(1:nAlternatives)
    if (time > 8760) then
	    continue
    endif

!    GET THE VALUES OF THE INPUTS TO THIS COMPONENT
    FuelCost(1:nAlternatives)    = XIN(1:nAlternatives)
    InstCost(1:nAlternatives)    = XIN(nAlternatives+1:2*nAlternatives)

     !update the running fuel cost total (integrate the fuel cost); WEIGHTED FOR TVOM
    IntFuelCost(1:nAlternatives) = IntFuelCost(1:nAlternatives)+((FuelCost(1:nAlternatives)*DELT)+InstCost(1:nAlternatives))/ &
     ((1+InterestRate-FuelInflationRate)**(TIME/8760.d0))

     !the remainder of the calculations can be performed at the end of the simulation.
    DO jj=1,nAlternatives
      OUT(1)      = IntFuelCost(jj)
    ENDDO

    IF (TIME /= TFINAL) then
        stored(1:nAlternatives) = IntFuelCost(1:nAlternatives) 
        RETURN
    endif

     !second part of the weighting of costs to account for TVOM; total time is adjusted
    jjd=0
    TADJ=0
    DO WHILE (jjd <= TFINAL)
	    TADJ = TADJ+DELT/((1+InterestRate-FuelInflationRate)**(jjd/8760.d0))
	    jjd = jjd+DELT
    ENDDO

 !calculate annual fuel cost based on the fuel cost during the simulation
    AnnualFuelCost(1:nAlternatives) = 8760.d0*(IntFuelCost(1:nAlternatives)/(TADJ))
    
     !if the simulation was less than a year, let the user know that the annual fuel cost was computed
     !based on the fuel cost calculated during the simulation. If the simulation was longer than a
     ! year, let the user know that the annual fuel cost was computed based on the average.
!    IF ((TFINAL-TIME0) < 8760.d0) THEN
!      write(*,510) "Simulation < 1 year; annual fuel cost computed based on fuel cost during the simulation"
!    ELSEIF ((TFINAL-TIME0) > 8760.d0) THEN
!      write(*,510) "Simulation > 1 year; annual fuel cost computed based on average fuel cost during the simulation"
!    ENDIF

 !if the mode is 1, P1 and P2 are parameters and do not need to be calcualted.
    IF (Mode == 1) THEN
      DO jj=1,nAlternatives
        LCC(jj) = P1*AnnualFuelCost(jj) + P2(jj)*EquipCost(jj)  
      ENDDO
     !if the mode is 2, P1 and P2 are computed from the specified parameter values.
    ELSEIF (Mode == 2) THEN
      DO jj=1,nAlternatives
        P1 = (1-cFlag*TaxRate)*PWF(nYears,FuelInflationRate,InterestRate)

        !purchase cost of the equipment
	    IF (DownPayFrac(jj) >= 1.d0) THEN
           P2Term1(jj) = 1.d0
	    ELSE
           P2Term1(jj) = DownPayFrac(jj)+(1-DownPayFrac(jj))*(PWF(nYears,0.D0,InterestRate)/PWF(nMortgage,0.D0,LoanRate))- &
	       TaxRate*(1-DownPayFrac(jj))*(PWF(nYears,0.D0,InterestRate)*(LoanRate-1.D0/(PWF(nMortgage,0.D0,InterestRate)))+ &
	       (PWF(nYears,0.D0,InterestRate)/PWF(nMortgage,0.D0,LoanRate)))
	    ENDIF

        !fraction of the purchase price necessary to install the equipment
        P2Term2(jj) = InstallFrac(jj)

        !property tax and miscelaneous expences (grouped because they inflate at the general inflation rate)
        P2Term3(jj) = (PropertyTaxRate*(1-TaxRate)+MiscExpFrac(jj)*(1-cFlag*TaxRate))*PWF(nYears,GeneralInflation,InterestRate)

        !effects of equipment depreciation 
        P2Term4(jj) = -cFlag*TaxRate*PWF(DepreciationLife(jj),0.D0,InterestRate)/DepreciationLife(jj)

        !fraction of equipment cost given as a rebate or tax credit
        P2Term5(jj) = -RebateFrac(jj)

        !salvage cost
        P2Term6(jj) = -SalvageFrac(jj)*SCPWF(nYears,InterestRate)

        !put the terms together into P2
	    P2(jj) = P2Term1(jj) + P2Term2(jj) + P2Term3(jj) + P2Term4(jj) + P2Term5(jj) + P2Term6(jj)
         

        !life cycle cost calculation for each system.
        LCC(jj) = P1*AnnualFuelCost(jj) + P2(jj)*EquipCost(jj)
      ENDDO
    ENDIF

!    THE REMAINING ECONOMIC FIGURES OF MERIT ARE BASED ON COMPARISONS BETWEEN SYSTEMS
    DO jj=1,nAlternatives
      !if the system is being compared to itself, set comparative figures of merit to -999.
      IF (jj == ComparisonSys(jj)) THEN
  	    LCS(jj)           = -999.d0
        Payback(jj)       = -999.d0
        ROI(jj)           = -999.d0
        AllowableCost(jj) = -999.d0
      ELSE
        !life cycle savings
        LCS(jj) = LCC(jj) - LCC(ComparisonSys(jj))
        !simple payback
        Payback(jj) = (EquipCost(jj)-EquipCost(ComparisonSys(jj)))/(AnnualFuelCost(ComparisonSys(jj))-AnnualFuelCost(jj)) 
	    !return on investment
	    ROI(jj) = (AnnualFuelCost(jj)-AnnualFuelCost(ComparisonSys(jj)))/(EquipCost(ComparisonSys(jj))-EquipCost(jj)) 
	    !allowable investment cost
        AllowableCost(jj) = 1/P2(jj)*(P1*(AnnualFuelCost(ComparisonSys(jj))-AnnualFuelCost(jj))+P2(ComparisonSys(jj))* &
         EquipCost(ComparisonSys(jj)))
      ENDIF
    ENDDO

!    SET THE OUTPUTS
    DO jj=1,nAlternatives
      OUT((8*jj)-7) = IntFuelCost(jj)    ![currency]
      OUT((8*jj)-6) = AnnualFuelCost(jj) ![currency]
      OUT((8*jj)-5) = LCC(jj)            ![currency]
      OUT((8*jj)-4) = LCS(jj)            ![currency]
      OUT((8*jj)-3) = Payback(jj)        ![years]
      OUT((8*jj)-2) = tadj  !ROI(jj)            ![0..1]
      OUT((8*jj)-1) = P1  !AllowableCost(jj)  ![currency]
      OUT(8*jj)     = P2(jj)			 !
    ENDDO

endif

RETURN 
END SUBROUTINE econ_analysis

!present worth factor of an ongoing (annual) cost
DOUBLE PRECISION FUNCTION PWF(N,F,I)

INTEGER, PARAMETER :: iknd=SELECTED_INT_KIND(8)  ! nobody does this
INTEGER, PARAMETER :: DP=SELECTED_REAL_KIND(14)  ! this is double precision
INTEGER, PARAMETER :: SP=SELECTED_REAL_KIND(6)  ! this is single precision

real(DP) F,I
INTEGER N

if(i.eq.f) then
   PWF = DBLE(n)/(i+1.d0)
else
   PWF = (1.d0/(i-f))*(1.d0-((1.d0+f)/(1.d0+i))**DBLE(n))
endif

end
