ข้ามไปยังเนื้อหา

บทที่ 12 ภาคผนวก

Glossary (คำศัพท์)

คำอธิบาย
COMComponent Object Model — เทคโนโลยี Windows ให้ process สื่อสารกัน
comtypesPython library สำหรับเรียก COM objects
ProgIDชื่อ string ที่ Windows ใช้ค้นหา COM object ที่ register ไว้
GetActiveObjectเชื่อม COM object ที่เปิดอยู่แล้ว
CreateObjectสร้าง COM object (และ process) ใหม่
SapModelCOM object หลักที่ใช้เข้าถึง ETABS model data
Load Caseกรณีแรงจริง เช่น DEAD, LIVE, EQX
Load Combinationการรวม Load Case ตามมาตรฐาน เช่น 1.2D+1.6L
retReturn code จาก API call (0 = สำเร็จ)
dataclassPython decorator สร้าง class สำหรับเก็บข้อมูลอัตโนมัติ
context managerPattern ใช้ with statement สำหรับ setup/cleanup อัตโนมัติ
venvVirtual environment — แยก Python packages ตามโปรเจกต์
pandasLibrary สำหรับจัดการข้อมูลตาราง (DataFrame)
gc.collect()บังคับ Python garbage collector เก็บ memory ทันที
pytestTesting framework สำหรับ Python
Dynamic Dispatchcomtypes เรียก COM methods แบบ runtime (ไม่มี compile-time check)
STASingle-Threaded Apartment — COM threading model (ห้ามใช้ข้าม thread)

API Quick Reference (Python)

Connection

import comtypes.client
# Attach to running ETABS
etabs = comtypes.client.GetActiveObject("CSI.ETABS.API.ETABSObject")
# Create new ETABS
etabs = comtypes.client.CreateObject("CSI.ETABS.API.ETABSObject")
etabs.ApplicationStart()
# Access SapModel
sap_model = etabs.SapModel

Model Info

sap_model.GetModelFilename() # → str
sap_model.GetPresentUnits() # → int
sap_model.SetPresentUnits(6) # 6 = kN_m_C
sap_model.SetModelIsLocked(False) # Unlock for editing

Read Element Names

ret, count, names = sap_model.FrameObj.GetNameList()
ret, count, names = sap_model.PointObj.GetNameList()
ret, count, names = sap_model.AreaObj.GetNameList()

Read Properties

ret, x, y, z = sap_model.PointObj.GetCoordCartesian("1")
ret, section, auto = sap_model.FrameObj.GetSection("B1")
ret, count, names = sap_model.PropFrame.GetNameList()
ret, count, names = sap_model.PropMaterial.GetNameList()

Stories & Load Patterns

ret, count, names, elevs, heights, *rest = sap_model.Story.GetStories_2()
ret, count, names, types, sw = sap_model.LoadPatterns.GetNameList()
ret, count, names = sap_model.LoadCases.GetNameList()
ret, count, names = sap_model.RespCombo.GetNameList()

Analysis

sap_model.Analyze.RunAnalysis()
sap_model.Results.Setup.DeselectAllCasesAndCombosForOutput()
sap_model.Results.Setup.SetCaseSelectedForOutput("DEAD", True)
sap_model.Results.Setup.SetComboSelectedForOutput("1.2D+1.6L", True)

Results

ret, nr, *data = sap_model.Results.FrameForce("B1", 0)
ret, nr, *data = sap_model.Results.JointDispl("1", 0)
ret, nr, *data = sap_model.Results.StoryDrifts()
ret, nr, *data = sap_model.Results.BaseReact()
ret, nr, *data = sap_model.Results.AreaForceShell("F1", 0)

Write Operations

sap_model.FrameObj.SetSection("B1", "W16X40")
sap_model.FrameObj.AddByCoord(x1, y1, z1, x2, y2, z2, "", "W12X26")
sap_model.PointObj.SetRestraint("1", [True, True, True, False, False, False])
sap_model.LoadPatterns.Add("SDL", 1, 0, True)
sap_model.FrameObj.SetLoadDistributed("B1", "LIVE", 1, 10, 0, 1, -10, -10, "Global", True, True)

Cleanup

import gc
del sap_model
del etabs
gc.collect()

Unit Systems

