CSXCAD

Initialization

An openEMS simulation always starts by creating a 3D model of the structure using the CSXCAD library. All created entities for the simulation are stored in the csx data structure (or Python object), which must be initialized first. It’s done by either the InitCSX() method in Matlab/Octave, or the ContinuousStructure class in Python:

csx = InitCSX();

Once initialized, 3D models can be created by the functions provided by CSXCAD. In Matlab/Octave, nearly all of them accept an old instance of the csx data structure, and returns a modified new one. The Python binding has a more modern coding style in comparison, which achieves this via class methods rather than functions:

% create a property (e.g. AddMetal, AddMaterial)
csx = AddExample(csx, arg1, arg2, arg3, ...)

These CSXCAD functions can be classified into two types, primitives and properties. They define shapes and their material properties respectively.

Primitives are the building blocks to create 1D, 2D, 3D shapes at given coordinates, so that one can create a simple object at a specific position, such as a Curve, a Polygon, a Box, or a Sphere.

Properties are always created before Primitives to give physical meanings to the created objects. A property can represent an ideal or imperfect material such as a metal, a thin conducting sheet, a dielectric material, a magnetic material, a lumped circuit component (resistor, capacitor, inductor), etc. Non-physical simulation entities are also properties, such as excitation sources, probes, and field dump boxes.

More complex structures can be created by combining various primitives. If the same position contains overlapping primitives, the primitive with the highest priority takes effect. For example, a metal sheet with cylindrical holes can be achieved by creating a metal (or thin conducting sheet) property, and deriving a box primitive without holes. Then, create another material property with \(\epsilon_r = \mu_r = 1.0\), and deriving several cylinder primitives with higher priorities.

Tip

Primitives tell the simulation where objects are, properties tell the simulation what objects do, priorities tell the simulation which object “wins.”

See Properties and Primitives for details.

Coordinate Systems

By default, a Cartesian coordinate system is used, which is suitable for most simulations. If the simulated structure is predominantly circular, the Cartesian mesh may have a difficult time aligning itself with an object’s shape. Hence openEMS provides the alternative cylindrical coordinate system to minimize staircasing errors:

csx = InitCSX('CoordSystem', '1');

Note

This mainly affects mesh coordinates of the simulation box, not the coordinates of 3D model themselves. When creating 3D models, one can choose both Cartesian and Cylindrical coordinates independently from the mesh (by default, models do follow the mesh coordinate system, but it can always be overridden).

Saving

Once modeled, the CSXCAD data structure is usually saved to disk as an .xml file. It can be inspected by the AppCSXCAD 3D viewer for debugging:

AppCSXCAD simulation.xml
../_images/appcsxcad.png

AppCSXCAD, CSXCAD’s official 3D model viewer and editor.

Two forms of savings are possible: A model-only saving via CSXCAD, or a model-and-simulation save via openEMS.

Model-Only Save

To save only the geometry as a self-contained .xml file, use the struct_2_xml() function in Matlab/Octave, or the CSXCAD.ContinuousStructure.Write2XML() method in Python:

% create and edit CSX here
csx = InitCSX();

path = '/tmp';
filename = 'simulation.xml';

% create an empty data structure "output", and assign "CSX"
% to its CSXCAD attribute.
output.CSXCAD = csx;
struct_2_xml(filename, output, 'openEMS');

One can view this .xml file via AppCSXCAD, but this file cannot be used as input to the openEMS executable in replay the simulation.

Note

In the previous versions of openEMS, this was the only available saving method in Python.

Model-and-Simulation Save

To save both the geometry and simulator settings as a self-contained .xml file, the WriteOpenEMS() function in Matlab/Octave, or the openEMS.openEMS.Write2XML() method in Python.

This file contains both the geometry and simulation data, the former is owned by CSXCAD, and latter is owned by openEMS. Hence, both inputs are required:

# create and edit CSX and simulation parameters here
csx = InitCSX();
fdtd = InitFDTD();

path = '/tmp';
filename = 'simulation.xml';

% write openEMS compatible xml-file
WriteOpenEMS([path '/' filename], fdtd, csx);

Models and Simulations Reuse

It’s possible to serialize a CSXCAD model or a full openEMS simulation to .xml files on disk. The simulations can then be replayed without accessing to the original scripts.

The development of the Matlab/Octave and Python bindings took different paths, as a result, they behave differently in terms of reusing models and simulations.

Matlab/Octave

The Matlab/Octave binding was written as a pure “XML generator frontend” to CSXCAD, and the openEMS executable was used as an “executable backend”. All Matlab/Octave operations (such as geometries and simulation settings) are only wrapper to the underlying .xml file writer. Once generated, openEMS is launched and took over the simulation independently. As a result, the same .xml file can be directly used to replay the same simulation via the openEMS command-line in the future, even without the source code. For example, it can be useful for constructing a simulation on the local machine, but running simulation on a headless server:

openEMS simulation.xml

