Closed joamatab closed 1 year ago
How can we make sure that any port convention works for 3D FDTD plugins?
https://github.com/gdsfactory/gdsfactory/discussions/502
import gdsfactory as gf
if __name__ == "__main__":
import gdsfactory.simulation.gtidy3d as gt
c = gf.components.straight(length=2)
c.unlock()
c.auto_rename_ports_layer_orientation()
gt.write_sparameters(c, run=False)
@momchil-flex @flaport @simbilod @HelgeGehring
I'd guess if we use a dict for the s-parameters, which uses the port names the problem should be solved :)
As long as the port names are associated to port objects containing the relevant properties (midpoint
, orientation
, width
, layer
, mode_cutoff
, and mode_symmetry
), we have everything we need to generate logical ports for FDTD.
I don't think we should store information about the port in its name string. A user could choose to also do that if they want e.g. with direction as E0, W0
, but it should not be required
This format could solve the issues
wavelength |port_in | port_out | mode_in | mode_out | magnitude | phase
| | | 0 | 0 | |
| | | | | |
| | | | | |
to query s12m and s12a we can write some convenience functions for each port naming convention
df.query("port_in=='o1' & port_out=='o2' ")
@flaport
How to internally represent Sparameters. Right now in CSV files.
TODO:
Convertor will allow you to convert old simulations to the new format
wavelength |port_in | port_out | mode_in | mode_out | magnitude | phase
| | | 0 | 0 | |
| | | | | |
| | | | | |
How about this?
d = hdf() d['wavelength'] = wavelength d['o1-0_o2-0'] = # port o1 mode0 to port o2 mode0, where mode_index starts in zero for fundamental mode
d['component_settings'] = component.settings d['simulation_settings'] = simulation_settings.
We could do something like this (I will open a PR tomorrow):
def old_to_new(df, port_map=None):
s_headers = sorted({c[:-1] for c in df.columns if c.lower().startswith('s')})
idxs = sorted({idx for c in s_headers for idx in _s_header_to_port_idxs(c)})
if port_map is None:
port_map = {f"o{i}@1": i for i in idxs}
rev_port_map = {i: p for p, i in port_map.items()}
assert len(rev_port_map) == len(port_map), "Duplicate port indices found in port_map"
s_map = {s: tuple(rev_port_map[i] for i in _s_header_to_port_idxs(s)) for s in s_headers}
dfs = {s: df[['wavelengths', 'freqs', f'{s}m', f'{s}a']].copy().rename(columns={f'{s}m': 'magnitude', f'{s}a': 'phase'}) for s in s_map}
for s, df_ in dfs.items():
pm1, pm2 = s_map[s]
(p1, m1), (p2, m2) = pm1.split('@'), pm2.split('@')
df_['port_in'], df_['port_out'] = p1, p2
df_['mode_in'], df_['mode_out'] = m1, m2
df = pd.concat(dfs.values(), axis=0)[['wavelengths', 'freqs', 'port_in', 'port_out', 'mode_in', 'mode_out', 'magnitude', 'phase']]
return df
def _s_header_to_port_idxs(s):
s = re.sub("[^0-9]", "", s)
inp, out = (int(i) for i in s)
return inp, out
def new_to_old(df, start_idx=1):
# just making sure...
df['mode_in'] = np.asarray(df['mode_in'].values, dtype=str)
df['mode_out'] = np.asarray(df['mode_out'].values, dtype=str)
df['port_in'] = np.asarray(df['port_in'].values, dtype=str)
df['port_out'] = np.asarray(df['port_out'].values, dtype=str)
ports_in = df['port_in'].unique()
ports_out = df['port_out'].unique()
modes_in = df['mode_in'].unique()
modes_out = df['mode_out'].unique()
ports = sorted(np.unique(np.concatenate([ports_in, ports_out])))
modes = sorted(np.unique(np.concatenate([modes_in, modes_out])))
port_modes = {f"{p}@{m}": i for i, (p, m) in enumerate(product(ports, modes), start=start_idx)}
s_map = {f"s{i}{j}": (p, q) for (p, i), (q, j) in product(port_modes.items(), port_modes.items())}
new_df = {}
df_ = None
for s, (pm1, pm2) in s_map.items():
p1, m1 = pm1.split('@')
p2, m2 = pm2.split('@')
df_ = df.query(f"port_in=={p1!r} & port_out=={p2!r} & mode_in=={m1!r} & mode_out=={m2!r}")
new_df[f"{s}a"] = df_['phase'].values
new_df[f"{s}m"] = df_['magnitude'].values
if df_ is not None:
new_df[f"wavelengths"] = df_['wavelengths'].values
new_df[f"freqs"] = df_['freqs'].values
new_df = pd.DataFrame(new_df)
return new_df, s_map
I don't exactly follow what's happening here, but we've been discussing improvements to Tidy3D's own S-matrix tool. I would suggest eventually using this inside write_sparameters
, but I guess even though port names in our tool can be whatever you want, I guess this won't exactly solve the problem as the write_sparameters
function itself will still need to change.
One other note: we're implementing our own version of the port symmetries as here, but we realized in the most general case, there can be a minus sign on some of the modes. Specifically, if a port is bisected by a symmetry line and the mode has negative symmetry w.r.t. that line. Then, the relation could be something like S10 = -S20 (e.g. in the case of a y junction where the mode of port 0 that gets split into 1 and 2 is anti-symmetric).
Nice We are trying to standardize the format where we store s parameters
What format do you plan on using for port symmetries?
What are your thoughts about hdf5, numpy or csv?
It would be great to have a standard both for tidy3d and gdsfactory that can hopefully become the standard for other tools such as circuit simulators
What I'm proposing is two converters between the proposed 'new' format and the old format. The advantage of the new format is that it's a sparse storage system and hence more flexible: you might for example be interested in a denser wavelength sampling for some port combinations (i.e. s-parameters) vs others, moreover it's easier to append port/mode combinations at a later stage which you might not have been initially interested in (just append rows).
Nice We are trying to standardize the format where we store s parameters
Yeah I think this is somewhat independent of Tidy3D internals, it will be entirely determined by gdsfactory and how it uses the solver.
What format do you plan on using for port symmetries?
Two options we are considering:
What are your thoughts about hdf5, numpy or csv?
We already store SimulationData in hdf5 and have been working on being able to export any Tidy3D model in hdf5 (similarly to how it can be exported to json). This is because we're working on adding more data to Simulation objects, e.g. custom (n, k) dependence, and json is not ideal for that. The nesting of hdf5 makes it a nice choice.
What do you think of using?
s['o1@0,o1@0'] # port o1, mode=0, to port o1 mode=0 (where mode=0 is fundamental TE for most cases)
some functions, like meep and tidy3d write sparameters assume the o1, o2, o3 ... port names.it would be great to enable other port naming conventions.
Here are some other port conventions