mcpField
F
FERS — structural FEM

FERS — structural FEM

远程服务
ferscloud.com科研与教育32 天前更新

关于 FERS — structural FEM

FERS 是面向工程师和 AI Agent 的 Python SDK、命令行工具及本地编译求解器,用于结构有限元分析。它支持 3D 框架、桁架、板件和 Eurocode 3 验算,并提供 Python 与 JSON 模型及结果工作流。

主要功能

  1. 013D 框架、桁架和板件分析
  2. 02Eurocode 3 构件验算
  3. 03命令行求解、检查与验证
  4. 04Python 模型和结果 API
  5. 05JSON 模型与结果存储
  6. 06可选结果请求

适用场景

  1. 01在本地分析结构模型
  2. 02按 Eurocode 3 验算梁承载力
  3. 03从终端运行模型验证
  4. 04保存并重新加载分析结果

MCP 相关

FERS 提供 MCP 服务,供 MCP 客户端调用结构分析能力。README 还介绍了面向编码 Agent 的 FERS skill。

配置

远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。

服务地址https://ferscloud.com/api/mcp
一键复制安装命令
Claude CodeTerminal
Cursor.cursor/mcp.json
VS Code.vscode/mcp.json
Claude Desktopclaude_desktop_config.json
Windsurf~/.codeium/windsurf/mcp_config.json

命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。

安装包
npm@ferscloud/fers-mcp2.76.0

工具

详细介绍

FERS:使用 Python 进行结构分析

FERS:面向工程师和 AI Agent 的结构有限元分析(3D 框架、桁架、板、Eurocode 3 验算)。这是它的 Python SDK 和 fers 命令。求解器是一个在你的机器上运行的编译引擎,无需账户,也不会发起网络请求:非商业用途免费,最多支持 100 个构件。

pip install FERS
from fers_core import check_beam

# IPE200 in S235, simply supported over 6 m, 10 kN/m. SI units: m, N, Pa.
# check_beam adds an EN 1993-1-1 member check on a ULS combination of 1.35 x the load.
model = check_beam(6.0, "IPE200", udl=10_000.0)
model.settings.analysis_options.include_member_deflected_shape = True
model.run_analysis()

beam = model.resultsbundle.loadcases["Q"].member_results["1"]
x_frac, (_, dy, _) = beam.member_displacement_peak
ec3 = model.unity_check_results()[0]
print(f"M {beam.local_minimums.mz / 1e3:.1f} kNm, sag {dy * 1e3:.1f} mm at x/L {x_frac:.2f}")
print(f"EC3 utilization {ec3['max_utilization']:.2f}")
M -45.0 kNm, sag -41.8 mm at x/L 0.50
EC3 utilization 2.85

挠度由 41.3 mm 的弯曲挠度(5wL⁴/384EI)和 0.4 mm 的剪切变形组成,后者也由引擎计算。此梁未通过验算:60.75 kNm 的 ULS 弯矩超过其 51.9 kNm 的抗弯承载力;由于没有构件约束其受压翼缘,侧扭屈曲使利用率升至 2.85。

  • Agent 和脚本: fers solve、fers check 和 fers validate 接受模型 JSON 文件,打印一行结果(或使用 --json),并通过退出代码说明这些数值是否可用;请参阅从终端和编码 Agent 调用。fers skill --install . 可向编码 Agent 提供 FERS 技能,MCP 客户端可使用 FERS MCP 服务。在 Claude Code 中,两者集成在同一个插件中:/plugin install fers --marketplace Jeroen124/FERS_core。
  • 附加功能: 绘图(pip install "FERS[plot]")、根据你自己的尺寸网格划分生成截面属性("FERS[sections]")和 DXF 导入("FERS[dxf]");"FERS[all]" 会安装 0.1.x 中的全部内容。目录中的截面(IPE200、HEB300、UB 457x191x67、W14X30 等)无需安装附加组件。
  • 许可证: SDK 采用 BSD-3-Clause 许可证;它安装的引擎可免费用于非商业用途(许可证)。

求解模型

手动构建模型时,先创建 FERS 对象,再创建节点、构件和荷载。创建 FERS 会重置 ID 计数器,因此,在它之前创建的节点会与之后创建的节点共享 ID;求解器也会拒绝不从 1 开始且存在间断的节点 ID。

