# Bimetallic Nanocluster Design¶

# Bimetallic Nanocluster Cohesive Energy Minimization - via Labeling¶

This notebook serves as an example application of the MatOpt framework. We consider an example optimization problem of identifying the global energy minimum bimetallic nanocluster configuration.

This is a continuation of the example given in ***Monometallic_Nanocluster_Design.ipynb***. In this example, we will show how a very similar model can be used to optimize a bimetallic cluster by "labelling" the sites of a pre-defined monometallic cluster.

For more information, see: Yin, X., et al. "Designing Stable Bimetallic Nanoclusters via an Iterative Two-Step Optimization Approach." *Molecular Systems Design & Engineering* (2021), Accepted for Publication.

## Importing Packages¶

We start by importing several standard Python modules for convienience.

```
import numpy as np
from math import sqrt
```

Then, we import the MatOpt package in its entirety.

```
from idaes.apps.matopt import *
```

## Setting Up a Material System¶

We first identify the optimal metal-independent nanocluster shape, using the code that wsas demonstrated in **Monometallic_Nanocluster_Design.ipynb**.

```
Lat = FCCLattice(IAD=1.0)
Canv = Canvas()
Canv.addLocation(np.array([0,0,0],dtype=float))
Canv.addShells(1,Lat.getNeighbors)
Atoms = [Atom('Cu')]
N = 6
m = MatOptModel(Canv,Atoms)
Vals = [sqrt(CN) for CN in range(0,13)]
BPs = [CN for CN in range(0,13)]
m.addSitesDescriptor('CNRi',bounds=(0,sqrt(12)),integer=False,
rules=PiecewiseLinear(values=Vals,
breakpoints=BPs,
input_desc=m.Ci))
m.addGlobalDescriptor('Ecoh',rules=EqualTo(SumSites(desc=m.CNRi,
coefs=(1/(N*sqrt(12))))))
m.addGlobalDescriptor('Size',bounds=(N,N),
rules=EqualTo(SumSites(desc=m.Yi)))
```

```
# This step requires the CPLEX solver
try:
D = m.maximize(m.Ecoh,tilim=100)
except Exception as err:
D = None # rest of this notebook won't do much
```

