1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
|
// -*- Mode: C++; tab-width: 2; -*-
// vi: set ts=2:
//
// $Id: steepestDescent.C,v 1.27.26.5 2007/05/18 10:58:39 aleru Exp $
//
#include <BALL/MOLMEC/MINIMIZATION/steepestDescent.h>
#include <BALL/MOLMEC/COMMON/forceField.h>
#include <BALL/MOLMEC/COMMON/snapShotManager.h>
#include <limits>
//#define BALL_DEBUG
#undef BALL_DEBUG
using namespace std;
namespace BALL
{
// Default constructor
SteepestDescentMinimizer::SteepestDescentMinimizer()
: EnergyMinimizer(),
line_search_()
{
line_search_.setMinimizer(*this);
}
// Copy constructor
SteepestDescentMinimizer::SteepestDescentMinimizer
(const SteepestDescentMinimizer & minimizer)
: EnergyMinimizer(minimizer),
line_search_(minimizer.line_search_)
{
line_search_.setMinimizer(*this);
}
// Constructor initialized with a force field
SteepestDescentMinimizer::SteepestDescentMinimizer(ForceField& force_field)
: EnergyMinimizer(),
line_search_()
{
line_search_.setMinimizer(*this);
valid_ = setup(force_field);
if (!valid_)
{
Log.error() << "SteepestDescentMinimizer: setup failed! " << std::endl;
}
}
// Constructor initialized with a force field and a snapshot manager
SteepestDescentMinimizer::SteepestDescentMinimizer(ForceField& force_field, SnapShotManager* ssm)
: EnergyMinimizer(),
line_search_()
{
line_search_.setMinimizer(*this);
valid_ = setup (force_field, ssm);
if (!valid_)
{
Log.error() << "SteepestDescentMinimizer: setup failed! " << std::endl;
}
}
// Constructor initialized with a force field and a set of options
SteepestDescentMinimizer::SteepestDescentMinimizer
(ForceField& force_field, const Options& new_options)
: EnergyMinimizer(),
line_search_()
{
line_search_.setMinimizer(*this);
options = new_options;
valid_ = setup (force_field, new_options);
if (!valid_)
{
Log.error() << "SteepestDescentMinimizer: setup failed! " << endl;
}
}
// Constructor initialized with a force field, a snapshot manager, and a set of options
SteepestDescentMinimizer::SteepestDescentMinimizer(ForceField& force_field,
SnapShotManager* ssm,
const Options& new_options)
: EnergyMinimizer(),
line_search_()
{
line_search_.setMinimizer(*this);
options = new_options;
valid_ = setup(force_field, ssm, new_options);
if (!valid_)
{
Log.error() << "SteepestDescentMinimizer: setup failed! " << std::endl;
}
}
// Destructor
SteepestDescentMinimizer::~SteepestDescentMinimizer()
{
}
// Assignment operator
const SteepestDescentMinimizer& SteepestDescentMinimizer::operator=
(const SteepestDescentMinimizer& minimizer)
{
if (&minimizer != this)
{
EnergyMinimizer::operator=(minimizer);
line_search_ = minimizer.line_search_;
line_search_.setMinimizer(*this);
}
return *this;
}
// virtual function for the specific setup of derived classes
bool SteepestDescentMinimizer::specificSetup()
{
// Make sure the force field is assigned and valid!
if (force_field_ == 0 || !force_field_->isValid())
{
return false;
}
// Invalidate the initial gradient in order to ensure
// its re-evaluation at the start of minimize().
initial_grad_.invalidate();
return true;
}
/* The minimizer optimizes the energy of the system
*/
bool SteepestDescentMinimizer::minimize(Size iterations, bool resume)
{
aborted_ = false;
// Check for validity of minimizer and force field
if (!isValid() || getForceField() == 0 || !getForceField()->isValid())
{
Log.error() << "SteepestDescentMinimizer: minimizer is not initialized correctly!" << std::endl;
aborted_ = true;
return false;
}
// Make sure we have something worth moving.
if (getForceField()->getNumberOfMovableAtoms() == 0)
{
return true;
}
// Some aliases
AtomVector& atoms(const_cast<AtomVector&>(getForceField()->getAtoms()));
// If the run is to be continued, don't reset the iteration counter and the initial step size
if (!resume)
{
// reset the number of iterations for a restart
setNumberOfIterations(0);
same_energy_counter_ = 0;
initial_grad_.invalidate();
current_grad_.invalidate();
// Obviously, we don't have "old" energies yet, so we initialize it a with
// sensible value. We don't need "old" gradients.
old_energy_ = std::numeric_limits<float>::max();
}
Size max_iterations = std::min(getNumberOfIterations() + iterations, getMaxNumberOfIterations());
// Save the current atom positions
atoms.savePositions();
bool converged = false;
while (!converged && (getNumberOfIterations() < max_iterations))
{
// Try to take a new step
double stp = findStep();
// Check whether we were successful.
if (stp > 0.0)
{
// Use this step as new reference step if findStep was successful
atoms.savePositions();
}
// Store the energy, there's no need to store the old gradient
old_energy_ = initial_energy_;
// Store the current gradient and energy
storeGradientEnergy();
#ifdef BALL_DEBUG
Log.info() << "SDM::minimize: end of main: current grad RMS = " << current_grad_.rms << std::endl;
#endif
// Check for convergence.
converged = isConverged() || (stp == 0.);
// Increment iteration counter, take snapshots, print energy,
// update pair lists, and check the same-energy counter
finishIteration();
if ((!converged) && (stp < 0.))
{
// Nasty case: No convergence and the step computation failed.
// We must give up:-(
aborted_ = true;
return false;
}
if (Maths::isNan(force_field_->getEnergy()))
{
aborted_ = true;
return false;
}
if (Maths::isNan(getGradient().rbegin()->x) ||
Maths::isNan(getGradient().rbegin()->y) ||
Maths::isNan(getGradient().rbegin()->z))
{
aborted_ = true;
return false;
}
if (abort_by_energy_enabled_)
{
if (force_field_->getEnergy() > abort_energy_)
{
aborted_ = true;
return false;
}
}
}
return converged;
}
double SteepestDescentMinimizer::findStep()
{
// Compute the new direction
updateDirection();
double step = 0.;
bool result = line_search_.minimize(step);
if (!result)
{
// Something went wrong.
// Some aliases
AtomVector& atoms(const_cast<AtomVector&>(getForceField()->getAtoms()));
// Just in case: force field update (to update the pair list)
atoms.resetPositions();
force_field_->update();
// Compute the initial energy and the initial forces
initial_energy_ = force_field_->updateEnergy();
force_field_->updateForces();
initial_grad_.set(force_field_->getAtoms());
direction_ = initial_grad_;
direction_.negate();
direction_.normalize();
Size iter = 0;
while ((!result) && (iter < 12))
{
// No need to assure the maximum displacement here since our
// line search pays attention to this constraint.
result = line_search_.minimize(step);
if (!result)
{
Size n = getForceField()->getNumberOfMovableAtoms();
for(Size i = 0; i < n; ++i)
{
direction_[i] *= 0.5;
}
direction_.norm *= 0.5;
direction_.rms *= 0.5;
direction_.inv_norm *= 2.;
atoms.resetPositions();
}
else
{
return step;
}
++iter;
}
// If we are here something went wrong
// Not even such scaled steepest descent steps can manage
// the line search to exit successfully?
// We must be at a local minimizer...
step_ = 0.;
}
return step;
}
void SteepestDescentMinimizer::updateDirection()
{
// If we do not have a valid gradient, recalculate the gradient, the energy,
// and the search direction
if (!initial_grad_.isValid())
{
// Compute the initial energy and the initial forces
updateEnergy();
updateForces();
// Store them for later use
storeGradientEnergy();
}
// The direction is the normalized negative gradient
direction_ = initial_grad_;
direction_.negate();
direction_.normalize();
}
} // namespace BALL
|