读取结果

run_analysis() 会将结果存入 model.resultsbundle。荷载工况和荷载组合以名称为键。其中的节点和构件以字符串形式的 ID 为键,因为结果会经过 JSON 往返转换。

case = model.resultsbundle.loadcases["Load"]    # or .loadcombinations["ULS 1"]

case.displacement_nodes["5"].dy                  # dx dy dz rx ry rz warp
case.reaction_nodes["1"].nodal_forces.fy         # fx fy fz mx my mz bw
beam = case.member_results["4"]
beam.local_maximums.mz, beam.local_minimums.mz   # envelope along the member
beam.local_start_forces.fy                       # end forces, member axes

start_node_forces、end_node_forces、maximums 和 minimums 使用全局坐标系;local_* 力、section_forces 和 internal_force_series 使用构件自身的坐标轴,其中 mz 为强轴弯矩。读取框架的弯矩时,应使用局部坐标系中的值:在 XY 平面以外,全局 mz 并非弯矩;而在 YZ 平面内的框架中,它的值为 0。

从 0.1.98 起,member_results 和 displacement_nodes 是只读表,而不再是由对象组成的字典。这样可以减少大型结果占用的内存。它们的用法类似字典:[]、get、in、len、.items(),并按求解器的顺序迭代。每次查找都会创建一个新对象,且对象内容不可更改:

beam.local_maximums.mz = 0.0                     # AttributeError: read-only
editable = case.member_results.copy()            # a plain dict of editable objects
editable["4"].local_maximums.mz = 0.0            # fine

section_forces、internal_force_series 和 member_displacements 返回的是元组。要检查某对象是否为表,请使用 isinstance(x, collections.abc.Mapping),而不是 dict。reaction_nodes 和 plate_results 仍是普通字典。

保存和加载
from fers_core import FERS

model.save_to_json("beam.json")                  # the model and its results
model = FERS.from_json("beam.json")

FERS.from_json 会逐条读取求解器或 SDK 写入的任何文件。加载典型结果所需的内存比文件本身还少,最多约为文件大小的两倍;在 0.1.98 之前,则需要文件大小的 15 到 20 倍。对于大型模型,请让求解器自行写入文件。这样,结果就不会以一个完整字符串的形式经过 Python:

model.run_analysis_to_file("beam_results.json")
solved = FERS.from_json("beam_results.json")

fers_core/examples/809_Reading_Results.py 会端到端运行上述全部流程。

保存已加载的模型时,会写回求解器读取的所有内容,并保留相同的 ID。这包括文件中定义但尚未使用的内容,例如编辑器保留在其库中的截面。加载后创建的节点、构件及其他对象会使用该模型未占用的 ID,因此保存时不会让新对象替换已加载的对象。

仅读取所需结果(engine 0.2.68)

AnalysisOptions(result_filter=[...]) 会保留或丢弃整个结果块。 result_requests 更进一步。每个 ResultRequest 会指定一个结果块,并将其范围缩小到所选成员或节点、荷载组合、分量以及一个构件端:

from fers_core import ResultBlock, ResultRequest
from fers_core.builders import check_beam
from fers_core.result_requests import nodes_of
from fers_core.unity_checks import classification

model = check_beam(5.0, "IPE300", udl=10000.0)    # one "Beam", one ULS combination
beams = classification("Beam")
model.settings.analysis_options.result_requests = [
    ResultRequest(ResultBlock.LOCAL_ENVELOPES, members=[beams],
                  limit_state="ULS", components=["fx", "my", "mz"]),
    ResultRequest(ResultBlock.NODE_DISPLACEMENTS, nodes=nodes_of(beams),
                  limit_state="ULS", components=["dy"]),
    ResultRequest(ResultBlock.REACTIONS, limit_state="ULS"),
]
model.run_analysis()

