program PPV
  implicit none
  ! ================== Parámetros de ejemplo ==================
  double precision :: G, Tc_C, Tref_C, Itot_ref
  double precision :: Isc_ref, Voc_ref, Imp_ref, Vmp_ref
  double precision :: muIsc_ref, muVoc_ref, eg_eV
  double precision :: sercell, area_m2
  double precision :: Rs, gam, IL, Io, Voc, Isc
  double precision :: imp, vmp, pmp, ff, eta, P_ele

  ! Datos de catálogo (ejemplo)
  Isc_ref   = 6.95d0     ! [A]  corriente de cortocircuito a ref
  Voc_ref   = 3.17d0     ! [V]  voltaje de circuito abierto a ref
  Imp_ref   = 6.76d0     ! [A]  corriente en MPP a ref
  Vmp_ref   = 2.94d0     ! [V]  voltaje en MPP a ref
  Tref_C    = 25.0d0     ! [°C] temperatura de referencia (STC)
  Itot_ref  = 500000.0d0 ! [W/m2] irradiancia de referencia
  muIsc_ref =  0.0006d0  ! [A/K] coef. temp. de Isc (ejemplo)
  muVoc_ref = -0.0023d0  ! [V/K] coef. temp. de Voc (ejemplo)
  eg_eV     = 0.67d0     ! [eV]  energía de banda prohibida
  sercell   = 1.0d0      ! [-]   celdas en serie en el módulo
  area_m2   = 1.0d0      ! [m2]  área del módulo (ejemplo)

  ! Condiciones de operación
  G    = 500000.d0 ! [W/m2] irradiancia sobre el módulo
  Tc_C = 25.d0     ! [°C]   temperatura de célula (si no la sabes, usa NOCT o balance aparte)

  ! ================== Cálculo principal ==================
  call Params(G,Tc_C,Tref_C,Itot_ref, &
       Isc_ref,Voc_ref,Imp_ref,Vmp_ref,muIsc_ref,muVoc_ref,eg_eV,sercell,area_m2, &
       Rs,gam,IL,Io,Voc,Isc,imp,vmp,pmp,ff,eta)


  P_ele = 2 * pmp

  ! ================== Salida de resultados ==================
  write(*,'(a)')   '========== MPPT (modelo FV 4-parámetros) =========='
  write(*,'(a,f8.2)') 'Irradiancia [W/m2] : ', G
  write(*,'(a,f8.2)') 'Tc [°C]            : ', Tc_C
  write(*,'(a,f8.3)') 'Rs [ohm]           : ', Rs
  write(*,'(a,f8.3)') 'gamma [-]          : ', gam
  write(*,'(a,f8.3)') 'IL [A]             : ', IL
  write(*,'(a,1pe12.4)') 'Io [A]          : ', Io
  write(*,'(a,f8.3)') 'Voc [V]            : ', Voc
  write(*,'(a,f8.3)') 'Isc [A]            : ', Isc
  write(*,'(a,f8.3)') 'Vmp [V]            : ', vmp
  write(*,'(a,f8.3)') 'Imp [A]            : ', imp
  write(*,'(a,f8.3)') 'Pmp [W]            : ', pmp
  write(*,'(a,f8.3)') 'FF  [-]            : ', ff
  write(*,'(a,f8.3)') 'Eta [-]            : ', eta
  write(*,'(a,f8.3)') 'P_ele [-]          : ', P_ele

end program PPV

