There is only a minor question left, pls see FIXME in the code below.
Should there be interest in merging, please let me know, I could push a PR.
Otherwise this might still help other users, hence I am posting this as an issue.
There is a problem doing so, since a CSXCAD.CSProperties.CSPropLumpedElement and other elements are stored in CSXCAD, but NOT the openEMS.ports.LumpedPort (as its own serialized object refering to the 4 objects created).
However, when reading in such XML again via ReadFromXML, this will re-create the 4 individual objects
port_resist_1
port_excite_1
port_ut_1
port_it_1
but it will not re-created a LumpedPort!!
Specifically, it will create a CSXCAD.CSProperties.CSPropLumpedElement but NOT a openEMS.ports.LumpedPort
from typing import List
import numpy as np
from CSXCAD import ContinuousStructure
from CSXCAD.CSProperties import CSProperties, CSPropLumpedElement
from CSXCAD.CSPrimitives import CSPrimBox
from CSXCAD.Utilities import CheckNyDir
from openEMS.ports import LumpedPort
class LumpedPortExt(LumpedPort):
"""
Extension class for ``LumpedPort`` which allows to instantiate a lumped port from
the CSXCAD continuous structure (as serialized in CSXCAD XML files) based on the
port number only, and assuming element names like openEMS creates.
For example, when creating a ``LumpedPort`` with ``port_nr == 1``, this will create
the following **four** CSXCAD objects (assuming no ``PortNamePrefix`` is used):
- a *LumpedElement* element named ``"port_resist_1"``
- an *Excitation* element named ``"port_excite_1"``
- a *ProbeBox* element named ``"port_ut_1"``
- a *ProbeBox* element named ``"port_it_1"`
.. seealso::
- `openEMS.openEMS.AddLumpedPort <https://docs.openems.de/python/openEMS/openEMS.html#openEMS.openEMS.AddLumpedPort>`_ # noqa
- `openEMS.ports.LumpedPort <https://docs.openems.de/python/openEMS/ports.html#openEMS.ports.LumpedPort>`_
"""
def __init__(self, CSX: ContinuousStructure, port_nr: int, **kw):
"""
:param CSX: continuous structure
:param port_nr: port number
"""
if "PortNamePrefix" in kw:
prefix = kw["PortNamePrefix"]
else:
prefix = ""
lbl_temp = prefix + "port_{}" + "_{}".format(port_nr)
elm_name = lbl_temp.format("resist")
elm_props: List[CSProperties] = CSX.GetPropertiesByName(elm_name)
if len(elm_props) != 1:
raise RuntimeError(
'unexpected element count {} for property "{}" - there must be exactly 1'.format(
len(elm_props), elm_name
)
)
elm_prop: CSProperties = elm_props[0]
elm_type = elm_prop.GetTypeString()
match elm_type:
case "LumpedElement":
port: CSPropLumpedElement = elm_prop # noqa
assert port.GetQtyPrimitives() == 1
pbox: CSPrimBox = port.GetAllPrimitives()[0]
# FIXME:
# How to get this stuff from a CSPropLumpedElement originally created from a LumpedPort, and
# saved to CSXCAD file?
# This isn't stored in CSXCAD at all, right?
excite = 1.0
if "priority" not in kw:
kw["priority"] = 5
# we cannot just call our direct base class constructor, since that will create new
# object in the CSX continuous structure - which we do not want, since the premise is
# our CSX has already all objects, e.g. like what ReadFromXML() does create. we _only_ want
# to create a LumpedPort Python side wrapping object
#
# super(LumpedPortExt, self).__init__(
# CSX,
# port_nr=port_nr,
# R=port.GetResistance(),
# start=pbox.GetStart(),
# stop=pbox.GetStop(),
# exc_dir=port.GetDirection(),
# **kw,
# )
#
# because of above, we call the indirect base constructor (Port.__init__) and
# manually initialize the LumpedPort object
#
super(LumpedPort, self).__init__(
CSX, port_nr=port_nr, start=pbox.GetStart(), stop=pbox.GetStop(), excite=excite, **kw
)
self.R = port.GetResistance()
self.exc_ny = CheckNyDir(port.GetDirection())
self.direction = np.sign(self.stop[self.exc_ny] - self.start[self.exc_ny])
if not self.start[self.exc_ny] != self.stop[self.exc_ny]:
raise Exception("LumpedPort: start and stop may not be identical in excitation direction")
self.U_filenames = [
self.lbl_temp.format("ut"),
]
self.I_filenames = [
self.lbl_temp.format("it"),
]
case "Metal":
raise NotImplementedError("Metal property not implemented")
case _:
raise RuntimeError('unexpected property type "{}" for property "{}"'.format(elm_type, elm_name))
I want to round-trip openEMS simulations via CSXCAD files stored via
Write2XML
and re-created usingReadFromXML
.Doing so required me to create a custom class LumpedPortExt - code is below.
I have tested this with the CSXCAD created from the tutorial "Simple Patch" antenna: https://gist.github.com/oberstet/cf93ac19332ccbedeb1d414b461b9692
There is only a minor question left, pls see FIXME in the code below.
Should there be interest in merging, please let me know, I could push a PR.
Otherwise this might still help other users, hence I am posting this as an issue.
There is a problem doing so, since a CSXCAD.CSProperties.CSPropLumpedElement and other elements are stored in CSXCAD, but NOT the openEMS.ports.LumpedPort (as its own serialized object refering to the 4 objects created).
When originally calling AddLumpedPort, e.g. as in Simple Patch Antenna
this will actually create 4 objects in the CSXCAD continuous structure
which gets serialized into XML via Write2XML like so
However, when reading in such XML again via ReadFromXML, this will re-create the 4 individual objects
but it will not re-created a LumpedPort!!
Specifically, it will create a CSXCAD.CSProperties.CSPropLumpedElement but NOT a openEMS.ports.LumpedPort