Download Nabla
Nabla User Manual 0.1.0

Automation

Automation

Driving Nabla headlessly from a Python script, or conversationally through an MCP-connected LLM.

Both paths sit on the same Python API and the same in-process JVM as the GUI — a script or an MCP tool call performs the same model mutation the corresponding GUI action would, and can mesh, solve and read results exactly as the GUI does. Neither needs the GUI running; a headless run needs only the bundled solver and the model files it produces.

The Python API

A packaged Nabla install ships a one-time setup script — setup_api.bat (Windows) or setup_api.sh (Linux) in the Nabla folder — that creates a private Python environment and installs the API into it (Python 3.10+ on the system is the only prerequisite). From then on, run any script with the environment's own interpreter, from anywhere, with no PYTHONPATH to set:

# Windows
<NablaDir>\nabla-python.bat  my_script.py

# Linux
<NablaDir>/nabla-python      my_script.py

Scripts mesh with Triangle too. The API uses the same mesh generator the GUI does, looked up in the same four places (see Getting started — Installing), so if you used the installer's Download Triangle step the API already has it. The setup script says which copy it found; if it finds none — the usual case when the API was installed on its own, without the desktop installer — it explains where Triangle comes from and offers to download it for you, and does so only if you say yes. Answer no and everything except generateMesh() still works; re-run the setup script (or pass --triangle download) whenever you want it.

A script looks like this — the same geometry/regions/BCs/coils/mesh/solve sequence as a GUI session, just written down instead of clicked through:

from nabla_api import Nabla

model = Nabla("my_model")
model.start()                      # boots the bundled JVM - no JDK needed
model.createNewModel("my_model")
# ... geometry / regions / BCs / coils ...
model.generateMesh()
model.saveModel()
model.run_solver_with_monitor()

Model and result files are written next to wherever you run the script from; call setWorkingDirectory(...) to choose a different location. Runnable examples ship under API/examples/ in the install — start with linear_01.py. Because nabla-python is a plain virtual environment, ordinary tooling works unmodified: nabla-python -m pip install scipy pandas to add a library, or registering it as a Jupyter kernel for interactive work.

A fuller example: build, mesh, solve, extract

The snippet above stops at run_solver_with_monitor(). A complete script normally continues into reading a result, matching the mesh → solve → post-process shape of a GUI session:

from nabla_api import Nabla

model = Nabla("my_pmsm")
model.start()

# Build a machine the same way New Machine would.
model.machinesModuleToggle(True)           # must precede createNewMachine
model.createNewMachine("PMSM")
model.setMachineGeometry("stator", "slots", 12)
model.setMachineGeometry("stator", "OD", 90)
model.setMachineGeometry("stator", "ID", 55)
model.setMachineGeometry("rotor", "OD", 53)
model.setWindingType("Concentrated", layers="Double")
model.setTurnsPerCoil(24)

model.generateMesh()
model.saveModel()
result = model.run_solver_with_monitor()

if not result.get("converged", True):
    print(model.getLogTail(50))            # see what the solver actually complained about
    raise RuntimeError("solve did not converge")

torque = model.getMachineTorque()
phase_a_flux = model.getResultsInCoil("PhaseA", "FluxLinkage")
print(f"torque={torque}, phase-A flux linkage={phase_a_flux}")

model.stop()

This mirrors the MCP tool sequence in the next section almost one-to-one — each Python call here has a corresponding mcp__nabla__* tool with a similar name and the same parameters, because both sides wrap the same underlying Java calls.

The MCP server

The same API is exposed as an MCP (Model Context Protocol) server, so any MCP-capable LLM client can drive Nabla conversationally: you describe what you want in natural language, the LLM calls MCP tools, the tools call the same Python API a script would. A packaged install ships a nabla-mcp launcher; point your client's MCP configuration at it (or, from a repo checkout, run python -m nabla_mcp with the API on PYTHONPATH). The server holds one model session per process — open a new client connection to start a fresh model.

