회사에서 구조해석 업무를 하다보면 드는 생각이 있다. 자동화만 어느 정도 되면 야근할 일이 줄어들겠다. 재직 중인 회사에서 SAP2000을 사용하고 있는데, 가장 성가신 부분은 하중을 조합하는 과정이다. 일을 하다보면, 각 프로젝트에서 요구하는 여러 나라의 코드를 참고하게 된다.
어느 정도 자동화를 해둘 필요가 있지 않을까 싶다. 하지만, 이 작업은 빠듯한 기한, 데드라인이 존재하는 엔지니어링 업계에서 근무 시간 내에 하기란 쉽지가 않다. 지금 현재는 내가 맡은 일이 많지 않기 때문에 이 작업을 해두면 많은 엔트리 레벨 엔지니어들이 쓸 수 있지 않을까 싶어 작업을 시작해보려 한다.
SAP2000 모델링 자동화(Automation)
프로그램 소개
SAP2000은 Computer and Structures에서 만든 구조해석 프로그램으로 주로 해외에서 많이 쓰고 있다. 이외에 ETABS, CSiBridge, Perform3D, CSiPlant 등 건설, 교량, 플랜트와 같은 분야 맞춤 소프트웨어를 제공한다. 대세에 맞춰 레빗(Revit)과 연동되는 BIM 관련 툴도 제공하는 것 같으니 관심 있으면 공식 홈페이지 참고 바란다.
국내에서는 AI역검(역량검사)을 만들기도 한 마이다스아이티에서 만든 마이다스(Midas)를 많이들 사용하는데, 해외 취직 시 요구사항으로 ETABS, SAP2000 tool을 다룰 수 있는지 여부가 적혀있는 기업이 다수다.
연구 쪽에서는 ABAQUS, ANSYS를 많이 쓰는데, 모델링/해석 자동화 관련해서는 ABAQUS를 다루면서 경험이 있다. API를 썼다기보다는 스크립트를 썼다. Computer and Structures에서는 여러 task의 자동화를 손쉽게 하기 위해 API를 제공하는데, 관련해서 한번 알아보자.
API(Application Programming Interface)
API는 사용자가 기능을 자동화하고 사용자 정의 작업을 수행하는데, 모델에 프로그래밍 방식으로 액세스할 수 있게 해주는 도구다. API를 이용하면 우리의 목적인 SAP2000의 모델링, 분석, 설계 기능을 자신의 소프트웨어 어플리케이션 또는 스크립트와 통합할 수 있다.
VBA, C#, python 등의 언어를 지원하며 관련하여 수행할 수 있는 주요 기능은 다음과 같다.
SAP2000 API로 수행할 수 있는 주요 기능 예시
- 모델 생성 및 수정: 사용자는 API를 통해 구조 모델을 새로 생성하거나 기존 모델을 수정할 수 있다. 요소 추가(frame, area 등), 속성 변경(section 등), 재료 정의 등을 포함한다.
- 해석 실행: 모델이 준비되면, API를 이용하여 정적, 동적, 비선형 해석 등을 실행할 수 있다.
- 결과 분석: 해석이 완료되면, API를 통해 결과 데이터(변위, 반력, 내력 등)를 조회하고 이를 사용자 정의 보고서나 그래픽으로 표현할 수 있다.
- 사용자 정의 설계 검토 및 조정: SAP2000의 설계 기능을 활용하여 사용자 정의 설계 기준에 따라 요소를 검토하고 조정할 수 있다.
- 자동화 및 배치 처리: 반복적인 작업이나 대규모 모델 분석을 자동화하고, 여러 모델에 대한 배치 처리를 수행할 수 있다.
CSI API 공식 document 예시
Computers and Structures에서는 다음과 같이 예시를 제공하고 있다. 요즘은 주로 python을 많이 활용할테니, 다음의 파이썬 코드를 예시로 가져와 봤다.
아래 예시는 간단한 예시로 재료, 단면, 프레임, 구속 조건을 정의하고 해석 수행 후 원하는 결과 값을 자동으로 뽑아주는 코드이다.
import os
import sys
import comtypes.client
#set the following flag to True to attach to an existing instance of the program
#otherwise a new instance of the program will be started
AttachToInstance = False
#set the following flag to True to manually specify the path to SAP2000.exe
#this allows for a connection to a version of SAP2000 other than the latest installation
#otherwise the latest installed version of SAP2000 will be launched
SpecifyPath = False
#if the above flag is set to True, specify the path to SAP2000 below
#ProgramPath = 'C:\Program Files\Computers and Structures\SAP2000 22\SAP2000.exe'
#full path to the model
#set it to the desired path of your model
APIPath = 'C:\CSiAPIexample'
if not os.path.exists(APIPath):
try:
os.makedirs(APIPath)
except OSError:
pass
ModelPath = APIPath + os.sep + 'API_1-001.sdb'
#create API helper object
helper = comtypes.client.CreateObject('SAP2000v1.Helper')
helper = helper.QueryInterface(comtypes.gen.SAP2000v1.cHelper)
if AttachToInstance:
#attach to a running instance of SAP2000
try:
#get the active SapObject
mySapObject = helper.GetObject("CSI.SAP2000.API.SapObject")
except (OSError, comtypes.COMError):
print("No running instance of the program found or failed to attach.")
sys.exit(-1)
else:
if SpecifyPath:
try:
#'create an instance of the SAPObject from the specified path
mySapObject = helper.CreateObject(ProgramPath)
except (OSError, comtypes.COMError):
print("Cannot start a new instance of the program from " + ProgramPath)
sys.exit(-1)
else:
try:
#create an instance of the SAPObject from the latest installed SAP2000
mySapObject = helper.CreateObjectProgID("CSI.SAP2000.API.SapObject")
except (OSError, comtypes.COMError):
print("Cannot start a new instance of the program.")
sys.exit(-1)
#start SAP2000 application
mySapObject.ApplicationStart()
#create SapModel object
SapModel = mySapObject.SapModel
#initialize model
SapModel.InitializeNewModel()
#create new blank model
ret = SapModel.File.NewBlank()
#define material property
MATERIAL_CONCRETE = 2
ret = SapModel.PropMaterial.SetMaterial('CONC', MATERIAL_CONCRETE)
#assign isotropic mechanical properties to material
ret = SapModel.PropMaterial.SetMPIsotropic('CONC', 3600, 0.2, 0.0000055)
#define rectangular frame section property
ret = SapModel.PropFrame.SetRectangle('R1', 'CONC', 12, 12)
#define frame section property modifiers
ModValue = [1000, 0, 0, 1, 1, 1, 1, 1]
ret = SapModel.PropFrame.SetModifiers('R1', ModValue)
#switch to k-ft units
kip_ft_F = 4
ret = SapModel.SetPresentUnits(kip_ft_F)
#add frame object by coordinates
FrameName1 = ' '
FrameName2 = ' '
FrameName3 = ' '
[FrameName1, ret] = SapModel.FrameObj.AddByCoord(0, 0, 0, 0, 0, 10, FrameName1, 'R1', '1', 'Global')
[FrameName2, ret] = SapModel.FrameObj.AddByCoord(0, 0, 10, 8, 0, 16, FrameName2, 'R1', '2', 'Global')
[FrameName3, ret] = SapModel.FrameObj.AddByCoord(-4, 0, 10, 0, 0, 10, FrameName3, 'R1', '3', 'Global')
#assign point object restraint at base
PointName1 = ' '
PointName2 = ' '
Restraint = [True, True, True, True, False, False]
[PointName1, PointName2, ret] = SapModel.FrameObj.GetPoints(FrameName1, PointName1, PointName2)
ret = SapModel.PointObj.SetRestraint(PointName1, Restraint)
#assign point object restraint at top
Restraint = [True, True, False, False, False, False]
[PointName1, PointName2, ret] = SapModel.FrameObj.GetPoints(FrameName2, PointName1, PointName2)
ret = SapModel.PointObj.SetRestraint(PointName2, Restraint)
#refresh view, update (initialize) zoom
ret = SapModel.View.RefreshView(0, False)
#add load patterns
LTYPE_OTHER = 8
ret = SapModel.LoadPatterns.Add('1', LTYPE_OTHER, 1, True)
ret = SapModel.LoadPatterns.Add('2', LTYPE_OTHER, 0, True)
ret = SapModel.LoadPatterns.Add('3', LTYPE_OTHER, 0, True)
ret = SapModel.LoadPatterns.Add('4', LTYPE_OTHER, 0, True)
ret = SapModel.LoadPatterns.Add('5', LTYPE_OTHER, 0, True)
ret = SapModel.LoadPatterns.Add('6', LTYPE_OTHER, 0, True)
ret = SapModel.LoadPatterns.Add('7', LTYPE_OTHER, 0, True)
#assign loading for load pattern 2
[PointName1, PointName2, ret] = SapModel.FrameObj.GetPoints(FrameName3, PointName1, PointName2)
PointLoadValue = [0,0,-10,0,0,0]
ret = SapModel.PointObj.SetLoadForce(PointName1, '2', PointLoadValue)
ret = SapModel.FrameObj.SetLoadDistributed(FrameName3, '2', 1, 10, 0, 1, 1.8, 1.8)
#assign loading for load pattern 3
[PointName1, PointName2, ret] = SapModel.FrameObj.GetPoints(FrameName3, PointName1, PointName2)
PointLoadValue = [0,0,-17.2,0,-54.4,0]
ret = SapModel.PointObj.SetLoadForce(PointName2, '3', PointLoadValue)
#assign loading for load pattern 4
ret = SapModel.FrameObj.SetLoadDistributed(FrameName2, '4', 1, 11, 0, 1, 2, 2)
#assign loading for load pattern 5
ret = SapModel.FrameObj.SetLoadDistributed(FrameName1, '5', 1, 2, 0, 1, 2, 2, 'Local')
ret = SapModel.FrameObj.SetLoadDistributed(FrameName2, '5', 1, 2, 0, 1, -2, -2, 'Local')
#assign loading for load pattern 6
ret = SapModel.FrameObj.SetLoadDistributed(FrameName1, '6', 1, 2, 0, 1, 0.9984, 0.3744, 'Local')
ret = SapModel.FrameObj.SetLoadDistributed(FrameName2, '6', 1, 2, 0, 1, -0.3744, 0, 'Local')
#assign loading for load pattern 7
ret = SapModel.FrameObj.SetLoadPoint(FrameName2, '7', 1, 2, 0.5, -15, 'Local')
#switch to k-in units
kip_in_F = 3
ret = SapModel.SetPresentUnits(kip_in_F)
#save model
ret = SapModel.File.Save(ModelPath)
#run model (this will create the analysis model)
ret = SapModel.Analyze.RunAnalysis()
#initialize for Sap2000 results
SapResult= [0,0,0,0,0,0,0]
[PointName1, PointName2, ret] = SapModel.FrameObj.GetPoints(FrameName2, PointName1, PointName2)
#get Sap2000 results for load cases 1 through 7
for i in range(0,7):
NumberResults = 0
Obj = []
Elm = []
ACase = []
StepType = []
StepNum = []
U1 = []
U2 = []
U3 = []
R1 = []
R2 = []
R3 = []
ObjectElm = 0
ret = SapModel.Results.Setup.DeselectAllCasesAndCombosForOutput()
ret = SapModel.Results.Setup.SetCaseSelectedForOutput(str(i+1))
if i <= 3:
[NumberResults, Obj, Elm, ACase, StepType, StepNum, U1, U2, U3, R1, R2, R3, ret] = SapModel.Results.JointDispl(PointName2, ObjectElm, NumberResults, Obj, Elm, ACase, StepType, StepNum, U1, U2, U3, R1, R2, R3)
SapResult[i] = U3[0]
else:
[NumberResults, Obj, Elm, ACase, StepType, StepNum, U1, U2, U3, R1, R2, R3, ret] = SapModel.Results.JointDispl(PointName1, ObjectElm, NumberResults, Obj, Elm, ACase, StepType, StepNum, U1, U2, U3, R1, R2, R3)
SapResult[i] = U1[0]
#close Sap2000
ret = mySapObject.ApplicationExit(False)
SapModel = None
mySapObject = None
#fill independent results
IndResult= [0,0,0,0,0,0,0]
IndResult[0] = -0.02639
IndResult[1] = 0.06296
IndResult[2] = 0.06296
IndResult[3] = -0.2963
IndResult[4] = 0.3125
IndResult[5] = 0.11556
IndResult[6] = 0.00651
#fill percent difference
PercentDiff = [0,0,0,0,0,0,0]
for i in range(0,7):
PercentDiff[i] = (SapResult[i] / IndResult[i]) - 1
#display results
for i in range(0,7):
print()
print(SapResult[i])
print(IndResult[i])
print(PercentDiff[i])
CSi OAPI 문서(Document)
[ Csi 공식 문서 ] |
C:\Program Files\Computers and Structures\SAP2000 23