2D example

In this example, we look at how the package can be used to identify a two-dimensional design space. Afterwards, investigation of nominal points can be carried out by identifying acceptable operating regions.

[1]:
# Imports
from dside import DSI
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

The problem

The design space identification technique used here is based on point cloud analysis using alpha shapes. The input into the DSI entity is simply a pandas dataframe containing the design inputs and the outputs (key performance indicators - KPIs). Here, we consider an example problem where we will be generating our input and outputs using some simple non-linear constraints.

[2]:
# ----- Generate Inputs ----- #
def Sobol_sequence(lbd, ubd, power_no):
    """
    Create 2^power_no of inputs for sampling based on the lists
    of lbd (lower bound) and ubd (upper bound).
    """
    from scipy.stats import qmc
    sampler = qmc.Sobol(d = len(lbd), scramble = False)
    inputs = sampler.random_base2(m = power_no)
    inputs = qmc.scale(inputs, lbd, ubd)
    return inputs

# Preparing example dataset
DI1_bounds    = [-10, 10]
DI2_bounds    = [-10, 10]

lbd = [DI1_bounds[0], DI2_bounds[0]]
ubd = [DI1_bounds[1], DI2_bounds[1]]

# Use Sobol sequence to generate inputs
pwr = 10
inputs = Sobol_sequence(lbd, ubd, pwr)
DI1 = inputs[:, 0]
DI2 = inputs[:, 1]

# ----- Create Outputs and dataframe ----- #
# Create example KPIs
KPI1 = DI1**2 + DI2*DI1 - DI2**2
KPI2 = DI1**2 + DI2*DI1 + DI2**2

d = np.array([DI1, DI2, KPI1, KPI2]).T
l = ['DI1', 'DI2', 'KPI1', 'KPI2']
data = pd.DataFrame(d, columns = l)
print(data.head(5))
    DI1   DI2    KPI1    KPI2
0 -10.0 -10.0  100.00  300.00
1   0.0   0.0    0.00    0.00
2   5.0  -5.0  -25.00   25.00
3  -5.0   5.0  -25.00   25.00
4  -2.5  -2.5    6.25   18.75

So we have created a dataframe containing the design inputs: DI1 & DI2 with the outputs: KPI1 & KPI2. Let’s visualize our data by creating some plots.

[3]:
fs = 14
fig, ax = plt.subplots(1, 2, figsize = (8, 3))
ax1 = ax[0].scatter(DI1, DI2, c = KPI1, marker = '.')
cbar1 = fig.colorbar(ax1, ax = ax[0])
ax[0].set_title('KPI1', fontsize = fs)
ax[0].set_xlabel('DI1', fontsize = fs)
ax[0].set_ylabel('DI2', fontsize = fs)
ax[0].set_yticks([-10, -5, 0, 5, 10])

ax2 = ax[1].scatter(DI1, DI2, c = KPI2, marker = '.')
cbar2 = fig.colorbar(ax2, ax = ax[1])
ax[1].set_title('KPI2', fontsize = fs)
ax[1].set_xlabel('DI1', fontsize = fs)
ax[1].set_ylabel('DI2', fontsize = fs)
ax[1].set_yticks([-10, -5, 0, 5, 10])
plt.tight_layout()
plt.show()
../_images/notebooks_example_2D_5_0.png

Design Space Identification

As shown from the plots, we can see clearly that the outputs are non-linear with respect to the design inputs. However, the design space identification method used in the package allows for the identification of non-convex hulls using alpha shapes. So let’s go ahead and identify our design space by first defining some constraints on the KPIs.

[4]:
# Define our design input labels
vn = ['DI1', 'DI2']
# Define the KPI constraints as a dictionary
# the keys corresponds to the column of the dataframe
constraints = {'KPI1': [-50, 1e20], 'KPI2': [-1e20, 100]}

# Initialize the design space entity
ds = DSI(data)
# Apply the constraints separating the satisfied and violated points
ds.screen(constraints)
# Finding the design space (alpha shape)
ds.find_DSp(vn)
# Plot the design space
ds.plot()
plt.show()

# Check if there are any violated points in the design space
print('Check for violations inside design space:')
print(ds.vindsp)
Bisection search for alpha multiplier (radius)
    tol: 1.00e-03  maxiter: 50
    lb:  1.00e-30  ub:      5.00e+00
    maxvp: 0.00e+00  maxvnum: 0.00
____________________________________________________________________________________________
 No iter  |  alpha multiplier  |   Violation Flag   | Number vio inside  |   Bisection Gap
____________________________________________________________________________________________
    1     |     2.500e+00      |        True        |         66         |     2.500e+00
    2     |     1.250e+00      |        True        |         66         |     1.250e+00
    3     |     6.250e-01      |        True        |         66         |     6.250e-01
    4     |     3.125e-01      |        True        |         66         |     3.125e-01
    5     |     1.562e-01      |        True        |         66         |     1.562e-01
    6     |     7.812e-02      |        True        |         66         |     7.812e-02
    7     |     3.906e-02      |        True        |         66         |     3.906e-02
    8     |     1.953e-02      |        True        |         66         |     1.953e-02
    9     |     9.766e-03      |        True        |         66         |     9.766e-03
    10    |     4.883e-03      |        True        |         34         |     4.883e-03
    11    |     2.441e-03      |       False        |         0          |     2.441e-03
    12    |     3.662e-03      |        True        |         13         |     1.221e-03
    13    |     3.052e-03      |        True        |         12         |     6.104e-04
    14    |     2.747e-03      |        True        |         5          |     3.052e-04
    15    |     2.594e-03      |        True        |         5          |     1.526e-04
    16    |     2.518e-03      |       False        |         0          |     7.629e-05
____________________________________________________________________________________________
[16] Optimal amul: 2.5177e-03   alpha: 2.508e-01
Tol: 1.000e-03   Bisection Gap: 7.629e-05
vnum: 0   maxvnum: 0.00
========================================== Results =========================================
No. satisfied points            : 641
No. violated points             : 383
No. violated points inside DSp  : 0
No. of regions                  : 1
No. of simplices                : 1237
Size of design space            : 2.410e+02
../_images/notebooks_example_2D_7_1.png
Check for violations inside design space:
Empty DataFrame
Columns: [DI1, DI2, KPI1, KPI2, SatFlag]
Index: []

As you can see, the alpha shapes can capture the non-convexity of the design space without any violated points inside the design space. Here, we used a bisection search to find the alpha radius value which gives no violation inside our design space.

We can check the details of the identified design space by calling ds.report and also ds.send_output() to output a text file with the details.

[5]:
# Print out the size of the identified design space
print('Design space size:', ds.report['space_size'])

# Output the details about the design space as a .txt file
ds.send_output('example_2D_1')
Design space size: 240.96145629882812

Further Analysis

We can also use the same dataset with different constraints to identify a different design space. This time, lets print out the alpha radius search iterations by giving some options.

[6]:
# New constraints
constraints = {'KPI1': [1, 1e20], 'KPI2': [-1e20, 70]}

# Initialize the design space entity
ds = DSI(data)
# Apply the constraints separating the satisfied and violated points
ds.screen(constraints)
# Finding the design space (alpha shape)
ds.find_DSp(vn, opt = {'printF': True})
# Plot the design space
ds.plot()
plt.show()

# Check if there are any violated points in the design space
print('Check for violations inside design space:')
print(ds.vindsp)

# The framework is also able to handle with disjointed spaces
print('Number of regions:', ds.report['no_reg'])
Bisection search for alpha multiplier (radius)
    tol: 1.00e-03  maxiter: 50
    lb:  1.00e-30  ub:      5.00e+00
    maxvp: 0.00e+00  maxvnum: 0.00
____________________________________________________________________________________________
 No iter  |  alpha multiplier  |   Violation Flag   | Number vio inside  |   Bisection Gap
____________________________________________________________________________________________
    1     |     2.500e+00      |        True        |        179         |     2.500e+00
    2     |     1.250e+00      |        True        |        179         |     1.250e+00
    3     |     6.250e-01      |        True        |        179         |     6.250e-01
    4     |     3.125e-01      |        True        |        179         |     3.125e-01
    5     |     1.562e-01      |        True        |        179         |     1.562e-01
    6     |     7.812e-02      |        True        |        179         |     7.812e-02
    7     |     3.906e-02      |        True        |        179         |     3.906e-02
    8     |     1.953e-02      |        True        |        179         |     1.953e-02
    9     |     9.766e-03      |        True        |        178         |     9.766e-03
    10    |     4.883e-03      |        True        |        149         |     4.883e-03
    11    |     2.441e-03      |        True        |         67         |     2.441e-03
    12    |     1.221e-03      |        True        |         14         |     1.221e-03
    13    |     6.104e-04      |       False        |         0          |     6.104e-04
____________________________________________________________________________________________
[13] Optimal amul: 6.1035e-04   alpha: 6.080e-02
Tol: 1.000e-03   Bisection Gap: 6.104e-04
vnum: 0   maxvnum: 0.00
========================================== Results =========================================
No. satisfied points            : 260
No. violated points             : 764
No. violated points inside DSp  : 0
No. of regions                  : 2
No. of simplices                : 446
Size of design space            : 8.652e+01
../_images/notebooks_example_2D_11_1.png
Check for violations inside design space:
Empty DataFrame
Columns: [DI1, DI2, KPI1, KPI2, SatFlag]
Index: []
Number of regions: 2

In addition, we can identify acceptable operating regions with respect to any nominal point. Let’s assume we have a nominal point of interest at [-5, 0].

[7]:
# The find_AOR function plots the AOR boundary
# therefore if we call ds.find_AOR() after ds.plot()
# the boudary will be drawn over the design space plot.
x = [-5, 0]
ds.plot()
ds.find_AOR(x)
# plt.show()
ax = ds.ax
ax.set_facecolor('white')
plt.savefig('2D.svg', facecolor = 'white', transparent = False)

# Call ds.all_x[str(x)] to access the details on the AOR
print('AOR size:', ds.all_x[str(x)]['space_size'])

# When we call ds.send_output() the details of any identified
# AOR will also be outputted in the text file.
ds.send_output('example_2D_2')

AOR size: 12.352629621824462
../_images/notebooks_example_2D_13_1.png

As shown, we can identify acceptable operating regions with respect to any nominal points. This allows for the quantification of acceptable ranges with respect to the nominal point. In other words, we can obtain +- value of the design input axes where operation is guaranteed to still be inside of the design space.