OpenSees 해석 열한 번 중 두 번은 왜 붕괴가 아니었나: 실제 지진기록·응답스펙트럼·층간변위·증분동적해석
가속도 숫자 한 열이 말해주지 않는 모든 것, 서로 독립적인 세 방법으로 계산해 6e-11 로 일치시킨 응답스펙트럼, 단주기 스펙트럼을 4% 옮기는 보간 선택, 기록의 시간간격 그대로 해석하면 0.1초에서 17% 틀리는 이유, 철근콘크리트 파이버 라멘의 층간변위와 잔류변위, 명시된 붕괴 기준까지의 증분동
아래 증분동적해석 11회 중 2회가 도중에 멈췄고, 그대로면 붕괴로 기록되었을 것이다. 둘 다 붕괴하지 않았다. 수렴 회복 캐스케이드를 넣으면 둘 다 기록 끝까지 완주한다. 보고되는 붕괴능력은 2.83 g에서 3.54 g로 바뀐다. 20% 오차이고, 구조적인 원인이 아니라 해석 루프를 어떻게 썼는가가 만든 것이다.
이 글은 설계지진·기록 선정 절차·스케일링 방법·허용치를 제시하지 않는다. 실제 가속도 기록 하나를 가져와, 쓰기 전에 반드시 알아야 할 것을 정리하고, 응답스펙트럼을 서로 독립적인 세 방법으로 계산하고, 답이 보간과 시간간격에 얼마나 의존하는지 측정한 다음, 6부의 철근콘크리트 라멘을 그 기록으로 흔들어 강도를 올려 파괴까지 간다. 모든 수치는 OpenSees에 함께 배포되는 Tabas 1978 기록으로 OpenSeesPy 3.8.0에서 생성했다. 이 글은 7부이며 1~6부를 전제한다.
1. 기록을 쓰기 전에 반드시 알아야 할 것
여기서 쓰는 기록은 1978년 이란 Tabas 지진의 단층 직교·평행 성분이며, OpenSees에 EXAMPLES/ExamplePython/tabasFN.txt로 함께 배포된다. 숫자만 한 열로 들어 있다. 헤더도, 단위도, 표본 간격도 없다. 아래의 모든 것은 다른 곳에서 와야 한다.
항목단층 직교단층 평행어떻게 얻는가
표본 간격0.02 s파일이 아니라 예제 스크립트에서
단위g예제의 -factor g가 주장하는 값
PGA0.8999 g0.9772 g절댓값의 최대
PGV1.100 m/s1.058 m/s가속도의 사다리꼴 적분
PGD0.512 m0.751 m2회 적분
Arias 강도13.32 m/s10.94 m/s(π/2g) ∫ a² dt
5–95% 지속시간18.52 s17.64 sArias 5%에서 95%까지
어떤 기록에나 적용되는 관찰 셋.
가장 중요한 두 속성이 파일에 없다. 숫자 한 열에는 시간간격도 단위도 없다. 둘 다 믿어야 하는 스크립트나 헤더에서 온다. 가정한 dt를 절반으로 바꾸면 모든 스펙트럼 값이 이동하고, g 단위 파일을 m/s²로 가정하면 해석 전체가 9.8배 얌전해진다. 불러온 직후 PGA를 g로 출력해 기대값과 비교한다. 두 줄이면 두 오류를 다 잡는다.
이 파일은 가공되었다. 단층 직교 성분의 최대값이 0.8999 g로, 정확히 0.9 g에 충분히 가까워서 배포용으로 스케일된 것이 거의 확실하다. 교육용으로는 괜찮고 프로젝트에는 쓸 수 없다. 실무용 기록은 이력이 기록된 아카이브에서 오고, 어떤 파일이든 첫 번째 할 일은 출처를 확인하는 것이다.
PGA 하나로는 기록을 설명하지 못한다. 단층 평행 성분의 PGA가 0.977 g로 더 높지만(직교는 0.900 g), 직교 성분의 Arias 강도가 22% 크고 구조물 요구도 더 크다. 최대 가속도는 지속시간·에너지·주파수 성분에 대해 아무 말도 하지 않는다.
2. 응답스펙트럼을 독립적인 세 방법으로
스펙트럼은 훑기다. 주기마다 감쇠된 1자유도 진동자를 기록에 통과시키고 최대값을 기록한다. OpenSees가 두 경로를, NumPy가 세 번째를 준다.
# 경로 1 — 내장 SDOF 솔버, 압도적으로 빠르다 w = 2 * math.pi / T r = ops.sdfResponse(1.0, 0.05, w*w, 1.0e12, 0.0, 0.02, 'force.txt', 0.02/4) Sd = abs(r[0]) # sdfResponse 는 가속도가 아니라 '힘' 파일을 받는다: -m * a # 경로 2 — 전체 OpenSees 모델, zeroLength 스프링 하나와 질량 ops.timeSeries('Path', 1, '-dt', 0.02, '-filePath', 'tabasFN.txt', '-factor', 9.80665) ops.pattern('UniformExcitation', 1, 1, '-accel', 1) # 경로 3 — NumPy Newmark, 40줄, OpenSees 없이
세 경로는 주기 0.1~3 s 구간에서 6 × 10⁻¹¹에서 2 × 10⁻⁴ 사이로 일치한다. 파일 읽기·단위 계수·가진 패턴·적분기까지 사슬 전체에 대한 진짜 검증이고, 실행에 1분이면 된다.
붉은 선이 발견이다. 같은 NumPy 적분기인데 기록을 표본 사이에서 선형보간하지 않고 그대로 유지(영차유지)했을 뿐이다. 이 선택 하나가 스펙트럼 변위를 T = 0.1 s에서 4.1%, 0.2 s에서 1.3%, 1 s 너머에서 0.1% 미만 바꾼다. OpenSees의 Path는 선형보간하므로 그 답을 재현하려면 같이 해야 한다. 자기 후처리기를 만들거나 다른 프로그램의 스펙트럼과 비교할 때 몇 퍼센트 차이가 가장 먼저 나오는 곳이 여기이고, 어느 쪽의 버그도 아니다.
3. 해석 시간간격은 기록 시간간격이 아니다
0.02 s로 표본화된 기록이 해석도 0.02 s로 하라는 뜻은 아니다. 기록 간격의 16분의 1을 기준으로 두고 해석 간격을 점점 세분하면 이렇다.
해석 간격T = 0.10 sT = 0.30 sT = 0.52 sT = 1.00 s
0.02 s (기록 간격)17%9.1%1.2%0.47%
0.01 s5.5%2.1%0.30%0.16%
0.005 s2.1%0.54%0.084%0.028%
0.0025 s0.42%0.096%0.012%0.006%
4부에서 Newmark 주기연장 법칙을 유도하며 1% 주기오차에는 Δt/T ≤ 0.055가 필요하다고 했다. 여기서는 그것이 산수다. T = 0.1 s에서 기록의 간격은 Δt/T = 0.2이고, 법칙이 주기연장 12%라고 하며, 측정된 스펙트럼 오차도 같은 차수다. 따라오는 규칙은 "작은 간격을 쓰라"가 아니라 중요한 최단 주기에서 간격을 정하고, 절반으로 줄여 확인하라다.
마지막 열도 보아 두자. T = 1 s에서는 기록의 간격 그대로도 0.5% 이내다. 시간간격 민감도는 해상해야 하는 주기의 함수이며, 응답이 긴 1차 모드에 지배되는 구조물은 스펙트럼 계산보다 훨씬 성긴 간격을 허용한다.
4. 스케일링, 그리고 강도지표가 확약하는 것
기록을 스케일한다는 것은 상수를 곱한다는 뜻이다. 유일한 질문은 그 상수를 무엇에 맞춰 고르느냐다.
# 구조물의 1차 주기에서의 스펙트럼 가속도로 스케일 target_Sa = 1.0 # g scale = target_Sa / Sa_of_record_at_T1 # T1 = 0.523 s, Sa = 1.416 g ops.timeSeries('Path', 2, '-dt', 0.02, '-filePath', GM, '-factor', 9.80665 * scale)
명시할 결론이 둘이다. Sa(T1)로 스케일하면 모든 기록이 1차 모드에 같은 탄성 요구를 주고 나머지 모든 모드에는 다른 요구를 준다. 그것이 목적이자 동시에 한계다. 구조물이 항복하면 유효주기가 길어지고 Sa(T1)은 구조물이 실제로 겪는 것을 더 이상 설명하지 못한다. 그리고 스케일 계수는 기록 전체에 대한 배수이므로 진폭은 바꾸지만 지속시간·주파수 성분·펄스 성격은 바꾸지 않는다. Tabas 같은 근단층 기록은 줄여도 여전히 근단층 기록이다.
5. 라멘: 지붕 변위가 아니라 층간변위
대상은 3층 1경간 철근콘크리트 라멘이다. 층고 4 m, 경간 6 m, 6부에서 검증한 기둥 단면, 400 × 600 보 단면, Lobatto 5점의 forceBeamColumn, 그리고 PDelta 변환. 질량은 6 × 6 m 부담면적에 8 kN/m² — 층당 288 kN, 29.37 t다. Rayleigh 감쇠는 4부와 같이 1차·3차 모드에 5%로 고정했다.
T1 = 0.5228 s T2 = 0.1587 s T3 = 0.0896 s 스케일하지 않은 기록의 Sa(T1) = 1.416 g
배율Sa(T1)1층2층3층2층 잔류
0.250.35 g0.476%0.616%0.499%0.018%
0.500.71 g0.737%1.021%0.819%0.009%
1.001.42 g2.358%2.807%2.064%0.315%
1.502.12 g4.745%5.215%4.285%0.476%
지붕 변위가 아니라 층간변위비를 보고한다. 배율 1.00에서 지붕은 0.16 m 움직이는데, 그 숫자는 맥락 없이는 놀랍지도 안심되지도 않는다. 최대 층간변위비 2.81%는 성능목표와 직접 비교되고 건물 높이와 무관하다.
잔류변위는 별개의 결과이고 갑자기 나타난다. 배율 0.50까지 잔류는 0.02% 미만이다. 라멘이 수직으로 돌아온다. 배율 1.00에서는 0.315%, 17배다. 단면이 항복해 되돌아올 수 없기 때문이다. 잔류변위가 건물의 보수 가능 여부를 정하며, 최대값으로부터 예측되지 않는다.
응답은 강도에 비례하지 않는다. 배율을 0.25에서 0.50으로 두 배 하면 최대 층간변위가 66% 늘고, 0.50에서 1.00으로 다시 두 배 하면 175% 는다. 단면이 항복하는 순간 선형해석으로 훈련된 직관은 모두 멈춘다.
라멘의 해석 간격은 따로 확인할 값어치가 있고, 스펙트럼과는 다른 결과가 나온다. 0.02, 0.01, 0.005, 0.0025 s로 돌리면 최대 층간변위가 0.16%, 0.03%, 0.01%, 그리고 0 차이다. 라멘의 응답은 T1 = 0.52 s에 지배되고, 3절에서 그 주기는 성긴 간격으로도 충분함을 보였다. 시간간격은 남의 스크립트에서 복사하는 것이 아니라 자기 모델에 대해 정당화해야 한다.
6. 증분동적해석
IDA는 같은 기록을 배율을 올려가며 반복 실행하고, 한 축에 강도지표를, 다른 축에 손상지표를 놓는 것이다. 해석 11회, 각 4초 정도다.
# 루프 전문 for scale in [0.1, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5]: build_frame() # 매번 새 모델 gravity() result = run_history(scale) # 최대 층간변위와 붕괴 플래그를 반환 ida.append((Sa_T1 * scale, result['max_drift'])) if result['collapsed']: break
해석상 짚을 점 셋.
점마다 모델을 다시 짓는다. OpenSees는 전역 모델 하나를 유지하고, 재료 상태는 경로의존적(6부)이므로, 이미 흔들린 모델을 재사용하면 이미 손상된 구조물이 된다. 모든 IDA 점은 손상되지 않은 모델에서 중력을 다시 걸고 시작해야 한다.
붕괴는 관찰이 아니라 정의다. 여기서는 최대 층간변위 10%이고 Sa(T1) = 3.54 g에서 도달한다. 똑같이 흔한 다른 정의는 동적 불안정 — IDA 곡선이 평평해져 강도를 조금만 올려도 변위가 한없이 커지는 지점 — 이다. 둘이 같은 답을 줄 이유는 없고, 어느 것을 썼는지가 보고서에 들어가야 한다.
기록 하나는 곡선 하나다. 붕괴능력 3.54 g는 Tabas 단층 직교 성분에 대한 능력이다. 기록별 붕괴능력 변동은 보통 2배 이상이며, 그래서 실제 취약도 평가는 기록 하나가 아니라 기록 세트를 쓴다. 이 글의 모든 것은 기록 하나를 위한 장치이고, 통계는 그 위의 층이다.
7. 붕괴가 아니었던 IDA 점 둘
강한 해석에서는 거의 언제나 어느 시점에 비선형 해석이 수렴하지 못한다. 그다음에 무엇이 일어나는지는 여러분의 루프가 내리는 결정이다.
회복이 없으면 analyze()의 0이 아닌 반환이 실행을 끝낸다. 배율 2.00과 2.25에서 그 일이 t = 11.46 s, 기록의 가장 강한 펄스 도중에 일어나고, 둘 다 붕괴로 기록된다. 회복을 넣으면 같은 두 실행이 각각 2회와 1회의 개입으로 30초를 완주하고 최대 층간변위 7.79%와 9.22%에 이른다. 어느 쪽도 붕괴하지 않았다.
배율 2.50에서는 두 방식이 일치한다. 변위 기준에 진짜로 도달한다. 회복 캐스케이드는 파괴된 구조물을 살려주지 않는다. 수치적 실패와 물리적 실패를 구분해 줄 뿐이다.
def recover(dt): """더 강한 알고리즘, 그다음 더 작은 스텝. 성공하면 0.""" for name, args in [('Newton', ()), ('ModifiedNewton', ()), ('NewtonLineSearch', (0.8,)), ('KrylovNewton', ()), ('Broyden', (8,))]: ops.algorithm(name, *args) ops.test('NormDispIncr', 1e-6, 100) # 반복 횟수도 늘린다 if ops.analyze(1, dt) == 0: ops.algorithm('Newton'); ops.test('NormDispIncr', 1e-6, 30) return 0 for div in (2, 4, 10, 20): # 그다음 스텝 분할 ops.algorithm('KrylovNewton'); ops.test('NormDispIncr', 1e-6, 200) if all(ops.analyze(1, dt / div) == 0 for _ in range(div)): ops.algorithm('Newton'); ops.test('NormDispIncr', 1e-6, 30) return 0 ops.algorithm('Newton'); ops.test('NormDispIncr', 1e-6, 30) return -1 # 정말로 막힘
측정에서 따라 나오는 규칙이 둘이다. 회복할 때마다 원래 알고리즘과 판정을 되돌린다. 그러지 않으면 나머지 해석이 조용히 느슨한 허용오차와 다른 솔버로 돈다. 그리고 회복 횟수를 세어 보고한다. 세 번 개입한 실행과 삼백 번 개입한 실행은 둘 다 끝났더라도 같은 결과가 아니다.
회복이 절대 되어서는 안 되는 것 하나. 느슨해진 수렴 허용오차를 그대로 두는 것이다. test()를 영구히 완화하면 거의 어떤 해석이든 "수렴"하고, 그렇게 나온 숫자는 여러분이 쓴 방정식의 해가 아니다.
8. 시간이력해석에 특유한 오류
증상유력한 원인확인
응답이 약 9.8배 작음기록이 g인데 factor 를 1.0 으로 둠불러온 뒤 PGA 를 g 로 출력
다른 프로그램과 스펙트럼이 몇 % 차이기록의 표본 사이 보간 방식2절, 단주기에서 비교
단주기 응답이 10~20% 틀림기록의 간격 그대로 해석간격을 절반으로 줄여 비교
붕괴능력이 예상보다 낮음미수렴을 붕괴로 기록7절의 캐스케이드를 넣고 재실행
두 번째 IDA 점이 이미 손상됨배율 사이에 모델 재사용매번 다시 짓고 중력 재적용
기록이 끝난 뒤에도 변위가 계속 증가복원력이 남지 않은 P-델타 — 실제 붕괴Linear 변환 실행과 비교
고차모드가 비현실적으로 오래 울림고정점 사이의 Rayleigh 골4부 5절
9. 같은 논리를 다섯 단계 깊이로
입문자는 기록을 불러 PGA를 g로 확인하고 이력 하나를 돌린다. 초급 실무자는 응답스펙트럼을 계산해 두 번째 방법으로 확인하고, 변위가 아니라 층간변위비를 보고한다. 실무 해석자는 중요한 주기에 대해 시간간격을 정당화하고, 강도마다 모델을 다시 지으며, 미수렴과 붕괴를 구분한다. 선임 엔지니어는 쓴 붕괴 기준을 명시하고 잔류변위를 최대값과 함께 보고하며, 기록 하나가 표본 하나임을 안다. 전문가는 기록 선정과 스케일링을 지배적 불확실성으로 다루고, 능력을 숫자가 아니라 분포로 보고하며, 모델이 표현할 수 없는 것 — 여기서는 6부에서 물려받은 전단파괴·철근좌굴·부착슬립 — 을 말한다.
10. 시간이력해석 최소 점검표
기록이 어디서 왔고 그 이력은 무엇인가?
단위와 표본 간격은 무엇이며, 그것을 어떻게 아는가?
계산한 PGA가 기대한 PGA와 맞는가?
해석 간격이 중요한 최단 주기에 대해 정당화되고 절반으로 줄여 확인되었는가?
스케일 계수가 명시된 주기의 명시된 강도지표에 묶여 있는가?
강도마다 모델을 다시 짓고 중력을 다시 걸었는가?
미수렴과 붕괴가 구분되고 회복 횟수가 세어졌는가?
회복 뒤에 원래 알고리즘과 허용오차가 복원되었는가?
층간변위비를 보고하고 잔류변위를 별도 결과로 두었는가?
붕괴 기준을 명시하고, 그 숫자가 기록 하나에 대한 것임을 밝혔는가?
다음 글은 같은 라멘을 3차원으로 옮긴다. 강막, 질량 편심에서 오는 비틀림, P-델타를 위한 leaning column, 그리고 모델이 비틀릴 수 있게 된 뒤에야 존재하는 모드가 주제다.
전체 스크립트
OpenSees 단자유도 모델로 만든 응답스펙트럼을, NumPy 로 직접 쓴 독립적인 Newmark 적분과 대조합니다. gm/tabasFN.txt 에 한 줄에 하나씩 g 단위 가속도가 든 기록 파일이 필요합니다.
# Part 7 - a response spectrum, OpenSees against an independent integration # units: m, s. The record file holds one acceleration per line, in g. import math import numpy as np import openseespy.opensees as ops G, DT, ZETA = 9.80665, 0.02, 0.05 REC = 'gm/tabasFN.txt' acc = np.array([float(v) for v in open(REC).read().split()]) * G print(f'record: {len(acc)} points at {DT} s, PGA {np.abs(acc).max()/G:.6f} g') def numpy_sdof(T, zeta, ag, dt, sub=8): """Average-acceleration Newmark, with the record interpolated linearly exactly as OpenSees Path interpolates it.""" h = dt / sub ag_fine = np.interp(np.arange(len(ag) * sub) * h, np.arange(len(ag)) * dt, ag) w = 2 * math.pi / T; k = w * w; c = 2 * zeta * w; m = 1.0 beta, gamma = 0.25, 0.5 kh = k + gamma * c / (beta * h) + m / (beta * h * h) a1 = m / (beta * h * h) + gamma * c / (beta * h) a2 = m / (beta * h) + (gamma / beta - 1.0) * c a3 = (0.5 / beta - 1.0) * m + h * (0.5 * gamma / beta - 1.0) * c u = v = a = 0.0; peak = 0.0 for p in ag_fine: un = (-m * p + a1 * u + a2 * v + a3 * a) / kh vn = gamma / (beta * h) * (un - u) - (gamma / beta - 1.0) * v \ - h * (0.5 * gamma / beta - 1.0) * a an = (un - u) / (beta * h * h) - v / (beta * h) - (0.5 / beta - 1.0) * a u, v, a = un, vn, an peak = max(peak, abs(u)) return peak def opensees_sdof(T, zeta, dt, sub=8): w = 2 * math.pi / T ops.wipe(); ops.model('basic', '-ndm', 1, '-ndf', 1) ops.node(1, 0.0); ops.node(2, 0.0) ops.fix(1, 1); ops.mass(2, 1.0) ops.uniaxialMaterial('Elastic', 1, w * w) ops.element('zeroLength', 1, 1, 2, '-mat', 1, '-dir', 1) ops.timeSeries('Path', 1, '-dt', dt, '-filePath', REC, '-factor', G) ops.pattern('UniformExcitation', 1, 1, '-accel', 1) ops.rayleigh(2 * zeta * w, 0.0, 0.0, 0.0) # mass proportional only ops.constraints('Plain'); ops.numberer('Plain'); ops.system('BandGeneral') ops.test('NormDispIncr', 1e-12, 50); ops.algorithm('Newton') ops.integrator('Newmark', 0.5, 0.25); ops.analysis('Transient') peak = 0.0 for _ in range(len(acc) * sub): ops.analyze(1, dt / sub) peak = max(peak, abs(ops.nodeDisp(2, 1))) return peak print(f"\n{'T (s)':>7s}{'OpenSees Sd (m)':>18s}{'NumPy Sd (m)':>15s}{'rel diff':>12s}") worst = 0.0 for T in (0.2, 0.5, 1.0, 2.0): o = opensees_sdof(T, ZETA, DT) n = numpy_sdof(T, ZETA, acc, DT) d = abs(o - n) / n worst = max(worst, d) print(f'{T:7.2f}{o:18.8f}{n:15.8f}{d:12.2e}') assert worst < 1e-3, f'the two integrations disagree by {worst:.2e}' print(f'\nOK worst disagreement {worst:.2e} between OpenSees and an independent Newmark')
실행하면 이렇게 나옵니다.
record: 2500 points at 0.02 s, PGA 0.899949 g T (s) OpenSees Sd (m) NumPy Sd (m) rel diff 0.20 0.02477240 0.02477240 1.04e-10 0.50 0.09958246 0.09958252 6.41e-07 1.00 0.17717621 0.17716692 5.25e-05 2.00 0.58245599 0.58249880 7.35e-05 OK worst disagreement 7.35e-05 between OpenSees and an independent Newmark
OpenSees 시리즈 전체
각 편은 닫힌해·독립적인 풀이·OpenSees 소스 중 하나와 대조해 검증했다. 순서대로 읽도록 썼지만 각 편이 자기 전제를 스스로 밝힌다.
1부 — 도메인 모델과 해석 조립
2부 — 단면·기하변환·분포하중
3부 — 파이버 단면·모멘트–곡률·푸시오버
4부 — 질량·감쇠·시간적분
5부 — 라이브러리 읽기와 재현 가능한 워크플로
6부 — 철근콘크리트 파이버 단면과 구속
7부 — 실제 지진기록·층간변위·증분동적해석 — 지금 보는 글
8부 — 3차원: 강막·비틀림·leaning column
9부 — 수렴하지 않을 때: 실패·알고리즘·한계점
10부 — Tcl 읽기와 공개 스크립트 이식
11부 — 쉘·솔리드·지반: 락킹·메시·부지응답
12부 — 후처리: recorder·질의, 그리고 그림이 증명하는 것
13부 — 좌굴과 한계점: 분기점·스냅백·호장법
14부 — Windows: 설치·파일 형식·조용한 실패
15부 — 검증된 정답이 딸린 연습문제와 한영 용어집
16부 — 어떻게 동작하는가: 시행상태·확정상태·해석 루프
17부 — 커뮤니티가 말하는 것, 측정해보면
18부 — 절점 변위에서 파이버 응력까지
19부 — 면진과 감쇠: 속도 의존 요소와 베어링
참고 자료
OpenSees Developers, ExamplePython — 여기 쓴 Tabas 기록은 OpenSees와 함께 배포되며, 시간간격과 단위를 밝히는 예제도 같이 있다.
OpenSeesPy, Path 시간이력, UniformExcitation — 기록이 모델로 들어가는 방법.
OpenSeesPy, sdfResponse — 내장 1자유도 솔버, 그리고 가속도가 아니라 힘 파일을 받는 이유.
OpenSeesPy, algorithm, KrylovNewton, NewtonLineSearch — 7절의 캐스케이드.
OpenSeesPy, RC 라멘 지진해석 예제 — 5절에 해당하는 공식 예제.
Vamvatsikos, D., Cornell, C. A., Incremental Dynamic Analysis, Earthquake Engineering & Structural Dynamics 31(3), 2002, pp. 491–514, doi:10.1002/eqe.141 — 6절에서 쓴 방법.
Arias, A., A Measure of Earthquake Intensity, Seismic Design for Nuclear Power Plants, MIT Press, 1970 — 1절의 강도지표.
Pacific Earthquake Engineering Research Center, NGA-West2 지반운동 데이터베이스 — 이력이 있는 기록을 얻는 곳.
Scott, M. H., OpenSees Digital — 동적해석·수렴·감쇠를 다루는 OpenSees 개발자 블로그.
Pacific Earthquake Engineering Research Center, OpenSees Wiki.
자료 확인 2026-08-31. 모든 수치는 OpenSeesPy 3.8.0에서 생성되었으며 위 스크립트로 재현할 수 있다. OpenSees와 함께 배포되는 Tabas 파일은 스케일된 것으로 보이며 여기서는 교육용 기록으로 사용했다. 프레임워크의 동작을 보여주는 것이지 프로젝트별 해석이나 기준 적합성 검토를 대신하지 않는다.