However, since the Matlab/Octave binding is only an .xml generator without direct access to CSXCAD itself, the generated .xml file is “a one-way street” that can’t be reloaded as a Matlab/Octave data structure after the fact. Source code must be kept if the generated structure or simulation needs any future modifications. Overall, the Matlab/Octave binding works like a static website generator. One can generate web pages, but can’t edit the HTML files back into the source format.

Python

In the Python binding, a decision was made to create a library-level binding to both CSXCAD and openEMS. When geometries are created, rather than generating the matching .xml code, it uses the actual funcions and internal data structures within CSXCAD and openEMS.

This design decision leads to difference consequences. Since one can start a simulation by invoking openEMS as a library directly, there’s no need to call any external command. Saving .xml files are optional. As a result, without explicitly serializing these data structures in disk as .xml files, no Python simulations can be externally viewed or replayed by default. On the other hand, if .xml files are explicitly saved, it’s possible to save and reload models and simulations thanks to the library-level access.

To save or load models, use ContinuousStructure’s Write2XML() or ReadFromXML(). To save or load both models and simulation parameters, use openEMS’s own Write2XML() and ReadFrom2XML().

In the past, Write2XML() and ReadFromXML() was only implemented in CSXCAD, not openEMS. Thus, no simulation parameters could saved or reloaded, because they were not controlled by CSXCAD. To restart a simulation from CSXCAD-only .xml file, one must manually reconstruct a openEMS instance with external simulation parameters. In the latest versions, one can save both CSXCAD and openEMS data structures using the method described above. The saved .xml simulation file can be replayed by openEMS independent of the script.

Note

Replaying Simulations. Sometimes it may be desirable to “replay” an existing simulation setup without access to the Matlab/Octave or Python source code. This is always possible in Matlab/Octave, but it’s only supported in Python if both the models and simulation parameters are saved.

Post-Processing

In all cases, post-processing simulation results always requires source code in order to parse and interpret the generated files on disk. The program openEMS itself is only a field solver engine. For analysis, we rely on Matlab/Octave or Python routines.

Interfacing CSXCAD with Third-Party Apps

If you’re developing a non-Python third-party EDA tool based on openEMS, it’s recommended to generate a simulation .xml and to call openEMS as an executable. Both the .xml file format and the CSXCAD library APIs are public by design, so it’s acceptable to either generate the .xml yourself, or invoke the CSXCAD library to do that.

On the other hand, although openEMS is also available as a shared library, but its C++ APIs are presently intended only for use by the Python binding, not public use. Breaking changes may occur at any time. Invoke openEMS as a C++ library only if you plan to follow upstream development closely.

As a result, the recommendation is to follow a hybrid of the approaches used by Matlab/Octave and Python bindings.

Import & Export

Several Matlab/Octave and Python functions are provided to import or export the CSXCAD model to other format.

Matlab/Octave

In Matlab/Octave, the following functions are available:

../_images/povray-2.4ghz-planar.png

Rendering of a 2.4 GHz planar circuit by raytracing.

Python

Unfortunately, with the exception to STL, none of the functions above have been implemented in Python. To import an external STL model, one uses an alternative method AddPolyhedronReader() from CSProperties:

import CSXCAD

csx = CSXCAD.ContinuousStructure()

# create a property (e.g. AddMetal, AddMaterial)
material = csx.AddExample(arg1, arg2, arg3, ...)

# create a special file-defined primitive
enclosure = enclosure.AddPolyhedronReader('enclosure.stl')
enclosure.ReadFile()

# can be manipulated like any other primitives
enclosure.AddTransform( ... )

Alternatively, one can use Matlab/Octave’s ImportPLY() to import a PTY model, export the result to .xml, and load the model in Python via ReadFromXML().

For exporting, AppCSXCAD itself can generate POV-Ray, STL, X3D, Polydata-VTK, and PNG file formats.

Note

Importing is easy, meshing is hard. Importing an external model is considered an advanced feature, beginners are not recommended to try them before familiarizing themselves with the CSXCAD/openEMS workflow first via basic simulations. Creating a meshing-friendly model (see Mesh) is often difficult, so a model and a mesh is usually co-developed. If a model comes from another source, yet the user is not already familiar with the meshing process and its pitfalls, confusing problems may arise.

Modeling via a GUI?

In principle, it’s feasible to make or tweak a 3D model using the AppCSXCAD GUI. Geometries and mesh lines can be added entirely by the GUI, which are then saved and read into Python later via ReadFromXML() with additional initialization and tweaks for simulations. Likewise, saving and reloading makes it possible to tune a script-generated 3D model in the AppCSXCAD GUI.

This technique circumvents programmatic modeling (as decribed in Primitives) entirely. However, it has not been put in use by anyone to our best knowledge, possibly because it’s not a coherent method. On the other hand, there are numerious attempts over the years to create models using GUI-based or high-level tools, to varying degrees of success.

The first general idea is to create the structure first in a general-purpose CAD like FreeCAD. This can then be exported as a 3D model and be loaded into CSXCAD via ImportSTL() for simulation. By editing the CSXCAD object further, ports and probes can also be modeled as a GUI.