这些值会载入相同的 member_results、displacement_nodes 和 reaction_nodes,因此读取这些值的代码仍可正常工作。**任何未请求的值读取时都是 None,而不是零。**因此,读取未请求值的检查会失败,而不会因读到零而通过。

  • members 接受统一性检查中的选择器:classification(...)、 members([...])、member_sets([...]) 和 all_members()。多个选择器会合并为一个并集。
  • nodes 接受 nodes([...]),或者使用 nodes_of(*selectors) 获取所选成员的端节点。
  • 荷载组合会按照统一性检查匹配组合时使用的 limit_state 进行匹配,也可以通过 load_combination_ids 匹配。若要按极限状态选择,请为每个 LoadCombination 设置 limit_state;求解器不会读取 situation。
  • 未设置 limit_state 或 load_combination_ids 的请求也会涵盖荷载工况。
  • end="start" 或 end="end" 会仅保留端部力和端部位移块中的一端。
  • 求解器会在启动前拒绝无法满足的请求。这包括 section_forces、 internal_force_series、结果块中不存在的分量、任何组合都未包含的极限状态,以及同时设置了 result_filter 的情况。

各个值位于 model.resultsbundle.selections 中,每个请求和结果集组对应一个条目。每个条目的 values 都是只读 numpy 数组,形状为 (result_sets, ids, fields, components),各轴的说明列在旁边:

sel = model.resultsbundle.selections[0]
sel["group"]                     # "loadcombinations" or "loadcases"
sel["result_sets"], sel["ids"]   # combination names, member or node ids
sel["fields"], sel["components"] # e.g. ["local_maximums", "local_minimums"], ["fx", "my", "mz"]
my = sel["values"][..., sel["components"].index("my")]   # every set, member and field

save_to_json 会按求解器写入时的形式保存这些选择,因此重新载入后结果保持不变。 fers_core/examples/810_Result_Requests.py 会端到端运行此流程。

剪刀铰:在其跨越的位置转动的连续梁(engine 0.2.68)

一根横跨梁并支承在每根梁上的导轨,沿长度方向连续,但可以在每根梁上自由转动。MemberHinge 会释放一个构件端,因此在某根梁处释放导轨的两端,就会把导轨分成两个跨度。将导轨的构件放入单独的构件集中,并为该集合设置 ScissorHinge。无需连接构件或额外节点:

from fers_core import FERS, DistributedLoad, Material, Member, MemberSet, NodalSupport, Node, ScissorHinge, Section
from fers_core.supports.supportcondition import SupportCondition as C

model = FERS()
steel = Material(name="S355", e_mod=210e9, g_mod=81e9, density=7850.0, yield_stress=355e6)
rail_section = Section(name="rail", material=steel, i_y=1e-6, i_z=1e-6, j=1e-7, area=1e-3)
beam_section = Section(name="beam", material=steel, i_y=1e-4, i_z=1e-4, j=1e-4, area=5e-3)

turns = {"X": C.fixed(), "Y": C.free(), "Z": C.free()}
a, m, b = Node(0.0, 0.0, 0.0), Node(1.0, 0.0, 0.0), Node(2.0, 0.0, 0.0)
a.nodal_support = NodalSupport(rotation_conditions=turns)
b.nodal_support = NodalSupport(
    displacement_conditions={"X": C.free(), "Y": C.fixed(), "Z": C.fixed()}, rotation_conditions=turns
)
beam_start, beam_end = Node(1.0, 0.0, -0.5), Node(1.0, 0.0, 0.5)
beam_start.nodal_support = beam_end.nodal_support = NodalSupport()  # clamped

rail_1 = Member(start_node=a, end_node=m, section=rail_section)
rail_2 = Member(start_node=m, end_node=b, section=rail_section)
model.add_member_set(MemberSet(
    members=[rail_1, rail_2],
    scissor_hinge=ScissorHinge(rotational_release_y=0.0, rotational_release_z=0.0),
))
model.add_member_set(MemberSet(members=[
    Member(start_node=beam_start, end_node=m, section=beam_section),
    Member(start_node=m, end_node=beam_end, section=beam_section),
]))