The tool surface is organised into packs so a client is not shown every tool at once: six core packs (model, geometry, regions, coils, simulation, results) are always loaded, and four extension packs (machine, materials, thermal, performance) load on demand — automatically the moment you open or create a machine model, or explicitly by asking to enable one. Only a pack your licence actually grants is ever registered, so an LLM never plans around a tool call that would just refuse.

What each pack contains

MCP tool packs
PackLoadedCoversExample tools
modelalwayssession, model lifecycle, inspection, tool packs new_model, open_model, save_model, get_model_state, nabla_status, list_tool_packs
geometryalwaysshapes, selection, transforms, DXF create_line, create_arc, select_in_box, mirror_selection, import_dxf
regionsalwaysregions, holes, region properties, materials, BCs create_region, set_region_material, assign_boundary_condition, list_materials
coilsalwayscoils, coil-to-region assignment, external circuit define_coil, assign_coil_to_region, add_circuit_element
simulationalwaysmeshing, units, solver configuration, motion, running the solver generate_mesh, configure_solver, add_circular_motion, run_solver
resultsalwaysfield probes, forces, region/coil/motion/circuit results, exports get_field_at_point, get_region_result, get_force, export_results
machineon demand
Machines
parametric PMSM/IM design, winding, operating point, machine results, PDF report create_machine, set_machine_geometry, set_winding_type, get_machine_waveform, create_report
materialson demand (free)material library and editor list_materials, get_material_curve, set_material_curve
thermalon demand
Thermal
thermal module: properties, heat sources, thermal solver enable_thermal, set_thermal_region_property, define_heat_source, run_thermal_solver
performanceon demand
Performance
performance analysis: curves, efficiency/loss maps, breakdowns get_im_equivalent_circuit, get_performance_curve, get_performance_map, get_loss_breakdown

Call nabla_status at any point to see which packs are loaded, which are available but not yet loaded, and which are hidden by your licence; call enable_tool_pack to load one mid-session — the client is told the tool list changed and re-reads it, so a newly loaded pack's tools become callable without restarting the session.

A conversational example

An MCP session looks like an ordinary conversation with the tool calls happening in between; you do not write MCP JSON yourself. For a prompt like “model a 12-slot 10-pole PMSM, 90 mm stator OD, mesh it and check the no-load back-EMF”, a client typically issues a call sequence like:

set_machine_type(machine_type="PMSM")
set_machine_geometry(part="stator", parameter="slots", value=12)
set_machine_geometry(part="stator", parameter="OD", value=90)
apply_slot_pole_combination(slots=12, poles=10)
set_winding_type(winding_type="Concentrated", layers="Double")
generate_mesh()
run_solver()
get_machine_waveform(quantity="BackEMF")

Each call returns a small JSON result — the applied value, any clamping, and warnings — which is why a model rarely needs to call get_model_state after every step: the previous tool's own response usually already says whether the change took.

Sessions and error handling

Both a Python script and an MCP server process hold exactly one model per process — there is no multi-model juggling inside a single session. Starting a new model (new_model / createNewModel) or opening one (open_model / openModel) replaces whatever was loaded, and both refuse to discard unsaved changes unless told to proceed anyway. Call set_working_directory / setWorkingDirectory early if you want model and result files to land somewhere other than the current directory.

A tool or API call that fails raises rather than silently doing nothing: an unknown region, coil, shape or material name, or an unknown property name, fails at the call boundary. When a call succeeds but the model behaved unexpectedly — a mesh that looks wrong, a solve that reports non-convergence — get_log_tail / getLogTail is the first thing to read: it surfaces the Java layer's own diagnostics, which are usually more specific than the exception message alone.

How licensing behaves in Python

The Python and MCP layers report your entitlements rather than enforcing them a second time — Python is the most editable layer in the stack, so a licence check written there would be a comment, not a gate. The real gates stay in Java and in the solver binary; Python asks Java for the answer and behaves accordingly:

Every gate here mirrors the GUI's own licensing rules one-to-one: what a script or an LLM session can do never depends on which door you walked in through — see Troubleshooting — licence errors for what a refusal means and Help → Licence... for the state of your own copy.

Next steps