Welcome to IBM(R) ILOG(R) CPLEX(R) Interactive Optimizer Community Edition 12.9.0.0 with Simplex, Mixed Integer & Barrier Optimizers 5725-A06 5725-A29 5724-Y48 5724-Y49 5724-Y54 5724-Y55 5655-Y21 Copyright IBM Corp. 1988, 2019. All Rights Reserved. Type 'help' for a list of available commands. Type 'help' followed by a command name for more information on commands. CPLEX> Logfile 'cplex.log' closed. Logfile '/tmp/tmpt6868vkb.cplex.log' open. CPLEX> New value for absolute mixed integer optimality gap tolerance: 0 CPLEX> New value for mixed integer optimality gap tolerance: 0 CPLEX> New value for time limit in seconds: 100 CPLEX> Problem '/tmp/tmp5e789y_h.pyomo.lp' read. Read time = 0.00 sec. (0.06 ticks) CPLEX> Problem name : /tmp/tmp5e789y_h.pyomo.lp Objective sense : Maximize Variables : 426 [Nneg: 1, Fix: 1, Box: 13, Free: 157, Binary: 241, General Integer: 13] Objective nonzeros : 1 Linear constraints : 583 [Less: 515, Greater: 13, Equal: 55] Nonzeros : 1866 RHS nonzeros : 86 Variables : Min LB: 0.000000 Max UB: 12.00000 Objective nonzeros : Min : 1.000000 Max : 1.000000 Linear constraints : Nonzeros : Min : 0.04811252 Max : 12.00000 RHS nonzeros : Min : 1.000000 Max : 1.000000 CPLEX> CPXPARAM_TimeLimit 100 CPXPARAM_MIP_Tolerances_AbsMIPGap 0 CPXPARAM_MIP_Tolerances_MIPGap 0 Tried aggregator 2 times. MIP Presolve eliminated 171 rows and 147 columns. MIP Presolve modified 12 coefficients. Aggregator did 38 substitutions. Reduced MIP has 374 rows, 241 columns, and 1149 nonzeros. Reduced MIP has 156 binaries, 0 generals, 0 SOSs, and 0 indicators. Presolve time = 0.00 sec. (1.49 ticks) Found incumbent of value 0.521849 after 0.00 sec. (2.09 ticks) Probing time = 0.00 sec. (1.36 ticks) Cover probing fixed 0 vars, tightened 1 bounds. Tried aggregator 1 time. MIP Presolve eliminated 2 rows and 1 columns. Reduced MIP has 372 rows, 240 columns, and 1144 nonzeros. Reduced MIP has 156 binaries, 0 generals, 0 SOSs, and 0 indicators. Presolve time = 0.00 sec. (1.50 ticks) Probing time = 0.00 sec. (1.31 ticks) Clique table members: 880. MIP emphasis: balance optimality and feasibility. MIP search method: dynamic search. Parallel mode: deterministic, using up to 32 threads. Root relaxation solution time = 0.00 sec. (3.19 ticks) Nodes Cuts/ Node Left Objective IInf Best Integer Best Bound ItCnt Gap * 0+ 0 0.5218 2.1667 315.19% 0 0 0.9861 105 0.5218 0.9861 278 88.96% 0 0 0.6455 96 0.5218 Cuts: 68 386 23.69% * 0+ 0 0.5485 0.6455 17.68% 0 0 0.6351 99 0.5485 Cuts: 82 430 15.80% 0 0 0.6329 93 0.5485 ZeroHalf: 24 446 15.39% 0 0 0.6326 99 0.5485 ZeroHalf: 34 465 15.33% 0 0 0.6324 99 0.5485 Cuts: 22 476 15.29% 0 0 0.6067 96 0.5485 Cuts: 41 533 10.62% 0 0 0.6050 96 0.5485 ZeroHalf: 19 553 10.30% 0 0 0.6048 97 0.5485 ZeroHalf: 28 563 10.27% 0 0 0.6033 99 0.5485 ZeroHalf: 18 591 9.99% 0 0 0.6021 97 0.5485 ZeroHalf: 20 619 9.77% 0 0 0.6005 100 0.5485 ZeroHalf: 41 637 9.48% 0 0 0.5994 95 0.5485 ZeroHalf: 35 671 9.28% 0 0 0.5988 98 0.5485 ZeroHalf: 12 686 9.17% 0 0 0.5983 99 0.5485 ZeroHalf: 19 719 9.08% 0 0 0.5980 99 0.5485 ZeroHalf: 13 731 9.02% 0 0 0.5976 99 0.5485 ZeroHalf: 11 753 8.95% 0 0 0.5973 94 0.5485 ZeroHalf: 15 769 8.90% 0 0 0.5968 99 0.5485 ZeroHalf: 4 793 8.81% 0 0 0.5963 99 0.5485 ZeroHalf: 4 815 8.72% * 0+ 0 0.5500 0.5963 8.42% 0 0 cutoff 0.5500 0.5500 831 0.00% Elapsed time = 0.21 sec. (98.42 ticks, tree = 0.01 MB, solutions = 3) Clique cuts applied: 10 Implied bound cuts applied: 20 Zero-half cuts applied: 17 Lift and project cuts applied: 1 Gomory fractional cuts applied: 1 Root node processing (before b&c): Real time = 0.21 sec. (98.44 ticks) Parallel b&c, 32 threads: Real time = 0.00 sec. (0.00 ticks) Sync time (average) = 0.00 sec. Wait time (average) = 0.00 sec. ------------ Total (root+branch&cut) = 0.21 sec. (98.44 ticks) Solution pool: 3 solutions saved. MIP - Integer optimal solution: Objective = 5.5003296046e-01 Solution time = 0.22 sec. Iterations = 831 Nodes = 0 Deterministic time = 98.45 ticks (444.83 ticks/sec) CPLEX> Incumbent solution written to file '/tmp/tmp06rzq3xj.cplex.sol'. CPLEX> The solver exited normally. A feasible and provably optimal solution is available. The Design has objective: 0.5500329604578591

We take the locations from the optimal monometallic problem to initialize a ***Canvas*** object for the bimetallic case.

```
Canv = Canvas()
if D: # may be None if CPLEX was not found
for i in range(len(D)):
if(D.Contents[i] is not None):
Canv.addLocation(D.Canvas.Points[i])
Canv.setNeighborsFromFunc(Lat.getNeighbors)
```