span = model.create_load_case(name="span")
DistributedLoad(member=rail_1, load_case=span, magnitude=1000.0, end_magnitude=1000.0, direction=(0, -1, 0))
model.run_analysis()
mz = model.resultsbundle.loadcases["span"].member_results[str(rail_1.id)].local_end_forces.mz
print(f"{abs(mz):.1f} Nm")  # 62.4; a knife edge gives qL²/16 = 62.5, a rigid joint 122.6
  • **适用位置:**在该集合中的构件与集合外的构件或板相交的每个节点处。此时,集合中的构件彼此保持连接,并沿释放的轴移动、绕释放的轴转动。集合仅与自身或支座相交的位置不会释放。因此,连续梁的构件应放入单独的集合;如果一个集合包含所有构件,它就不会与任何对象相交,求解器会拒绝该模型。
  • 仅限其中部分节点:MemberSet(scissor_hinge_nodes=[...]) 用于列出铰接生效的位置,适用于导轨在与某些构件相交处转动、而在其余位置连接固定的情况。
  • 各项数值的作用方式与 MemberHinge 的释放相同:None 表示保持,0.0 表示自由,正值表示集合与节点之间的弹簧;对于平移(translational_release_x/_y/_z),单位为力/长度;对于转动(rotational_release_x/_y/_z),单位为力矩/弧度。
  • 除非铰链指定自己的轴,否则轴均为全局轴: ScissorHinge(..., axes=ReleaseAxes.user(x=..., y=...)) 将第一轴沿 x,第二轴沿与其垂直的 y,第三轴则为两者的叉积,适用于在平面内转向的桁架。
  • 释放节点处的支座和节点荷载作用于节点,也就是说,作用于集合外的构件。节点报告的位移就是这些构件的位移。集合自身在该处的运动,记录在其构件的 local_displacement_start_node 和 local_displacement_end_node 中。
  • 两个铰接集合相交且没有其他对象时,每个集合都会与节点释放,因此任一集合释放的轴在它们之间都是自由的。
  • 仅含一个构件的集合会在该构件的两端绕铰链轴释放。
  • **二阶分析:**与构件铰链一样,只有指定 NonlinearMethod.COROTATIONAL 时,包含剪刀铰的模型才会进行共转修正。

也可以让导轨在立柱上滑动,从而释放该方向的平移。此处导轨只在第一根立柱上滑动,因此第二根立柱会承受相应的拉力:

from fers_core import (
    FERS, DistributedLoad, Material, Member, MemberPointLoad, MemberSet, NodalSupport, Node,
    ScissorHinge, Section,
)

model = FERS()
steel = Material(name="S355", e_mod=210e9, g_mod=81e9, density=7850.0, yield_stress=355e6)
frame = Section(name="frame", material=steel, i_y=4e-6, i_z=4e-6, j=1e-7, area=2e-3)
anchor, top_1, top_2 = Node(0.0, 0.0, 0.0), Node(2.0, 0.0, 0.0), Node(4.0, 0.0, 0.0)
foot_1, foot_2 = Node(2.0, -1.5, 0.0), Node(4.0, -1.5, 0.0)
anchor.nodal_support = foot_1.nodal_support = foot_2.nodal_support = NodalSupport()

rail_1 = Member(start_node=anchor, end_node=top_1, section=frame)
rail_2 = Member(start_node=top_1, end_node=top_2, section=frame)
model.add_member_set(MemberSet(
    members=[rail_1, rail_2],
    scissor_hinge=ScissorHinge(translational_release_x=0.0),  # slides along X
    scissor_hinge_nodes=[top_1],                               # over the first post only
))
for foot, top in ((foot_1, top_1), (foot_2, top_2)):
    model.add_member_set(MemberSet(members=[Member(start_node=foot, end_node=top, section=frame)]))

pull = model.create_load_case(name="pull")
DistributedLoad(member=rail_1, load_case=pull, magnitude=2000.0, end_magnitude=2000.0, direction=(0, -1, 0))
MemberPointLoad(member=rail_2, load_case=pull, position=0.5, magnitude=1000.0, direction=(1, 0, 0))
model.run_analysis()
reactions = model.resultsbundle.loadcases["pull"].reaction_nodes
takes = [abs(reactions[str(foot.id)].nodal_forces.fx) for foot in (foot_1, foot_2)]
print(f"post 1 takes {takes[0]:.1f} N, post 2 {takes[1]:.1f} N")  # 0.0 N and 67.2 N