ค่าชื่อForceLengthTemperature
1lb_in_Flbin°F
2lb_ft_Flbft°F
3kip_in_Fkipin°F
4kip_ft_Fkipft°F
5kN_mm_CkNmm°C
6kN_m_CkNm°C
7kgf_mm_Ckgfmm°C
8kgf_m_Ckgfm°C
9N_mm_CNmm°C
10N_m_CNm°C
11Ton_mm_CTonmm°C
12Ton_m_CTonm°C
13kN_cm_CkNcm°C
14kgf_cm_Ckgfcm°C
15N_cm_CNcm°C
16Ton_cm_CToncm°C

Complete End-to-End Example

"""
Complete ETABS API Example (Python)
===================================
Connect → Read → Analyze → Export Results
ต้องเปิด ETABS พร้อมโมเดลก่อนรัน
"""
import comtypes.client
import pandas as pd
import gc
from pathlib import Path
def check(ret: int, name: str):
if ret != 0:
raise RuntimeError(f"❌ {name} failed (ret={ret})")
def main():
etabs = None
sap_model = None
try:
# ═══════ 1. CONNECT ═══════
print("═══════════════════════════════════════")
print(" ETABS API — Python Complete Example ")
print("═══════════════════════════════════════")
etabs = comtypes.client.GetActiveObject(
"CSI.ETABS.API.ETABSObject"
)
sap_model = etabs.SapModel
if sap_model is None:
print("❌ ไม่สามารถเข้าถึง SapModel")
return
model_file = sap_model.GetModelFilename()
print(f"\n📁 Model: {model_file}")
# ═══════ 2. SET UNITS ═══════
sap_model.SetPresentUnits(6) # kN_m_C
print("📐 Units: kN_m_C")
# ═══════ 3. READ MODEL ═══════
ret, frame_count, frame_names = sap_model.FrameObj.GetNameList()
check(ret, "FrameObj.GetNameList")
print(f"\n🏗️ Frames: {frame_count}")
ret, joint_count, point_names = sap_model.PointObj.GetNameList()
check(ret, "PointObj.GetNameList")
print(f"📍 Joints: {joint_count}")
# ═══════ 4. ANALYZE ═══════
print("\n⏳ Running analysis...")
ret = sap_model.Analyze.RunAnalysis()
check(ret, "RunAnalysis")
print("✅ Analysis completed")
# ═══════ 5. SETUP OUTPUT ═══════
sap_model.Results.Setup.DeselectAllCasesAndCombosForOutput()
sap_model.Results.Setup.SetCaseSelectedForOutput("DEAD", True)
# ═══════ 6. READ RESULTS ═══════
rows = []
for name in frame_names:
ret, nr, _obj, obj_sta, _elm, _es, \
load_case, _st, _sn, \
p_vals, v2_vals, _v3, _t, _m2, m3_vals = \
sap_model.Results.FrameForce(name, 0)
if ret != 0 or nr == 0:
continue
for i in range(nr):
rows.append({
"Frame": name,
"Station": obj_sta[i],
"LoadCase": load_case[i],
"P": p_vals[i],
"V2": v2_vals[i],
"M3": m3_vals[i],
})
df = pd.DataFrame(rows)
print(f"\n📊 Total results: {len(df)}")
print(df.head(10).to_string(index=False))
# ═══════ 7. EXPORT ═══════
output_dir = Path("output")
output_dir.mkdir(exist_ok=True)
csv_path = output_dir / "frame_forces.csv"
df.to_csv(csv_path, index=False)
print(f"\n✅ Export: {csv_path}")
# Story Drift
ret, nr_drift, d_story, d_case, d_dir, d_drift, *_ = \
sap_model.Results.StoryDrifts()
if ret == 0 and nr_drift > 0:
df_drift = pd.DataFrame({
"Story": d_story,
"Case": d_case,
"Direction": d_dir,
"Drift": d_drift,
})
drift_path = output_dir / "story_drifts.csv"
df_drift.to_csv(drift_path, index=False)
print(f"✅ Export: {drift_path}")
print("\n═══════════════════════════════════════")
print(" ALL DONE! ")
print("═══════════════════════════════════════")
except OSError as e:
print(f"❌ COM Error: {e}")
print(" → ตรวจสอบว่า ETABS เปิดอยู่")
except Exception as e:
print(f"❌ Error: {e}")
finally:
# ═══════ 8. CLEANUP ═══════
if sap_model is not None:
del sap_model
if etabs is not None:
del etabs
gc.collect()
print("\n🧹 Cleanup complete")
if __name__ == "__main__":
main()

