技能创建器
skill-creator
创建、改进和评估自定义技能,把专业流程转化为可重复使用的 AI 能力。
comsol【数据与研究】General-purpose COMSOL Multiphysics automation via the mph Python library and COMSOL Java API. Use when the user needs to: (1) Create COMSOL models programmatically (geometry, materials, physics, multiphysics, mesh, probes), (2) Set up and run parametric/time-domain/frequency studies, (3) Extract and export simulation results (probe data, field evalu…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Use the mph Python library to control COMSOL programmatically.
Always prefer the Java API bridge (model.java) over mph's Python wrapper
for physics/material/mesh/study/probe setup — the mph Python API is mainly for
parameters, geometry, saving, and solving.
On the first COMSOL-related task in a session, run from this skill directory:
node scripts/check-update.js
If the script is missing, fetch
https://raw.githubusercontent.com/2p1c/comsol-skills/master/skills/comsol/VERSION
and compare it with metadata.version in this file. If GitHub is newer, tell
the user:
FORCE_INSTALL=1 npm install @2p1c/comsol-ai@latest
# or
git clone https://github.com/2p1c/comsol-skills.git && cd comsol-skills && FORCE_INSTALL=1 node scripts/install-skill.js
Do not overwrite their installed skill files unless they ask.
Required:
pip install mph numpy pandasAuto-detection: mph finds COMSOL automatically via Windows registry (Windows),
default install paths, or the comsol command in PATH. No manual path config needed.
mph.discovery.backend() returns the install root.
If COMSOL not installed: mph.start() raises an error. Check:
C:\Program Files\COMSOL\COMSOL6*\Multiphysics\bin\win64\comsol.exe/usr/local/comsol*/multiphysics/bin/glnxa64/comsol/Applications/COMSOL*/Multiphysics/bin/maci64/comsolIf mph not installed: ModuleNotFoundError: No module named 'mph'
→ pip install mph
pip install mph numpy pandasmph.option('session', 'stand-alone') on Windows (fastest)client = mph.start(cores=N) → pymodel = client.create('Name')model = pymodel.javapymodel.save('file.mph')pymodel.solve()model.modelNode().create("comp1") # required BEFORE any geometry
model.geom().create("geom1", 3) # 3D geometry
model.geom("geom1").feature().create("blk1", "Block")
model.geom("geom1").feature("blk1").set("size", ["Lx", "Ly", "Lz"])
model.geom("geom1").run("fin")
comp = model.component("comp1") # now accessible
Critical: model.component("comp1") only works after modelNode().create("comp1").
Do NOT mix mph Python geometry creation with Java API — choose one path and stick to it.
model.param().set("Lx", "40 [mm]")
model.param().set("T_amb", "293.15 [K]")
mat = comp.material().create("mat1", "Common")
mat.label("My Material")
mat.selection().all()
grp = mat.propertyGroup("def")
grp.set("density", "2700 [kg/m^3]")
grp.set("youngsmodulus", "69e9 [Pa]")
grp.set("poissonsratio", "0.33")
grp.set("thermalconductivity", "167 [W/(m*K)]")
grp.set("heatcapacity", "896 [J/(kg*K)]")
grp.set("thermalexpansioncoefficient", "23.6e-6 [1/K]")
Critical: NEVER use mat.set(prop, val) — it will fail. ALWAYS go through
mat.propertyGroup("def").set(prop, val).
| Desired physics | Correct Java tag | Via |
|---|---|---|
| Heat Transfer in Solids | "HeatTransfer" | comp.physics().create("ht", "HeatTransfer", "geom1") |
| Solid Mechanics | "SolidMechanics" | comp.physics().create("solid", "SolidMechanics", "geom1") |
| Thermal Expansion (multiphysics) | "ThermalExpansion" | comp.multiphysics().create("te1", "ThermalExpansion", "geom1") |
Wrong tags that will FAIL: "HeatTransferInSolids", "HeatTransferSolids",
"StructuralMechanics", "Solid".
| Feature | Tag | Dimension |
|---|---|---|
| Boundary heat source (laser) | "BoundaryHeatSource" | 2 (3D boundary) |
| Low-reflecting boundary | "LowReflectingBoundary" | 2 (3D boundary) |
Wrong tags that will FAIL: "HeatFlux", "InwardHeatFlux", "AbsorbingBoundary".
Named selections via feature.selection().set("name") FAILS in client API.
Use .named("name") for mesh features or direct entity numbers for physics:
# Physics features: direct entity numbers (MUST use jpype.JArray)
bhs.selection().set(jpype.JArray(jpype.JInt, 1)([6])) # boundary 6 = top
# Mesh Size features: named selection via .named() (NOT .set())
size_fine.selection().named("sel_wave_region")
# Ball selection for mesh refinement (entitydim is STRING, radius is "r")
fine_sel = comp.selection().create("sel_wave_region", "Ball")
fine_sel.set("entitydim", "3") # string "3", not int 3!
fine_sel.set("posx", "x0")
fine_sel.set("posy", "y0")
fine_sel.set("posz", "Lz/2")
fine_sel.set("r", "r_coarse") # "r" not "radius"!
mesh = comp.mesh().create("mesh1")
mesh.feature("size").set("hmax", "h_coarse")
mesh.feature("size").set("hmin", "0.01 [mm]")
# Local refinement (Ball selection, entitydim string, r not radius)
fine = mesh.feature().create("size_fine", "Size")
fine.set("hmax", "h_fine")
fine_sel = comp.selection().create("sel_fine", "Ball")
fine_sel.set("entitydim", "3") # STRING!
fine_sel.set("posx", "x0")
fine_sel.set("posy", "y0")
fine_sel.set("posz", "Lz/2")
fine_sel.set("r", "r_coarse") # "r" NOT "radius"!
fine.selection().named("sel_fine") # .named() NOT .set()!
mesh.feature().create("ftet1", "FreeTet")
mesh.run()
CutPoint3D + res.numerical() does not work via the client API. Instead,
evaluate the full field and find nearest mesh nodes:
import numpy as np
# Get node coordinates at t=0
x0 = pymodel.evaluate("x", "mm") # (n_timesteps, n_nodes)
coords = np.column_stack([x0[0], pymodel.evaluate("y","mm")[0], pymodel.evaluate("z","mm")[0]])
# Full displacement field
w_all = pymodel.evaluate("w", "mm") # (n_timesteps, n_nodes)
# Nearest-node lookup for each target point
for pt in target_points:
dists = np.linalg.norm(coords - [pt["x"], pt["y"], pt["z"]], axis=1)
nearest = np.argmin(dists)
time_series = w_all[:, nearest] # extract this node's time series
study = model.study().create("std1")
step = study.feature().create("time", "Transient")
step.set("tlist", "range(0, 5e-9, 10e-6)")
step.set("rtol", "1e-5")
The comp.multiphysics().create("te1","ThermalExpansion") node cannot be
configured via the client API (its ThermalExpansionModel sub-feature
reports "Unknown feature ID"). Instead, add ThermalExpansion as a
sub-feature of the Solid Mechanics Linear Elastic Material node:
lemm = solid.feature("lemm1") # Linear Elastic Material
tef = lemm.feature().create("tef1", "ThermalExpansion")
tef.set("Tref", "T_amb") # reference temperature
tef.set("alpha", "23.6e-6 [1/K]") # CTE
Gaussian expressions like exp(-r²/(2*sigma_s²)) evaluate to zero globally
if the denominator D = 2*sigma_s² is ≤ 1e-5. At far mesh nodes exp(-r²/D)
underflows to 0, and COMSOL then zeros the entire expression.
Rule: Ensure D > 5e-5 (sigma_s ≥ 5 mm). For a plate of size X by Y,
the max distance from center is r_max = sqrt((X/2)²+(Y/2)²), and you need
r_max²/D < ~40 to avoid underflow.
Also broken via API: max(), min(), if(), and spatial comparisons like
((x-x0)² < 2*sigma_s²) — all evaluate to 0 on boundaries.
comsolbatch does NOT save solution datasets to the output .mph file,
making post-solve data extraction impossible. Use pymodel.solve() instead.
mph stand-alone mode runs in a headless JVM (no Swing GUI), so the
COMSOL progress window won't appear. For progress monitoring during long
solves, use --build-only first, then solve interactively in COMSOL Desktop.
python laser_ultrasound_model.py --build-only
pymodel.solve() # blocking, solution data preserved
pymodel.save("name.mph") fails if the filename was used earlier in the
same session. Save with a UUID suffix:
import uuid
pre_solve_name = f"_pre_solve_{uuid.uuid4().hex[:8]}.mph"
pymodel.save(pre_solve_name)
Output looks like:
Time-step 1, Nonlinear iterations: 2, Convergence: 1.2e-6
Time-step 2, Nonlinear iterations: 1, Convergence: 8.3e-7
...
client = mph.start(cores=4)
model = client.load(str(output_dir / "solved_model.mph"))
# Then use CutPoint3D / result().numerical() to extract data
Open the .mph in COMSOL Desktop → Study → Compute. Full progress window
with convergence plots, real-time probe graphs, and solver log.
mph.start() stand-alone fails → switch to mph.option('session', 'client-server')references/api-reference.md for verified tagsmat.set() fails → MUST use mat.propertyGroup("def").set(prop, val)model.component("comp1") fails → modelNode().create("comp1") must be called firstset("entitydim", 2) ambiguous → pass as string: set("entitydim", "2")set("radius", ...) unknown → use "r" not "radius"selection().set("name") → use .named("name") for named, .set(JArray([N])) for numsBoxSelection / BallSelection unknown → drop Selection suffix: "Box", "Ball"DomainPointProbe fails → use result().dataset().create("cpt", "CutPoint3D") post-solveselection().all() on free1 → Free BC is read-only, skip itW/m^2, --, ---build-only, then open the .mph in COMSOL Desktoppymodel.solve() stdout, or use COMSOL Desktopcomsolbatch drops solution datasets — do not use it if you need to extract resultsAfter every debugging session where new COMSOL API behavior is discovered,
update references/debugging-log.md with:
Also update references/api-reference.md if new tags are discovered or old ones
are found to be version-specific.
references/api-reference.md — complete tag reference for physics, features, materialsreferences/laser-ultrasound.md — worked example: laser-induced guided wavesreferences/debugging-log.md — accumulated debugging experiencescripts/laser_ultrasound_model.py — copy of the working simulation scriptscripts/plot_results.py — visualization scriptscripts/animate_results.py — wave-field animation from a solved .mphscripts/check-update.js — compare local VERSION with GitHub (notify only)