使用剪刀铰的模型会以 schema_version 3 写入,因此低于 0.2.68 的求解器会拒绝该模型,而不会将每个连接都作为刚接来求解。不使用剪刀铰时,模型的写入方式与之前完全相同。 fers_core/examples/054_Scissor_Hinge_Rail_On_Beam.py 对比了刚接、构件铰链、剪刀铰和弹簧;fers_core/examples/055_Scissor_Hinge_Sliding_Rail_And_Release_Axes.py 则让导轨滑动,将铰链限制在一根立柱上,并使桁架在平面内转向。

全局轴或用户轴下的构件铰链(engine 0.2.68)

MemberHinge 会释放绕构件自身轴的转动。对于平面内倾斜、并通过铰接悬挂在主梁上的横梁,需要绕主梁的轴设置铰链;该轴在横梁自身的轴系中会同时包含弯曲和扭转。rotation_axes 用于指定旋转释放、其刚度曲线以及力矩–转角图所采用的轴:

from fers_core import MemberHinge, ReleaseAxes

pin_about_x = MemberHinge(rotational_release_mx=0.0, rotation_axes=ReleaseAxes.GLOBAL)

ReleaseAxes.LOCAL(默认值)保留构件轴;ReleaseAxes.user(x, y) 则像上文一样指定一个坐标系。此类铰链上的曲线读取同一轴系下的端弯矩;构件的结果仍使用其局部轴系。平移释放仍使用构件局部轴系。构件铰链在其他轴系下释放的模型同样会以 schema_version 3 写入。释放轴在空间中固定,因此共转求解不会让这些轴随结构转动。

构件自重

求解器会计算每个构件的自重,即密度·面积·g,并将其放入 self_weight_load_case_id 指定的荷载工况中。只有包含该荷载工况的荷载组合才会计入自重,因此请将其指定为永久荷载工况:create_load_case("G", self_weight=True) 会创建 G,并设置这两个选项。

from fers_core.builders import create_beam

model = create_beam(6.0, "IPE300", udl=5_000.0, load_case_name="Q")   # 5 kN/m imposed load
imposed = model.load_cases[0]
dead = model.create_load_case("G", self_weight=True)                   # G takes the self-weight
model.create_load_combination("ULS", {dead: 1.35, imposed: 1.5}, limit_state="ULS")
model.run_analysis()

uls = model.resultsbundle.loadcombinations["ULS"]
print(round(sum(r.nodal_forces.fy for r in uls.reaction_nodes.values()), 1))   # 48357.4

ULS 反力为 1.5 × 30 kN 加上梁自重 2.49 kN 的 1.35 倍。如果设置了 enable_self_weight,但没有设置 self_weight_load_case_id,求解器会将自重放入单独的荷载工况“Self-weight”中,而由你的荷载工况构建的荷载组合不会包含该工况。对于包含荷载组合的模型,run_analysis 会拒绝求解,而不是在不计自重的情况下逐一求解所有组合;求解器还会针对每个组合发出 self_weight_not_in_combinations 警告。没有荷载组合的模型可以求解,其“Self-weight”工况会作为单独的结果,如下所示。

求解器还会根据截面计算每个构件的质量,用于模态和地震分析。承载额外荷载(例如幕墙或管线)的构件可以覆盖这两项:

from fers_core.builders import create_beam

model = create_beam(5.0, "IPE180")                  # no load but its own weight
model.settings.analysis_options.enable_self_weight = True
for member in model.members:
    member.weight_override = 1500.0                 # N/m, gravity included
model.run_analysis()

self_weight = model.resultsbundle.loadcases["Self-weight"]
print(round(sum(r.nodal_forces.fy for r in self_weight.reaction_nodes.values()), 1))   # 7500.0

求解器会用 1500 N/m 替代截面的 184 N/m,并将 1500 / 9.81 kg/m 作为构件的质量。weight_override 使用模型的力/长度单位。Member.weight 则是另一回事:它是 SDK 计算的构件质量,即密度·面积·长度,不会发送给求解器。

地震分析

add_seismic_analysis 会请求求解器执行模态反应谱分析(默认)、侧向力法,或两者都执行。谱辅助函数会构建 EN 1998-1 设计谱。无论模型使用何种单位,加速度都以 m/s² 为单位,周期都以秒为单位;节点质量以 kg 为单位。