Additionally, we create a few data structures for holding bimetallic material information. First, we make a list of multiple ***Atom*** objects that will be the building blocks of the model. Next, we specify a dictionary with the bounds to impose on composition.

```
Atoms = [Atom('Cu'),Atom('Ag')]
CompBounds = {Atom('Cu'):(3,3),
Atom('Ag'):(3,3)}
```

## Specifying an Optimization Model¶

We start by creating a ***MatOptModel*** object that will hold the information about the problem variables and constraints. At a minimum, ever model requires a Canvas object to be defined. Additionally, the list of building blocks and conformations that are present in the model should be defined.

```
m = MatOptModel(Canv,Atoms)
```

By default, several basic variables are pre-defined. See the first example, ***Monometallic_Nanocluster_Design.ipynb*** for a description of basic variables, expressions, and constraint rules.

To start, we inidcate that the choice to place an atom is fixed so that each canvas site is required to have an atom. This simplifies the problem significantly and results in a model that will seek to find the optimal labeling of metals on the nanocluster.

```
m.Yi.rules.append(FixedTo(1.0))
```

Next, we define a descriptor for the energy of bonds as a function of properties at each site. Since the locations of the atoms are fixed, the only decision is how to label each site as either Atom A or Atom B. This allows us to simplify the model and compute coefficients that rely on coordination number. In the block below, we implement the bimetallic model for bond energy defined in Yan et al., 2018.

```
GklCoefs = {(Atom('Cu'),Atom('Cu')):3.520,
(Atom('Cu'),Atom('Ag')):2.112,
(Atom('Ag'),Atom('Ag')):2.580,
(Atom('Ag'),Atom('Cu')):3.612}
BEijCoefs = {}
for i in range(len(Canv)):
CNi = sum(1 for _ in Canv.NeighborhoodIndexes[i] if _ is not None)
for j in Canv.NeighborhoodIndexes[i]:
if(j is not None):
CNj = sum(1 for _ in Canv.NeighborhoodIndexes[j] if _ is not None)
for k in Atoms:
for l in Atoms:
BEijCoefs[i,j,k,l] = GklCoefs[k,l]*1/sqrt(CNi) + GklCoefs[l,k]*1/sqrt(CNj)
m.addBondsDescriptor('BEij',
rules=EqualTo(SumBondTypes(m.Xijkl,coefs=BEijCoefs)),
symmetric_bonds=True)
```

Next, we define the cohesive energy as a sum of contributions from all BEij bond descriptors.

```
m.addGlobalDescriptor('Ecoh',rules=EqualTo(SumBonds(desc=m.BEij,
coefs=1/(N*sqrt(12)))))
```

Finally, we add constraints on the size and composition of the resulting designs.

```
m.addGlobalTypesDescriptor('Composition',bounds=CompBounds,
rules=EqualTo(SumSites(desc=m.Yik)))
```

## Solving the Model¶

Once the model is fully specified, we can optimize in light of a global descriptor. In this example, we choose to maximize the cohesive energy defined previously. Additionally, we can specify basic optimization parameters such as the time limit and memory limit* for the optimizer.

```
D = None
try:
D = m.maximize(m.Ecoh,tilim=360,trelim=4096)
except:
print('MaOpt can not find usable solver (CPLEX or NEOS-CPLEX)')
```

