# -*- coding: utf-8 -*-
from abaqus import *
from abaqusConstants import *
from caeModules import *
from odbAccess import openOdb
import regionToolset
import os
import sys
import math
import time
import uuid
import traceback
class GeostaticAnalysis:
"""主类,处理地应力分析的所有功能"""
def __init__(self):
self.model = None
self.analysis_completed = False
self.start_time = time.time()
self.displacement_history = [] # 存储位移历史记录
self.convergence_data = [] # 存储收敛性数据
def print_msg(self, text, *args):
"""打印带时间戳的格式化消息"""
if args:
text = text % args
# 确保使用正确的编码输出中文
if isinstance(text, unicode):
text = text.encode('utf-8')
elif isinstance(text, str):
try:
text = text.decode('utf-8').encode('gbk')
except:
pass
print("%s" % (text))
def is_cancelled(self, inputs):
"""检查用户是否取消了操作"""
if inputs is None:
return True
if any(value is None for value in inputs):
return True
return False
def validate_model_exists(self, model_name):
"""检查模型是否存在于数据库中"""
if model_name not in mdb.models:
raise ValueError("Model '%s' does not exist" % model_name)
return True
def suppress_all_predefined_fields(self, model):
"""抑制模型中的所有预定义场"""
try:
suppressed_count = 0
if hasattr(model, 'predefinedFields') and model.predefinedFields:
for field_name in list(model.predefinedFields.keys()):
try:
model.predefinedFields[field_name].suppress()
suppressed_count += 1
self.print_msg("Suppressed predefined field: %s", field_name)
except Exception as e:
self.print_msg("Error suppressing field %s: %s", field_name, str(e))
self.print_msg("Suppressed %d predefined fields", suppressed_count)
else:
self.print_msg("No predefined fields found in the model")
return suppressed_count
except Exception as e:
self.print_msg("Error accessing predefined fields: %s", str(e))
return 0
def get_max_displacement(self, odb_path):
"""从ODB文件中计算最大位移"""
try:
odb = openOdb(odb_path)
step_names = list(odb.steps.keys())
if not step_names:
self.print_msg("Warning: No steps found in ODB")
odb.close()
return 0.0
last_step_name = step_names[-1]
last_step = odb.steps[last_step_name]
if not last_step.frames:
self.print_msg("Warning: No frames found in step %s", last_step_name)
odb.close()
return 0.0
last_frame = last_step.frames[-1]
if 'U' not in last_frame.fieldOutputs:
self.print_msg("Warning: No displacement field found in last frame")
odb.close()
return 0.0
displacement_field = last_frame.fieldOutputs['U']
max_disp = 0.0
for value in displacement_field.values:
disp_magnitude = math.sqrt(value.data[0]**2 + value.data[1]**2 + value.data[2]**2)
if disp_magnitude > max_disp:
max_disp = disp_magnitude
odb.close()
self.print_msg("Max displacement in %s: %.6f", odb_path, max_disp)
return max_disp
except Exception as e:
self.print_msg("Error calculating max displacement: %s", str(e))
return 0.0
def create_job(self, model_name, job_name, description, num_cpus, memory_percent, num_gpus):
"""创建分析作业"""
return mdb.Job(
name=job_name,
model=model_name,
description=description,
type=ANALYSIS,
atTime=None,
waitMinutes=0,
waitHours=0,
queue=None,
memory=memory_percent,
memoryUnits=PERCENTAGE,
getMemoryFromAnalysis=True,
explicitPrecision=SINGLE,
nodalOutputPrecision=SINGLE,
echoPrint=OFF,
modelPrint=OFF,
contactPrint=OFF,
historyPrint=OFF,
userSubroutine='',
scratch='',
resultsFormat=ODB,
numThreadsPerMpiProcess=1,
multiprocessingMode=DEFAULT,
numCpus=num_cpus,
numDomains=num_cpus,
numGPUs=num_gpus
)
def create_elastic_trial_model(self, original_model_name, uniform_elastic_modulus=None):
"""
创建弹性试验模型副本并将所有材料转换为纯线性弹性
参数:
original_model_name: 原始模型名称
uniform_elastic_modulus: 统一弹性模量值(kPa),如果为None则使用原始材料值
"""
try:
# 生成弹性试验模型名称
elastic_model_name = "%s_ElasticTrial" % original_model_name
# 检查原始模型是否存在
self.validate_model_exists(original_model_name)
# 删除同名的现有模型
if elastic_model_name in mdb.models:
del mdb.models[elastic_model_name]
self.print_msg("Deleted existing model: %s", elastic_model_name)
time.sleep(1) # 等待模型完全删除
# 复制模型
original_model = mdb.models[original_model_name]
elastic_model = mdb.Model(name=elastic_model_name, objectToCopy=original_model)
self.print_msg("Successfully created elastic trial model: %s", elastic_model_name)
# 修改材料属性:将所有材料转换为纯线性弹性
for mat_name, material in elastic_model.materials.items():
self.print_msg("Processing material: %s", mat_name)
# 保存原始弹性参数(如果可用)
original_elastic_params = None
original_poisson_ratio = 0.3 # 默认泊松比
if hasattr(material, 'elastic') and material.elastic:
try:
original_elastic_params = material.elastic.table
if original_elastic_params and len(original_elastic_params) > 0:
# 提取原始泊松比
original_poisson_ratio = original_elastic_params[0][1] if len(original_elastic_params[0]) > 1 else 0.3
self.print_msg("Saved original elastic properties for material: %s", mat_name)
except Exception as e:
self.print_msg("Error reading elastic properties for material %s: %s", mat_name, str(e))
# 删除所有已知的塑性属性
plasticity_properties = [
'mohrCoulomb', 'mohrCoulombPlasticity', # Mohr-Coulomb塑性
'plastic', 'plasticity', # 通用塑性
'druckerPrager', # Drucker-Prager塑性
'clayPlasticity', # 粘土塑性
'capPlasticity', # 帽盖塑性
'creep', # 蠕变
'viscoelastic', 'viscous', # 粘弹性/粘性
'concrete', # 混凝土
'crushableFoam', # 可压碎泡沫
'ductileDamage', # 韧性损伤
'gasketPlasticity', # 垫片塑性
'hashinDamage', # Hashin损伤
'johnsonCook', # Johnson-Cook模型
'porousFailure', 'porousMetal', # 多孔材料失效
'tensileFailure', # 拉伸失效
'failureRatios', # 失效比率
'castIronPlasticity' # 铸铁塑性
]
# 使用del命令删除属性
for prop_name in plasticity_properties:
try:
if hasattr(material, prop_name):
# 直接删除属性
delattr(material, prop_name)
self.print_msg("Successfully deleted %s property from material: %s", prop_name, mat_name)
except Exception as prop_e:
self.print_msg("Error deleting %s property from material %s: %s",
prop_name, mat_name, str(prop_e))
# 确保正确的弹性属性
try:
# 删除现有的弹性属性(如果有)
if hasattr(material, 'elastic'):
try:
delattr(material, 'elastic')
self.print_msg("Removed existing elastic property from material: %s", mat_name)
except Exception as e:
self.print_msg("Error removing elastic property from material %s: %s", mat_name, str(e))
# 重新创建弹性属性
if uniform_elastic_modulus is not None:
# 使用统一弹性模量
new_elastic_params = ((uniform_elastic_modulus, original_poisson_ratio),)
material.Elastic(table=new_elastic_params)
self.print_msg("Set uniform elastic modulus %.2f kPa for material: %s", uniform_elastic_modulus, mat_name)
elif original_elastic_params:
# 使用原始弹性参数
material.Elastic(table=original_elastic_params)
self.print_msg("Restored original elastic properties for material: %s", mat_name)
else:
# 添加默认弹性属性
default_elastic_params = ((100.0, 0.3),) # E=100000kPa, ν=0.3
material.Elastic(table=default_elastic_params)
self.print_msg("Added default elastic properties to material: %s (E=100000kPa, ν=0.3)", mat_name)
except Exception as elastic_e:
self.print_msg("Error setting elastic properties for material %s: %s", mat_name, str(elastic_e))
# 即使发生错误也继续处理其他材料
# 特殊处理:确保没有塑性属性残留
material_attrs = [attr for attr in dir(material) if not attr.startswith('_')]
plastic_attrs_remaining = [attr for attr in material_attrs if any(plastic_term in attr.lower()
for plastic_term in ['plastic', 'coulomb', 'prager', 'creep', 'visc'])]
if plastic_attrs_remaining:
self.print_msg("Warning: Material %s still has possible plastic attributes: %s",
mat_name, plastic_attrs_remaining)
# 尝试删除这些残留属性
for attr_name in plastic_attrs_remaining:
try:
if hasattr(material, attr_name):
delattr(material, attr_name)
self.print_msg("Deleted residual plastic attribute %s from material: %s", attr_name, mat_name)
except Exception as e:
self.print_msg("Error deleting residual attribute %s from material %s: %s",
attr_name, mat_name, str(e))
self.print_msg("Completed converting all materials to linear elastic in model: %s", elastic_model_name)
# 验证所有材料现在都是纯弹性的
self.print_msg("Verifying all materials are now purely elastic:")
for mat_name, material in elastic_model.materials.items():
material_attrs = [attr for attr in dir(material) if not attr.startswith('_')]
elastic_attrs = [attr for attr in material_attrs if 'elastic' in attr.lower()]
plastic_attrs = [attr for attr in material_attrs if any(plastic_term in attr.lower()
for plastic_term in ['plastic', 'coulomb', 'prager', 'creep', 'visc'])]
self.print_msg("Material %s - Elastic attributes: %s", mat_name, elastic_attrs)
if plastic_attrs:
self.print_msg("Warning: Material %s still has plastic-like attributes: %s", mat_name, plastic_attrs)
else:
self.print_msg("Material %s is now purely elastic", mat_name)
return elastic_model_name
except Exception as e:
self.print_msg("Error creating elastic trial model: %s", str(e))
return None
def run_elastic_trial(self, model_name, base_odb_path, set_name, num_cpus, memory_percent, num_gpus, uniform_elastic_modulus=None):
"""
运行弹性试验以获取初始应力场
参数:
uniform_elastic_modulus: 统一弹性模量值(kPa),如果为None则使用原始材料值
"""
self.print_msg("Starting elastic trial for initial stress field...")
# 创建弹性试验模型
elastic_model_name = self.create_elastic_trial_model(model_name, uniform_elastic_modulus)
if elastic_model_name is None:
self.print_msg("Error: Failed to create elastic trial model")
return None
# 创建并运行弹性试验作业
elastic_job_name = 'Elastic_Trial'
try:
elastic_job = self.create_job(
elastic_model_name,
elastic_job_name,
'Elastic trial for initial stress',
num_cpus,
memory_percent,
num_gpus
)
# 提交作业
elastic_job.submit(consistencyChecking=OFF)
elastic_job.waitForCompletion()
# 检查作业状态
if elastic_job.status != COMPLETED:
self.print_msg("Elastic trial job failed with status: %s", elastic_job.status)
return None
self.print_msg("Elastic trial completed successfully")
# 获取ODB路径
elastic_odb_path = os.path.join(base_odb_path, elastic_job_name + '.odb')
# 记录弹性试验的位移结果
max_disp = self.get_max_displacement(elastic_odb_path)
self.displacement_history.append({
'job_name': elastic_job_name,
'iteration': -1, # 弹性试验标记为-1
'max_displacement': max_disp,
'type': 'elastic_trial'
})
return elastic_odb_path
except Exception as e:
self.print_msg("Error running elastic trial: %s", str(e))
return None
def analyze_convergence_quality(self):
"""分析收敛质量和地应力场平衡质量"""
if len(self.displacement_history) < 2:
return "Insufficient data for evaluation"
# 提取迭代数据(排除弹性试验)
iter_data = [d for d in self.displacement_history if d['type'] == 'iteration']
if len(iter_data) < 2:
return "Insufficient iteration data for evaluation"
# 计算收敛指标
displacements = [d['max_displacement'] for d in iter_data]
final_disp = displacements[-1]
# 收敛率计算
convergence_rates = []
for i in range(1, len(displacements)):
if displacements[i-1] > 0:
rate = abs(displacements[i] - displacements[i-1]) / displacements[i-1]
convergence_rates.append(rate)
avg_convergence_rate = sum(convergence_rates) / len(convergence_rates) if convergence_rates else 1.0
# 质量评估标准
quality_assessment = []
# 1. 最终位移绝对值评估
if final_disp <= 1e-6:
quality_assessment.append("Excellent displacement magnitude (< 1e-6)")
elif final_disp <= 1e-4:
quality_assessment.append("Good displacement magnitude (< 1e-4)")
elif final_disp <= 1e-3:
quality_assessment.append("Acceptable displacement magnitude (< 1e-3)")
elif final_disp <= 1e-2:
quality_assessment.append("Large displacement magnitude (< 1e-2)")
else:
quality_assessment.append("Excessive displacement magnitude (> 1e-2)")
# 2. 收敛性评估
if len(convergence_rates) > 0:
if avg_convergence_rate <= 0.1:
quality_assessment.append("Excellent convergence (average rate < 10%)")
elif avg_convergence_rate <= 0.3:
quality_assessment.append("Good convergence (average rate < 30%)")
elif avg_convergence_rate <= 0.5:
quality_assessment.append("Fair convergence (average rate < 50%)")
else:
quality_assessment.append("Poor convergence (average rate > 50%)")
# 3. 收敛稳定性评估
if len(convergence_rates) > 2:
# 修复生成器问题:使用列表推导式而不是生成器表达式
stable_rates = [rate for rate in convergence_rates if rate <= 0.5]
stable_count = len(stable_rates)
stability_ratio = stable_count / len(convergence_rates)
if stability_ratio >= 0.8:
quality_assessment.append("Stable convergence process")
elif stability_ratio >= 0.6:
quality_assessment.append("Moderately stable convergence")
else:
quality_assessment.append("Unstable convergence process")
# 4. 总体评价
# 修复生成器问题:使用列表推导式而不是生成器表达式
excellent_items = [item for item in quality_assessment if "Excellent" in item]
excellent_count = len(excellent_items)
warning_items = [item for item in quality_assessment if any(warning_term in item
for warning_term in ["Large", "Excessive", "Poor", "Unstable"])]
warning_count = len(warning_items)
if warning_count > 0:
overall = "Geostatic field balance quality: POOR"
elif excellent_count >= 2:
overall = "Geostatic field balance quality: EXCELLENT"
else:
overall = "Geostatic field balance quality: GOOD"
return overall + "\n" + "\n".join(quality_assessment)
def print_displacement_summary(self):
"""打印位移历史总结报告"""
if not self.displacement_history:
self.print_msg("No displacement history data")
return
self.print_msg("\n" + "="*80)
self.print_msg("Geostatic Analysis Displacement History Summary")
self.print_msg("="*80)
# 打印表格头
self.print_msg("\n%-20s %-12s %-15s %-20s", "Job Name", "Iteration", "Max Displacement", "Type")
self.print_msg("-" * 70)
# 打印每个作业的数据
for record in self.displacement_history:
job_name = record['job_name']
iteration = record['iteration']
max_disp = record['max_displacement']
record_type = record['type']
if iteration == -1:
iter_str = "Elastic Trial"
else:
iter_str = str(iteration)
self.print_msg("%-20s %-12s %-15.6f %-20s", job_name, iter_str, max_disp, record_type)
# 只分析迭代数据(排除弹性试验)
iter_data = [d for d in self.displacement_history if d['type'] == 'iteration']
if len(iter_data) > 1:
self.print_msg("\nConvergence Analysis:")
self.print_msg("-" * 40)
displacements = [d['max_displacement'] for d in iter_data]
for i in range(1, len(displacements)):
prev_disp = displacements[i-1]
curr_disp = displacements[i]
if prev_disp > 0:
reduction = (prev_disp - curr_disp) / prev_disp * 100
convergence_rate = abs(curr_disp - prev_disp) / prev_disp * 100
self.print_msg("Iteration %d -> %d: Displacement from %.6f to %.6f (reduction %.2f%%, convergence rate %.2f%%)",
i-1, i, prev_disp, curr_disp, reduction, convergence_rate)
# 总体收敛统计
total_reduction = (displacements[0] - displacements[-1]) / displacements[0] * 100
self.print_msg("\nOverall convergence: From %.6f to %.6f (total reduction %.2f%%)",
displacements[0], displacements[-1], total_reduction)
# 质量评估
self.print_msg("\nGeostatic Field Balance Quality Assessment:")
self.print_msg("-" * 40)
quality_report = self.analyze_convergence_quality()
self.print_msg(quality_report)
self.print_msg("="*80)
def run_analysis(self, control_mode, control_value, base_odb_path, set_name, model_name, num_cpus, memory_percent, num_gpus, use_elastic_trial=True, uniform_elastic_modulus=None):
"""
运行迭代地应力分析
参数:
uniform_elastic_modulus: 统一弹性模量值(kPa),如果为None则使用原始材料值
"""
# 清空历史记录
self.displacement_history = []
self.convergence_data = []
try:
self.validate_model_exists(model_name)
except Exception as e:
self.print_msg("Error validating model: %s", str(e))
return False
model = mdb.models[model_name]
self.print_msg("Checking for existing predefined fields...")
self.suppress_all_predefined_fields(model)
self.print_msg("-" * 40)
# 根据用户选择决定是否执行弹性试验
elastic_odb_path = None
if use_elastic_trial:
elastic_odb_path = self.run_elastic_trial(model_name, base_odb_path, set_name, num_cpus, memory_percent, num_gpus, uniform_elastic_modulus)
if elastic_odb_path is None:
if use_elastic_trial:
self.print_msg("Elastic trial failed, proceeding without initial stress field")
else:
self.print_msg("Skipping elastic trial as requested by user")
use_elastic_trial = False
else:
use_elastic_trial = True
self.print_msg("Elastic trial completed, ODB path: %s", elastic_odb_path)
self.print_msg("-" * 40)
try:
a = model.rootAssembly
if set_name in a.sets:
region = a.sets[set_name]
self.print_msg("Using predefined set: %s", set_name)
else:
region = regionToolset.Region(elements=a.elements)
self.print_msg("Set '%s' not found, using all elements: %d", set_name, len(a.elements))
odb = openOdb(elastic_odb_path)
step_names = list(odb.steps.keys())
if not step_names:
self.print_msg("Warning: No steps found in elastic trial ODB")
odb.close()
use_elastic_trial = False
else:
last_step_name = step_names[-1]
step_obj = odb.steps[last_step_name]
total_increments = len(step_obj.frames) - 1 if hasattr(step_obj, 'frames') else 1
total_increments = max(1, total_increments)
odb.close()
model.Stress(
name='Initial_Stress_Field_From_Elastic',
distributionType=FROM_FILE,
fileName=elastic_odb_path,
step=len(step_names),
increment=total_increments,
region=region
)
self.print_msg("Created initial stress field from elastic trial")
except Exception as e:
self.print_msg("Error creating initial stress field from elastic trial: %s", str(e))
use_elastic_trial = False
self.print_msg("Creating initial job...")
initial_job_name = 'Geostatic_Initial'
try:
initial_job = self.create_job(
model_name,
initial_job_name,
'Initial geostatic step',
num_cpus,
memory_percent,
num_gpus
)
except Exception as e:
self.print_msg("Error creating initial job: %s", str(e))
return False
self.print_msg("Submitting initial job...")
try:
initial_job.submit(consistencyChecking=OFF)
initial_job.waitForCompletion()
except Exception as e:
self.print_msg("Error submitting or waiting for initial job: %s", str(e))
return False
if initial_job.status != COMPLETED:
self.print_msg("Error: Initial job failed with status: %s", initial_job.status)
return False
self.print_msg("Initial job completed successfully")
# 记录初始作业的位移
initial_odb_path = os.path.join(base_odb_path, initial_job_name + '.odb')
max_disp = self.get_max_displacement(initial_odb_path)
self.displacement_history.append({
'job_name': initial_job_name,
'iteration': 0,
'max_displacement': max_disp,
'type': 'iteration'
})
if use_elastic_trial:
try:
if 'Initial_Stress_Field_From_Elastic' in model.predefinedFields:
model.predefinedFields['Initial_Stress_Field_From_Elastic'].suppress()
self.print_msg("Suppressed elastic trial stress field")
except Exception as e:
self.print_msg("Error suppressing elastic trial stress field: %s", str(e))
if control_mode == 'displacement':
if max_disp <= control_value:
self.print_msg("Initial job already meets displacement requirement (%.6f <= %.6f)", max_disp, control_value)
# 打印总结报告
self.print_displacement_summary()
return True
iteration_count = 0
max_iterations = control_value if control_mode == 'iterations' else 99
while (control_mode == 'iterations' and iteration_count < max_iterations) or \
(control_mode == 'displacement' and iteration_count < 99 and max_disp > control_value):
try:
iteration_count += 1
self.print_msg("=" * 50)
if control_mode == 'iterations':
self.print_msg("Processing iteration %d/%d", iteration_count, max_iterations)
else:
self.print_msg("Processing iteration %d/99", iteration_count)
self.print_msg("Current max displacement: %.6f, Target: %.6f", max_disp, control_value)
prev_job_name = 'Geostatic_Iter_%d' % (iteration_count - 1) if iteration_count > 1 else initial_job_name
current_job_name = 'Geostatic_Iter_%d' % iteration_count
predefined_field_prev = 'Stress_Field_Iter_%d' % (iteration_count - 1) if iteration_count > 1 else 'Initial_Stress_Field'
predefined_field_current = 'Stress_Field_Iter_%d' % iteration_count
self.print_msg("Current job: %s", current_job_name)
self.print_msg("Reference job: %s", prev_job_name)
odb_path = os.path.join(base_odb_path, prev_job_name + '.odb')
if not os.path.exists(odb_path):
self.print_msg("Error: ODB file %s does not exist", odb_path)
break
odb = openOdb(odb_path)
step_names = list(odb.steps.keys()) if hasattr(odb, 'steps') and odb.steps else []
if not step_names:
self.print_msg("Warning: No steps found in ODB, using defaults")
step_number = 1
total_increments = 1
else:
last_step_name = step_names[-1]
step_number = len(step_names)
step_obj = odb.steps[last_step_name]
total_increments = len(step_obj.frames) - 1 if hasattr(step_obj, 'frames') else 1
total_increments = max(1, total_increments)
self.print_msg("Using step: %s (number: %d)", last_step_name, step_number)
self.print_msg("Increment count: %d", total_increments)
odb.close()
try:
a = model.rootAssembly
if set_name in a.sets:
region = a.sets[set_name]
self.print_msg("Using predefined set: %s", set_name)
else:
region = regionToolset.Region(elements=a.elements)
self.print_msg("Set '%s' not found, using all elements: %d", set_name, len(a.elements))
except Exception as e:
self.print_msg("Error creating region: %s", str(e))
break
try:
self.print_msg("Creating stress field:")
self.print_msg(" Name: %s", predefined_field_current)
self.print_msg(" File: %s", odb_path)
self.print_msg(" Step: %d", step_number)
self.print_msg(" Increment: %d", total_increments)
model.Stress(
name=predefined_field_current,
distributionType=FROM_FILE,
fileName=odb_path,
step=step_number,
increment=total_increments,
region=region
)
self.print_msg("Successfully created predefined field: %s", predefined_field_current)
except Exception as e:
self.print_msg("Error creating stress field: %s", str(e))
break
try:
current_job = self.create_job(
model_name,
current_job_name,
'Geostatic iteration %d' % iteration_count,
num_cpus,
memory_percent,
num_gpus
)
except Exception as e:
self.print_msg("Error creating job: %s", str(e))
break
try:
current_job.submit(consistencyChecking=OFF)
self.print_msg("Job %s submitted", current_job_name)
current_job.waitForCompletion()
if current_job.status != COMPLETED:
self.print_msg("Warning: Job %s completed with status: %s",
current_job_name, current_job.status)
break
else:
self.print_msg("Job %s completed successfully", current_job_name)
except Exception as e:
self.print_msg("Job submission or execution failed: %s", str(e))
break
# 记录当前迭代的位移
current_odb_path = os.path.join(base_odb_path, current_job_name + '.odb')
max_disp = self.get_max_displacement(current_odb_path)
self.displacement_history.append({
'job_name': current_job_name,
'iteration': iteration_count,
'max_displacement': max_disp,
'type': 'iteration'
})
if control_mode == 'displacement':
current_odb_path = os.path.join(base_odb_path, current_job_name + '.odb')
max_disp = self.get_max_displacement(current_odb_path)
try:
if predefined_field_prev in model.predefinedFields:
model.predefinedFields[predefined_field_prev].suppress()
self.print_msg("Suppressed predefined field: %s", predefined_field_prev)
except Exception as e:
self.print_msg("Error suppressing field: %s", str(e))
if control_mode == 'iterations':
self.print_msg("Iteration %d completed", iteration_count)
else:
self.print_msg("Iteration %d completed, max displacement: %.6f", iteration_count, max_disp)
self.print_msg("=" * 50)
except Exception as e:
self.print_msg("Unexpected error in iteration %d: %s", iteration_count, str(e))
break
if control_mode == 'iterations':
self.print_msg("Completed %d iterations as requested", iteration_count)
success = True
else:
if max_disp <= control_value:
self.print_msg("Displacement requirement met after %d iterations! (%.6f <= %.6f)",
iteration_count, max_disp, control_value)
success = True
elif iteration_count >= 99:
self.print_msg("Reached maximum iteration limit (99) without meeting displacement requirement")
self.print_msg("Final displacement: %.6f, Target: %.6f", max_disp, control_value)
success = True
else:
self.print_msg("Iteration stopped due to error")
success = False
# 打印位移总结报告
try:
self.print_displacement_summary()
except Exception as e:
self.print_msg("Error printing displacement summary: %s", str(e))
self.analysis_completed = success
# 如果分析成功,显示保存对话框
if success:
try:
self.show_completion_dialog()
except Exception as e:
self.print_msg("Error showing completion dialog: %s", str(e))
# 即使对话框出错,分析本身是成功的
return True
return success
def show_completion_dialog(self):
"""显示完成对话框"""
try:
# 确保在GUI环境中才显示对话框
if not hasattr(__main__, 'gui') and 'ABQcae' not in sys.executable:
self.print_msg("Analysis completed successfully. Please save the model manually.")
return True
response = getInputs(
fields=(('Type "SAVE" to confirm saving:', 'SAVE'),),
label='All jobs completed successfully!\n\nType "SAVE" and click OK to save the Model.\nClick Cancel or close dialog to exit without saving.',
dialogTitle='Completion'
)
if response is None:
self.print_msg("Dialog cancelled or closed by user. Model not saved.")
return False
if len(response) > 0 and response[0] is not None:
user_input = response[0].strip().upper() if hasattr(response[0], 'strip') else str(response[0])
if user_input == "SAVE":
try:
mdb.save()
self.print_msg("Model saved successfully after user confirmation.")
return True
except Exception as e:
self.print_msg("Error saving model: %s", str(e))
return False
else:
self.print_msg("User did not confirm saving. Model not saved.")
return False
else:
self.print_msg("No confirmation received. Model not saved.")
return False
except Exception as e:
self.print_msg("Error in completion dialog: %s", str(e))
self.print_msg("Model not saved due to dialog errors.")
return False
def show_warning_message(self, message, title="Warning"):
"""显示警告消息(兼容不同版本的Abaqus API)"""
try:
# 确保在GUI环境中才显示对话框
if not hasattr(__main__, 'gui') and 'ABQcae' not in sys.executable:
self.print_msg("WARNING: %s", message)
return
# 尝试使用新版API
getWarningReply(message=message, buttons=('OK',))
except TypeError:
# 如果新版API不支持,使用旧版API
try:
getWarningReply(message=message)
except:
# 如果所有API都失败,直接打印消息
self.print_msg("WARNING: %s", message)
def show_error_message(self, message, title="Error"):
"""显示错误消息(兼容不同版本的Abaqus API)"""
try:
# 确保在GUI环境中才显示对话框
if not hasattr(__main__, 'gui') and 'ABQcae' not in sys.executable:
self.print_msg("ERROR: %s", message)
return
# 尝试使用新版API
getWarningReply(message=message, buttons=('OK',))
except TypeError:
# 如果新版API不支持,使用旧版API
try:
getWarningReply(message=message)
except:
# 如果所有API都失败,直接打印消息
self.print_msg("ERROR: %s", message)
def run_gui_interface(self):
"""运行GUI界面"""
self.print_msg("=" * 60)
self.print_msg("Auto Geostatic Analysis Program (GUI Mode)")
self.print_msg("=" * 60)
try:
# 获取当前CAE文件路径作为默认ODB路径
default_odb_path = ""
try:
# 尝试获取当前CAE文件的目录
if hasattr(mdb, 'path') and mdb.path:
# mdb.path 返回CAE文件所在目录
default_odb_path = mdb.path
self.print_msg("Current CAE directory: %s", default_odb_path)
else:
# 如果CAE文件未保存,使用当前工作目录
default_odb_path = os.getcwd()
self.print_msg("Using current working directory: %s", default_odb_path)
except Exception as path_error:
self.print_msg("Warning: Could not determine default path: %s", str(path_error))
default_odb_path = os.getcwd() # 降级到当前工作目录
control_inputs = getInputs(
fields=(
('Control mode (iterations/displacement):', 'iterations'),
),
label='Select control mode:\n- "iterations": Fixed number of iterations\n- "displacement": Stop when displacement is below threshold',
dialogTitle='Geostatic Analysis Control Mode'
)
if self.is_cancelled(control_inputs):
self.print_msg("Operation cancelled by user")
return
control_mode = control_inputs[0].strip().lower() if control_inputs[0] is not None else 'iterations'
if control_mode not in ['iterations', 'displacement']:
self.show_warning_message('Control mode must be "iterations" or "displacement"')
return
# 根据控制模式创建参数输入对话框,添加弹性试验选项
if control_mode == 'iterations':
inputs = getInputs(
fields=(
('Number of iterations:', '5'),
('ODB file path (without filename):', default_odb_path), # 自动填充默认路径
('Geostatic set name:', 'Geostatic-Set'),
('Model name:', 'Model-1'),
('Number of CPUs:', '24'),
('Memory usage (%):', '90'),
('Number of GPUs:', '2'),
('Use elastic trial? (yes/no):', 'yes'),
('Uniform elastic modulus for trial (kPa, optional):', '')
),
label='Enter parameters for iteration-controlled analysis:\n(ODB path is pre-filled with current CAE file directory. Modify if needed.)\n(If using elastic trial, you can optionally specify a uniform elastic modulus for all materials.)\n',
dialogTitle='Geostatic Analysis Parameters (Iteration Control)'
)
else:
inputs = getInputs(
fields=(
('Maximum allowed displacement:', '0.001'),
('ODB file path (without filename):', default_odb_path), # 自动填充默认路径
('Geostatic set name:', 'Geostatic-Set'),
('Model name:', 'Model-1'),
('Number of CPUs:', '24'),
('Memory usage (%):', '90'),
('Number of GPUs:', '2'),
('Use elastic trial? (yes/no):', 'yes'),
('Uniform elastic modulus for trial (kPa, optional):', '')
),
label='Enter parameters for displacement-controlled analysis:\n(ODB path is pre-filled with current CAE file directory. Modify if needed.)\n(If using elastic trial, you can optionally specify a uniform elastic modulus for all materials.)\n',
dialogTitle='Geostatic Analysis Parameters (Displacement Control)'
)
if self.is_cancelled(inputs):
self.print_msg("Operation cancelled by user")
return
# 提取输入参数,包括弹性试验选项和统一模量选项
if control_mode == 'iterations':
control_value_str, odb_path, set_name, model_name, num_cpus_str, memory_percent_str, num_gpus_str, use_elastic_trial_str, uniform_elastic_modulus_str = inputs
try:
control_value = int(control_value_str)
if control_value <= 0:
self.show_warning_message('Number of iterations must be greater than 0')
return
except (ValueError, TypeError):
self.show_warning_message('Number of iterations must be a number')
return
else:
control_value_str, odb_path, set_name, model_name, num_cpus_str, memory_percent_str, num_gpus_str, use_elastic_trial_str, uniform_elastic_modulus_str = inputs
try:
control_value = float(control_value_str)
if control_value <= 0:
self.show_warning_message('Maximum displacement must be greater than 0')
return
except (ValueError, TypeError):
self.show_warning_message('Maximum displacement must be a number')
return
# 处理弹性试验选项
use_elastic_trial_str = use_elastic_trial_str.strip().lower() if use_elastic_trial_str is not None else 'yes'
if use_elastic_trial_str in ['yes', 'y', 'true', '1']:
use_elastic_trial = True
elif use_elastic_trial_str in ['no', 'n', 'false', '0']:
use_elastic_trial = False
else:
self.show_warning_message('Use elastic trial must be "yes" or "no"')
return
# 处理统一模量选项
uniform_elastic_modulus = None
if use_elastic_trial and uniform_elastic_modulus_str and uniform_elastic_modulus_str.strip():
try:
uniform_elastic_modulus = float(uniform_elastic_modulus_str.strip())
if uniform_elastic_modulus <= 0:
self.show_warning_message('Uniform elastic modulus must be greater than 0')
return
except (ValueError, TypeError):
self.show_warning_message('Uniform elastic modulus must be a number')
return
odb_path = odb_path.strip()
if not odb_path:
self.show_warning_message('ODB path cannot be empty')
return
set_name = set_name.strip()
if not set_name:
self.show_warning_message('Set name cannot be empty')
return
model_name = model_name.strip()
if not model_name:
self.show_warning_message('Model name cannot be empty')
return
try:
num_cpus = int(num_cpus_str)
if num_cpus <= 0:
self.show_warning_message('Number of CPUs must be greater than 0')
return
except (ValueError, TypeError):
self.show_warning_message('Number of CPUs must be a number')
return
try:
memory_percent = int(memory_percent_str)
if memory_percent <= 0 or memory_percent > 100:
self.show_warning_message('Memory usage must be between 1 and 100 percent')
return
except (ValueError, TypeError):
self.show_warning_message('Memory usage must be a number')
return
try:
num_gpus = int(num_gpus_str)
if num_gpus < 0:
self.show_warning_message('Number of GPUs cannot be negative')
return
except (ValueError, TypeError):
self.show_warning_message('Number of GPUs must be a number')
return
# 输出参数确认,包括弹性试验选项和统一模量选项
self.print_msg("\nParameter confirmation:")
self.print_msg("Control mode: %s", control_mode)
if control_mode == 'iterations':
self.print_msg("Number of iterations: %d", control_value)
else:
self.print_msg("Maximum allowed displacement: %.6f", control_value)
self.print_msg("ODB path: %s", odb_path)
self.print_msg("Set name: %s", set_name)
self.print_msg("Model name: %s", model_name)
self.print_msg("Number of CPUs: %d", num_cpus)
self.print_msg("Memory usage: %d%%", memory_percent)
self.print_msg("Number of GPUs: %d", num_gpus)
self.print_msg("Use elastic trial: %s", "Yes" if use_elastic_trial else "No")
if use_elastic_trial and uniform_elastic_modulus is not None:
self.print_msg("Uniform elastic modulus: %.2f kPa", uniform_elastic_modulus)
elif use_elastic_trial:
self.print_msg("Uniform elastic modulus: Using original material values")
self.print_msg("-" * 40)
# 使用所有验证过的参数调用核心分析函数
try:
success = self.run_analysis(control_mode, control_value, odb_path, set_name, model_name,
num_cpus, memory_percent, num_gpus, use_elastic_trial, uniform_elastic_modulus)
# 捕获分析期间的异常(作业失败,ODB读取错误)
except Exception as e:
error_msg = "Analysis failed: %s" % str(e)
self.print_msg(error_msg)
# 显示错误对话框:通知用户失败原因
self.show_error_message(error_msg)
# 捕获GUI界面创建/交互期间的全局异常
except Exception as e:
error_msg = "GUI interface failed: %s" % str(e)
self.print_msg(error_msg)
# 对于取消的操作不显示错误对话框
if "cancelled" not in str(e).lower() and "cancel" not in str(e).lower():
self.show_error_message(error_msg)
def run_cli_interface(self):
"""运行CLI界面"""
self.print_msg("=" * 60)
self.print_msg("Auto Geostatic Analysis Program (CLI Mode)")
self.print_msg("=" * 60)
self.print_msg("Command line arguments: %s" % str(sys.argv))
if '--' not in sys.argv:
self.print_msg("Error: Missing '--' separator")
self.print_msg("Usage: abaqus cae noGUI=script.py -- <control_mode> <control_value> <odb_path> <set_name> [model_name] [num_cpus] [memory_percent] [num_gpus] [use_elastic_trial] [uniform_elastic_modulus]")
self.print_msg("Control mode: 'iterations' or 'displacement'")
self.print_msg("Example (iteration control): abaqus cae noGUI=script.py -- iterations 5 /path/to/odb/files Geostatic-Set Model-1 4 80 1 yes 1000")
self.print_msg("Example (displacement control): abaqus cae noGUI=script.py -- displacement 0.001 /path/to/odb/files Geostatic-Set Model-1 4 80 1 yes 1000")
return
try:
separator_index = sys.argv.index("--")
user_args = sys.argv[separator_index + 1:]
if len(user_args) < 4:
self.print_msg("Error: Not enough user parameters after '--'")
self.print_msg("Expected: <control_mode> <control_value> <odb_path> <set_name> [model_name] [num_cpus] [memory_percent] [num_gpus] [use_elastic_trial] [uniform_elastic_modulus]")
self.print_msg("Got: %s" % str(user_args))
return
control_mode = user_args[0].lower()
control_value_str = user_args[1]
base_odb_path = user_args[2]
set_name = user_args[3]
model_name = user_args[4] if len(user_args) > 4 else 'Model-1'
num_cpus = int(user_args[5]) if len(user_args) > 5 else 1
memory_percent = int(user_args[6]) if len(user_args) > 6 else 70
num_gpus = int(user_args[7]) if len(user_args) > 7 else 0
use_elastic_trial_str = user_args[8].lower() if len(user_args) > 8 else 'yes'
uniform_elastic_modulus_str = user_args[9] if len(user_args) > 9 else ''
# 处理弹性试验选项
if use_elastic_trial_str in ['yes', 'y', 'true', '1']:
use_elastic_trial = True
elif use_elastic_trial_str in ['no', 'n', 'false', '0']:
use_elastic_trial = False
else:
self.print_msg("Error: Use elastic trial must be 'yes' or 'no'")
return
# 处理统一模量选项
uniform_elastic_modulus = None
if use_elastic_trial and uniform_elastic_modulus_str and uniform_elastic_modulus_str.strip():
try:
uniform_elastic_modulus = float(uniform_elastic_modulus_str.strip())
if uniform_elastic_modulus <= 0:
self.print_msg("Error: Uniform elastic modulus must be greater than 0")
return
except (ValueError, TypeError):
self.print_msg("Error: Uniform elastic modulus must be a number")
return
if control_mode not in ['iterations', 'displacement']:
self.print_msg("Error: Control mode must be 'iterations' or 'displacement'")
return
if control_mode == 'iterations':
try:
control_value = int(control_value_str)
if control_value <= 0:
self.print_msg("Error: Number of iterations must be greater than 0")
return
except (ValueError, TypeError):
self.print_msg("Error: Number of iterations must be an integer")
return
else:
try:
control_value = float(control_value_str)
if control_value <= 0:
self.print_msg("Error: Maximum displacement must be greater than 0")
return
except (ValueError, TypeError):
self.print_msg("Error: Maximum displacement must be a number")
return
if num_cpus <= 0:
self.print_msg("Error: Number of CPUs must be greater than 0")
return
if memory_percent <= 0 or memory_percent > 100:
self.print_msg("Error: Memory usage must be between 1 and 100 percent")
return
if num_gpus < 0:
self.print_msg("Error: Number of GPUs cannot be negative")
return
if not os.path.exists(base_odb_path):
self.print_msg("Error: ODB path does not exist: %s", base_odb_path)
return
# 输出参数确认,包括弹性试验选项和统一模量选项
self.print_msg("\nParameter confirmation:")
self.print_msg("Control mode: %s", control_mode)
if control_mode == 'iterations':
self.print_msg("Number of iterations: %d", control_value)
else:
self.print_msg("Maximum allowed displacement: %.6f", control_value)
self.print_msg("ODB path: %s", base_odb_path)
self.print_msg("Set name: %s", set_name)
self.print_msg("Model name: %s", model_name)
self.print_msg("Number of CPUs: %d", num_cpus)
self.print_msg("Memory usage: %d%%", memory_percent)
self.print_msg("Number of GPUs: %d", num_gpus)
self.print_msg("Use elastic trial: %s", "Yes" if use_elastic_trial else "No")
if use_elastic_trial and uniform_elastic_modulus is not None:
self.print_msg("Uniform elastic modulus: %.2f kPa", uniform_elastic_modulus)
elif use_elastic_trial:
self.print_msg("Uniform elastic modulus: Using original material values")
self.print_msg("-" * 40)
# 使用所有参数调用核心分析函数
success = self.run_analysis(control_mode, control_value, base_odb_path, set_name, model_name,
num_cpus, memory_percent, num_gpus, use_elastic_trial, uniform_elastic_modulus)
# 在CLI模式下分析成功后自动保存模型
if success:
try:
mdb.save()
self.print_msg("All jobs completed successfully! Model saved automatically in CLI mode.")
except Exception as e:
self.print_msg("Error saving model: %s", str(e))
# 捕获参数类型错误(非数字参数,格式错误)
except ValueError as e:
self.print_msg("Parameter error: %s", str(e))
self.print_msg("Please ensure numeric parameters are valid numbers")
# 捕获其他异常(分析失败,内存问题)
except Exception as e:
error_msg = "Analysis failed: %s" % str(e)
self.print_msg(error_msg)
def main():
"""应用程序的主入口点"""
try:
analysis = GeostaticAnalysis()
is_gui_environment = hasattr(__main__, 'gui') or 'ABQcae' in sys.executable
if is_gui_environment:
analysis.print_msg("Running in GUI environment")
analysis.run_gui_interface()
else:
analysis.print_msg("Running in CLI environment")
analysis.run_cli_interface()
except Exception as e:
error_msg = "Fatal error in main execution: %s" % str(e)
print(error_msg)
if 'analysis' in locals() and hasattr(analysis, 'print_msg'):
analysis.print_msg(error_msg)
if __name__ == '__main__':
main()