首页/文章/ 详情

解密:如何实现坤坤在ABAQUS中一曲肝肠断

4月前浏览877
预警:帖子纯属搞怪,慎重观看!

前情提要

  前几天我上传了坤坤在abaqus中为我一舞的视频,当时视频画风是这样。

  这引起了小黑子们的极大不满。他们一度企图攻陷我的评论区和私信。他们声称要抱走我的9950X,16核心,32线程,满载频率5.5ghz的CPU。更可怕的是,他们还要拔走我的32GX4一共是128G,而且是去年2000RMB买的内存条(懂的人请起立鼓掌,我就是为了这句话才发的这篇帖子)。
  这使我鸡动的夜不能寐,鉴于此,我连夜制作了灰度版本的坤坤视频,送给大家,希望大家在繁重的工作中能够嘴角上扬,欢乐一下。

缘起

  我看过坤坤在示波器上面跳舞;我看过坤坤在LCD灯带上跳舞;我还看过坤坤用汉字跳舞。
  我已经在有限元领域练习时长2.4坤年了,但是,,,,我就是没有见过坤坤在ABAQUS中为我跳舞,我的余生将得不到坤坤为我起舞,这不合理,这太不抽象了。
  我就开始思考,怎么能让坤坤为我一曲肝肠断呢?

AI的倾情巨献

  我开始了忙忙碌碌,但是碌碌无为的思考过程,我想了很多的方案,包括但不限于下面提到的

  • 我想利用位移的子程序,直接计算相应的位移,然后施加在节点上面,最后因为算不出来坤坤的姿态,放弃了
  • 我想利用材料有关的子程序设置,但是我压根不懂笨狗(本构)啊,这根本没法玩儿啊
  • 我还想vweny!~654?》《“:{}*……%¥¥#

  以上方案全部失败了,是的,我要失败了,我不能让坤坤为我一舞了,我感觉我要带着无尽的遗憾卸载abaqus了,我的坤坤,呜呜呜......
  但我可是练习时长2.4坤年的有限元先生啊!
  就在某一个平平无奇的办公室午休时间,我的师弟打着呼噜让我睡不着觉,就像是张雪开着820RR在我脸上折返骑,与此同时我又尽力让自己睡着,再加上眼球被胳膊硌住了,大腿也麻了,想调整屁 股的位置但动不了的时候,这一系列听觉、触觉、睡觉混合在一起的时候,我仿佛看到了坤坤在我的脑海里闪着诡异的光!
  坤坤,我悟了!
  什么?我知道怎么写程序了?怎么可能!我想起了AI啊!!!!!
  后面的事情就像是吃了椒麻拐拐筋之后肚子疼窜稀一样顺畅。
  我一开始自作聪明,把我的方案给AI,让他评价一下,就在我已经准备享受这个人工智障对我这个高等灵长类动物的崇拜了!
  想到这里,我不由自主的想起了祖先从遥远的非洲大陆,跨越大半个地球,最终来到亚欧大陆的感人故事,
  想到这里,生活在伦敦郊区下水道的人也不由自主的顿了顿自己的领带,彷佛他们的前途被大英帝国的金碧辉煌照亮的睁不开眼睛,尽管他们没有领带,尽管他们没有衬衫,尽管他们没有外套,尽管他们只有满身泥污,
  想到这里,我看到了墙上的斑点,我不知道这个斑点是怎么形成的,是某种动物留下的吗......
  停,这太抽象了...
  让我看看AI是怎么惊叹我思想的伟大的,AI一定会说:我对你的赞叹犹如滔滔江水连绵不绝,只可惜你电脑的1T固态+2T机械的容量放不下。
  你们失望了,实际上,AI并没有侮辱我的智商低下和唐氏行为,因为我在提示词里面写了,无论我多么抽象,我都是你爹,你不能忤逆我。
  但是,我看了他给我的解决方案之后,我发现,我的自惭形秽是真的放不下我的1T固态+2T机械的容量。
  如果说人类的想象力和AI相差了一个蜉蝣,我的能力和AI就相差了1mol爱因斯坦+3葛洪数个高斯。
       AI给了我一个木凳狗带、拍案惊奇、一刀封喉的解决方案:直接修改ODB文件
  我当时的表情就好像吃到了黄三石没炒熟的豆角那样。就好像曾小贤吃到了陆展博的意大利面。就好像八十万对六十万,优势在我一样。
  我甚至都不知道ODB可以修改!我觉得,发明天才可以修改的人一定是个ODB,究竟是出于什么原因发明了ODB可以修改呢,好难猜啊!伟大的掌管圆周率的神——拉马努金,你注意到这个问题了吗?你在做梦的时候有限元女神有没有告诉你为什么ODB可以修改呢?真的好难猜啊
  但是AI他就是知道ODB可以修改!他一定是个甜菜,是的,他一定是个甜菜,下次打瓦的时候喊上AI。
  严肃起来,下面开始解释方案。
      方案非常简单:视频帧的RGB数据+ODB的云图显示功能
      我上面胡扯了这么多,就是为了凑篇幅,想必你们这么聪明一定早就看出来了,并且直接跳转到这里了。
  不许揍我,下面看程序!

