-
Notifications
You must be signed in to change notification settings - Fork 18
The EMerge Datasets
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.
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 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:
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'] = 4A 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.
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(...).EIf 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(...).EIf 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(...).EVery 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)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
...
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 FrequencyThe 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