from fers_core import (
    FERS, Member, MemberSet, MaterialLibrary, NodalMass, NodalSupport, Node, Section,
    SeismicAnalysisSettings,
)

model = FERS()                                                    # first: it restarts the ids
steel = MaterialLibrary.S235()
column = Section.from_name("HEB200", steel)
nodes = [Node(0.0, float(height), 0.0) for height in range(7)]    # a 6 m mast
nodes[0].nodal_support = NodalSupport()                           # fixed base
model.add_member_set(MemberSet(members=[Member(a, b, section=column) for a, b in zip(nodes, nodes[1:])]))
model.add_nodal_mass(NodalMass(nodes[-1], 2000.0))                # 2 t of equipment on top

model.add_seismic_analysis(
    SeismicAnalysisSettings.eurocode_spectrum(ag=2.5, ground_type="C", q=1.5),
    directions=["X"],
    num_modes=4,
)
model.run_analysis()

x = model.resultsbundle.seismic["modal_response_spectrum"]["per_direction"][0]
print(f"{x['participating_mass_ratio']:.0%} of the mass, base shear {x['base_shear'] / 1000:.1f} kN")

mass_sources=[(load_case, psi)] 会将重力荷载工况转换为地震质量(永久荷载的 ψE = 1.0)。direct_spectrum 可直接指定 S、T_B、T_C 和 T_D,以符合国家附录的要求;custom_spectrum 则接受周期和加速度表。结果是 resultsbundle.seismic 中的普通字典,每种方法各有一项。fers_core/examples/151_Seismic_Response_Spectrum.py 会使用两种方法分析一个门式刚架。

利用率检查报告

求解器可以在结果中嵌入一份包含所有利用率检查的 HTML 报告:

from fers_core.builders import check_beam

model = check_beam(5.0, "IPE300", udl=10000.0)
model.settings.analysis_options.include_report_html = True
model.run_analysis()
html = model.unity_report_html()                    # a complete HTML page

render_unity_reports = True 还会根据每个已检查构件的 report_template 填充其 rendered_report,并将这些说明添加到 HTML 报告中。两者默认均为关闭,以减小结果体积。

高级版求解与超时

未提供 API 密钥时,求解器会按免费版限额运行(100 个构件),并且完全不会发起网络调用。免费版仅供非商业用途;无论模型大小,商业用途都需要 Pro 密钥。传入 api_key 可提高这些限额:

model.run_analysis(api_key=KEY)

使用密钥意味着每次求解前都会向 ferscloud.com 验证许可证,因此该握手有超时限制。如果许可证服务器在 license_timeout 秒内(默认 30 秒)没有响应,调用会在求解开始前抛出 FersTimeoutError,因此可以安全地重试:

import fers_calculations

try:
    model.run_analysis(api_key=KEY, license_timeout=60)
except fers_calculations.FersTimeoutError:
    ...   # transient stall — retry or skip this model
except RuntimeError:
    ...   # definitive — bad key, non-premium account, or a rejected model

将这两种情况分开处理正是关键:重试 RuntimeError 仍会得到相同的失败,而将两者都合并到 except Exception 中,则会把可重试的短暂故障变成结果丢失。完整的无人值守批量处理模式请参阅 fers_core/examples/203_Premium_Batch_Solve.py。

FersTimeoutError 是内置 TimeoutError 的子类,因此普通的 except TimeoutError 也能捕获它。

设置 FERS_LICENSE_TIMEOUT(秒)即可更改默认值,无需修改任何调用代码。它必须是真正的环境变量——引擎不会读取 .env 文件。

license_timeout 限制的是许可证握手时间,而不是求解时间。求解在原生代码中运行,没有可取消点,因此进程内超时无法中断求解。若要严格限制每个模型的总运行时间,请在可终止的子进程中运行每次求解——这样也能回收其内存。

需要引擎版本 fers_calculations >= 0.2.53。

从终端和编码 Agent 使用

pip install FERS 会在路径中添加 fers 命令:它是对上述对象和引擎的轻量封装,供 Shell、脚本、CI 作业和编码 Agent 使用。所有大型输出都会写入文件;标准输出每条命令只输出一行,或在使用 --json 时输出一个 JSON 对象。