What’s Next

หลังจบหนังสือเล่มนี้แล้ว สามารถต่อยอดได้:

หัวข้อรายละเอียด
pandas ขั้นสูงPivot table, groupby สำหรับรายงาน
matplotlib / plotlyสร้างกราฟ force diagram, drift plot
FastAPI / Streamlitสร้าง web dashboard แสดงผล ETABS data
Jupyter + widgetsInteractive notebook สำหรับ parametric study
Databaseเก็บผลลัพธ์ใน SQLite/PostgreSQL สำหรับ tracking
CI/CDGitHub Actions สำหรับ automated testing
win32comทางเลือกแทน comtypes สำหรับ COM interop (อ่านเพิ่มเติมด้านล่าง)
pyautoguiAuto-click ใน ETABS GUI (เมื่อ API ไม่ครอบคลุม)

Cookbook: สร้าง Model จากศูนย์ (Model from Scratch)

ตัวอย่างการสร้างโครงสร้างคอนกรีตเสริมเหล็ก (Frame + Shell) ตั้งแต่เริ่มต้น:

"""
Create Model from Scratch Example
สร้าง Grid, Define Material, Draw Frame/Area, Assign Load
"""
import comtypes.client
def create_model():
# 1. Connect new instance
etabs = comtypes.client.CreateObject("CSI.ETABS.API.ETABSObject")
etabs.ApplicationStart()
model = etabs.SapModel
# 2. Init New Model (kN-m units)
ret = model.InitializeNewModel(6)
# 3. Create Grid (4x4 bays, 3 stories)
ret = model.File.NewGridOnly(4, 12, 12, 4, 12, 12, 3, 3)
# 4. Define Material (Concrete 30MPa)
# AddMaterial(Name, Type, Region, Standard, Grade)
ret, mat_name = model.PropMaterial.AddMaterial(
"C30", 2, "United States", "ACI 318-14", "4000Psi"
)
# 5. Define Frame Sections (Rectangular)
# SetRectangle(Name, Mat, Depth, Width)
ret = model.PropFrame.SetRectangle("C40x40", "C30", 0.4, 0.4)
ret = model.PropFrame.SetRectangle("B30x60", "C30", 0.6, 0.3)
# 6. Define Area Section (Slab 20cm)
# SetShell(Name, Type, Mat, Thickness...)
ret = model.PropArea.SetShell(
"S20", 1, "C30", 0, 0.2, 0.2
)
# 7. Draw Columns & Beams
# (Simplified loop)
# Columns at intersections
for x in range(0, 48+1, 12):
for y in range(0, 48+1, 12):
ret, name = model.FrameObj.AddByCoord(
x, y, 0, x, y, 3, "", "C40x40"
)
# Beams (X-direction)
for y in range(0, 48+1, 12):
ret, name = model.FrameObj.AddByCoord(
0, y, 3, 48, y, 3, "", "B30x60"
)
# 8. Draw Slab
ret, name = model.AreaObj.AddByCoord(
4, [0, 48, 48, 0], [0, 0, 48, 48], [3, 3, 3, 3],
"", "S20"
)
# 9. Assign Supports (Pin at base)
# Select joints at Z=0 -> SetRestraint
print("✅ Model created successfully!")
# run: create_model()

Alternative: win32com

นอกจาก comtypes แล้ว Python ยังมี pywin32 (win32com.client) ที่ใช้ได้เหมือนกัน:

ติดตั้ง

Terminal window
pip install pywin32

การใช้งาน

ต่างกันแค่ตอน connect และการจัดการ constants:

import win32com.client
# Connect
etabs = win32com.client.GetActiveObject("CSI.ETABS.API.ETABSObject")
sap_model = etabs.SapModel
# การเรียก method เหมือนกัน
ret, count, names = sap_model.FrameObj.GetNameList()
# ข้อดี: บางคนว่าเสถียรกว่าในบาง environment
# ข้อเสีย: Syntax ในการเข้าถึง Enum ต่างกันเล็กน้อย