! ==============================================================
! Subrutina principal: calcula parámetros y MPPT
! ==============================================================
subroutine Params(G,Tc_C,Tref_C,Itot_ref, &
     Isc_ref,Voc_ref,Imp_ref,Vmp_ref,muIsc_ref,muVoc_ref,eg_eV,sercell,area_m2, &
     Rs,gam,IL,Io,Voc,Isc,imp,vmp,pmp,ff,eta)

  implicit none
  double precision, intent(in)  :: G,Tc_C,Tref_C,Itot_ref
  double precision, intent(in)  :: Isc_ref,Voc_ref,Imp_ref,Vmp_ref
  double precision, intent(in)  :: muIsc_ref,muVoc_ref,eg_eV,sercell,area_m2
  double precision, intent(out) :: Rs,gam,IL,Io,Voc,Isc,imp,vmp,pmp,ff,eta

  double precision :: Tc,Tref,q_bz,Io_ref,IL_ref,a
  integer :: ierr

  Tc    = Tc_C   + 273.15d0
  Tref  = Tref_C + 273.15d0
  q_bz  = 11604.45d0    ! Constante q/k en K/V

  ! --- Rs por bisección
  call RRS(Rs,q_bz,sercell,Tref,Imp_ref,Vmp_ref,Isc_ref,Voc_ref,muVoc_ref,muIsc_ref,eg_eV,ierr)

  ! --- Parámetros base
  gam    = q_bz*(Vmp_ref - Voc_ref + Imp_ref*Rs)/(Tref*dlog(1.d0 - Imp_ref/Isc_ref))
  a      = gam/sercell
  IL_ref = Isc_ref
  Io_ref = IL_ref/dexp(q_bz*Voc_ref/(gam*Tref))

  ! --- Escalamiento
  IL = (G/Itot_ref)*(IL_ref + muIsc_ref*(Tc - Tref))
  if (IL < 0.d0) IL = 0.d0
  Io = Io_ref*((Tc/Tref)**3)*dexp((q_bz*eg_eV/a)*((1.d0/Tref)-(1.d0/Tc)))

  if (IL > 0.d0) then
     Voc = gam*Tc/q_bz * dlog(IL/Io + 1.d0)
  else
     Voc = 0.d0
  end if
  Isc = IL

  ! --- MPPT
  call MMPPT(IL,Io,gam,Tc,q_bz,Rs,Imp_ref,muIsc_ref,Itot_ref,G,Tref,imp,vmp,pmp)

  ! --- Métricas
  if (Voc > 0.d0 .and. Isc > 0.d0) then
     ff = (vmp*imp)/(Voc*Isc)
  else
     ff = 0.d0
  end if

  if (G > 1.d-9 .and. area_m2 > 0.d0) then
     eta = pmp*12                    ! eta = pmp/(G*area_m2)
  else
     eta = 0.d0
  end if

end subroutine Params

! ==============================================================
! MPPT por Newton-Raphson
! ==============================================================
subroutine MMPPT(IL,Io,gam,T,q_bz,Rs,Imp_ref,muIsc_ref,Itot_ref,G,Tref,imp,vmp,pmp)
  implicit none
  double precision, intent(in)  :: IL,Io,gam,T,q_bz,Rs
  double precision, intent(in)  :: Imp_ref,muIsc_ref,Itot_ref,G,Tref
  double precision, intent(out) :: imp,vmp,pmp
  double precision :: imxo, imxn, f1, f1p
  integer :: it

  if (G > 0.d0) then
     imxn = (G/Itot_ref)*(Imp_ref + muIsc_ref*(T - Tref))
  else
     imp = 0.d0; vmp = 0.d0; pmp = 0.d0
     return
  end if

  do it = 1,200
     imxo = imxn
     if (imxo > IL) imxo = 0.99d0*IL
     if (Io > 0.d0) then
        f1  = imxo + (imxo - IL - Io) * ( dlog((IL - imxo + Io)/Io) - imxo*q_bz*Rs/(gam*T) ) &
              / ( 1.d0 + (IL - imxo + Io)*(q_bz*Rs/(gam*T)) )
        f1p = 2.d0 + ( dlog((IL - imxo + Io)/Io) - (q_bz*Rs*imxo/(gam*T)) ) &
              / ( (1.d0 + (IL - imxo + Io)*(q_bz*Rs/(gam*T)))**2 )
     else
        f1  = 0.d0
        f1p = 1.d0
     end if
     imxn = imxo - f1/f1p
     if (dabs(imxn - imxo) <= 1.d-3) exit
  end do

  imp = max(0.d0, min(imxn, IL))
  if (Io > 0.d0) then
     vmp = dlog(1.d0 + (IL - imp)/Io)*(T*gam/q_bz) - imp*Rs
  else
     vmp = 0.d0
  end if
  pmp = imp*vmp
  !P_ele = pmp * 1200