fers validate model.json                 # the input schema: the failing field, and keys the engine ignores
fers solve model.json --out results.json # the engine streams the results to the file
fers check model.json                    # solve, print each unity check's utilization and a verdict
fers report results.json --out report.html
fers schema                              # the schema's definitions; `fers schema Member` prints one
fers sections get IPE200                 # catalogue properties in SI; `sections list` names the series
fers materials list
fers examples list                       # the numbered examples; `examples show 001`, `examples run 001`
fers skill --install .                   # the skill for coding agents, into .claude/skills
fers skill --zip fers-skill.zip          # the same skill as a zip to upload to Claude.ai
fers doctor                              # versions, and whether the key is accepted

fers solve 会将文件原样发送给引擎,并且只读取结果来生成摘要行:荷载工况、组合、记录的失败、未收敛的结果集和消息。即使模型会生成数百 MB 的内容,这个进程也不会因此增加负担。

退出代码表示是否可以使用这些数值:

代码含义
0已完成;对于 check,所有检查均通过
1引擎拒绝或未能完成求解
2输入错误:文件缺失、JSON 无效、模型不符合架构(使用 validate --strict 时,也包括架构不认识的键)
3许可证问题:需要密钥但未提供、密钥被拒绝、许可证超时
4已求解,但请勿使用这些数值:结果集存在错误(机构为 singular_model)、未收敛,或记录了失败。输出会说明原因。
5check:无法得出结论:某项检查的结果为 Indeterminate、部分组合尚未求解,或模型没有检查项
6check:某项检查失败(利用率大于 1)

如果一次 check 同时符合多个退出条件,将优先返回 4、6、5 中最先匹配的代码。使用 --json 时,solve 和 check 会添加 verdict 及其 reasons。出现 “DO NOT USE these results” 后会说明原因:求解器的首个错误,或荷载工况或组合在彻底失败时记录的错误。fers check 会指出每项检查的控制项,例如 governing member 4 (Column, HEA300) in "ULS 1" (combination 2);--json 会通过 governing_entity_label 和 governing_combination_name 返回相同信息。

如果未提供 --api-key,FERS_API_KEY 会用于提供 Premium 限制所需的密钥;没有密钥时,引擎会以 Free 层级限制运行,且不会进行网络调用,如上所述。--solve-token(或 FERS_SOLVE_TOKEN)则使用来自 FERS Cloud 的短期 Premium 令牌,引擎会离线验证该令牌:即使机器无法连接 ferscloud.com,也能使用 Premium 限制。在 Python 中,对应参数为 run_analysis(solve_token=...)。

对于 Agent,技能文件在一页中说明了完整工作循环:选择使用哪种方式(MCP 工具、Shell 或代码沙盒,或供 FERS Studio 使用的文件)、验证、求解、检查,以及会影响答案的约定;reference/api.md 中提供了可运行的模型。该技能随软件包一同发布(fers_core/skills/fers/);fers skill --install . 会将其复制到 .claude/skills/fers/,Claude Code 会在此查找;fers skill --zip fers-skill.zip 会将其打包,以便上传到 Claude.ai;fers skill --path 则会输出 SKILL.md 的路径,供其他 Agent 使用。MCP 客户端也可以改用本地 MCP 服务运行同一引擎(npx -y @ferscloud/fers-mcp,在用户的机器上进行求解);无法启动程序的客户端可使用托管服务 https://ferscloud.com/api/mcp,该服务会返回一个浏览器求解链接。各客户端的设置说明见:https://ferscloud.com/mcp。

fers_core/examples/811_Command_Line_Workflow.py 会端到端地运行整个工作循环。

许可证

本软件包 FERS 采用 BSD 3-Clause License(LICENSE)开源。

它安装的求解器引擎 fers_calculations 使用单独的许可证。从 0.2.70 版起,该引擎采用 PolyForm Strict License 1.0.0:任何非商业用途均可免费使用,例如学习、教学、研究和个人爱好项目。商业用途需要购买付费的 FERS Cloud 方案,其中包含商业许可证。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。