The second general idea, specific to planar circuits and circuit board simulations, is to first create the circuit board using an EDA tool such as gEDA, pcb-rnd, or KiCad. The circuit layout can then be exported as a 2D vector image format, such as HyperLynx, Gerber, SVG or PDF. The polygons in these images are then extracted and imported as CSXCAD polygons.

The third general idea, is to create a high-level programming library for defining high-level objects such as traces, vias, circuit board layers, so that they can be created one object at a time, rather than one polygon at a time.

A common subgoal of all tools is an automatic meshing algorithm, which turned out to be far from straightforward. CSXCAD models are usually designed as highly simplified test cases to check specific design parameters. In manual modeling, geometries and meshing are co-designed to simplify each other. However, automatic meshing algorithms deal with arbitrary models imported by users, a more difficult problem.

Important

Third-Party Tools. These tools are developed by third parties, and not officially supported by the openEMS project. Most of them are highly experimental and incomplete. They’re described here for completeness. The project forum is also open to the discussions of their uses.

Importing is easy, simulation is hard. These tools should be considered advanced applications. Beginners are not recommended to try them before familiarizing themselves with the CSXCAD/openEMS workflow first via basic simulations, as described in first_lessons. Trying to import a circuit board without understanding the concept of ports, boundary conditions or meshing rules leads to failures, especially when most of these tools are highly experimental and incomplete.

Don’t work in isolation. So far there are already 7 different tools that attempt to automate modeling of circuit boards and 3D objects for openEMS. Instead of creating another one from scratch, it’s probably a good idea to have a discussion with the authors of these existing tools.

Examples of these tools include:

  • OpenEMSH, developed by Thomas Lepoix.

    • The next-generation automatic mesher for openEMS simulations, with funding from NLnet. It aims to overcome the difficulties encountered by all previous projects, but it’s still in the early-development stage.

    • https://github.com/Open-RFlab/openemsh

  • Qucs-RFlayout, developed by Thomas Lepoix.

  • FreeCAD-OpenEMS-Export, developed by Lubomir Jagos.

  • IntuitionRF, developed by Juleinn.

  • pcb2csx, developed by Evan Foss.

  • gerber2ems, developed by Antmicro.

  • pcbmodelgen, developed by jcyrax.

  • pyems, developed by Matt Huszagh.

    • It’s a high-level Python interface to openEMS, which allows the programmatic creation of high-level structures such as circuit boards, traces, vias, PCB layers. It has also an experimental auto-mesh generation algorithm.

    • https://github.com/matthuszagh/pyems

  • hyp2mat, developed by Koen De Vleeschauwer and distributed officially as part of openEMS.

    • It converts a HyperLynx layout file (can be generated by PCB EDA tools, including EAGLE or KiCad 6). The geometries are extracted to generate an Octave script with commands to create the CSXCAD model.

    • In principle, it can be used with Python as well, by exporting the model to XML in Octave via WriteOpenEMS(), and importing the model via ReadFromXML(). But no one has tested it.

    • Currently it’s retired and no longer maintained.

    • https://github.com/koendv/hyp2mat

Developer Notes

The old project wiki also described this following idea to convert circuit board layouts from Gerber, PDF, DXF, or SVG into CSXCAD models. This idea may be of interest to developers working on automated CSXCAD model generation.

Note

PCB layers in Gerber files can be converted to PDF by means of gerber2pdf which can be found on Sourceforge (editor’s note: native PDF, SVG and DXF exports are available in many EDA packages). The PDF can be imported into Inkscape just as it is the case for DXF files. Within Inkscape, the usually closed paths can be modified (either manually or with filters) such that they result in a suitable list of polygon nodes for openEMS.

Sometimes these polygons or curves have too many nodes. The number of nodes can be reduced with the Inkscape function “Path” > “Simplify”. The amount of reduction is controlled by the parameter “Simplification Threshold” which can be found under “Preferences” > “Behavior”. These paths are still Bézier curves which must be converted into polygons. This is achieved with “Extensions” > “Modify Path” > “Flatten Béziers”. The parameter in this dialog also controls the number of resulting points.

When all nodes are as required, the paths can be exported as a HTML5 Canvas. The resulting file can then be processed with an ASCII Editor. The numbers after the moveTo and lineTo statements are the polygon nodes X- and Y- coordinates respectively. However, they still must be transformed ba a liner transform given in the transform statement. The first four numbers a matrix by which the node coordinates have to be multiplied and the remaining two numbers are a vector which has to be added.

The result will be the node coordinates in HTML pixels with X counting from left to right and Y counting from top to bottom, which does not conform to the coordinate system of the Inkscape canvas.

In order to have the same axes as in Inkscape (X left to right and Y bottom to top), the fourth an sixth number have to be multiplied by -1 and the image height has to be added to the sixth number. Now the coordinates are in the usual coordinate system but still in HTML5 pixels. The ratio of pixels to mm or other units of length can finally be found under the document properties in Inkscape. This factor can be applied in Octave/Matlab. This finally gives polygons which can be processed by openEMS.