WARNING: DEPRECATED: SetProduct.set_tuple is deprecated. Use SetProduct.subsets() to get the operator arguments. (deprecated in 5.7) (called from /home/runner/.conda/envs/idaes-env/lib/python3.8/site- packages/idaes/apps/matopt/../matopt/opt/pyomo_modeling.py:324) Welcome to IBM(R) ILOG(R) CPLEX(R) Interactive Optimizer Community Edition 12.9.0.0 with Simplex, Mixed Integer & Barrier Optimizers 5725-A06 5725-A29 5724-Y48 5724-Y49 5724-Y54 5724-Y55 5655-Y21 Copyright IBM Corp. 1988, 2019. All Rights Reserved. Type 'help' for a list of available commands. Type 'help' followed by a command name for more information on commands. CPLEX> Logfile 'cplex.log' closed. Logfile '/tmp/tmp_n7ozdru.cplex.log' open. CPLEX> New value for absolute mixed integer optimality gap tolerance: 0 CPLEX> New value for mixed integer optimality gap tolerance: 0 CPLEX> New value for time limit in seconds: 360 CPLEX> New value for upper limit on size of tree in megabytes: 4096 CPLEX> Problem '/tmp/tmpdl2zsnb3.pyomo.lp' read. Read time = 0.00 sec. (0.01 ticks) CPLEX> Problem name : /tmp/tmpdl2zsnb3.pyomo.lp Objective sense : Maximize Variables : 71 [Nneg: 1, Fix: 2, Free: 12, Binary: 56] Objective nonzeros : 1 Linear constraints : 159 [Less: 138, Equal: 21] Nonzeros : 414 RHS nonzeros : 57 Variables : Min LB: 0.000000 Max UB: 3.000000 Objective nonzeros : Min : 1.000000 Max : 1.000000 Linear constraints : Nonzeros : Min : 0.04811252 Max : 4.064546 RHS nonzeros : Min : 1.000000 Max : 1.000000 CPLEX> CPXPARAM_TimeLimit 360 CPXPARAM_MIP_Tolerances_AbsMIPGap 0 CPXPARAM_MIP_Tolerances_MIPGap 0 CPXPARAM_MIP_Limits_TreeMemory 4096 Tried aggregator 2 times. MIP Presolve eliminated 20 rows and 15 columns. Aggregator did 6 substitutions. Reduced MIP has 133 rows, 50 columns, and 314 nonzeros. Reduced MIP has 50 binaries, 0 generals, 0 SOSs, and 0 indicators. Presolve time = 0.00 sec. (0.44 ticks) Found incumbent of value 1.546227 after 0.00 sec. (0.80 ticks) Probing time = 0.00 sec. (0.23 ticks) Tried aggregator 1 time. Reduced MIP has 133 rows, 50 columns, and 314 nonzeros. Reduced MIP has 50 binaries, 0 generals, 0 SOSs, and 0 indicators. Presolve time = 0.00 sec. (0.30 ticks) Probing time = 0.00 sec. (0.23 ticks) Clique table members: 263. MIP emphasis: balance optimality and feasibility. MIP search method: dynamic search. Parallel mode: deterministic, using up to 32 threads. Root relaxation solution time = 0.00 sec. (0.30 ticks) Nodes Cuts/ Node Left Objective IInf Best Integer Best Bound ItCnt Gap * 0+ 0 1.5462 6.5036 320.61% * 0+ 0 1.6517 6.5036 293.74% 0 0 3.2518 50 1.6517 3.2518 50 96.87% * 0+ 0 1.6556 3.2518 96.41% 0 0 2.1027 38 1.6556 Cuts: 112 96 27.01% * 0+ 0 1.6754 2.1027 25.50% 0 0 1.8260 31 1.6754 Cuts: 41 120 8.99% 0 0 1.8217 16 1.6754 ZeroHalf: 9 121 8.73% 0 0 1.8121 33 1.6754 ZeroHalf: 2 124 8.16% 0 0 1.7548 15 1.6754 Cuts: 7 126 4.74% 0 0 cutoff 1.6754 129 --- Elapsed time = 0.08 sec. (8.81 ticks, tree = 0.01 MB, solutions = 4) Clique cuts applied: 40 Implied bound cuts applied: 6 Zero-half cuts applied: 13 Lift and project cuts applied: 1 Root node processing (before b&c): Real time = 0.08 sec. (8.81 ticks) Parallel b&c, 32 threads: Real time = 0.00 sec. (0.00 ticks) Sync time (average) = 0.00 sec. Wait time (average) = 0.00 sec. ------------ Total (root+branch&cut) = 0.08 sec. (8.81 ticks) Solution pool: 4 solutions saved. MIP - Integer optimal solution: Objective = 1.6754118151e+00 Solution time = 0.08 sec. Iterations = 129 Nodes = 0 Deterministic time = 8.81 ticks (106.68 ticks/sec) CPLEX> Incumbent solution written to file '/tmp/tmplypn1ef_.cplex.sol'. CPLEX> The solver exited normally. A feasible and provably optimal solution is available. The Design has objective: 1.6754118151174977

## Processing Solutions¶

If a design was identified (optimal or otherwise), then a ***Design*** object is returned from the optimization method. The optimal design can be plotted via any of the supported parsers.

```
if D is not None:
D.toPDB('result.pdb')
```