2010-12-17 5 views
1

수천 개의 배정도 실수가있는 표가 있습니다.효율적인 수렴 확인

반복을 통해 소수점 이하 3 자리로 수렴 할 때 중단해야합니다.

대상은 가능한 한 빨리 실행해야하지만 매번 같은 결과 (매 3dp)를 줄 필요가 있습니다. 분에서

는 내가 생각하고이

REAL(KIND=DP) :: TOL = 0.001_DP 

DO WHILE(.NOT. CONVERGED) 
    CONVERGED = .TRUE. 
    DO I = 1, NUM_POINTS 
     NEW POTENTIAL = !blah blah blah 
     IF (CONVERGED) THEN 
      IF (NEW_POTENTIAL < OLD_POTENTIAL - TOL .OR. NEW_POTENTIAL > OLD_POTENTIAL + TOL) THEN 
       CONVERGED = .FALSE. 
      END IF 
     END IF 
     OLD_POTENTIAL = NEW POTENTIAL 
    END DO 
END DO 

같은 것을하고 있어요 문 성능에 대한 너무 큰 수없는 많은 경우 그. 나는 끝에 융합을 점검하는 것에 대해 생각했다. 평균값 (전체 그리드 합계, num_points로 나눔)을 확인하고 위와 같은 방식으로 수렴했는지 확인합니다. 그러나 이것이 항상 정확할 것이라고 확신하지는 않습니다.

가장 좋은 방법은 무엇입니까?

답변

3

내가 올바르게 이해하고 있다면, 에서 계산하여 new_potential에 값을 만드는 일종의 시간 이동이 있습니다. 그리고 노인을 새 것과 똑같이 만들고 계속하십시오.

당신은 더 빨리 될 수있는 하나의 문

converged = all(abs(new_potential - old_potential)<tol) 

으로 기존의 통합 테스트를 대체 할 수 있습니다.

1) 2 개면과 잠재적 인 배열을 사용하는 경우 : 시험의 속도는 당신이 단지 다른 모든 테스트 할 수있는 주요 관심사 (또는 매 3, 4를 ...) 반복

몇 가지 코멘트 경우 , old_ 및 new_potential 대신에 각 반복 끝에 인덱스를 교환하여 new_를 old_로 전송할 수 있습니다. 귀하의 코드가 진행됨에 따라 많은 데이터 이동이 진행되고 있습니다.

2) 의미 론적으로 while 루프를 사용하는 것이 좋지만 컨버전스 기준이 결코 충족되지 않는 경우를 대비하여 최대 반복 횟수의 do 루프를 사용합니다.

3) 선언에서 REAL(KIND=DP) :: TOL = 0.001_DP은 TOL의 숫자 값에 대한 DP 지정이 중복되어 REAL(KIND=DP) :: TOL = 0.001이 적합합니다. 이 매개 변수를 만들 수도 있습니다. 컴파일러는 불변이라는 것을 알고있는 경우 컴파일러가 그 사용을 최적화 할 수 있습니다.

4) 가장 바깥 쪽 루프 안에 CONVERGED = .TRUE.을 실행할 필요가 없습니다. 첫 번째 반복 전에 설정하십시오. 그러면 나노초 또는 2 분이 절약됩니다.

마지막으로 잠재적 인 배열의 모든 요소가 3dp로 수렴했다는 컨버전스 기준이 있다면 테스트해야합니다. 제안 된 평균에 대한 반례 표를 만드는 것은 상대적으로 쉽습니다. 그러나 내 관심사는 시스템이 모든 요소에 수렴하지 않으며 수렴을 결정하기 위해 일부 매트릭스 노멀 계산을 사용해야한다는 것입니다. 그래서 그 주제에 대한 교훈의 장소가 아닙니다.

+3

잘 – janneb

+0

나는 과거에 _DP를하지 않았기 때문에 추격 당했고, 이제는 습관으로 해낸다. –

+0

그리고 조언을 주셔서 감사합니다;) 제 생각에는 약간 잘못 생각한 것 같습니다. 첫 번째 점은 실제로 적용되지 않습니다. (실제로는 하나의 격자 만 있고, NEW_POTENTIAL은 하나의 변수이고 OLD_POTENTIAL은 배열입니다.). OLD_POTENTIAL의 한 지점 만 업데이트해야합니다. –

0

수렴 조건에 대한 계산은 무엇입니까? 그들이 더 나쁠 때를 제외하고는 잠재적 인 가능성을 높이기위한 계산이 좋은 해결책을 얻으려면 매우 많은 수의 반복을 추측하기보다는 가능한 한 빨리 루프를 종료하도록 IF 문을 사용하는 것이 좋습니다.

Re 고성능 마크의 제안 # 1, 복사 작업이 런타임의 중요한 부분 인 경우 포인터를 사용할 수도 있습니다.

이 항목에 대해 확신 할 수있는 유일한 방법은 실행 시간을 측정하는 것입니다 ... Fortran은 CPU와 시계 시간을 모두 측정 할 수있는 내장 함수를 제공합니다. 그렇지 않으면 코드의 일부분을 수정하여 코드를 더 빨리 만들 수 있습니다. 버그를 이해하기가 쉽지 않고 버그를 도입 할 수도 있습니다. 아마도 런타임에 많은 개선을하지 않아도 될 것입니다 ... 만약 그 부분이 전체 런타임 중 적은 양을 차지한다면, 영리함의 양은 큰 차이를 만들 수 없습니다.

하이 퍼포먼스 마크 (High Performance Mark)에 따르면 현재의 의미는 우아하지만 무한 루프를 막기를 원할 것입니다. 한 가지 방법 : 어쩌면 더 나은

I = 1 
DO 
    NEWPOT = !bla bla bla 
    IF (ABS(NEWPOT-OLDPOT).LT.TOL) EXIT 
    OLDPOT = NEWPOT 
    IF (I.EQ.NUMPOINTS) THEN 
    I = 1 
    ELSE 
    I = I + 1 
    END IF 
END DO 
0
I = 1 
DO 
    NEWPOT = !bla bla bla 
    IF (ABS(NEWPOT-OLDPOT).LT.TOL) EXIT 
    OLDPOT = NEWPOT 
    I = MOD(I,NUMPOINTS) + 1 
END DO 

PotentialLoop: do i=1, MaxIter 

    blah 

    Converged = test... 
    if (Converged) exit PotentialLoop 

    blah 

end do PotentialLoop 

if (.NOT. Converged) write (*, *) "error, did not converge" 
, 내가 조언 # 3에 동의하지 않는다. 예, 경우에 따라 리터럴이 기본 유형으로 정확하게 표현 가능한 경우 리터럴의 종류를 지정하는 것이 중복됩니다. 그러나 특정 리터럴이 정확히 표현 가능한지 아닌지를 염려하기보다는 항상이를 과정으로 지정하는 것이 더 쉽다는 것을 알게되었습니다.