! homoegeneity.f90 ! written by Tim Mitchell ! module contains routines to carry out homoegenity testing ! based upon Int J Clim 17(1):25-34 (1997) Appendix 4 module Homogeneity use Regress implicit none contains !******************************************************************************* subroutine CruTSTestMon (MonScore,QPassFail,Statistic,DataO,DataD,Order,Suffix,& BegN,EndN,BegO,EndO,BegD,EndD,CandO,CandD,Factor, & XBreakYear,SmallOrder) real, pointer, dimension (:,:) :: MonScore ! NYear,NMonth real, pointer, dimension (:,:) :: DataR ! NRStn,NYear real, pointer, dimension (:) :: DataC ! NYear real, pointer, dimension (:) :: SubOut ! NYear real, pointer, dimension (:) :: AnnScore ! NYear real, dimension (12) :: MaxValue integer, pointer, dimension (:,:,:) :: DataO,DataD integer, pointer, dimension (:) :: Order logical, pointer, dimension (:) :: SourceBreak real, intent(in), optional :: Factor ! default=1;>1 makes it easier to pass test real, intent(out) :: Statistic ! the test stat at failure year integer, intent(in), optional :: SmallOrder ! Order size is not NDStn, but NRStn integer, intent(in), optional :: XBreakYear ! force exam this year only (1=first in common per) integer, intent(in) :: BegO,EndO ! beg/end indices of common per in O integer, intent(in) :: BegD,EndD ! beg/end indices of common per in D integer, intent(in) :: BegN,EndN ! b/e i of nml per within the common per integer, intent(in) :: CandO ! candidate stn index (from DataO) integer, intent(in), optional :: CandD ! candidate stn index (from DataD) to merge integer, intent(out) :: QPassFail ! -999=no-test,0=pass,>0=fail-at-this-year integer, dimension (12) :: MaxIndex character (len=4), intent(in) :: Suffix real, parameter, dimension (12) :: SigStat = [11.2,8.9,7.1,5.6,4.4,3.5,2.9,2.4,2.2,2.1,2.0,2.0] real, parameter :: MissVal = -999.0 integer, parameter :: DataMissVal = -9999 real :: OpTot,OpEn,SigFactor integer :: ReadStatus,AllocStat, QCurrent,QTestYear,QTestMonth integer :: NYear,NDStn,NRStn,NOYear,NDYear,NMonth,NTestMonth integer :: XYear,XDStn,XRStn,XOYear,XDYear,XMonth,XTestMonth integer :: AnomType,Break,Comm0,Comm1 !*************************************** introduction !write (99,"(6i6)"), BegN,EndN,BegO,EndO,BegD,EndD ! @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ AnomType = 0 if (Suffix.EQ.".pre".OR.Suffix.EQ."prc".OR.Suffix.EQ.".prd" & .OR.Suffix.EQ.".prp".OR.Suffix.EQ.".prr") AnomType = 1 if (present(Factor)) then SigFactor = Factor else SigFactor = 1 end if NDStn = size(DataD,3) ; NMonth = 12 if (present(SmallOrder)) then NRStn = size(Order,1) else NRStn = 0 ; XDStn = 0 do XDStn = XDStn + 1 if (Order(XDStn).NE.MissVal) NRStn = NRStn + 1 if (XDStn.EQ.NDStn.OR.Order(XDStn).EQ.MissVal) exit end do end if if (present(CandD)) then NYear = size(DataD,1) NOYear = size(DataO,1) else NYear = EndO - BegO + 1 end if if (EndN.NE.MissVal.AND.BegN.NE.MissVal) then Comm0=BegN ; Comm1=EndN else if (NYear.LT.30) then Comm0=1 ; Comm1=NYear else Comm0=NYear-29 ; Comm1=NYear end if end if allocate (MonScore(NYear,NMonth),stat=AllocStat) if (AllocStat.NE.0) print*, " > ##### ERROR: CruTSTestMon: Allocation failure #####" MonScore = MissVal !*************************************** MAIN (don't bother if potential baseline < 10) if ((Comm1-Comm0).GE.9) then allocate (DataR(NRStn,NYear), & DataC(NYear), & SubOut(NYear), & AnnScore(NYear),stat=AllocStat) if (AllocStat.NE.0) print*, " > ##### ERROR: CruTSTestMon: Allocation failure #####" AnnScore = MissVal ; MaxIndex = MissVal ; MaxValue = MissVal !*************************************** compare original and database series if (present(CandD)) then ! write (99,*), "compare original and database series" ! @@@@@@@@@@@@@@@@@@@@@@@@ do XMonth = 1, NMonth DataC=MissVal ; DataR=MissVal do XDYear = 1, NYear ! fill arrays XYear = XDYear XOYear=XDYear+BegO-BegD if (DataD(XDYear,XMonth,CandD).NE.DataMissVal) then DataC(XYear) = real(DataD(XDYear,XMonth,CandD))/10 else if (XOYear.GE.1.AND.XOYear.LE.NOYear) then if (DataO(XOYear,XMonth,CandO).NE.DataMissVal) & DataC(XYear) = real(DataO(XOYear,XMonth,CandO))/10 end if if (DataC(XYear).NE.MissVal) then do XRStn = 1, NRStn if (Order(XRStn).NE.CandD.AND.DataD(XDYear,XMonth,Order(XRStn)).NE.DataMissVal) & DataR(XRStn,XYear) = real(DataD(XDYear,XMonth,Order(XRStn)))/10 end do end if end do SubOut=MissVal ! carry out test (not BreakVec - see below score calc) if (present(XBreakYear)) then call MultiSiteTest (DataC,DataR,Comm0,Comm1,AnomType,QPassFail,TestVec=SubOut,XBreakYear=XBreakYear) else call MultiSiteTest (DataC,DataR,Comm0,Comm1,AnomType,QPassFail,TestVec=SubOut) end if MaxIndex(XMonth)=QPassFail if (MaxIndex(XMonth).GT.0) MaxValue(XMonth)=SubOut(MaxIndex(XMonth)) do XYear = 1, NYear ! store monthly scores MonScore (XYear,XMonth) = SubOut (XYear) end do end do end if !*************************************** examine original series only if (.not.present(CandD)) then ! write (99,*), "examine original series only" ! @@@@@@@@@@@@@@@@@@@@@@@@ do XMonth = 1, NMonth ! write (99,*), "month=",xmonth ! @@@@@@@@@@@@@@@@@@@@@@@@ DataC=MissVal ; DataR=MissVal do XYear = 1, NYear ! fill arrays XOYear=BegO+XYear-1 ; XDYear=BegD+XYear-1 if (DataO(XOYear,XMonth,CandO).NE.DataMissVal) then DataC(XYear) = real(DataO(XOYear,XMonth,CandO))/10 do XRStn = 1, NRStn if (DataD(XDYear,XMonth,Order(XRStn)).NE.DataMissVal) & DataR(XRStn,XYear) = real(DataD(XDYear,XMonth,Order(XRStn)))/10 end do end if end do SubOut=MissVal ! carry out test ! write (99,*), "call MultiSiteTest" ! @@@@@@@@@@@@@@@@@@@@@@@@ call MultiSiteTest (DataC,DataR,Comm0,Comm1,AnomType,QPassFail,TestVec=SubOut) ! write (99,*), "done MultiSiteTest",QPassFail ! @@@@@@@@@@@@@@@@@@@@@@@@ MaxIndex(XMonth)=QPassFail if (MaxIndex(XMonth).GT.0) MaxValue(XMonth)=SubOut(MaxIndex(XMonth)) do XYear = 1, NYear ! store monthly scores MonScore (XYear,XMonth) = SubOut (XYear) end do ! write (99,*), "done month=",xmonth ! @@@@@@@@@@@@@@@@@@@@@@@@ end do ! write (99,*), "done original series only" ! @@@@@@@@@@@@@@@@@@@@@@@@ end if !*************************************** calc scores ! write (99,*), "calc scores" ! @@@@@@@@@@@@@@@@@@@@@@@@ QPassFail = 0 ; Statistic = MissVal ; QTestMonth = 0 do XMonth = 1, NMonth if (MaxIndex(XMonth).EQ.0) then ! no inhomog found for month QTestMonth = QTestMonth + 1 else if (MaxIndex(XMonth).GT.0) then ! inhomog found for month OpEn = 0 ; OpTot = 0 ; QTestMonth = QTestMonth + 1 do XTestMonth = 1, NMonth ! find average peak within +-5 year range if (MaxIndex(XTestMonth).GT.0) then if (abs(MaxIndex(XMonth)-MaxIndex(XTestMonth)).LE.5) then OpEn = OpEn + 1 ; OpTot = OpTot + abs(MaxValue(XTestMonth)) end if end if end do if (OpEn.GT.0) then ! find highest sig by calendar month if ((OpTot/OpEn).GT.(SigFactor*SigStat(OpEn))) then ! @@@@ NOTE SigFactor @@@@@@@ if (((OpTot/OpEn)/SigStat(OpEn)).GT.Statistic) then Statistic = (OpTot/OpEn)/SigStat(OpEn) QPassFail = MaxIndex(XMonth) end if end if end if end if end do ! if few months tested and no inhomog found if (QTestMonth.LT.3.AND.QPassFail.EQ.0) QPassFail = MissVal deallocate (DataR,DataC,SubOut,AnnScore,stat=AllocStat) if (AllocStat.NE.0) print*, " > ##### ERROR: CruTSTestMon: Deallocation failure #####" !*************************************** INSUFFICIENT RECORD FOR CHECK else QPassFail = MissVal ; Statistic = MissVal end if end subroutine CruTSTestMon !******************************************************************************* ! C = candidate t-s to be tested for homogeneity ! R = reference t-s from adjacent sites subroutine MultiSiteTest (DataC,DataR,BegN,EndN,AnomType,QPassFail,Break,TestVec,BreakVec,XBreakYear) real, pointer, dimension (:,:) :: DataR ! NRStn,NYear real, pointer, dimension (:) :: DataC ! NYear real, pointer, dimension (:) :: DifferencesR ! NYear real, pointer, dimension (:) :: DifferencesC ! NYear real, pointer, dimension (:) :: Anomalies ! NYear real, pointer, dimension (:) :: Correlation ! NRStn real, pointer, dimension (:) :: BaselineR ! NRStn real, pointer, dimension (:), optional :: TestVec ! NYear: test stats: requires alloc/init logical, pointer, dimension (:), optional :: BreakVec ! NYear: only test where .TRUE. logical, pointer, dimension (:) :: TestYear ! NYear: only test where .TRUE. integer, intent(out) :: QPassFail ! -999=untested,0=pass,>0=fail-year integer, intent(in), optional :: Break ! only potential break point to test integer, intent(in), optional :: XBreakYear ! force exam this year only (1=first in common per) integer, intent(in) :: BegN,EndN ! the ideal indices of the beg/end years integer, intent(in) :: AnomType ! 0=abs,1=rel real, parameter :: MissVal = -999.0 real :: Aye,Bee, OpTot,OpTotSq,OpEn,OpThresh, OpDenom,OpNumer, OpMean,OpStDev real :: BaselineC, TestRatio,MaxRatio,CorrSum integer :: AllocStat,ReadStatus,QNoTest,QPrescribed,QFirstA,QLastA,Comm0,Comm1,NormLen integer :: NRStn,NYear integer :: XRStn,XYear !*************************************** introduction NRStn = size(DataR,1) ; NYear = size(DataR,2) ; QPassFail=-999 ; QNoTest=0 allocate (Correlation (NRStn), & DifferencesR(NYear), & DifferencesC(NYear), & BaselineR (NRStn), & Anomalies (NYear), & TestYear (NYear), stat=AllocStat) if (AllocStat.NE.0) print*, " > ##### ERROR: MultiSiteTest: Allocation failure #####" Correlation = MissVal ; DifferencesC = MissVal ; DifferencesR = MissVal BaselineR = MissVal ; Anomalies = MissVal ; TestYear = .FALSE. !*************************************** calc baseline values !write (99,*), "calc normal period" ! @@@@@@@@@@@@@@@@@@@@@@@@ BaselineC=MissVal ; QPrescribed = 0 NormLen = EndN-BegN+1 ; OpThresh = 10+((NormLen-15)*0.8) if (OpThresh.LT.10) OpThresh = 10 Comm0=BegN ; Comm1=EndN !write (99,"(a,4i6)"), "BEGIN",BegN,EndN,NormLen,nint(OpThresh) ! @@@@@@@@@@@@@@@@@@@@@@ do OpTot=0 ; OpEn=0 do XYear = Comm0,Comm1 ! calc stats for period if (DataC(XYear).NE.MissVal) then OpEn=OpEn+1 ; OpTot=OpTot+DataC(XYear) end if end do if (OpEn.GE.OpThresh) then ! enough valid vals, so calc baseline BaselineC = OpTot / OpEn else ! else if (QPrescribed.EQ.0) then ! the prescribed period fails Comm1 = NYear ; Comm0 = Comm1-NormLen+1 QPrescribed = 1 else ! the tested unprescribed period fails Comm1 = Comm1 - 1 ; Comm0 = Comm0 - 1 end if ! write (99,"(a,2i6)"), "SHIFT", Comm0,Comm1 ! @@@@@@@@@@@@@@@@@@@@@@ end if if (Comm0.LT.1.AND.BaselineC.EQ.MissVal) then ! no more periods of this length to test if (NormLen.GE.20) then ! but we can test shorter periods NormLen = NormLen-5 ; OpThresh = OpThresh-4 Comm1=EndN ; Comm0=EndN-NormLen+1 ; QPrescribed=0 else ! and we cannot shorten the period any further NormLen = MissVal end if ! write (99,"(a,4i6)"), "REVIS",Comm0,Comm1,NormLen,nint(OpThresh) ! @@@@@@@@@@@@@@@@@@@@@@ end if if (BaselineC.NE.MissVal.OR.NormLen.EQ.MissVal) exit end do if (BaselineC.EQ.MissVal) then QNoTest = 1 end if if (QNoTest.EQ.0) then do XRStn = 1, NRStn OpTot=0 ; OpEn=0 do XYear = Comm0,Comm1 if (DataR(XRStn,XYear).NE.MissVal) then OpEn=OpEn+1 ; OpTot=OpTot+DataR(XRStn,XYear) end if end do if (OpEn.GE.OpThresh) BaselineR(XRStn) = OpTot / OpEn end do end if !*************************************** calc correlation coefficients if (QNoTest.EQ.0) then ! write (99,*), "calc correlation coefficients" ! @@@@@@@@@@@@@@@@@@@@@@@@ do XYear = 2, NYear ! calc C difference series if (DataC(XYear).NE.MissVal.AND.DataC((XYear-1)).NE.MissVal) & DifferencesC(XYear) = DataC(XYear)-DataC((XYear-1)) end do CorrSum = 0 do XRStn = 1, NRStn ! iterate by R stn if (BaselineR(XRStn).NE.MissVal) then DifferencesR = MissVal do XYear = 2, NYear ! calc R difference series if (DataR(XRStn,XYear).NE.MissVal.AND.DataR(XRStn,(XYear-1)).NE.MissVal) & DifferencesR(XYear) = DataR(XRStn,XYear)-DataR(XRStn,(XYear-1)) end do call LinearLSRVec (DifferencesC,DifferencesR,Aye,Bee,Correlation(XRStn)) ! calc correlation if (Correlation(XRStn).GT.0) CorrSum = CorrSum + Correlation(XRStn) end if end do if (CorrSum.LT.2) then QNoTest = 1 ! require decent regional series to test homogeneity end if end if !*************************************** calc weighted anomalies if (QNoTest.EQ.0) then ! write (99,*), "calc weighted anomalies" ! @@@@@@@@@@@@@@@@@@@@@@@@ do XYear = 1, NYear OpNumer=0 ; OpDenom=0 ; CorrSum=0 if (DataC(XYear).NE.MissVal) then do XRStn = 1, NRStn if (DataR(XRStn,XYear).NE.MissVal.AND.BaselineR(XRStn).NE.MissVal.AND. & Correlation(XRStn).GT.0) then if (AnomType.EQ.0) then OpNumer = OpNumer + ((Correlation(XRStn) ** 2) * (DataR(XRStn,XYear) - & BaselineR(XRStn))) else if (AnomType.EQ.1.AND.BaselineR(XRStn).NE.0) then OpNumer = OpNumer + (((Correlation(XRStn) ** 2) * DataR(XRStn,XYear)) / & BaselineR(XRStn)) end if OpDenom = OpDenom + (Correlation(XRStn) ** 2) CorrSum = CorrSum + Correlation(XRStn) end if end do if (OpDenom.NE.0.AND.CorrSum.GT.2) then if (AnomType.EQ.0) then Anomalies(XYear) = DataC(XYear) - BaselineC - (OpNumer/OpDenom) else if (AnomType.EQ.1.AND.BaselineC.NE.0.AND.OpNumer.NE.0) then Anomalies(XYear) = (DataC(XYear)/BaselineC) / (OpNumer/OpDenom) end if end if end if end do end if !*************************************** standardise weighted anomaly series !if (QNoTest.EQ.0) then !! write (99,*), "standardise" ! @@@@@@@@@@@@@@@@@@@@@@ ! OpEn=0 ; OpTot=0 ; OpTotSq=0 ! do XYear = 1, NYear ! if (Anomalies(XYear).NE.MissVal) then ! OpEn=OpEn+1 ; OpTot=OpTot+Anomalies(XYear) ; OpTotSq=OpTotSq+(Anomalies(XYear)**2) ! end if ! end do ! if (OpEn.GT.1) then ! OpMean=OpTot/OpEn ! OpStDev=sqrt((OpEn/(OpEn-1))*((OpTotSq/OpEn)-(OpMean**2))) ! do XYear = 1, NYear ! if (Anomalies(XYear).NE.MissVal) Anomalies(XYear) = (Anomalies(XYear) - OpMean) / OpStDev ! end do ! else ! QNoTest = 1 ! end if !end if !*************************************** decide which years to test if (QNoTest.EQ.0) then ! write (99,*), "decide which years to test" ! @@@@@@@@@@@@@@@@@@@@@@@@ if (present(Break)) then TestYear(Break) = .TRUE. else if (present(XBreakYear)) then TestYear(XBreakYear) = .TRUE. else if (present(BreakVec)) then do XYear = 1, NYear if (BreakVec(XYear).EQ..TRUE.) TestYear(XYear) = .TRUE. end do else QFirstA = MissVal ; QLastA = MissVal ! don't consider first5 / last5 XYear = 0 do XYear = XYear + 1 if (Anomalies(XYear).NE.MissVal) QFirstA = XYear if (QFirstA.NE.MissVal.OR.XYear.EQ.NYear) exit end do XYear = NYear + 1 do XYear = XYear - 1 if (Anomalies(XYear).NE.MissVal) QLastA = XYear if (QLastA.NE.MissVal.OR.XYear.EQ.1) exit end do if ((QLastA-5).GE.(QFirstA+5)) then do XYear = (QFirstA+5), (QLastA-5) TestYear(XYear) = .TRUE. end do end if end if end if !*************************************** test for single shift if (QNoTest.EQ.0) then ! write (99,*), "test for single shift" ! @@@@@@@@@@@@@@@@@@@@@@@@ QPassFail = MissVal ; MaxRatio = 0.0 do XYear = 2, NYear-1 if (TestYear(XYear).EQ..TRUE.) then call SingleShift (Anomalies,XYear,TestRatio,Simple=1) if (present(TestVec)) TestVec(XYear) = TestRatio if (TestRatio.NE.MissVal) then if (QPassFail.EQ.MissVal) QPassFail = 0 if (abs(TestRatio).GE.MaxRatio) then MaxRatio = abs(TestRatio) ; QPassFail = XYear end if end if end if end do if (MaxRatio.LT.2.AND.QPassFail.NE.MissVal) QPassFail = 0 end if !*************************************** deallocate deallocate (Correlation,DifferencesR,DifferencesC,BaselineR,Anomalies,TestYear,stat=AllocStat) if (AllocStat.NE.0) print*, " > ##### ERROR: MultiSiteTest: Deallocation failure #####" end subroutine MultiSiteTest !******************************************************************************* ! returns the result of the homg test for the alternative hypothesis that there is an ! inhomogeneity in the series at the specified break point ! an inhomogeneity is detected if abs(TestRatio)>1 subroutine SingleShift (Series,Break,TestRatio,Simple) real, pointer, dimension (:) :: Series integer, intent (in) :: Break real, intent(out) :: TestRatio integer, intent (in), optional :: Simple real, parameter :: MissVal = -999.0 real :: Sum1,Sum2,SumSq1,SumSq2,StDev1,StDev2,Miss1,Miss2,Valid1,Valid2 integer :: En,XVal TestRatio = MissVal En = size(Series) Sum1=0 ; Sum2=0 ; SumSq1=0 ; SumSq2=0 ; Miss1=0 ; Miss2=0 do XVal = 1, Break if (Series(XVal).NE.MissVal) then Sum1 = Sum1 + Series(XVal) SumSq1 = SumSq1 + (Series(XVal)**2) else Miss1 = Miss1 + 1 end if end do do XVal = Break+1, En if (Series(XVal).NE.MissVal) then Sum2 = Sum2 + Series(XVal) SumSq2 = SumSq2 + (Series(XVal)**2) else Miss2 = Miss2 + 1 end if end do Valid1 = Break-Miss1 ; Valid2 = En-Break-Miss2 if (Valid1.GE.5.AND.Valid2.GE.5) then if (present(Simple)) then StDev1 = (Valid1/(Valid1-1))*((SumSq1/Valid1)-((Sum1/Valid1)**2)) StDev2 = (Valid2/(Valid2-1))*((SumSq2/Valid2)-((Sum2/Valid2)**2)) if (StDev1.GT.0.AND.StDev2.GT.0) then TestRatio = ((Sum1/Valid1)-(Sum2/Valid2)) / sqrt((StDev1/Valid1)+(StDev2/Valid2)) end if else StDev1 = sqrt((SumSq1-((Sum1**2)/Valid1))/Valid1) StDev2 = sqrt((SumSq2-((Sum2**2)/Valid2))/Valid2) if (StDev1.GT.0.AND.StDev2.GT.0) then TestRatio = (-2*Valid1*log(StDev1)) - (2*Valid2*log(StDev2)) - 1 TestRatio = TestRatio / ((0.3752*log(Valid2))+15.17) end if end if end if end subroutine SingleShift !******************************************************************************* ! although this 'works' in the sense of running without execution errors, it does not ! seem to do a very good job of detecting inhomogeneities, whether with the 'Simple' ! option turned on or off. This appears to be because there is no indication within ! the testing procedure itself as to whether the change is gradual or sudden ! I am now developing CruTSTestMon to see whether the same testing procedure can be made ! more effective by utilising the multiple streams of information in the same year ! to detect a simultaneous change across all months subroutine CruTSTestAnn (QPassFail,DataO,DataD,Order,Suffix,& BegN,EndN,BegO,EndO,BegD,EndD,CandO,CandD) real, pointer, dimension (:,:) :: DataR ! NRStn,NYear real, pointer, dimension (:) :: DataC ! NYear integer, pointer, dimension (:,:,:) :: DataO,DataD integer, pointer, dimension (:) :: Order integer, intent(in) :: BegO,EndO ! beg/end indices of common per in O integer, intent(in) :: BegD,EndD ! beg/end indices of common per in D integer, intent(in) :: BegN,EndN ! b/e i of nml per within the common per integer, intent(in) :: CandO ! candidate stn index (from DataO) integer, intent(in), optional :: CandD ! candidate stn index (from DataD) to merge integer, intent(out) :: QPassFail ! -999=no-test,0=pass,>0=fail-at-this-year character (len=4), intent(in) :: Suffix real, parameter :: MissVal = -999.0 integer, parameter :: DataMissVal = -9999 integer :: ReadStatus,AllocStat, QFirstO,QFirstD,QCurrent integer :: NYear,NDStn,NRStn,NOYear,NDYear,NMonth integer :: XYear,XDStn,XRStn,XOYear,XDYear,XMonth integer :: OmitD,AnomType,Break !*************************************** introduction if (present(CandD)) then OmitD = CandD else OmitD = MissVal end if NDStn = size(DataD,3) ; NMonth = 12 NRStn = 0 ; XDStn = 0 do XDStn = XDStn + 1 if (Order(XDStn).NE.MissVal) NRStn = NRStn + 1 if (XDStn.EQ.NDStn.OR.Order(XDStn).EQ.MissVal) exit end do NYear = EndO - BegO + 1 allocate (DataR(NRStn,NYear), & DataC(NYear), stat=AllocStat) if (AllocStat.NE.0) print*, " > ##### ERROR: CruTSTest: Allocation failure #####" DataR=0 ; DataC=0 !*************************************** create series (annual averages) XYear = 0 ! get series for original candidate do XOYear = BegO, EndO XYear = XYear + 1 ; XMonth = 0 do XMonth = XMonth + 1 if (DataO(XOYear,XMonth,CandO).NE.DataMissVal) then DataC(XYear)=DataC(XYear)+(real(DataO(XOYear,XMonth,CandO))/10.0) if (XMonth.EQ.12) DataC(XYear)=DataC(XYear)/12.0 else DataC(XYear)=MissVal end if if (XMonth.EQ.12.OR.DataC(XYear).EQ.MissVal) exit end do end do if (present(CandD)) then ! create merged series XYear=0 ; Break=0 ; QFirstO=MissVal ; QFirstD=MissVal ; QCurrent=MissVal do XDYear = BegD, EndD XYear = XYear + 1 ; XMonth = 0 if (DataC(XYear).EQ.MissVal) then ! don't overwrite original do XMonth = XMonth + 1 if (DataD(XDYear,XMonth,CandD).NE.DataMissVal) then DataC(XYear)=DataC(XYear)+(real(DataD(XDYear,XMonth,CandD))/10.0) if (XMonth.EQ.12) then DataC(XYear)=DataC(XYear)/12.0 ; QCurrent=1 end if else DataC(XYear)=MissVal end if if (XMonth.EQ.12.OR.DataC(XYear).EQ.MissVal) exit end do else QCurrent = 0 end if if (DataC(XYear).NE.MissVal.AND.Break.NE.MissVal) then ! monitor break point if (QCurrent.EQ.0) then if (QFirstO.EQ.MissVal) then QFirstO = XYear else if (QFirstD.GT.QFirstO) Break = MissVal end if else if (QFirstD.EQ.MissVal) then QFirstD = XYear else if (QFirstD.LT.QFirstO) Break = MissVal end if end if end if end do if (Break.NE.MissVal) then ! find break point (MissVal indicates multiple breaks) if (QFirstD.EQ.MissVal.OR.QFirstO.EQ.MissVal) then Break = 0 ! no merged series - no point testing else if (QFirstD.GT.QFirstO) then Break = QFirstD-1 ! D element follows O element else Break = QFirstO-1 ! opposite is true end if end if else Break = MissVal end if do XRStn = 1, NRStn if (Order(XRStn).NE.OmitD) then ! don't use merged stn as ref stn XYear = 0 do XDYear = BegD, EndD XYear = XYear + 1 ; XMonth = 0 do XMonth = XMonth + 1 if (DataD(XDYear,XMonth,Order(XRStn)).NE.DataMissVal) then DataR(XRStn,XYear)=DataR(XRStn,XYear)+(real(DataD(XDYear,XMonth,Order(XRStn)))/10.0) if (XMonth.EQ.12) DataR(XRStn,XYear)=DataR(XRStn,XYear)/12.0 else DataR(XRStn,XYear)=MissVal end if if (XMonth.EQ.12.OR.DataR(XRStn,XYear).EQ.MissVal) exit end do end do else do XYear = 1, NYear DataR(XRStn,XYear) = MissVal end do end if end do !*************************************** run test AnomType = 0 if (Suffix.EQ.".pre".OR.Suffix.EQ."prc".OR.Suffix.EQ.".prd" & .OR.Suffix.EQ.".prp".OR.Suffix.EQ.".prr") AnomType = 1 if (BegN.NE.MissVal.AND.EndN.NE.MissVal.AND.(EndN-BegN).GE.21) then if (Break.EQ.MissVal) then call MultiSiteTest (DataC,DataR,BegN,EndN,AnomType,QPassFail) else if (Break.NE.0) then call MultiSiteTest (DataC,DataR,BegN,EndN,AnomType,QPassFail,Break=Break) else QPassFail = MissVal end if else QPassFail = MissVal end if deallocate (DataR,DataC, stat=AllocStat) if (AllocStat.NE.0) print*, " > ##### ERROR: CruTSTest: Deallocation failure #####" !*************************************** end subroutine CruTSTestAnn !******************************************************************************* end module Homogeneity