Repository navigation
OpenEMS Workflow
openEMS is a free FDTD solver; its geometry front end CSXCAD has no STEP reader but imports STL
files, each into one material. KESE's openems preset writes one closed STL per net and per
dielectric, in mm, on KiCad's origin, with materials.json carrying εr, tanδ and conductivity.
Note
This page describes the intended route and a script skeleton built from the CSXCAD Python API. A full openEMS simulation of a KESE export, compared against a measurement, is planned but has not been done yet. Research underway.
cd Generator
python run_KESE.py Input/board.kicad_pcb --for openems --verifygives Output/board/simulation_STL/:
stl/Board_dielectric_1.stl
stl/Copper_GND.stl
stl/Copper_RF_IN.stl
stl/Copper_no_net.stl
stl/Top_Solder_Mask.stl
stl/Bottom_Solder_Mask.stl
stl/Top_Silkscreen.stl
...
materials.json
parameters.txt
report.txt
The preset sets simulation mode, STL, mm, ORIGIN = "kicad", copper per net and dielectrics per
layer, so x in the STL files equals the layout's x in mm and y is the layout's y negated (KiCad's Y axis
points down, KESE's up, with the same origin; report.txt names the origin on its origin:
line).
Consider before exporting:
-
--set Board.DIELECTRIC_GROUPING=mergedif all dielectrics share one material: one STL instead of several. - The mask and silk STL files exist too; leave them out of the simulation unless their εr matters
to you, since
materials.jsoncarries no εr for them. -
Finish.FINISH_ENABLEDis off in the preset: 80 nm gold cannot be resolved by an FDTD mesh.
For each part in materials.json, create one property from its properties and import the
STL named in file_names.stl. Metals become AddMetal (PEC) or a lossy material with
kappa = conductivity; dielectrics become AddMaterial with epsilon and a
kappa derived from tanδ at the frequency of interest. FDTD models dielectric loss as a
conductivity, which equals the loss tangent at one frequency:
| Symbol | Meaning | Source |
|---|---|---|
| equivalent conductivity of the dielectric, S/m | ||
| frequency at which the loss is matched, Hz | your choice | |
| vacuum permittivity, |
||
|
|
relative permittivity and loss tangent of the part |
materials.json: epsilon_r, loss_tangent
|
Example: FR4 with
import json, os
from CSXCAD import ContinuousStructure
from openEMS import openEMS
from openEMS.physical_constants import EPS0
run = "Output/board/simulation_STL"
mats = json.load(open(os.path.join(run, "materials.json")))
f0 = 2.45e9 # frequency used to turn tan d into a conductivity
unit = 1e-3 # the STL files are in mm (Run.UNITS = "mm")
FDTD = openEMS(NrTS=200000, EndCriteria=1e-4)
FDTD.SetGaussExcite(f0, f0)
FDTD.SetBoundaryCond(["PML_8"] * 6)
CSX = ContinuousStructure()
FDTD.SetCSX(CSX)
for name, part in mats["parts"].items():
role, pr = part["role"], part["properties"]
stl = os.path.join(run, "stl", part["file_names"]["stl"])
if not os.path.exists(stl):
continue # parts with an empty mesh have no file
if role in ("copper", "plating"):
prop = CSX.AddMetal(name) # or a lossy metal:
# prop = CSX.AddMaterial(name, kappa=pr["conductivity_S_per_m"])
elif role == "board":
kappa = 2 * 3.141592653589793 * f0 * EPS0 * pr["epsilon_r"] * pr["loss_tangent"]
prop = CSX.AddMaterial(name, epsilon=pr["epsilon_r"], kappa=kappa)
else:
continue # mask, silk: skipped here
prop.AddPolyhedronReader(stl, priority=10 if role == "board" else 20).ReadFile()
mesh = CSX.GetGrid()
mesh.SetDeltaUnit(unit)
# ... mesh lines, ports and dumps as in any openEMS script ...Points that matter:
- Priority. Copper must win over the dielectric where both claim a cell; give the metal properties the higher priority, as above.
-
Mesh lines at the copper heights.
report.txtlists the z band of every copper layer in mm (F.Cu z +0.8000 .. +0.8350 mm). Put mesh lines on those values, and on the dielectric boundaries, so the 35 µm copper is resolved or, with coarser cells, is at least on a cell boundary. openEMS's other way to avoid cells of 35 µm,AddConductingSheet, works only on flat (2D) shapes: KESE's STL parts are closed solids, so openEMS treats a sheet made from them as PEC. To use a conducting sheet, draw the copper as flat polygons at its height instead of importing its STL. -
Units.
SetDeltaUnit(1e-3)matchesRun.UNITS = "mm". If you exported in metres, use1. -
Ports. Place them in the layout's coordinates: with
ORIGIN = "kicad"the x of a pad is the same as in KiCad, and y is KiCad's y negated.
The same route exists with the Octave interface. Each call returns the updated CSX, which
must be assigned:
CSX = AddMetal(CSX, 'Copper_GND');
CSX = ImportSTL(CSX, 'Copper_GND', 20, 'stl/Copper_GND.stl');
CSX = AddMaterial(CSX, 'Board_dielectric_1');
CSX = SetMaterialProperty(CSX, 'Board_dielectric_1', 'Epsilon', 4.5, 'Kappa', kappa);In Octave the STL is read only when openEMS runs, from inside the simulation folder
(RunOpenEMS changes into Sim_Path first), so give ImportSTL an absolute path, for example
fullfile(pwd, 'Output/board/simulation_STL/stl/Copper_GND.stl'). Read the property values from
materials.json with jsondecode.
- The number of imported STL files should equal the number of parts you kept from
materials.json(a part whose mesh came out empty has an entry but no file). - View the structure with AppCSXCAD before running: every net should be a separate colour, and the vias should pass through the dielectrics.
- Run
--verifyon the export first: an STL that is not closed (closed=Falsein the Verifier) will not voxelize correctly.
| Previous | Next |
|---|---|
| KiCad And Solver Compatibility | Glossary |
Code and documentation: GNU GPL v3 or later, including material derived from KiCad · Font data: KiCad newstroke notices (see License) · Repository
Start Here
Working Principle
Features
Reference
- Parameter Reference
- Checks, Errors And Warnings
- Material Properties
- Project Output Organization
- KiCad And Solver Compatibility
- openEMS Workflow
Research And Community