Terminating a minimization based on condition

Hi all,

My specific use case is plotting a curve of volumes (and energies, etc.) over a range of hydrostatic pressures for an anisotropic crystal using an interatomic potential. Because the crystal is anisotropic, I am specifically interested in imposing a pressure, not prescribing a volume (because the cell shape may change under different hydrostatic pressures). So I use FrechetCellFilter with scalar_pressure. However, if I set the scalar pressure to a large negative number, the atoms explode (see output below). This is completely expected, of course, as I have made my system gaseous and it is incapable of sustaining the pressure I have requested, so it expands infinitely. If the minimizer simply failed, that would be fine, but what happens instead is that the OS kills the Python process, probably due to out-of-memory or something like that, leaving me unable to easily recover the results of the rest of the scan.

Is there an easy way to add a stopping condition to an optimizer, like “fail if atoms.get_volume() > max_vol” or something like that?

Of course, I can append the results to a file and collect them later, but this is going to be run in a high-throughput unattended fashion as part of OpenKIM, so I’d rather not have to wait for the optimizer to sit on a node for 20 minutes doing nothing waiting to be killed, and then have to hack something together to collect the results – it would be really nice if I could have it fail quickly.

Thanks, ilia

                 Step     Time          Energy          fmax
LBFGSLineSearch:    0 21:06:26      -26.200735        1.039986
LBFGSLineSearch:    1 21:06:26      -26.779877        2.888670
LBFGSLineSearch:    2 21:06:27     -137.851523       86.245922
LBFGSLineSearch:    3 21:06:27     -137.853801       88.184727
LBFGSLineSearch:    4 21:06:27     -259.668790      911.759389
LBFGSLineSearch:    5 21:06:27     -270.138959     1593.369838
LBFGSLineSearch:    6 21:06:27     -446.520268     5595.616385
LBFGSLineSearch:    7 21:06:27     -510.352364    11246.125066
LBFGSLineSearch:    8 21:06:27     -802.892180    33111.189731
LBFGSLineSearch:    9 21:06:27    -1004.481312    62013.657575
LBFGSLineSearch:   10 21:06:27    -1484.916299   159286.369473
LBFGSLineSearch:   11 21:06:27    -2003.022754   311379.886993
LBFGSLineSearch:   12 21:06:28    -2906.502507   789414.471683
LBFGSLineSearch:   13 21:06:28    -4368.945927  1980074.047782
LBFGSLineSearch:   14 21:06:28    -6669.446587  4286318.914054
LBFGSLineSearch:   15 21:06:28    -9534.029257  9539874.838886
LBFGSLineSearch:   16 21:06:28   -14453.260670 22139615.797655
LBFGSLineSearch:   17 21:06:28   -24786.991540 45866680.472511
LBFGSLineSearch:   18 21:06:28   -36681.858500 108838152.786941
LBFGSLineSearch:   19 21:06:28   -51358.294110 128125205.647571
LBFGSLineSearch:   20 21:06:28   -70395.187519 373445360.067087
LBFGSLineSearch:   21 21:06:28   -89737.121678 803654414.123993
LBFGSLineSearch:   22 21:06:28  -132304.653373 1913791021.406311
LBFGSLineSearch:   23 21:06:29  -164548.185979 3925149969.059348
LBFGSLineSearch:   24 21:06:29  -300813.454479 8981736893.865471
LBFGSLineSearch:   25 21:06:29  -304359.981726 11807696053.340485

I figured out a solution using a custom constraint that stalls the minimization:

class MaxVolumeConstraint:
    def __init__(self, max_volume):
        self.max_volume = max_volume
        
    def adjust_positions(self, atoms, newpositions):
        pass
    
    def adjust_forces(self, atoms, forces):
        pass
    
    def adjust_cell(self, atoms, cell):
        if cell.volume > self.max_volume:
            cell *= (self.max_volume/cell.volume)**3

Instead of running a geometry optimizer with run(), which internally loops over the steps, you can drive the loop using irun() and insert extra logic:

opt = LBGS(atoms)
for _ in opt.irun():
    if my_stopping_condition(atoms):
        break

1 Like