Heat exchanger topology optimization
Here was an optimization problem where I wanted to maximize the outlet temperature of a flow while minimizing pressure drop. The flow had a constant velocity inlet on the left and a zero pressure outlet on the right. The mass-flow-rate across the inlet was constant for every design (independent of the size of the inlet). The walls were constant temperature, and the flow was steady-state and incompressible.
My design space consisted of a 3x3 grid, with each cell having a value of either of either 0 (filled with fluid), or 1 (filled with solid). In this case there were 2^9 (512) possible designs. For more complex design spaces, such as 10x10, there are 2^100 (1.268e30) possible designs. The nubmer of possible designs is so high that it is computationally infeasible to evalulate them all so an optimization algorithm which avoids this is required.
Each design was represented by a 9-length binary string (chromosome). The picture below is of the winning chromosome, 100001100 (top row 1-0-0, middle row 0-0-1, bottom row 1-0-0):
***** IX O I XO IX O *****
The "best design"
There were many designs which were not feasible. The chromosome 111111111 was entirely solid and the chromosome 001001001 had solids blocking off all the outlets.
***** IXXXO IXXXO IXXXO ***** ***** I XO I XO I XO *****The code checked to make sure every fluid cell had access to both an inlet and an outlet, otherwise it was given an extremely low fitness (-1). This also removed designs which had "lakes", inlets which dead-ended, etc. Originally I filled in the "lakes" with solid, but later I decided to let the genetic algorithm deal with them. Below is an animation of all the valid designs (145 of them):
All of the valid designs for the 3x3 design space
I chose to use a genetic algorithm as my optimization routine. Genetic algorithms try to mimic the process of natural selection by mating and mutating designs in an iterative process. The measure of fitness is an important factor and in this case I used the (velocity-weighted) inlet pressure and outlet temperature. In my case, the higher the pressure, the lower the fitness, but the higher the temperature, the higher the fitness. The exact function I used was fitness = T - P*1e3.
First N chromosomes were randomly generated, and then their fitness was evaluated. Trivial designs such as 111111111 took no time to calculate but valid designs ran for minutes using OpenFOAM (CFD). These jobs were run in parallel on 8 CPUs.
Designs were mated using crossover and roulette wheel selection. Designs which had higher fitness were more likely mated compared to lower fitness designs. Mating involved single-point crossover where a random point was chosen and the two chromosomes exchanged at that point:
Chromosome A before crossover: 001101010
Chromosome B before crossover: 101101111
Chromosome A after crossover: 001101111
Chromosome B after crossover: 101101010
Mutation involved selecting a small percentage (.01) of chromosomes and flipping a random bit (0 to 1 or 1 to 0).
This process was repeated for a fixed number of trials. Criteria such as a certain level of fitness, or the amount of change in maximum fitness, could also have been used. Below is a graph of temperature and pressure for all of the valid designs, colored by fitness:
Graph of pressure and temperature for all designs, colored by fitness
For this fitness function, designs with high temperature and lower pressure are fitter. Test runs consisted of 8 trials of 8 chromosomes each. This resulted in a maximum of 64 calculations taking place, out of the entire design space of 512 (12.5%). In reality 64 (compute-heavy CFD) calculations did not take place because some of the designs were not valid (so no CFD run occured).
Fitness versus trial number
In each of the 5 test runs, the final result was a fitness of 309 or greater (green on the graph above). None of them however reached the global maximum fitness (309.91).
On the cloud
Due to environmental issues (the room my computer is in doesn't have an air conditioner), the calculations were done on servers rented from Amazon EC2. Using spot instances, I was able to rent 8 CPUs (instance type c4.2xlarge) for about 7-8 cents per hour, instead of the on-demand price of 44.1 cents per hour.
Because spot instances could terminate at any time, I stored the results after each trial. Upon restarting the code, previous results were imported in and the code continues from where it stopped.
The code currently generates meshes for OpenFOAM which turns whole cells on and off. The designs it creates aren't really usable for realistic fluid design. If the code was changed to say, add heated-wall cylindrical obstructions instead of solid squares, this would be more fesible. The shape and size of the obstructions could be a design variable. In addition, instead of being restricted to a grid, they could be placed anywhere. The genetic algorithm framework is general enough where it can be reused with a different design spaces or even programs (CFD, FEA, etc).
An 8x8 grid test