API reference
Every public method of the Nabla class, with signatures,
parameters, return values, and a runnable example of each.
The Nabla class is the single entry point for scripting — see
Automation for how to start a session. Methods that require a
licence beyond the free tier carry the corresponding badge; an unlicensed call raises
NablaLicenseError with a message naming the missing entitlement. Every example below
assumes a model = Nabla(...) instance that has already had start()
called on it.
Session & licensing
Nabla(modelName)
Create a Nabla model handle. Does not start the JVM; call start() afterwards.
| modelName | str | Name used as the default model file name. Characters invalid in a file name are replaced with underscores. |
|---|
from nabla_api import Nabla
model = Nabla("my_motor")
start()
Boot the bundled JVM, load the Java classes, and connect to the Automation API. This is the one hard gate in the Python layer: without the Automation entitlement it raises NablaLicenseError. Must be called before any other method except license_status.
Raises: NablaLicenseError when the Automation module is not licensed.
model = Nabla("my_motor")
model.start()
stop()
Shut down the JVM and release all resources. The model is not saved automatically.
model.saveModel() # stop() does not save for you
model.stop()
license_status(refresh=False) → dict
Return the licensing status behind this session.
| refresh | bool | If True, re-read from Java instead of using the cached value from start(). |
|---|
Returns: a dict with keys state (licensed/grace/free/viewer), edition, licensee, features (list of strings), expires (ISO-8601 or None), maxNodes, and reason. Does not start the JVM — safe to call before start().
status = model.license_status()
print(status["state"], status["features"])
has_feature(feature) → bool
Check whether this installation is entitled to a specific capability.
| feature | str | One of "core", "core_pro", "machines", "perf", "thermal", "automation", "materials_pro". |
|---|
When the licence status cannot be read, answers True (permissive) — the Java layer and the solver are the real gates.
if model.has_feature("thermal"):
model.setThermalEnabled(True)
else:
print("Thermal module not licensed - skipping heat-conduction pass")
getLogTail(lines=50) → list[str]
Return the tail of the Java model log buffer, oldest first. Each entry is a diagnostic, rejection, or warning emitted by the Java layer. Use it to understand why a call silently did nothing.
| lines | int | How many trailing entries to return. 0 or negative returns all. |
|---|
exit_code = model.runSolver()
if exit_code != 0:
print("\n".join(model.getLogTail(50)))
Model lifecycle
createNewModel(modelName)
Create a new, empty model. This is the starting point for building geometry from scratch.
| modelName | str | Name for the model; invalid filename characters are replaced with _. The project directory defaults to the script's location unless overridden. |
|---|
model.createNewModel("my_motor")
openModel(modelName)
Open an existing model from disk. Supports both bare names (resolved against the project directory) and full paths without the .nbl extension. Models saved by earlier versions (.json, .ujsn) open too.
| modelName | str | Bare name or absolute path. A bare name is detected by the absence of a path separator. |
|---|
model.openModel("my_motor") # resolved against the project directory
model.openModel("C:/NablaProjects/other/motor2") # absolute path, no .nbl extension
saveModel()
Save the current model to disk (.nbl + .dxf). Also called internally by generateMesh() and the solver pipeline.
model.saveModel()
clearModel()
Clear the model (Model → Clear Model). Resets geometry, regions, holes, BCs, coils, motions, and results while keeping the model name and project directory.
model.clearModel() # start the same model file over from a blank canvas
setProjectDir(projectDir)
Set the project directory without creating or reopening a model. Model files are written here; the path receives a trailing separator if missing. After this call the directory is locked in — createNewModel/openModel no longer default it to the script location.
model.setProjectDir("C:/NablaProjects/motor1")
model.createNewModel("my_motor") # written under C:/NablaProjects/motor1
setWorkingDirectory(workingDir)
Set the project directory where model and result files are written. The directory must exist.
| workingDir | str | Path to an existing directory. The model's .dxf path is recomputed under it. |
|---|
Raises: FileNotFoundError if the directory does not exist.
model.setWorkingDirectory("C:/NablaProjects/motor1")
Geometry
line(x1, y1, x2, y2)
Create a straight line segment. Coordinates are in the model length unit (default mm).
model.line(0.0, 0.0, 50.0, 0.0)
arc(x, y, radius, start_angle, extent_angle)
Create a circular arc.
| x, y | float | Arc centre. |
|---|---|---|
| radius | float | Arc radius. |
| start_angle | float | Start angle in degrees, CCW from +X. |
| extent_angle | float | Angular sweep in degrees (CCW positive). The arc ends at start_angle + extent_angle. |
model.arc(0.0, 0.0, 25.0, 0.0, 90.0) # quarter circle, centre origin, radius 25
circle(cx, cy, radius)
Create a full circle.
model.circle(0.0, 0.0, 10.0) # e.g. the shaft outline
Selection
Transforms (mirror, translate, rotate, array, delete) act on the current selection. Select shapes first, then apply the transform.
selectAt(x, y)
Replace the current selection with the single shape at the given point.
model.selectAt(12.5, 0.0)
selectIn(x1, y1, x2, y2)
Select all edges whose endpoints fall inside the axis-aligned rectangle defined by two opposite corners. Order of the corners does not matter.
model.selectIn(-10.0, -10.0, 10.0, 10.0)
selectShape(shapesName)
Replace the current selection with the named shape(s).
| shapesName | str | list[str] | A shape name or a list of names. Names not found in the model are skipped. |
|---|
model.selectShape("Line_1")
model.selectShape(["Line_1", "Arc_2"])
selectInSector(cx, cy, radius, startAngle, extentAngle)
Select shapes inside an annular sector.
| cx, cy | float | Sector centre. |
|---|---|---|
| radius | float | Outer radius. |
| startAngle | float | Start angle in degrees, CCW from +X. |
| extentAngle | float | Angular width in degrees. The sector spans startAngle to startAngle + extentAngle. |
model.selectInSector(0.0, 0.0, 30.0, 0.0, 45.0) # one 45-degree wedge
selectAddIn(x, y)
Add the shape at the given point to the current selection (extends it instead of replacing).
model.selectAt(0.0, 25.0)
model.selectAddIn(15.0, 3.0) # selection now has two shapes
invertSelectedShapes()
Invert the selection: select all unselected shapes and deselect the currently selected ones.
model.selectShape("Slot_1")
model.invertSelectedShapes() # now everything except Slot_1 is selected
Transforms
mirror(mirroCopy, x1, y1, x2, y2)
Mirror the selected shapes across a line defined by two points.
| mirroCopy | bool | True: keep originals and add mirrored copies. False: move originals to their mirrored position. |
|---|---|---|
| x1, y1, x2, y2 | float | Two points defining the mirror line. |
model.selectIn(-10.0, -10.0, 10.0, 10.0)
model.mirror(True, 0.0, 0.0, 0.0, 1.0) # mirror across the Y axis, keep originals
translate(xOffset, yOffset)
Translate the selected shapes by an offset vector.
model.selectShape("Slot_1")
model.translate(5.0, 0.0)
rotate(angle, cX, cY)
Rotate the selected shapes about a centre point.
| angle | float | Rotation angle in degrees, CCW positive. |
|---|---|---|
| cX, cY | float | Centre of rotation. |
model.selectShape("Magnet_1")
model.rotate(30.0, 0.0, 0.0)
createArray(rows, cols, xOffset, yOffset)
Duplicate the selected shapes into a rectangular grid.
| rows, cols | int | Number of copies in each direction (including the original). |
|---|---|---|
| xOffset, yOffset | float | Spacing between adjacent copies. |
model.selectShape("Vent_1")
model.createArray(3, 2, 20.0, 15.0) # a 3x2 grid of vents
createCircularArray(items, span, cX, cY)
Duplicate the selected shapes in a circular pattern around a centre.
| items | int | Total number of copies (including the original). |
|---|---|---|
| span | float | Total angular span in degrees over which copies are spread. |
| cX, cY | float | Centre of the circular pattern. |
model.selectShape("Slot_1")
model.createCircularArray(12, 360.0, 0.0, 0.0) # 12 slots around the origin
deleteSelectedShapes()
Delete the currently selected shapes from the model.
model.selectShape("ConstructionLine_1")
model.deleteSelectedShapes()
Regions & holes
createRegion(x, y, meshMaxArea=0.0, regionName="")
Mark a closed area as a meshable region, seeded at an interior point.
| x, y | float | A point inside the closed area. |
|---|---|---|
| meshMaxArea | float | Maximum triangle area for this region. 0 uses the global default. |
| regionName | str | Name used to assign material, coil, properties, and read results. |
model.createRegion(0.0, 12.5, meshMaxArea=2.0, regionName="Rotor")
createHole(x, y, holeName="")
Mark a closed area as a hole (excluded from the mesh), seeded at an interior point.
| x, y | float | A point inside the area to exclude. |
|---|---|---|
| holeName | str | Name for the hole. |
model.createHole(0.0, 0.0, holeName="Shaft")
assignRegionProperties(regionName, propName, propValue=0.0)
Assign a physical or meshing property to a region. Unknown region names are ignored.
| regionName | str | Target region. |
|---|---|---|
| propName | str | Property name: "Jz" (MA/m²), "sigma" (S/m), "mu", "mu_ortho", "mu_norm", "mu_angle", "M" (A/m), "DOM" (deg), "rhoM" (kg/m³), "mesh_max_area", "EnableAnisotropy", "DisableAnisotropy", "EnableIronLoss", "DisableIronLoss", "IronLoss_Kh", "IronLoss_alpha", "IronLoss_Ke", "IronLoss_FitCurve". |
| propValue | float | Numeric value; ignored for toggle properties. |
Note: "Jz" is in mega-amps per square metre (MA/m²) — matching the GUI field. The solver multiplies by 1e6 internally, so Jz=1.0 means 1×10&sup6; A/m².
model.assignRegionProperties("Magnet_1", "M", 900000.0) # magnetization, A/m
model.assignRegionProperties("Magnet_1", "DOM", 90.0) # direction of magnetization, deg
model.assignRegionProperties("StatorCore", "EnableIronLoss")
setAnisotropicMu(regionName, mu_ortho, mu_norm, mu_angle=0.0)
Convenience wrapper that enables anisotropic (laminated-core) permeability on a region in one call. Sets the two principal permeabilities and the direction angle, then switches the region to the anisotropic tensor. Applies only to linear regions; a nonlinear BH-curve material overrides it.
| mu_ortho | float | Relative permeability along the ortho principal axis. |
|---|---|---|
| mu_norm | float | Relative permeability along the norm principal axis. |
| mu_angle | float | Angle in degrees from global X to the mu_ortho axis. Default 0. |
model.setAnisotropicMu("StatorCore", mu_ortho=8000.0, mu_norm=2000.0, mu_angle=0.0)
renameRegion(oldName, newName)
Rename an existing region. Unknown oldName values are ignored.
model.renameRegion("Region_1", "Rotor")
getValidNames(category) → list[str]
Return the valid names for a string-keyed setter/getter, from Java. Useful for validating parameter names before calling methods that accept them.
| category | str | One of "region_property", "stator_geometry", "rotor_geometry", "machine_sim_parameter", "machine_parameter", "performance_input". |
|---|
print(model.getValidNames("stator_geometry"))
Library materials
getAvailableMaterials()
Load the materials library into the model so setRegionMaterial can resolve names. Called automatically by setRegionMaterial on first use.
model.getAvailableMaterials() # rarely needed directly - setRegionMaterial calls it for you
setRegionMaterial(regionName, materialName)
Assign a library material to a region. Loads the materials library automatically if needed.
| regionName | str | Target region. |
|---|---|---|
| materialName | str | Material name as it appears in the library (e.g. "M19_29Ga", "N42"). |
model.setRegionMaterial("StatorCore", "M19_29Ga")
model.setRegionMaterial("Magnet_1", "N42")
Boundary conditions
assignBC(bc_type, value, bcName, motion=None)
Assign a boundary condition to the currently selected edges. See Boundary conditions for the conceptual guide.
| bc_type | int | Boundary-condition type: 1–11 (magnetic), 20–22 (thermal). See below. |
|---|---|---|
| value | list[float] | Numeric values whose meaning depends on bc_type. A ValueError is raised on wrong length. |
| bcName | str | Name assigned to this boundary condition. |
| motion | str | None | For sliding-band types (5/6/9/10): the name of the motion this ring belongs to. Required once the model has more than one motion. |
Magnetic BC types:
| Type | Name | value |
|---|---|---|
| 1 | Dirichlet | [A] |
| 2 | Neumann | [dA/dn] |
| 3 | Periodic (circular) | [angle, x, y, secondary_marker] |
| 4 | Anti-periodic (circular) | [angle, x, y, slave_marker] |
| 5 | Periodic (arc sliding band) | [band_index, _, _, moving_flag] |
| 6 | Anti-periodic (arc sliding band) | [band_index, _, _, moving_flag] |
| 7 | Parallel-periodic | [direction_deg, displacement, secondary_marker] |
| 8 | Parallel-anti-periodic | [direction_deg, displacement, secondary_marker] |
| 9 | Periodic (line sliding band) | [band_index, moving_flag] |
| 10 | Anti-periodic (line sliding band) | [band_index, moving_flag] |
| 11 | Far-field (balloon) | [n, centre_x, centre_y, R_override?] — Core Pro |
Thermal BC types: (require setThermalEnabled(True))
| Type | Name | value |
|---|---|---|
| 20 | Fixed temperature | [T_degC] |
| 21 | Heat flux | [q_W_per_m2] (positive = into domain) |
| 22 | Convection (Robin) | [h_W_per_m2K, T_inf_degC] |
model.selectShape("OuterBoundary")
model.assignBC(1, [0.0], "Outer_A0") # Dirichlet, A = 0
model.selectShape("Band_Rotor")
model.assignBC(5, [1, 0, 0, 1], "Band_Rotor_BC", motion="RotorSpin") # arc sliding band, moving ring
model.selectShape("HousingOuterEdge")
model.assignBC(22, [15.0, 25.0], "Housing_Convection") # h = 15 W/m2K, ambient 25C
Coils & circuits
defineCoil(name, nTurns, currentType, isEddyCurrentEnabled=False, param=[0.0])
Define a new coil and its excitation current.
| name | str | Coil name (used by assignCoilToRegion and addToCircuit). |
|---|---|---|
| nTurns | int | Number of series turns in the coil (≥ 1). |
| currentType | str | "DC", "AC", "PWM", or "Custom". See below. |
| isEddyCurrentEnabled | bool | Enable eddy-current modelling in the coil conductor. |
| param | list | Waveform parameters; meaning depends on currentType. |
Waveform parameters:
| currentType | param |
|---|---|
"DC" | [iDC] |
"AC" | [amplitude, frequency_Hz, phase_deg, offset] |
"PWM" | [amplitude, frequency_Hz, pulseWidth_pct, offset] |
"Custom" | [[t0, i0], [t1, i1], …] — strictly time-increasing pairs |
model.defineCoil("PhaseA", nTurns=40, currentType="AC", param=[5.0, 50.0, 0.0, 0.0])
model.defineCoil("FieldWinding", nTurns=200, currentType="DC", param=[2.0])
assignCoilToRegion(regionName, coilName, currentDirection=1)
Assign a coil to a region. A coil may be assigned to several regions to form its go/return paths.
| regionName | str | Target region. |
|---|---|---|
| coilName | str | Name from defineCoil. |
| currentDirection | int | +1 for +Z (out of page), −1 for −Z (into page). |
model.assignCoilToRegion("Slot_1_Go", "PhaseA", currentDirection=1)
model.assignCoilToRegion("Slot_7_Return", "PhaseA", currentDirection=-1)
addToCircuit(type, name="", values=None)
Place a component on the external circuit schematic.
| type | str | Component type: "coil", "R", "L", "C", "VS", "CS", "VM", "SW", "GND", "wire". |
|---|---|---|
| name | str | Component name. For "coil" it must match an existing coil. |
| values | list[float] | Placement: [x, y, angle_deg] for single components, a flat polyline [x0, y0, x1, y1, …] for "wire". |
model.addToCircuit("coil", "PhaseA", [0.0, 0.0, 0.0])
model.addToCircuit("R", "R_ext", [20.0, 0.0, 0.0])
model.addToCircuit("wire", values=[0.0, 0.0, 20.0, 0.0])
Motion
addMotion(motionName, motion_type, params, motionPoints)
Define a moving (rotating or translating) part. Several motions may be defined and enabled at once Core Pro (the first is Core).
| motionName | str | Name used by enableMotion and result queries. |
|---|---|---|
| motion_type | str | "Linear" or "Circular". |
| params | list[float] | Zone geometry: [refX, refY, boxWidth, boxHeight, direction_deg] for Linear; [cX, cY, outerRadius, innerRadius?] for Circular. |
| motionPoints | list | Strictly time-increasing [time, speed] pairs. Speed for Linear, angular speed for Circular. |
model.addMotion("RotorSpin", "Circular",
[0.0, 0.0, 30.0, 0.0], # zone: centre (0,0), outer radius 30
[[0.0, 1500.0], [1.0, 1500.0]]) # constant 1500 RPM
enableMotion(motionName)
Enable a motion and bind the regions inside its zone to it. Other motions keep their state.
model.enableMotion("RotorSpin")
disableMotion(motionName)
Disable one motion and release its regions. Other motions are untouched.
model.disableMotion("RotorSpin")
disableAllMotions()
Disable every motion and clear all region bindings.
model.disableAllMotions()
setMotionInitialPosition(motionName, value)
Set where a motion starts at t = 0. The unit depends on the motion kind: degrees for Circular, model length units for Linear. Default 0.
model.setMotionInitialPosition("RotorSpin", 7.5) # start 7.5 degrees offset
listMotions() → list[dict]
Return every motion in the model with its zone geometry, enabled state, initial position, band indices, and bound regions.
for m in model.listMotions():
print(m["motionName"], m["enabled"], m["initialPosition"])
Meshing
generateMesh()
Save the model and generate the triangular mesh. Raises RuntimeError if the mesher refused — the previous mesh (if any) is left untouched. Call before solving.
model.generateMesh()
setDefaultMeshSize(defaultMeshSize)
Set the global default maximum element size (model length unit). Regions with their own mesh_max_area override this.
model.setDefaultMeshSize(1.5)
subdivideShape(method, param)
Subdivide the selected shapes for meshing. Select shapes first.
| method | str | "Number of Divisions" (param = integer count) or "Minimum Edge Length" (param = target length). |
|---|---|---|
| param | float | Interpreted according to method. |
model.selectShape("AirGap_Arc")
model.subdivideShape("Number of Divisions", 120)
setSubdivisionProperties(param)
Control which shapes the mesher subdivides.
| param | str | "no_subdivision", "subdiv_ext_shapes" (default), or "subdiv_all_shapes". |
|---|
model.setSubdivisionProperties("subdiv_all_shapes")
Solver configuration
setUnits(lengthUnit=None, timeUnit=None) → tuple
Set the model's length and/or time unit without touching other solver settings. Does not rescale existing geometry.
| lengthUnit | str | None | "mm", "cm", or "m". None keeps the current unit. |
|---|---|---|
| timeUnit | str | None | "s", "ms", "us", or "ns". None keeps the current unit. |
Returns: (lengthUnit, timeUnit) as they now stand.
length_unit, time_unit = model.setUnits(lengthUnit="mm", timeUnit="ms")
defineAxialLength(axialLength)
Define the axial (out-of-plane) length of the 2D model. Used to scale field results to 3D quantities (force, torque, flux, losses). Not used in axisymmetric mode.
model.defineAxialLength(65.0) # mm
solverSettings(solverType="SimplicialLDLT", simulationType="Static", timeUnit="s", lengthUnit="mm", solverOptions=[False, False, False], solverParameters=[0.0, 0.0, 1, 1e-3, 100, 500, 1e-6])
Configure the solver type, simulation type, units, and parameters in one call.
| solverType | str | "SimplicialLDLT" or "Conjugate Gradient". |
|---|---|---|
| simulationType | str | "Static", "Transient", or "Time-Harmonic" Core Pro. |
| timeUnit | str | "s", "ms", "us", "ns". |
| lengthUnit | str | "mm", "cm", "m". |
| solverOptions | list[bool] | [isSaturationEnabled, isEddyCurrentEnabled, isCircuitSolverEnabled]. |
| solverParameters | list[float] | [timeStep, timeEnd, polyOrder, tolerance, maxIter, CG_maxIter, CG_tolerance]. polyOrder=2 needs Core Pro. |
model.solverSettings(
solverType="SimplicialLDLT",
simulationType="Transient",
solverOptions=[True, True, True], # saturation, eddy current, circuit solver
solverParameters=[1e-4, 0.02, 1, 1e-3, 100, 500, 1e-6])
setProblemType(problemType="Planar 2D")
Select the 2D formulation. Must be set before meshing/solving.
| problemType | str | int | "Planar 2D" (0) or "Axisymmetric 2D" (1). |
|---|
model.setProblemType("Axisymmetric 2D")
setTimeIntegration(scheme="Backward Euler", cnStartupSteps=2)
Choose the transient time-integration scheme.
| scheme | str | "Backward Euler" (1st order, default) or "Crank-Nicolson" (2nd order) Core Pro. |
|---|---|---|
| cnStartupSteps | int | Leading steps forced to Backward Euler for Crank-Nicolson (Rannacher startup). |
model.setTimeIntegration("Crank-Nicolson", cnStartupSteps=2)
setTransientPeriods(simulatedPeriods, stepsPerPeriod, frequencyHz)
Express the transient window in electrical periods. Recomputes timeStep and timeEnd from the frequency.
| simulatedPeriods | int | Number of electrical periods (≥ 1). |
|---|---|---|
| stepsPerPeriod | int | Time steps per period (≥ 3). |
| frequencyHz | float | Electrical frequency in Hz (> 0). |
model.setTransientPeriods(simulatedPeriods=4, stepsPerPeriod=60, frequencyHz=50.0)
Solving
runChecklist() → list[str]
Run the pre-solve model checklist. Returns a list of problem descriptions; empty means the model is ready to solve.
issues = model.runChecklist()
if issues:
print("\n".join(issues))
else:
model.generateMesh()
runSolver() → int
Run a single solver pass and load the results. For machine models with back-EMF or Ld/Lq stages, prefer run_solver_with_monitor() which runs the full staged pipeline.
Returns: solver exit code (0 = success).
Raises: NablaLicenseError if the solver exits 3 (licence refusal) or the mesh exceeds the node cap.
exit_code = model.runSolver()
if exit_code != 0:
print(model.getLogTail(50))
run_solver_with_monitor(observer=NULL_OBSERVER) → dict
Run the full solver pipeline (back-EMF, Ld/Lq, main transient run) matching GUI behaviour. The observer argument (api_dependencies.SolverObserver) lets a non-console caller track progress and cancel the run.
Returns: dict with "exit_code", "nOfSimSteps", "residual", "maxError", "iterations", and "stages" (per-stage breakdown).
result = model.run_solver_with_monitor()
print(result["exit_code"], result["nOfSimSteps"], result["stages"])
Field probes
getFieldInPoint(fieldName, x, y, timeStamp=None) → float
Get a field value at a single point.
| fieldName | str | Magnetic: "A", "B", "Bx", "By", "H", "Hx", "Hy", "mu", "Jz", "Demag Risk". Axisymmetric: "Br", "Bz", "Hr", "Hz", "Jphi". Thermal: "T", "q", "qx", "qy", "gradT", "dT/dx", "dT/dy", "k", "qv". |
|---|---|---|
| x, y | float | Probe coordinates. |
| timeStamp | float | None | Simulation time for transient runs. None reads the currently selected step. |
b = model.getFieldInPoint("B", 12.5, 0.0)
b_at_1ms = model.getFieldInPoint("B", 12.5, 0.0, timeStamp=0.001)
getFieldOnShape(shapeName, fieldName, nSamples=1000, timeStamp=None) → list
Sample a field along a named shape.
| shapeName | str | Name of the shape to sample along. |
|---|---|---|
| fieldName | str | Field component, plus "Bn"/"Bt"/"Hn"/"Ht" for normal/tangential on the shape. |
| nSamples | int | Number of evenly spaced samples (min 3). |
| timeStamp | float | None | Simulation time; None reads the selected step. |
Returns: list of [arcLength, value] rows.
profile = model.getFieldOnShape("AirGap_Arc", "Bn", nSamples=360)
getAverageField(shapeName=None, fieldName=None, timeStamp=None) → float
Get the average of a field over a named shape ("A" = normal flux in Wb, "Bn" = average normal flux density in T), or over the whole model when shapeName is omitted.
avg_b = model.getAverageField("StatorTooth_1", "Bn")
getForce(timeStamp=None) → (Fx, Fy)
Compute the Maxwell-stress force on the body enclosed by a probe contour. Call addProbeContour first to define a closed contour in air around the body.
model.addProbeContour("circle", [0.0, 0.0, 27.0])
fx, fy = model.getForce()
addProbeContour(shapeType, param)
Define a probe contour for getFieldInContour or getForce.
| shapeType | str | "line", "arc", or "circle". |
|---|---|---|
| param | list[float] | "line": [x1, y1, x2, y2]. "arc": [x, y, radius, startAngle, extentAngle]. "circle": [cx, cy, radius]. |
model.addProbeContour("circle", [0.0, 0.0, 27.0]) # ring of air just outside the rotor
getFieldInContour(fieldName, nSamples=1000, timeStamp=None) → list
Sample a field along the probe contour defined by addProbeContour. Returns [arcLength, value] rows.
model.addProbeContour("circle", [0.0, 0.0, 27.0])
b_profile = model.getFieldInContour("Bn", nSamples=180)
clearProbeContours()
Clear all defined probe contours.
model.clearProbeContours()
Results
setSelectedSimStep(step) → int
Select which simulated time step the field probes read (0-based). Out-of-range values are clamped.
Returns: the step actually selected.
actual_step = model.setSelectedSimStep(10)
getSelectedSimStep() → int
Return the currently selected simulation step index.
step = model.getSelectedSimStep()
getSimulatedStepCount() → int
Return the number of simulated time steps in the loaded solution.
n_steps = model.getSimulatedStepCount()
for step in range(n_steps):
model.setSelectedSimStep(step)
print(model.getFieldInPoint("B", 12.5, 0.0))
getResultsInRegion(regionName, dataName, timeStamp=None)
Get a post-processed scalar or vector result for a named region.
| regionName | str | Target region. |
|---|---|---|
| dataName | str | "Flux" (Wb), "Field Energy" (J), "Force" (N, returns [Fx, Fy]), "Joule Losses" (W), "Iron AC Losses Total", "Iron AC Losses Hysteresis", "Iron AC Losses Eddy" (W). |
| timeStamp | float | None | Simulation time; None reads the selected step. |
joule = model.getResultsInRegion("StatorWinding", "Joule Losses")
fx, fy = model.getResultsInRegion("Rotor", "Force")
getResultsInCoil(coilName, dataName) → list
Get a time series for a named coil. Returns [timeStep, value] rows.
| dataName | str | "Flux Linkage" (Wb), "Voltage" (V), "Current" (A), "Losses" (W). |
|---|
flux_linkage = model.getResultsInCoil("PhaseA", "Flux Linkage")
getResultsInMotion(motionName, dataName) → list
Get a mechanical time series for a named motion. Returns [timeStep, value] rows.
| dataName | str | "Torque" (N·m, rotary), "Force" (N, thrust, linear), "Force Normal" (N, attraction, linear). Omitted/empty gives the default for the motion's kind. |
|---|
torque = model.getResultsInMotion("RotorSpin", "Torque")
getResultsInCircuitComponent(componentType, name, dataName="Current") → list
Get a circuit-component time series. Returns [timeStep, value] rows.
| componentType | str | "R", "L", "C", "SW", "VS", "CS", "VM", "coil". |
|---|---|---|
| dataName | str | "Current", "Voltage", "Node1Voltage", "Node2Voltage". |
i_r = model.getResultsInCircuitComponent("R", "R_ext", "Current")
Export & import
importDXF(dxfFilePath)
Import geometry from a DXF file into the current model. Raises FileNotFoundError if the file does not exist.
model.importDXF("C:/CAD/rotor_sector.dxf")
exportDXF(dxfFilePath)
Export the current model geometry to a DXF file.
model.exportDXF("C:/CAD/my_motor.dxf")
exportFieldData(filename)
Write the last getFieldOnShape sampling to a CSV/TXT file. Call getFieldOnShape first.
model.getFieldOnShape("AirGap_Arc", "Bn", nSamples=360)
model.exportFieldData("airgap_flux.csv")
exportCoilData(filename)
Write the selected coils' time series to a CSV/TXT file.
model.exportCoilData("phase_currents.csv")
exportProbeData(filename)
Write the selected custom field probes' time series to a CSV/TXT file.
model.exportProbeData("probe_results.csv")
renderFieldImage(fieldName, outputPath, step=0, overlayGeometry=True, fluxLines=0) → dict
Render a solved field as a PNG image — the same surface plot the GUI shows.
| fieldName | str | What to draw: "A", "B", "Bx", "By", "H", "Hx", "Hy", "mu", "Jz", "Demag Risk", "T", "q", "qx", "qy", "gradT", "dT/dx", "dT/dy", "k", "qv". |
|---|---|---|
| outputPath | str | Destination .png path. |
| step | int | Field-step index (0-based). |
| overlayGeometry | bool | Draw region/boundary outlines over the field. |
| fluxLines | int | Number of flux-line isolines to overlay (0 = none). |
Returns: dict with "path", "field", "label", "unit", "step", "steps", "time", "min", "max", "caption".
info = model.renderFieldImage("B", "field_b.png", step=0, fluxLines=20)
print(info["min"], info["max"])
Machine module
MachinesEvery method in this section requires the Machines entitlement. See Machines for the GUI workflow these methods mirror.
machinesModuleToggle(toggle)
Enable or disable the machine-design module.
model.machinesModuleToggle(True) # must precede createNewMachine
createNewMachine(machineType)
Create and initialize a new machine of the given type.
| machineType | str | "PMSM", "PMSM - Outrunner", "IM", or "IM - Outrunner". |
|---|
model.machinesModuleToggle(True)
model.createNewMachine("PMSM")
setMachineType(machineType)
Switch an existing machine to another topology. Resets the rotor to that family's default type.
model.setMachineType("PMSM - Outrunner")
setStatorType(statorType)
Set the stator tooth/slot type: "Rectangular" or "Trapezoidal".
model.setStatorType("Trapezoidal")
setRotorType(rotorType)
Set the rotor type. Valid values depend on the machine family:
| Family | Valid rotor types |
|---|---|
| PMSM | "Surface-Mounted PM", "Spoke-Type PM", "Halbach Array", "Custom Rotor" |
| PMSM - Outrunner | Same minus "Spoke-Type PM" |
| IM / IM - Outrunner | "Rectangular Tooth", "Trapezoidal Tooth", "Custom Rotor" |
model.setRotorType("Surface-Mounted PM")
setMachineGeometry(part, parameter, value)
Set a single stator or rotor geometry parameter. The model regenerates after each call.
| part | str | "stator" or "rotor". |
|---|---|---|
| parameter | str | Parameter name. Stator: "slots", "OD", "ID", "sleeveThickness", "slotWidth", "slotDepth", "slotBottomRadius", "slotOpening" (0..1), "lipRadius", "lipHeight", "lipAngle", "skewAngle", "slices". Rotor: depends on family and rotor type. Use getValidNames("stator_geometry") / getValidNames("rotor_geometry") for the current list. |
| value | float | Numeric value (lengths in model length unit, angles in degrees, counts as integers). |
model.setMachineGeometry("stator", "slots", 12)
model.setMachineGeometry("stator", "OD", 90.0)
model.setMachineGeometry("stator", "ID", 55.0)
model.setMachineGeometry("rotor", "OD", 53.0)
setWindingType(windingType, layers=None)
Set the winding distribution and layer count.
| windingType | str | "Distributed" or "Concentrated" (IM supports only Distributed). |
|---|---|---|
| layers | str | None | "Single" or "Double". |
model.setWindingType("Concentrated", layers="Double")
getParallelBranches() → list[int]
Return the allowed parallel-branch counts for the current winding. Empty if the winding type is unknown.
branches = model.getParallelBranches()
setPhaseNumber(phaseNumber)
Set the number of stator phases (1–77).
model.setPhaseNumber(3)
setTurnsPerCoil(turnsPerCoil)
Set the number of turns per coil section (≥ 1).
model.setTurnsPerCoil(24)
setSlotFill(slotFill)
Set the slot fill (copper packing) factor (0 < fill ≤ 1.0).
model.setSlotFill(0.42)
endWindingExtension(extension)
Set the end-winding axial extension length (≥ 0, model length unit).
model.endWindingExtension(8.0)
setSimParameterMachine(parameter, value=None, toggle=False)
Set a machine simulation parameter. Valid names depend on the machine family; use getValidNames("machine_sim_parameter") for the current list.
| parameter | str | Parameter name. PMSM examples: "PhaseExcitation", "enableBEMFcalculation", "SimPeriods", "SimStepsPerPeriod". IM examples: "extractIMparams", "EnableCircuitSolver". |
|---|---|---|
| value | bool | str | float | None | Value appropriate to the parameter (bool for checkboxes, str for combo choices, float for numbers). |
| toggle | bool | Legacy flag; most boolean parameters are passed through value directly. |
model.setSimParameterMachine("enableBEMFcalculation", value=True)
model.setSimParameterMachine("SimPeriods", value=4)
Machine operating point
MachinesdefineIdIqSpeed(param=[0.0, 0.0, 0.0])
Define the PMSM operating point from Id, Iq, and mechanical speed.
| param | list[float] | [Id (A), Iq (A), Speed (RPM)]. Electrical frequency is derived from speed and pole pairs. |
|---|
model.defineIdIqSpeed([0.0, 15.0, 3000.0])
defineIdIqFreq(param=[0.0, 0.0, 0.0])
Define the PMSM operating point from Id, Iq, and electrical frequency.
| param | list[float] | [Id (A), Iq (A), Frequency (Hz)]. Speed is derived from frequency and pole pairs. |
|---|
model.defineIdIqFreq([0.0, 15.0, 200.0])
defineIsSlipSpeed(param=[0.0, 0.0, 0.0])
Define the IM operating point from stator current, slip, and speed.
| param | list[float] | [Is (A), Slip (0..1), Speed (RPM)]. |
|---|
model.defineIsSlipSpeed([10.0, 0.03, 1450.0])
defineIsSlipFreq(param=[0.0, 0.0, 0.0])
Define the IM operating point from stator current, slip, and frequency.
| param | list[float] | [Is (A), Slip (0..1), Frequency (Hz)]. |
|---|
model.defineIsSlipFreq([10.0, 0.03, 50.0])
Machine results
MachinesgetAvailablePhaseNames() → list[str]
Return the list of phase names for the machine (e.g. "Phase A", "Phase B", "Phase C").
phases = model.getAvailablePhaseNames()
getAvailableRotorBarNames() → list[str]
Return the IM rotor bar names (e.g. "Bar_1", "Bar_2", …). Empty for non-IM machines.
bars = model.getAvailableRotorBarNames()
getFromPhase(parameter, phaseName) → list
Get a time series result from a named phase. Returns [timeStep, value] rows.
| parameter | str | "FluxLinkage" (Wb), "bEMF" (V), "Voltage" (V), "Current" (A), "JouleLoss" (W). |
|---|---|---|
| phaseName | str | Phase name from getAvailablePhaseNames(). |
bemf = model.getFromPhase("bEMF", "Phase A")
getRotorBarCurrent(rotorBarName) → list
Get the current series for a single IM rotor bar. Returns [timeStep, current_A] rows.
i_bar1 = model.getRotorBarCurrent("Bar_1")
getRotorBarJouleLoss(rotorBarName) → list
Get the Joule loss series for a single IM rotor bar. Returns [timeStep, loss_W] rows.
loss_bar1 = model.getRotorBarJouleLoss("Bar_1")
getVdVq(parameter) → list
Get the d- or q-axis voltage time series.
| parameter | str | "Vd" or "Vq". |
|---|
vd = model.getVdVq("Vd")
vq = model.getVdVq("Vq")
getValphaVbeta(parameter) → list
Get the alpha- or beta-axis (Clarke) voltage time series.
| parameter | str | "Valpha" or "Vbeta". |
|---|
valpha = model.getValphaVbeta("Valpha")
getIdIq(parameter) → list
Get the d- or q-axis current time series.
| parameter | str | "Id" or "Iq". |
|---|
id_series = model.getIdIq("Id")
iq_series = model.getIdIq("Iq")
getIalphaIbeta(parameter) → list
Get the alpha- or beta-axis (Clarke) current time series.
| parameter | str | "Ialpha" or "Ibeta". |
|---|
ialpha = model.getIalphaIbeta("Ialpha")
getMachineTorque() → list
Get the electromagnetic torque time series as [timeStep, torque_Nm] rows.
torque = model.getMachineTorque()
getCoggingTorque() → list
Get the cogging torque profile as [timeStep, torque_Nm] rows.
cogging = model.getCoggingTorque()
getMechanicalPower() → list
Get the mechanical power time series as [timeStep, power_W] rows.
p_mech = model.getMechanicalPower()
getActivePower() → list
Get the active (real) power time series as [timeStep, power_W] rows. P > 0 = motoring. Requires a transient simulation with current and voltage results.
p_active = model.getActivePower()
getReactivePower() → list
Get the reactive power time series as [timeStep, power_var] rows. Q > 0 = absorbed (inductive). Requires a transient simulation with current and voltage results.
q_reactive = model.getReactivePower()
getApparentPower() → list
Get the apparent power time series as [timeStep, power_VA] rows. S = sqrt(P² + Q²).
s_apparent = model.getApparentPower()
getMachineCalculatedParameter(parameter) → float
Get a single scalar from the machine “Calculated Parameters” summary. Unknown names return 0.0.
| parameter | str | See below for the recognised names. |
|---|
Recognised parameters:
| Group | Names |
|---|---|
| Operating Point | "MechSpeed", "ElecFreq", "Slip" |
| DQ (PMSM) | "Ld", "Lq", "SaliencyRatio" |
| Flux / Back-EMF | "bEMF1stHarmonicPeak", "bEMF_RMS", "bEMF_THD", "PhaseVoltageRMS" |
| Torque | "AverageTorque", "TorqueRipplePercent", "TorqueConstantKt", "RotorInertia" |
| Power / Efficiency | "ApparentPower", "ActivePower", "ReactivePower", "MechanicalPower", "Efficiency", "PowerFactor", "BackEMFConstantKe" |
| Losses | "JouleLosses", "CurrentDensity", "PhaseResistance", "SlotFillFactor", "IronLossesStator", "IronLossesRotor", "TotalLosses" |
| Flux Density | "MaxFluxDensityStatorCore", "MaxFluxDensityStatorTooth", "MaxFluxDensityRotor", "MaxFluxDensityRotorTooth" |
Note: units are as shown in the GUI summary table, which is not SI throughout — inductances in µH, resistances in mΩ, efficiency and THD in per cent.
ld = model.getMachineCalculatedParameter("Ld")
lq = model.getMachineCalculatedParameter("Lq")
avg_torque = model.getMachineCalculatedParameter("AverageTorque")
Machine tools
MachinesgetWindingFactor(nSlots, polePairs, phases=3, doubleLayer=True) → dict
Compute the fundamental winding factor for a slot/pole/phase combination.
Returns: dict with "feasible" (bool), "windingFactor" (kw1), "q" (slots per pole per phase), and "reason".
wf = model.getWindingFactor(nSlots=12, polePairs=5, phases=3, doubleLayer=True)
print(wf["windingFactor"], wf["feasible"])
applySlotPoleCombination(slots, polePairs)
Apply a slot/pole combination and rebuild the winding (the S/P Advisor's “apply”). Destructive: the winding is regenerated from scratch.
model.applySlotPoleCombination(slots=12, polePairs=5)
runGeometrySanityCheck() → list[str]
Run the machine geometry sanity check. Returns a list of formatted finding strings; empty when the geometry is clean.
findings = model.runGeometrySanityCheck()
if findings:
print("\n".join(findings))
geometrySanityCheckHasErrors() → bool
Return True when the last sanity check found at least one error.
model.runGeometrySanityCheck()
if model.geometrySanityCheckHasErrors():
raise RuntimeError("geometry is not clean")
checkSimulationFeasibility(verbose=True) → dict
Run the GUI's pre-simulation gate headless. Returns whether the machine geometry is physically realizable. Non-machine models always come back feasible.
Returns: dict with "canSimulate", "hasErrors", "hasWarnings", "findings" (list of dicts), "errors", "warnings", "summary".
feasibility = model.checkSimulationFeasibility()
if not feasibility["canSimulate"]:
print(feasibility["errors"])
getGeometryFindings() → list[dict]
Return the findings from the last feasibility check without re-auditing. Free to call (no side effects).
findings = model.getGeometryFindings()
getIMEquivalentCircuit() → dict
PerformanceReturn the extracted IM T-equivalent-circuit parameters.
Returns: dict with "valid" (bool), "Rs" (mΩ), "Ls" (H), "Rr" (mΩ), "Lr" (µH), "Lm" (µH). All zero when the extraction has not run.
ec = model.getIMEquivalentCircuit()
if ec["valid"]:
print(ec["Rs"], ec["Lm"])
getReportPerfItemNames() → list[str]
Return the performance-analysis item names the PDF report can include for this model.
items = model.getReportPerfItemNames()
createReport(outputPath, sections=None, waveforms=None, fields=None, perfItems=None, useFinalStep=True, fieldStep=0)
Generate the machine PDF report (Model → Create Report). Requires a completed machine solve.
| outputPath | str | Destination .pdf path. |
|---|---|---|
| sections | list[str] | None | Sections to include: "PARAMETER_TABLE", "KEY_WAVEFORMS", "LOSS_SUMMARY", "PERFORMANCE_ANALYSIS", "FIELD_IMAGES". None = all. |
| waveforms | list[str] | None | Waveforms: "BEMF", "FLUX_LINKAGE", "PHASE_VOLTAGE", "PHASE_CURRENT", "TORQUE", "COGGING_TORQUE", and others. None = none. |
| fields | list[str] | None | Field images: "VECTOR_POTENTIAL", "B_MAGNITUDE", "BX", "BY", etc. None = none. |
| perfItems | list[str] | None | Performance items by display name. None = none. |
| useFinalStep | bool | Render field images at the last step (True) or at fieldStep (False). |
| fieldStep | int | Step index used when useFinalStep is False. |
Raises: RuntimeError when generation fails.
model.createReport(
"motor_report.pdf",
sections=["PARAMETER_TABLE", "KEY_WAVEFORMS", "LOSS_SUMMARY"],
waveforms=["BEMF", "TORQUE"],
fields=["B_MAGNITUDE"])
Thermal module
ThermalEvery method in this section requires the Thermal entitlement, except isThermalEnabled, setActiveResultsPhysics and getActiveResultsPhysics which are always available. See Thermal for the conceptual guide.
setThermalEnabled(enabled=True)
Enable or disable the Thermal Module (Addons → Thermal Module). Must be enabled before assigning thermal BCs or region properties.
model.setThermalEnabled(True)
isThermalEnabled() → bool
Return True when the Thermal Module is enabled for this model.
if not model.isThermalEnabled():
model.setThermalEnabled(True)
thermalSolverSettings(analysisType="Steady State", timeStep=1.0, nOfSimSteps=50, T0=20.0, scheme="Backward Euler", cnStartupSteps=2)
Configure the thermal analysis. Independent of the electromagnetic time settings.
| analysisType | str | "Steady State" or "Transient". |
|---|---|---|
| timeStep | float | Transient time step in seconds. Ignored for steady state. |
| nOfSimSteps | int | Number of records including t = 0 (min 2). Ignored for steady state. |
| T0 | float | Uniform initial temperature in °C. |
| scheme | str | "Backward Euler" (1st order) or "Crank-Nicolson" (2nd order). |
| cnStartupSteps | int | Leading BE steps for Crank-Nicolson (Rannacher startup). |
model.thermalSolverSettings(analysisType="Transient", timeStep=1.0, nOfSimSteps=60, T0=25.0)
setThermalRegionProperty(regionName, propName, value)
Set one thermal property of a region.
| propName | str | "k" (W/(m K)), "rho" (kg/m³), "cp" (J/(kg K)), "q" (W/m³), "T0" (°C). |
|---|---|---|
| value | float | Numeric value. |
model.setThermalRegionProperty("StatorWinding", "k", 1.5)
model.setThermalRegionProperty("StatorWinding", "rho", 8000.0)
model.setThermalRegionProperty("StatorWinding", "q", 850000.0)
getThermalRegionProperty(regionName, propName) → float
Read back a thermal property of a region. Returns NaN when the region or property is unknown.
k = model.getThermalRegionProperty("StatorWinding", "k")
defineHeatSource(name, sourceValues)
Define a time-dependent volumetric heat source q(t). A region with an assigned heat source ignores its constant "q".
| name | str | Heat-source name. |
|---|---|---|
| sourceValues | list | Strictly time-increasing [time_s, q_W_per_m3] pairs. |
model.defineHeatSource("LoadProfile", [[0.0, 0.0], [10.0, 900000.0], [60.0, 900000.0]])
assignHeatSourceToRegion(regionName, heatSourceName)
Assign a defined heat source to a region. Unknown names are ignored.
model.assignHeatSourceToRegion("StatorWinding", "LoadProfile")
getHeatSourceNames() → list[str]
Return the names of all defined heat sources.
sources = model.getHeatSourceNames()
runThermalSolver(observer=NULL_OBSERVER) → int
Run a thermal simulation and load the thermal results. Call generateMesh() first. After this returns, field probes accept thermal aliases ("T", "q", "qx", etc.).
model.generateMesh()
exit_code = model.runThermalSolver()
setActiveResultsPhysics(physics)
Switch loaded results between magnetic and thermal solution files.
| physics | str | int | "magnetic" (0) or "thermal" (1). |
|---|
model.setActiveResultsPhysics("thermal")
hotspot = model.getFieldInPoint("T", 0.0, 27.0)
model.setActiveResultsPhysics("magnetic")
getActiveResultsPhysics() → int
Return 0 when magnetic results are loaded, 1 for thermal.
physics = model.getActiveResultsPhysics() # 0 = magnetic, 1 = thermal
Materials editor
These methods mirror the GUI's material-editing tabs (BH Curve, Specific Losses, Additional). Every mutating call rewrites the material's CSV file and re-applies the material to regions bound to it. The material editor is free on every tier, so all of these methods are always available.
getMaterialNames() → list[str]
Return every material in the currently loaded library as "group/name" strings.
names = model.getMaterialNames()
createMaterial(materialName)
Create an empty user material in the "user_materials" group. Populate it with setBHCurve / setSpecificLosses / setMaterialAdditional. Existing names are rejected.
model.createMaterial("MyLamination")
materialHasBHCurve(materialName) → bool
Return True when the material has a usable nonlinear BH curve (a material without one is treated as linear).
if model.materialHasBHCurve("M19_29Ga"):
model.assignRegionProperties("StatorCore", "EnableAnisotropy")
setBHCurve(materialName, H, B)
Replace the material's BH curve. H in A/m, B in T; must be equal-length, non-empty lists.
model.setBHCurve("MyLamination",
H=[0, 100, 500, 2000, 10000],
B=[0.0, 0.5, 1.2, 1.55, 1.75])
getBHCurve(materialName) → list
Return the material's BH curve as [H, B] rows. Empty if none.
bh = model.getBHCurve("M19_29Ga")
setSpecificLosses(materialName, frequency, flux, loss)
Replace the specific-loss table. frequency in Hz, flux in T, loss in W/kg. All three lists must be the same length.
model.setSpecificLosses("MyLamination",
frequency=[50, 50, 400, 400],
flux=[1.0, 1.5, 1.0, 1.5],
loss=[1.2, 2.8, 9.5, 22.0])
getSpecificLosses(materialName) → list
Return the specific-loss table as [f, B, loss] rows.
losses = model.getSpecificLosses("M19_29Ga")
setMaterialAdditional(materialName, Br, Hc, mu=1.0)
Set the hard-magnet properties. When Hc > 0, mu is recomputed from Br = mu · μ₀ · Hc and the supplied mu is ignored. Pass Hc = 0 to set a linear material's μ directly.
| Br | float | Remanence in T. |
|---|---|---|
| Hc | float | Coercivity in A/m. |
| mu | float | Relative permeability (used only when Hc = 0). |
model.setMaterialAdditional("N42", Br=1.29, Hc=955000.0)
getMaterialAdditional(materialName) → list
Return [Br, Hc, mu] from the Additional section.
br, hc, mu = model.getMaterialAdditional("N42")
setMaterialColor(materialName, r, g, b, a=1.0)
Set the display colour (RGBA, each 0..1) of regions bound to this material.
model.setMaterialColor("MyLamination", 0.6, 0.6, 0.65, 1.0)
setDemagCurve(materialName, T, H, B)
Replace the demagnetization table driving the Demag Risk postprocessing. All three lists must be the same length. T in °C, H in A/m (normally negative), B in T.
model.setDemagCurve("N42",
T=[20, 100, 150],
H=[-950000, -650000, -450000],
B=[0.0, 0.0, 0.0])
getDemagCurve(materialName) → list
Return the demagnetization table as [T, H, B] rows.
demag = model.getDemagCurve("N42")
syncRegionsToMaterial(materialName) → int
Re-apply an edited material to every region bound to it. Returns the number of regions updated.
model.setBHCurve("MyLamination", H=[0, 100, 2000], B=[0.0, 0.5, 1.6])
updated = model.syncRegionsToMaterial("MyLamination")
fitIronLossCoefficients(regionName, csvPath) → list
Fit Steinmetz coefficients from a specific-loss CSV onto a region. Returns [Kh, alpha, Ke]; zeros when the fit fails.
kh, alpha, ke = model.fitIronLossCoefficients("StatorCore", "m19_losses.csv")
steinmetzLoss(Kh, alpha, Ke, f, B) → float
Evaluate the Steinmetz model: Kh·f·Balpha + Ke·(f·B)² in W/kg. Pure computation, no model interaction.
loss_w_per_kg = model.steinmetzLoss(Kh=0.02, alpha=1.8, Ke=0.0001, f=400.0, B=1.2)
Performance analysis
PerformanceEvery method in this section requires the Performance entitlement. Requires a completed machine solve — the analysis is built on extracted machine parameters. See Machines for the GUI workflow.
getPerformanceItemNames() → list[str]
Return the analysis items available for this machine family. PMSM models expose dq items (MTPA, Torque vs Load Angle); IM models expose slip-based ones (Torque vs Slip, Iph vs Slip).
items = model.getPerformanceItemNames()
setPerformanceInput(name, value)
Set one performance-analysis input. Voltage entries are mutually consistent: setting any of "Vdc", "Vph" or "Vll" recomputes the other two through the stator connection.
| name | str | "Vdc", "Vph", "Vll", "CurrentLimitIph", "MaxSpeedRPM", "SpeedRPM", "Slip" (IM), "SpeedGridSize", "TorqueGridSize", "ContourLineCount", "EfficiencyMapScaleMin", "EfficiencyMapScaleMax". |
|---|---|---|
| value | float | Numeric value. |
model.setPerformanceInput("Vdc", 400.0)
model.setPerformanceInput("CurrentLimitIph", 25.0)
getPerformanceCurve(item) → dict
Compute a line item and return its curves.
| item | str | e.g. "Max Torque vs Speed", "Efficiency vs Speed", "Torque vs Slip", "Torque vs Load Angle". |
|---|
Returns: dict with "x" (list), "series" (list of {"label", "y"}), "xLabel", "yLabel".
curve = model.getPerformanceCurve("Max Torque vs Speed")
print(curve["xLabel"], curve["series"][0]["label"])
getEfficiencyMap() → dict
Compute the efficiency map over the drive torque-speed envelope. Returns "rows", "cols", "values" (row-major grid, %), speed/torque extents, colour-scale range, and drive envelope.
eff_map = model.getEfficiencyMap()
print(eff_map["rows"], eff_map["cols"])
getLossMap() → dict
Compute the loss map over the drive torque-speed envelope. Same shape as getEfficiencyMap(); values in watts.
loss_map = model.getLossMap()
getLossBreakdown(item=None) → list[dict]
Compute a component breakdown pie. Returns [{"label": str, "value": float}, …].
| item | str | None | "Loss Breakdown" (PMSM default), "Loss Breakdown vs Speed" (IM default), "Loss Breakdown vs Slip", "Machine Mass" (values in kg). None picks the family default. |
|---|
breakdown = model.getLossBreakdown("Loss Breakdown")
for item in breakdown:
print(item["label"], item["value"])
getMachineMass() → list[dict]
Compute the machine mass distribution. Equivalent to getLossBreakdown("Machine Mass").
mass = model.getMachineMass()
getTrajectory(item="MTPA Trajectory") → dict
Compute a PMSM id-iq locus. PMSM only.
| item | str | "MTPA Trajectory" or "MTPV Trajectory". |
|---|
Returns: dict with "id" and "iq" lists (A).
mtpa = model.getTrajectory("MTPA Trajectory")
print(mtpa["id"], mtpa["iq"])