将 odb 中的状态变量名称替换为 python
Replacing State variable names in odb with python
在 Abaqus 中,我有自己的 material 描述 (VUMAT)。此 VUMAT 生成名为 SDV1、SDV2 等的状态变量。这些变量与 abaqus 中的其他输出数据一起存储在二进制 .odb 文件中。因为我有大量这样的变量,所以我想给它们起有意义的名称,如 S1、S2、E1、E2 等。因此,当在 Abaqus 查看器中查看 .odb 时,可以清楚地知道哪个变量是哪个。
现在,abaqus 提供了 python 接口来读取和写入该 .odb 文件。但据我所知,我找不到重命名这些变量的方法。当我尝试更改它们时,出现只读错误。
因此,我尝试用 Notepad++ 打开 .odb,发现如果手动将该文件中的所有 SDV 条目替换为我想要的并保存它。这些名称也会在 Abaqus 查看器中更改。太好了!
但我想使这个过程自动化。所以我写了一个 python 脚本来读取初始的 .odb,替换 SDV 并将更改后的 .odb 保存为不同的文件。
import sys
with open('User.odb','rb') as f:
content = f.read()
if b"SDV2" in content:
print('Found')
content = content.replace(b'SDV2',b'works')
with open('User.temp.odb','wb') as fw:
fw.write(content)
但是当我在 abaqus 查看器中打开新的 .odb 时,我收到以下消息:
***ERROR: The Abaqus database file is corrupt. If this file was transferred from another machine using FTP or equivalent, ensure that
the file was copied using the binary mode instead of ASCII mode.
此外,目前,此代码还替换了 SDV20,如何避免这种情况并仅替换 SDV2 而不是 SDV20、SDV21 等的一部分?
我错过了什么?我正在使用 python 2.7。
编辑:
如果在 HEX 编辑器中打开 ODB,可以看到以下模式:
对于 SDV9 后跟 SDV10:
04 53 44 56 39 00 00 00 05 53 44 56 31 30 00 00 00 00 00 00
可以看出编码是从字符数开始的。 SDV9 为 04,SDV10 为 05,后跟 NULL 值。 SDV1 到 SDV9 为 3,其余为 6。我尝试将 SDV9 部分更改为:
05 53 44 56 39 00 00 00 00 00 00 00
它给了我同样的错误,同时将 SDV10 部分更改为:
05 53 44 56 39 00 00 00 00 00 00 00
工作得很好。如果有人熟悉这一点,将不胜感激。
编辑2:
我的代码不起作用,因为新变量的长度应该与旧变量的长度完全相同。如果长度匹配则没有问题。较短的变量后面可以有空格以获得所需的字符长度。
通过使用 SSchneid 建议的代码,我能够匹配精确的 SDV 进行替换,不包括较长的部分。
添加涉及 FieldOutput() 和 addData() 的字段变量的标准方法不是一个好的解决方案,因为它会用不同的名称复制现有的 SDV。对于非常大的分析,它会显着增加 odb 的大小。除非有办法删除旧的。
当然,我们可以更进一步,将需要的变量直接从子例程输出到单独的文本文件。然后使用该文本文件在 odb 中生成新的 Field 输出。之后可以删除文本文件。而这一切都无需请求 SDV 输出。
或者可以将 SDV 输出请求到 .fil 文件,然后可以使用 FieldOutput() 和 addData() 将其组装回 odb。但这些都是非常棘手的解决方案,需要大量写入磁盘和大量代码行来解析输出文本文件。
我不回答这个问题,直到 post 找到一个完整的解决方案或者我自己想出来(在这种情况下,我会 post 一个答案野兔)
感谢您的帮助!
问题是,您匹配以 SDV2 开头的所有内容。这意味着无论后面发生什么,开头有 SDV2 的所有内容都将 return 为正。
我认为解决匹配问题的最佳方法是使用正则表达式和 re:
import re
import sys
with open('User.odb','rb') as f:
content = f.read()
content = re.sub('\bSDV2\b', 'works', content)
with open('User.temp.odb','wb') as fw:
fw.write(content)
我打赌有一种方法可以做到这一点,而无需按照您建议的方式重新进行。
您的方法很有创意,但是有一种 "standard" 方法可以使用 Abaqus 实现您的目标 Python API:
frame.FieldOutput(name='works', description='this is a vector', type=VECTOR).addData(position=INTEGRATION_POINT, instance=grout_instance, labels=elementLabels, data=elementData)
在你的代码中我观察到两个可能的错误:
- 新旧名称('SDV2' 和 'works')的长度不匹配。
- ODB 文件的二进制数据部分可能包含意外匹配 'SDV2' 的 4 个后续字节。在这种情况下,您的脚本会覆盖这些字节并损坏整个文件。
编辑1
由于 Abaqus 没有提供任何删除现有字段的方法(正如他们所说的出于一致性原因),如果您想通过使用 addData
方法来防止重复,则有必要创建一个新的 ODB 并将字段复制到那里。然后你可以删除旧文件。我们已经使用这种方法一年了。好处是您可以从不同的 SDV 创建向量或张量以实现更好的可视化。
要修复我之前提到的可能的错误(长度不匹配和意外替换),您可以使用 find
而不是 replace
。
你可以试试这个代码
content = bytearray(content)
old2new={b'SDV2':b'work'} # add more renaming patterns as you need
for oldn, newn in old2new.items():
i = content.find(oldn)
# or using regex pattern:
# re.search(oldn, content).start()
content[i:i+len(newn)] = newn
其他代码遵循您的脚本。我假设您只需要替换文件中每个名称的一次出现。否则请使用re
的finditer
方法。
在 Abaqus 中,我有自己的 material 描述 (VUMAT)。此 VUMAT 生成名为 SDV1、SDV2 等的状态变量。这些变量与 abaqus 中的其他输出数据一起存储在二进制 .odb 文件中。因为我有大量这样的变量,所以我想给它们起有意义的名称,如 S1、S2、E1、E2 等。因此,当在 Abaqus 查看器中查看 .odb 时,可以清楚地知道哪个变量是哪个。
现在,abaqus 提供了 python 接口来读取和写入该 .odb 文件。但据我所知,我找不到重命名这些变量的方法。当我尝试更改它们时,出现只读错误。
因此,我尝试用 Notepad++ 打开 .odb,发现如果手动将该文件中的所有 SDV 条目替换为我想要的并保存它。这些名称也会在 Abaqus 查看器中更改。太好了!
但我想使这个过程自动化。所以我写了一个 python 脚本来读取初始的 .odb,替换 SDV 并将更改后的 .odb 保存为不同的文件。
import sys
with open('User.odb','rb') as f:
content = f.read()
if b"SDV2" in content:
print('Found')
content = content.replace(b'SDV2',b'works')
with open('User.temp.odb','wb') as fw:
fw.write(content)
但是当我在 abaqus 查看器中打开新的 .odb 时,我收到以下消息:
***ERROR: The Abaqus database file is corrupt. If this file was transferred from another machine using FTP or equivalent, ensure that the file was copied using the binary mode instead of ASCII mode.
此外,目前,此代码还替换了 SDV20,如何避免这种情况并仅替换 SDV2 而不是 SDV20、SDV21 等的一部分?
我错过了什么?我正在使用 python 2.7。
编辑:
如果在 HEX 编辑器中打开 ODB,可以看到以下模式: 对于 SDV9 后跟 SDV10:
04 53 44 56 39 00 00 00 05 53 44 56 31 30 00 00 00 00 00 00
可以看出编码是从字符数开始的。 SDV9 为 04,SDV10 为 05,后跟 NULL 值。 SDV1 到 SDV9 为 3,其余为 6。我尝试将 SDV9 部分更改为:
05 53 44 56 39 00 00 00 00 00 00 00
它给了我同样的错误,同时将 SDV10 部分更改为:
05 53 44 56 39 00 00 00 00 00 00 00
工作得很好。如果有人熟悉这一点,将不胜感激。
编辑2:
我的代码不起作用,因为新变量的长度应该与旧变量的长度完全相同。如果长度匹配则没有问题。较短的变量后面可以有空格以获得所需的字符长度。
通过使用 SSchneid 建议的代码,我能够匹配精确的 SDV 进行替换,不包括较长的部分。
添加涉及 FieldOutput() 和 addData() 的字段变量的标准方法不是一个好的解决方案,因为它会用不同的名称复制现有的 SDV。对于非常大的分析,它会显着增加 odb 的大小。除非有办法删除旧的。
当然,我们可以更进一步,将需要的变量直接从子例程输出到单独的文本文件。然后使用该文本文件在 odb 中生成新的 Field 输出。之后可以删除文本文件。而这一切都无需请求 SDV 输出。
或者可以将 SDV 输出请求到 .fil 文件,然后可以使用 FieldOutput() 和 addData() 将其组装回 odb。但这些都是非常棘手的解决方案,需要大量写入磁盘和大量代码行来解析输出文本文件。
我不回答这个问题,直到 post 找到一个完整的解决方案或者我自己想出来(在这种情况下,我会 post 一个答案野兔)
感谢您的帮助!
问题是,您匹配以 SDV2 开头的所有内容。这意味着无论后面发生什么,开头有 SDV2 的所有内容都将 return 为正。
我认为解决匹配问题的最佳方法是使用正则表达式和 re:
import re
import sys
with open('User.odb','rb') as f:
content = f.read()
content = re.sub('\bSDV2\b', 'works', content)
with open('User.temp.odb','wb') as fw:
fw.write(content)
我打赌有一种方法可以做到这一点,而无需按照您建议的方式重新进行。
您的方法很有创意,但是有一种 "standard" 方法可以使用 Abaqus 实现您的目标 Python API:
frame.FieldOutput(name='works', description='this is a vector', type=VECTOR).addData(position=INTEGRATION_POINT, instance=grout_instance, labels=elementLabels, data=elementData)
在你的代码中我观察到两个可能的错误:
- 新旧名称('SDV2' 和 'works')的长度不匹配。
- ODB 文件的二进制数据部分可能包含意外匹配 'SDV2' 的 4 个后续字节。在这种情况下,您的脚本会覆盖这些字节并损坏整个文件。
编辑1
由于 Abaqus 没有提供任何删除现有字段的方法(正如他们所说的出于一致性原因),如果您想通过使用
addData
方法来防止重复,则有必要创建一个新的 ODB 并将字段复制到那里。然后你可以删除旧文件。我们已经使用这种方法一年了。好处是您可以从不同的 SDV 创建向量或张量以实现更好的可视化。要修复我之前提到的可能的错误(长度不匹配和意外替换),您可以使用
find
而不是replace
。
你可以试试这个代码
content = bytearray(content)
old2new={b'SDV2':b'work'} # add more renaming patterns as you need
for oldn, newn in old2new.items():
i = content.find(oldn)
# or using regex pattern:
# re.search(oldn, content).start()
content[i:i+len(newn)] = newn
其他代码遵循您的脚本。我假设您只需要替换文件中每个名称的一次出现。否则请使用re
的finditer
方法。