Skip to content

The EMerge Datasets

Robert Fennis edited this page Jul 13, 2025 · 3 revisions

Introduction

Simulations run in EMerge return SimulationDataset objects that contain the results of your simulation. The structure of these datasets seems a bit verbose. However, there is a reason for the organization.

  • The dataset will remain valid when new physics are added such as heattransfer in Solids
  • The dataset functions for frequency sweeps, frequency sweeps plus parametric sweeps and even optimizations with unstructured data when added.

This page will explain how data is organized in the simulation dataset.

The SimulationData object

Each Simulation3D object has a single SimulationDataset instance in .data. This SimulationData object contains two attributes currently:

data.globals: dict[str, Any]
data.mw: MWData

The globals attribute is meant for users to store arbitrary parameters and data in.

WARNING: Make sure that all data you store is pickleable, otherwise saving files is not possible.

The data from Microwave simulations is stored in the .mw attribute which is a MWData object. More about that one now

The MWData object

The MWData object contains all data that is generated through a single or multiple calls to .frequency_domain() inside a simulation file.

The attributes of this dataset that are important are:

  • scalar: All scalar data such as the frequency point, S-parameters, k0 etc
  • field: The solution field. This contains the solution vector with basis-function amplitudes and the mesh needed to compute the field at various points

The scalar and field properties are both of type BaseDataset which generalizes a list of simulation results. More about the BaseDataset now:

Auxilliary data and Simulation Reports

Besides the scalar and field attributes, each physics dataset also has a place to store auxilliary data and simulation reports. The auxilliary data is imply a dict with string - value pairs and it will be created for a given set of parameters as well. In a simulation, you can access the last simulation iteration with the .last property. You can store some relevant data for your simulation such as boundary conditions that where valid at that time:

for param in mymodel.parameter_sweep(True, param=[1,2,3,4]):
    # Setup your simulation
    data = mymodel.mw.frequency_domain()
    data.last['bc'] = my_boundary_condition_data
    data.last['other_values'] = 4

The BaseDataset object

A BaseDataset object is essentially just a list of data entries. Whenever you run a frequency sweep, each sweep will store its data into a new entry in the dataset. The BaseDataset internally remembers a pairing of the simulation results itself and the variables it was simulated for. In that sense it is just a long list of datasets stored in order.

The simulation frequency is stored as a value for the freq variable. Any simulation will thus at least have an entry for the simulation frequency. If you run a parametric sweep, those variables will be added to the list of variables. Staying with just a frequency sweep for now, your data may be accessed or found as following.

By number

You can find a specific solution simply by an index

S11 = model.data.mw.scalar[2].S(1,1)
Efield = model.data.mw.field[2].interpolate(...).E

By parameter

If you have a specific frequency say 1.2GHz that you ran the simulation for, you can get the results using:

S11 = model.data.mw.scalar.select(freq=1.2e9).S(1,1)
Efield = model.data.mw.field.select(freq=1.2e9).interpolate(...).E

If you don't know exactly what the frequency value is or struggle to find it due to floating point rounding, you can approximate it using find. This will simply return the closest match

S11 = model.data.mw.scalar.find(freq=1.2e9).S(1,1)
Efield = model.data.mw.field.find(freq=1.2e9).interpolate(...).E

Very often you want to get your results in the array structure that you simulated. To do this you can generate a gridded version of your dataset. This only works for scalar data.

freqs = model.data.mw.scalar.grid.freq
S11 = model.data.mw.scalar.grid.S(1,1)

The simulation data hierarchy.

Simulation data can roughly be compartmentalized as following:

+ --------- SimulationDataset ----------------+
|+-Globals-+ +-------------MWData------------+|    
||         | | +--Scalars---+ +----Field---+ ||
||         | | |            | |            | ||
||         | | +------------+ +------------+ ||
|+---------+ +-------------------------------+|
+---------------------------------------------+

Inside each MWDat object you have the following data

   Param1=1, Param2=2:
      - dict: Data
      - scalars: BaseDataset
           +---> freq=f1: MWScalars
           +---> freq=f2: MWScalars
           |
           +---> freq=fN: MWScalars
      - field: BaseDataset
           +---> freq=f1: MWField
           +---> freq=f1: MWField
           |
           +---> freq=fN: MWField
   Param1=2, Param2=5:
      - dict: Data
      - scalars: BaseDataset
       ...

Multi-dimensional data

You can run parametric sweeps in EMerge using the .parameter_sweep() function. The first argument to the parameter sweep is the the clear_mesh argument. If your parameter sweep changes the geometry of your model, you'll have to recompute the geometry and the mesh. In this case you'll have to make sure clear_mesh=True. If you change say only boundary conditions or material properties, you can keep the mesh in which case you may set clear_mesh=False. The next step is simply a list of keyword argument. The keyword argument name will be the variable name and the value should be a list of parameters. Lets look at this example where a frequency sweep is run for some width and length parameter.

import emerge as em
import numpy as np

mm = 0.001 #define mm

width_values = np.linspace(1,10,5) * mm # 5 widths from 1mm to 10mm
length_values = np.linspace(5,20,6) *mm # 6 length values from 5mm to 20mm

m = em.Simulation3D('param_sweep')

for L, W in m.parameter_sweep(True, width=width_values, length=length_values): # YIELD IN ALPHABETICAL ORDER!!
   # define your geometry
   m.mw.set_frequency_range(1e9, 2e9, 21)
   data = m.mw.frequency_domain(...)

** WARNING **: The parameters will always be returned in alphabetical order. The order in which keyword arguments are provided cannot be guaranteed in Python.

After setting up the simulation like this, the variables W and L will assume the complete permulation set of your length and width values. You can se them to generate your geometry. Underwater, EMerge will pass these parameters to your physics module to store them into the dataset along with the frequency.

Because in this case your length vs width sweep is a structured dataset, you can grid your output data in N-dimensional arrays.

# Previous code

freq = data.scalar.axis('freq') # Only the freq values
width = data.scalar.axis('width') # Only the width axis
length = data.scalar.axis('length') # Only the length axis
S11 = data.scalar.grid.S(1,1) # The total Ndimensional dataset

S11.shape = (6,5,21) # Dims: Length x Width x Frequency

The order of the parameters is always alphabetical. However, the frequency axes will always be the last.

You can also select all datasets with a single condition using the .filter method. For example:

S11  = data.scalar.filter(freq=1.2e9)

Will Look for all datasets that have a frequency exactly equal to 1.2e9. You can select any field value using:

Ez = data.field.select(freq=1e9, width=2*mm, length=6*mm).interpolate(...).Ez

Clone this wiki locally