视频解析程序

  下面的程序将MP4文件处理成一帧一阵的rgb数据,导入到外部文件中,等待后续的程序读取并且写入到内存中。
  有小伙伴可能会问,为什么不直接把视频文件的rgb信息读取到内存中,直接写出到odb文件中,这不是节省了数据的读写时间吗?
  原因很简单:我电脑里的abaqus python环境不支持通过pip安装库文件,所以解析视频的程序无法在我的abaqus python环境中运行,如果你的abaqus版本比较高,可能可以用pip安装库文件,这个我没研究过。

# -*- coding: utf-8 -*-
import cv2
import os
import numpy as np

def an alyze_video_to_bin(video_path):
   
    #严谨的数据前处理
    #直接利用 Numpy 矩阵操作完成 Y 轴翻转,并将灰度值转化为 C 语言标准的单精度浮点数 (float32)。
    #输出纯二进制文件 (.bin),每帧体积精确锁定为 852 * 480 * 4 bytes = 1,635,840 bytes (约 1.56 MB)。
   
    if not os.path.exists(video_path):
        print("Error: Video file not found: " + video_path)
        return

    cap = cv2.VideoCapture(video_path)
    if not cap.isOpened():
        print("Error: Cannot open video file.")
        return

    width  = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    fps    = cap.get(cv2.CAP_PROP_FPS)
    total  = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

    print("Video: " + video_path)
    print("Resolution: {} x {}".format(width, height))
    print("FPS: {}".format(fps))
    print("Total frames: {}".format(total))
    print("=" * 60)

    base_dir = os.path.dirname(os.path.abspath(video_path))
    out_dir = os.path.join(base_dir, "bin_data")
    # Python 3 语法,确保输出目录存在
    os.makedirs(out_dir, exist_ok=True) 
    print("Output binary folder: " + out_dir)

    frame_index = 0
    while True:
        ret, frame = cap.read()
        if not ret:
            break
            
        # 1. 直接转换为灰度图 (0-255,dtype=uint8)
        gray_img = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        
        # 2. 坐标系对齐:Abaqus 单元编号从底部开始,因此直接将矩阵在 Y 轴翻转
        gray_flipped = gray_img[::-1, :]
        
        # 3. 展平为一维数组,并强制转换为单精度浮点型 (float32)
        # Abaqus SCALAR 场通常接受浮点数,float32 完美兼容且节省空间
        gray_float_array = gray_flipped.flatten().astype(np.float32)
        
        # 4. 直接将底层内存数据以二进制字节流写入文件
        bin_path = os.path.join(out_dir, "frame_{:06d}.bin".format(frame_index + 1))
        with open(bin_path, 'wb') as f:
            f.write(gray_float_array.tobytes())
            
        frame_index += 1
        if frame_index % 50 == 0:
            print("Processed {:6d} / {} frames".format(frame_index, total))

    cap.release()
    print("=" * 60)
    print("Done. Processed {} frames. Binary files saved to: {}".format(frame_index, out_dir))

if __name__ == '__main__':
    an alyze_video_to_bin(os.path.join(os.path.dirname(__file__), 'hua.mp4'))

修改ODB程序

  下面的程序读取外部的rgb数据,然后将rgb数据直接写入到odb文件!对!就是直接简单粗暴的修改odb文件。没有任何弯弯绕绕,没有任何高阶的操作,大道至简。

# -*- coding: utf-8 -*-
import os
import glob
import shutil
import struct
import time
import sys
from odbAccess import openOdb
from abaqusConstants import CENTROID, SCALAR