end subroutine MMPPT

! ==============================================================
! Cálculo de Rs por bisección (Type103)
! ==============================================================
subroutine RRS(RSeries,q_bz,sercell,Tcell_ref,Imppt_ref,Vmppt_ref,Isc_ref,Voc_ref,muVoc_ref,muIsc_ref,eg,ierr)
  implicit none
  double precision, intent(out) :: RSeries
  double precision, intent(in)  :: q_bz,sercell,Tcell_ref
  double precision, intent(in)  :: Imppt_ref,Vmppt_ref,Isc_ref,Voc_ref
  double precision, intent(in)  :: muVoc_ref,muIsc_ref,eg
  double precision :: aup, alow, anew, gamup, gamlow, gamnew
  double precision :: rsup, rslow, rsnew, ioup, iolow, ionew
  double precision :: fup, flow, fnew
  integer :: it, ierr

  ierr = 0
  rsup = ((sercell*Tcell_ref*dlog(1.d0 - Imppt_ref/Isc_ref)/q_bz)+Voc_ref - Vmppt_ref)/Imppt_ref
  aup  = 1.d0
  gamup = sercell
  ioup = Isc_ref*dexp(-q_bz*Voc_ref/(gamup*Tcell_ref))

  rslow = 0.d0
  gamlow = q_bz*(Vmppt_ref - Voc_ref)/(Tcell_ref*dlog(1.d0 - Imppt_ref/Isc_ref))
  alow   = gamlow/sercell
  iolow  = Isc_ref*dexp(-q_bz*Voc_ref/(gamlow*Tcell_ref))

  do it = 1,10000
     if (dabs(rsup - rslow) <= 5.d-4) exit
     rsnew = 0.5d0*(rsup + rslow)
     gamnew = q_bz*(Vmppt_ref - Voc_ref + Imppt_ref*rsnew)/(Tcell_ref*dlog(1.d0 - Imppt_ref/Isc_ref))
     anew = gamnew/sercell
     ionew = Isc_ref*dexp(-q_bz*Voc_ref/(gamnew*Tcell_ref))

     fup  = -muVoc_ref + (gamup/q_bz)*( dlog(1.d0 + Isc_ref/ioup) + (Tcell_ref/(Isc_ref + ioup)) &
          *(muIsc_ref - Isc_ref*((q_bz*eg/(aup*Tcell_ref*Tcell_ref)) + 3.d0/Tcell_ref)) )
     flow = -muVoc_ref + (gamlow/q_bz)*( dlog(1.d0 + Isc_ref/iolow) + (Tcell_ref/(Isc_ref + iolow)) &
          *(muIsc_ref - Isc_ref*((q_bz*eg/(alow*Tcell_ref*Tcell_ref)) + 3.d0/Tcell_ref)) )
     fnew = -muVoc_ref + (gamnew/q_bz)*( dlog(1.d0 + Isc_ref/ionew) + (Tcell_ref/(Isc_ref + ionew)) &
          *(muIsc_ref - Isc_ref*((q_bz*eg/(anew*Tcell_ref*Tcell_ref)) + 3.d0/Tcell_ref)) )

     if (flow*fnew < 0.d0) then
        rsup = rsnew
        gamup = q_bz*(Vmppt_ref - Voc_ref + Imppt_ref*rsup)/(Tcell_ref*dlog(1.d0 - Imppt_ref/Isc_ref))
        aup   = gamup/sercell
        ioup  = Isc_ref*dexp(-q_bz*Voc_ref/(gamup*Tcell_ref))
     else
        rslow = rsnew
        gamlow = q_bz*(Vmppt_ref - Voc_ref + Imppt_ref*rslow)/(Tcell_ref*dlog(1.d0 - Imppt_ref/Isc_ref))
        alow   = gamlow/sercell
        iolow  = Isc_ref*dexp(-q_bz*Voc_ref/(gamlow*Tcell_ref))
     end if
  end do

  RSeries = rsnew
  if (RSeries < 1.d-3) ierr = 1
end subroutine RRS

Embed on website

To embed this program on your website, copy the following code and paste it into your website's HTML: