# Pysmo Flowsheet Optimization¶

# Autothermal Reformer Flowsheet Optimization with PySMO Surrogate Object¶

## 1. Introduction¶

This example demonstrates autothermal reformer optimization leveraging the PySMO Polynomial surrogate trainer. Other than the specific training method syntax, this workflow is identical for PySMO RBF and PySMO Kriging surrogate models. In this notebook, sampled simulation data will be used to train and validate a surrogate model. IDAES surrogate plotting tools will be utilized to visualize the surrogates on training and validation data. Once validated, integration of the surrogate into an IDAES flowsheet will be demonstrated.

# 2. Problem Statement¶

Within the context of a larger NGFC system, the autothermal reformer generates syngas from air, steam and natural gas for use in a solid-oxide fuel cell (SOFC).

## 2.1. Main Inputs:¶

- Bypass fraction (dimensionless) - split fraction of natural gas to bypass AR unit and feed directly to the power island
- NG-Steam Ratio (dimensionless) - proportion of natural relative to steam fed into AR unit operation

## 2.2. Main Outputs:¶

- Steam flowrate (kg/s) - inlet steam fed to AR unit
- Reformer duty (kW) - required energy input to AR unit
- Composition (dimensionless) - outlet mole fractions of components (Ar, C2H6, C3H8, C4H10, CH4, CO, CO2, H2, H2O, N2, O2)

```
from IPython.display import Image
Image("AR_PFD.png")
```

## 3. Training and Validating Surrogates¶

First, let's import the required Python, Pyomo and IDAES modules:

```
# Import statements
import os
import numpy as np
import pandas as pd
# Import Pyomo libraries
from pyomo.environ import ConcreteModel, SolverFactory, value, Var, \
Constraint, Set, Objective, maximize
from pyomo.common.timing import TicTocTimer
# Import IDAES libraries
from idaes.core.surrogate.sampling.data_utils import split_training_validation
from idaes.core.surrogate.pysmo_surrogate import PysmoPolyTrainer, PysmoSurrogate
from idaes.core.surrogate.plotting.sm_plotter import surrogate_scatter2D, surrogate_parity, surrogate_residual
from idaes.core.surrogate.surrogate_block import SurrogateBlock
from idaes.core import FlowsheetBlock
```

## 3.1 Importing Training and Validation Datasets¶

In this section, we read the dataset from the CSV file located in this directory. 2800 data points were simulated from a rigorous IDAES NGFC flowsheet using a grid sampling method. For simplicity and to reduce training runtime, this example randomly selects 100 data points to use for training/validation. The data is separated using an 80/20 split into training and validation data using the IDAES `split_training_validation()`

method.

```
# Import Auto-reformer training data
np.set_printoptions(precision=6, suppress=True)
csv_data = pd.read_csv(r'reformer-data.csv') # 2800 data points
data = csv_data.sample(n = 100) # randomly sample points for training/validation
input_data = data.iloc[:, :2]
output_data = data.iloc[:, 2:]
# Define labels, and split training and validation data
# note that PySMO requires that labels are passed as string lists
input_labels = list(input_data.columns)
output_labels = list(output_data.columns)
n_data = data[input_labels[0]].size
data_training, data_validation = split_training_validation(data, 0.8, seed=n_data) # seed=100
```

## 3.2 Training Surrogates with PySMO¶

IDAES builds a model class for each type of PySMO surrogate model. In this case, we will call and build the Polynomial Regression class. Regression settings can be directly passed as class arguments, as shown below. In this example, allowed basis terms span a 6th order polynomial as well as a variable product, and data is internally cross-validated using 10 iterations of 80/20 splits to ensure a robust surrogate fit. Note that PySMO uses cross-validation of training data to adjust model coefficients and ensure a more accurate fit, while we separate the validation dataset pre-training in order to visualize the surrogate fits.

Finally, after training the model we save the results and model expressions to a folder which contains a serialized JSON file. Serializing the model in this fashion enables importing a previously trained set of surrogate models into external flowsheets. This feature will be used later.

```
# capture long output (not required to use surrogate API)
from io import StringIO
import sys
stream = StringIO()
oldstdout = sys.stdout
sys.stdout = stream
# Create PySMO trainer object
trainer = PysmoPolyTrainer(input_labels=input_labels,
output_labels=output_labels,
training_dataframe=data_training)
# Set PySMO options
trainer.config.maximum_polynomial_order = 6
trainer.config.multinomials = True
trainer.config.training_split = 0.8
trainer.config.number_of_crossvalidations = 10
# Train surrogate (calls PySMO through IDAES Python wrapper)
poly_train = trainer.train_surrogate()
# create callable surrogate object
xmin, xmax = [0.1, 0.8], [0.8, 1.2]
input_bounds = {input_labels[i]: (xmin[i], xmax[i])
for i in range(len(input_labels))}
poly_surr = PysmoSurrogate(poly_train, input_labels, output_labels, input_bounds)
# save model to JSON
model = poly_surr.save_to_file('pysmo_poly_surrogate.json', overwrite=True)
# revert back to normal output capture
sys.stdout = oldstdout
# display first 50 lines and last 50 lines of output
celloutput = stream.getvalue().split('\n')
for line in celloutput[:50]:
print(line)
print('.')
print('.')
print('.')
for line in celloutput[-50:]:
print(line)
```

2023-03-04 01:46:31 [INFO] idaes.core.surrogate.pysmo_surrogate: Model for output Steam_Flow trained successfully 2023-03-04 01:46:32 [INFO] idaes.core.surrogate.pysmo_surrogate: Model for output Reformer_Duty trained successfully 2023-03-04 01:46:34 [INFO] idaes.core.surrogate.pysmo_surrogate: Model for output AR trained successfully 2023-03-04 01:46:35 [INFO] idaes.core.surrogate.pysmo_surrogate: Model for output C2H6 trained successfully 2023-03-04 01:46:37 [INFO] idaes.core.surrogate.pysmo_surrogate: Model for output C3H8 trained successfully 2023-03-04 01:46:38 [INFO] idaes.core.surrogate.pysmo_surrogate: Model for output C4H10 trained successfully 2023-03-04 01:46:40 [INFO] idaes.core.surrogate.pysmo_surrogate: Model for output CH4 trained successfully 2023-03-04 01:46:41 [INFO] idaes.core.surrogate.pysmo_surrogate: Model for output CO trained successfully 2023-03-04 01:46:43 [INFO] idaes.core.surrogate.pysmo_surrogate: Model for output CO2 trained successfully 2023-03-04 01:46:45 [INFO] idaes.core.surrogate.pysmo_surrogate: Model for output H2 trained successfully 2023-03-04 01:46:46 [INFO] idaes.core.surrogate.pysmo_surrogate: Model for output H2O trained successfully 2023-03-04 01:46:48 [INFO] idaes.core.surrogate.pysmo_surrogate: Model for output N2 trained successfully 2023-03-04 01:46:49 [INFO] idaes.core.surrogate.pysmo_surrogate: Model for output O2 trained successfully ===========================Polynomial Regression=============================================== No iterations will be run. Default parameter estimation method is used. Parameter estimation method: pyomo max_fraction_training_samples set at 0.5 Number of adaptive samples (no_adaptive_samples) set at 4 Maximum number of iterations (Max_iter) set at: 0 Initial surrogate model is of order 1 with a cross-val error of 0.000000 Initial Regression Model Performance: Order: 1 / MAE: 0.000000 / MSE: 0.000000 / R^2: 1.000000 / Adjusted R^2: 1.000000 Polynomial regression generates a good surrogate model for the input data. ------------------------------------------------- ------------------------------------------------- Best solution found: Order: 1 / MAE: 0.000000 / MSE: 0.000000 / R_sq: 1.000000 / Adjusted R^2: 1.000000 ------------------------------------------------------------ The final coefficients of the regression terms are: k | -0.0 (x_ 1 )^ 1 | 0.0 (x_ 2 )^ 1 | 1.211862 x_ 1 .x_ 2 | -1.211862 Results saved in solution.pickle ===========================Polynomial Regression=============================================== Warning: solution.pickle already exists; previous file will be overwritten. No iterations will be run. Default parameter estimation method is used. Parameter estimation method: pyomo max_fraction_training_samples set at 0.5 Number of adaptive samples (no_adaptive_samples) set at 4 Maximum number of iterations (Max_iter) set at: 0 Initial surrogate model is of order 6 with a cross-val error of 41.885963 Initial Regression Model Performance: Order: 6 / MAE: 5.014706 / MSE: 37.089720 / R^2: 0.999999 / Adjusted R^2: 0.999999 Polynomial regression generates a good surrogate model for the input data. . . . The final coefficients of the regression terms are: k | -0.824545 (x_ 1 )^ 1 | -0.118322 (x_ 2 )^ 1 | 5.876537 (x_ 1 )^ 2 | 0.11258 (x_ 2 )^ 2 | -11.25678 (x_ 1 )^ 3 | -0.578794 (x_ 2 )^ 3 | 10.555034 (x_ 1 )^ 4 | 0.839237 (x_ 2 )^ 4 | -4.895373 (x_ 1 )^ 5 | -0.584919 (x_ 2 )^ 5 | 0.898329 x_ 1 .x_ 2 | 0.044771 Results saved in solution.pickle ===========================Polynomial Regression=============================================== Warning: solution.pickle already exists; previous file will be overwritten. No iterations will be run. Default parameter estimation method is used. Parameter estimation method: pyomo max_fraction_training_samples set at 0.5 Number of adaptive samples (no_adaptive_samples) set at 4 Maximum number of iterations (Max_iter) set at: 0 Initial surrogate model is of order 1 with a cross-val error of 0.000000 Initial Regression Model Performance: Order: 1 / MAE: 0.000000 / MSE: 0.000000 / R^2: -2481854771.585894 / Adjusted R^2: 0.000000 Polynomial regression performs poorly for this dataset. ------------------------------------------------- ------------------------------------------------- Best solution found: Order: 1 / MAE: 0.000000 / MSE: 0.000000 / R_sq: -2481854771.585894 / Adjusted R^2: 0.000000 ------------------------------------------------------------ The final coefficients of the regression terms are: k | -0.0 (x_ 1 )^ 1 | -0.0 (x_ 2 )^ 1 | 0.0 x_ 1 .x_ 2 | 0.0 Results saved in solution.pickle

/home/runner/.conda/envs/idaes-env/lib/python3.8/site-packages/idaes/core/surrogate/pysmo/polynomial_regression.py:1401: UserWarning: Polynomial regression generates poor fit for the dataset warnings.warn(

## 3.3 Visualizing surrogates¶

Now that the surrogate models have been trained, the models can be visualized through scatter, parity and residual plots to confirm their validity in the chosen domain. The training data will be visualized first to confirm the surrogates are fit the data, and then the validation data will be visualized to confirm the surrogates accurately predict new output values.

```
# visualize with IDAES surrogate plotting tools
surrogate_scatter2D(poly_surr, data_training, filename='pysmo_poly_train_scatter2D.pdf')
surrogate_parity(poly_surr, data_training, filename='pysmo_poly_train_parity.pdf')
surrogate_residual(poly_surr, data_training, filename='pysmo_poly_train_residual.pdf')
```