# ── Configuration ─────────────────────────────────────────────────────────────
SCRIPT_DIR  = os.getcwd()
ODB_SRC     = os.path.join(SCRIPT_DIR, 'Job-1.odb')
ODB_PATH    = os.path.join(SCRIPT_DIR, 'Job-1-videos.odb')

# 指向新的纯二进制数据目录
BIN_DIR     = os.path.join(SCRIPT_DIR, 'videos''bin_data')

WIDTH   = 852   # 视频像素,需要手动修改
HEIGHT  = 480   # 视频像素,需要手动修改
NUM_ELEMENTS = WIDTH * HEIGHT   # 408960
MAX_FRAMES = None
# ──────────────────────────────────────────────────────────────────────────────

def read_frame_from_bin(bin_path):
    """
    利用 struct 从二进制文件一次性还原单精度浮点数。
    Abaqus Python 2.7 原生支持,速度极快。
    "
""
    # 按照 4 bytes (float32) * 408960 读取整个文件
    with open(bin_path, 'rb') as f:
        raw_bytes = f.read(NUM_ELEMENTS * 4)
        
    # 'f' 代表标准单精度浮点数。构造格式化字符串,如 '408960f'
    fmt = '{0}f'.format(NUM_ELEMENTS)
    
    # unpack 会直接在底层用 C 语言解析字节流,返回一个包含所有数据的 tuple (元组)
    gray_vals = struct.unpack(fmt, raw_bytes)
    return gray_vals

def collect_frames_from_bin(bin_dir):
    """生成器:按序读取二进制文件"""
    files = sorted(glob.glob(os.path.join(bin_dir, 'frame_*.bin')))
    if MAX_FRAMES is not None:
        files = files[:MAX_FRAMES]
    for i, fpath in enumerate(files):
        yield i, read_frame_from_bin(fpath)

def main():
    if not os.path.exists(ODB_PATH):
        print("Copying {0} -> {1}".format(ODB_SRC, ODB_PATH))
        shutil.copy2(ODB_SRC, ODB_PATH)
    else:
        print("Using existing working copy: " + ODB_PATH)

    odb  = openOdb(path=ODB_PATH, readOnly=False)
    inst = odb.rootAssembly.instances['PART-1-1']
    step = odb.steps['Step-1']
    existing_frames = len(step.frames)

    # 在 Abaqus Python 2.7 中构建标签 tuple
    elem_labels = tuple(range(1, NUM_ELEMENTS + 1))
    
    frame_gen = collect_frames_from_bin(BIN_DIR)

    bin_files = sorted(glob.glob(os.path.join(BIN_DIR, 'frame_*.bin')))
    total_frames = len(bin_files) if MAX_FRAMES is None else min(MAX_FRAMES, len(bin_files))
    
    print("Total binary frames to write: {0}".format(total_frames))

    written  = 0
    t_start  = time.time()


    for frame_idx, gray_vals in frame_gen:
        frame_time = float(frame_idx)
        odb_frame = step.Frame(
            incrementNumber = existing_frames + frame_idx,
            frameValue      = frame_time,
            description     = 'VideoFrame_{0:06d}'.format(frame_idx + 1)
        )

        field = odb_frame.FieldOutput(
            name        = 'SDV1',
            description = 'Grayscale intensity',
            type        = SCALAR
        )

        # 1. 极致优化:利用 C 语言底层的 zip 函数,瞬间完成序列重组
        data_block = zip(gray_vals)

        field.addData(
            position = CENTROID,
            instance = inst,
            labels   = elem_labels,
            data     = data_block
        )

        written += 1
        
        # 2. 内存管理优化:强制定期刷写 C++ 底层缓存,防止 ODB 内存碎片化导致降速
        # 建议每 10~20 帧 save 一次,具体频率可根据实际内存压力微调
        if written % 10 == 0:
            odb.save() 
            
        # 3. 严谨的进度与耗时计算
        elapsed = time.time() - t_start
        # 避免除以零的错误
        if written > 0:
            eta = (elapsed / written) * (total_frames - written)
        else:
            eta = 0.0
            
        # 使用 Python 2.7 兼容的 format 语法格式化输出
        print("  写入进度: 帧 {0:5d} / {1:5d} ({2:5.1f}%) | 已用时: {3:4.0f}s | 预计剩余: {4:02.0f}m{5:02.0f}s".format(
            written, total_frames,
            100.0 * written / total_frames,
            elapsed,
            eta // 60, eta % 60))
        
        # 关键操作:强制刷新 Abaqus 的标准输出缓冲区,确保控制台实时显示
        sys.stdout.flush()

    # 循环结束后,进行最后一次保存并关闭数据库
    odb.save()
    odb.close()
    print("=" * 60)
    print("Done. {0} frames successfully written to ODB.".format(written))
    
    
if __name__ == '__main__':
    main()

写在最后

  魔术的魅力在于
  当看到表演效果的时候
  觉得这世界真的有魔法
  但是当知道原理的时候
  却只道寻常


  当看到坤坤在abaqus中跳舞
  觉得abaqus还能这么玩
  有限元还能这么用
  但是核心过程没用abaqus
  只是简单粗暴的修改ODB
  你会不会只道寻常?


  其实
  我真正想说的是
  这世界
  只有你想不到
  没有你做不到


  充满想象力
  去做你喜欢的事

喜欢就点个关注呗


来源:有限元先生
AbaqusSTEPSpythonUMBIM材料控制
著作权归作者所有,欢迎分享,未经许可,不得转载
首次发布时间:2026-04-08
最近编辑:4月前
外太空土豆儿
博士 我们穷极一生,究竟在追寻什么?
获赞 46粉丝 48文章 117课程 0
点赞
收藏
作者推荐

数值积分:牛顿-柯特斯公式与代数精度全解析

概述  在微积分的世界里,牛顿-莱布尼茨公式是计算定积分的“正统方法”,但现实的工程问题中,往往需要数值计算。今天帖子讲牛顿-柯特斯数值积分,以及衡量其精度的关键指标——代数精度。一、为什么需要数值积分?——牛顿-莱布尼茨公式的局限 回忆微积分中的牛顿-莱布尼茨公式:  它能精准计算连续函数的定积分,但以下两类场景中,这个公式会“失灵”:场景1:被积函数以离散点列 形式给出(比如实验测量数据、数值模拟的输出结果);场景2:被积函数的原函数无法用初等函数表示(如 ),或原函数过于复杂,计算成本极高。 这时,数值积分公式就成了破局关键——它通过函数在离散点的函数值“线性组合”,近似计算定积分,核心形式为: 其中: 是求积节点(即选取的离散点); 是求积系数(线性组合的权重); 是精确积分值, 是近似积分值。二、代数精度:数值积分的“精度标尺” 怎么判断一个数值积分公式好不好用?代数精度是最核心的衡量标准之一。定义 若数值积分公式 满足: 且  则称该公式具有 阶代数精度。 通俗来说:它能对所有次数≤ 的多项式精确积分,但对 次多项式不精确——代数精度越高,公式对“光滑函数”(可近似为高次多项式)的积分近似效果越好。三、插值型数值积分:从拉格朗日插值到数值积分 数值积分的经典构造方法是插值型数值积分,核心逻辑是“用插值多项式近似被积函数,再对插值多项式积分”。构造步骤对被积函数 在节点 处作拉格朗日插值多项式: (其中 是拉格朗日基函数,描述每个节点对插值多项式的贡献)用 近似 ,即: 定义求积系数 ,则数值积分公式为: 误差分析 数值积分的误差源于插值多项式的误差,即: (其中 是插值余项, 是区间内的某点) 特别地,若 是次数≤ 的多项式,则 ,误差 。因此,** 阶插值型数值积分公式至少具有 阶代数精度**。四、例题实战:构造非等距节点的数值积分公式题目:建立区间 上以 、 、 为节点的 数值积分公式。步骤1:构造拉格朗日插值多项式 以 、 、 为节点,构造二次拉格朗日插值多项式 ,其中拉格朗日基函数 定义为: 步骤2:计算求积系数 对于 (节点 ): 对于 (节点 ): 对于 (节点 ): 步骤3:写出数值积分公式 将求积系数代入 ,最终得到: 小结 牛顿-柯特斯数值积分是数值分析的基础工具,代数精度是判断其性能的核心指标。通过“插值型构造法”,我们可以从拉格朗日插值多项式推导出数值积分公式,且这类公式至少具有与节点数匹配的代数精度。来源:有限元先生

未登录
还没有评论
课程
培训
服务
行家
VIP会员 学习计划 福利任务
下载APP
联系我们
帮助与反馈